From 5aa336a0e3251ac88d75a66d26e48bcbc5604861 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:49:23 +0400 Subject: [PATCH 001/234] [AUTO-7471] add automation node upgrade test to nightly CI (#11469) * add automation node upgrade test to nightly CI * increase nodes to 6 for upgrade test --- .../workflows/automation-nightly-tests.yml | 108 ++++++++++++++++++ .../workflows/automation-ondemand-tests.yml | 2 +- CODEOWNERS | 1 + 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/automation-nightly-tests.yml diff --git a/.github/workflows/automation-nightly-tests.yml b/.github/workflows/automation-nightly-tests.yml new file mode 100644 index 00000000000..3c05786b666 --- /dev/null +++ b/.github/workflows/automation-nightly-tests.yml @@ -0,0 +1,108 @@ +name: Automation Nightly Tests +on: + schedule: + - cron: "0 0 * * *" # Run nightly + push: + tags: + - "*" + workflow_dispatch: + +jobs: + build-chainlink: + environment: integration + permissions: + id-token: write + contents: read + name: Build Chainlink Image + runs-on: ubuntu20.04-16cores-64GB + steps: + - name: Collect Metrics + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: Build Chainlink Image + continue-on-error: true + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Build Chainlink Image + uses: ./.github/actions/build-chainlink-image + with: + tag_suffix: "" + dockerfile: core/chainlink.Dockerfile + git_commit_sha: ${{ github.sha }} + GRAFANA_CLOUD_BASIC_AUTH: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} + AWS_REGION: ${{ secrets.QA_AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + + automation-upgrade-test: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink] + env: + CHAINLINK_COMMIT_SHA: ${{ github.sha }} + CHAINLINK_ENV_USER: ${{ github.actor }} + TEST_LOG_LEVEL: debug + strategy: + fail-fast: false + matrix: + tests: + - name: Upgrade + suite: smoke + nodes: 6 + os: ubuntu20.04-8cores-32GB + network: SIMULATED + command: -run ^TestAutomationNodeUpgrade$ ./smoke + runs-on: ${{ matrix.tests.os }} + name: Automation ${{ matrix.tests.name }} Test + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.head_ref || github.ref_name }} + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + env: + SELECTED_NETWORKS: ${{ matrix.tests.network }} + TEST_SUITE: ${{ matrix.tests.suite }} + UPGRADE_VERSION: ${{ github.sha }} + UPGRADE_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink + with: + test_command_to_run: cd ./integration-tests && go test -timeout 60m -count=1 -json -test.parallel=${{ matrix.tests.nodes }} ${{ matrix.tests.command }} 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: 'public.ecr.aws/chainlink/chainlink' + cl_image_tag: 'latest' + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + artifacts_location: ./integration-tests/${{ matrix.tests.suite }}/logs + publish_check_name: Automation Results ${{ matrix.tests.name }} + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Upload test log + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + if: failure() + with: + name: test-log-${{ matrix.tests.name }} + path: /tmp/gotest.log + retention-days: 7 + continue-on-error: true + - name: Collect Metrics + if: always() + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: Automation ${{ matrix.tests.name }} Test + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index 016a10252be..e023006d58b 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -140,7 +140,7 @@ jobs: command: -run ^TestAutomationReorg$ ./reorg - name: upgrade suite: smoke - nodes: 3 + nodes: 6 os: ubuntu20.04-8cores-32GB pyroscope_env: ci-automation-on-demand-upgrade network: SIMULATED diff --git a/CODEOWNERS b/CODEOWNERS index e34d3ea1bef..83505b0ed8c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -120,6 +120,7 @@ core/scripts/gateway @bolekk @pinebit /.github/workflows/automation-ondemand-tests.yml @smartcontractkit/keepers /.github/workflows/automation-benchmark-tests.yml @smartcontractkit/keepers /.github/workflows/automation-load-tests.yml @smartcontractkit/keepers +/.github/workflows/automation-nightly-tests.yml @smartcontractkit/keepers /core/chainlink.Dockerfile @smartcontractkit/prodsec-public From 2f17dd6d7eb54f2625d44a996a144a2bc3ea3b44 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 4 Dec 2023 09:55:58 -0600 Subject: [PATCH 002/234] core/services/relay/evm/mercury: use chainlink-data-streams (#11293) --- core/scripts/go.mod | 3 +- core/scripts/go.sum | 6 ++- .../ocr2/plugins/mercury/integration_test.go | 17 +++--- core/services/ocr2/plugins/mercury/plugin.go | 6 +-- core/services/ocrcommon/telemetry.go | 12 ++--- core/services/ocrcommon/telemetry_test.go | 12 ++--- .../services/relay/evm/mercury/transmitter.go | 4 +- .../relay/evm/mercury/v1/data_source.go | 27 +++++----- .../relay/evm/mercury/v1/data_source_test.go | 20 +++---- .../mercury/v1/reportcodec/report_codec.go | 7 ++- .../v1/reportcodec/report_codec_test.go | 9 ++-- .../relay/evm/mercury/v2/data_source.go | 21 ++++---- .../relay/evm/mercury/v2/data_source_test.go | 14 ++--- .../mercury/v2/reportcodec/report_codec.go | 6 +-- .../v2/reportcodec/report_codec_test.go | 8 +-- .../relay/evm/mercury/v3/data_source.go | 25 ++++----- .../relay/evm/mercury/v3/data_source_test.go | 8 +-- .../mercury/v3/reportcodec/report_codec.go | 7 ++- .../v3/reportcodec/report_codec_test.go | 8 +-- core/services/relay/evm/mercury_provider.go | 53 ++++++++++--------- go.mod | 3 +- go.sum | 6 ++- integration-tests/go.mod | 3 +- integration-tests/go.sum | 6 ++- 24 files changed, 151 insertions(+), 140 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 21f00df02de..4b44c7a00ec 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -303,8 +303,9 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 36504924e16..78faaf6839a 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1502,10 +1502,12 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 h1:qau0/AHvPwMR3p6gWsFWC4qVfEtSEALtBetTOpHA2IU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index e8adb55b397..51f9eaa6683 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -34,10 +34,11 @@ import ( ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/wsrpc/credentials" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaycodecv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - relaycodecv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" - relaycodecv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" + v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" + v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + relaymercury "github.com/smartcontractkit/chainlink-data-streams/mercury" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -62,7 +63,7 @@ var ( f = uint8(1) n = 4 // number of nodes multiplier int64 = 100000000 - rawOnchainConfig = relaymercury.OnchainConfig{ + rawOnchainConfig = mercurytypes.OnchainConfig{ Min: big.NewInt(0), Max: big.NewInt(math.MaxInt64), } @@ -154,7 +155,7 @@ func TestIntegration_MercuryV1(t *testing.T) { serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) serverPubKey := serverKey.PublicKey srv := NewMercuryServer(t, ed25519.PrivateKey(serverKey.Raw()), reqs, func() []byte { - report, err := (&reportcodecv1.ReportCodec{}).BuildReport(relaycodecv1.ReportFields{BenchmarkPrice: big.NewInt(234567), Bid: big.NewInt(1), Ask: big.NewInt(1), CurrentBlockHash: make([]byte, 32)}) + report, err := (&reportcodecv1.ReportCodec{}).BuildReport(v1.ReportFields{BenchmarkPrice: big.NewInt(234567), Bid: big.NewInt(1), Ask: big.NewInt(1), CurrentBlockHash: make([]byte, 32)}) if err != nil { panic(err) } @@ -503,7 +504,7 @@ func TestIntegration_MercuryV2(t *testing.T) { serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) serverPubKey := serverKey.PublicKey srv := NewMercuryServer(t, ed25519.PrivateKey(serverKey.Raw()), reqs, func() []byte { - report, err := (&reportcodecv2.ReportCodec{}).BuildReport(relaycodecv2.ReportFields{BenchmarkPrice: big.NewInt(234567), LinkFee: big.NewInt(1), NativeFee: big.NewInt(1)}) + report, err := (&reportcodecv2.ReportCodec{}).BuildReport(v2.ReportFields{BenchmarkPrice: big.NewInt(234567), LinkFee: big.NewInt(1), NativeFee: big.NewInt(1)}) if err != nil { panic(err) } @@ -779,7 +780,7 @@ func TestIntegration_MercuryV3(t *testing.T) { serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) serverPubKey := serverKey.PublicKey srv := NewMercuryServer(t, ed25519.PrivateKey(serverKey.Raw()), reqs, func() []byte { - report, err := (&reportcodecv3.ReportCodec{}).BuildReport(relaycodecv3.ReportFields{BenchmarkPrice: big.NewInt(234567), Bid: big.NewInt(1), Ask: big.NewInt(1), LinkFee: big.NewInt(1), NativeFee: big.NewInt(1)}) + report, err := (&reportcodecv3.ReportCodec{}).BuildReport(v3.ReportFields{BenchmarkPrice: big.NewInt(234567), Bid: big.NewInt(1), Ask: big.NewInt(1), LinkFee: big.NewInt(1), NativeFee: big.NewInt(1)}) if err != nil { panic(err) } diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index d443008334c..f5c11dc7313 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -7,10 +7,10 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" - relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + relaymercuryv1 "github.com/smartcontractkit/chainlink-data-streams/mercury/v1" + relaymercuryv2 "github.com/smartcontractkit/chainlink-data-streams/mercury/v2" + relaymercuryv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index c9d3e85cd2c..18080fe22b6 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -21,9 +21,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/utils" - relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" - relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + v1types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" + v2types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" + v3types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" ) type eaTelemetry struct { @@ -41,9 +41,9 @@ type EnhancedTelemetryData struct { } type EnhancedTelemetryMercuryData struct { - V1Observation *relaymercuryv1.Observation - V2Observation *relaymercuryv2.Observation - V3Observation *relaymercuryv3.Observation + V1Observation *v1types.Observation + V2Observation *v2types.Observation + V3Observation *v3types.Observation TaskRunResults pipeline.TaskRunResults RepTimestamp ocrtypes.ReportTimestamp FeedVersion mercuryutils.FeedVersion diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index 24c798259d9..ae58e89d22e 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -14,9 +14,9 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - mercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - mercury_v2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + mercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" + mercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -696,7 +696,7 @@ func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { chTelem <- EnhancedTelemetryMercuryData{ TaskRunResults: trrsMercuryV2, - V2Observation: &mercury_v2.Observation{ + V2Observation: &mercuryv2.Observation{ BenchmarkPrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(111111)}, MaxFinalizedTimestamp: mercury.ObsResult[int64]{Val: 321}, LinkPrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(4321)}, @@ -749,7 +749,7 @@ func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { Value: nil, }}, }, - V2Observation: &mercury_v2.Observation{}, + V2Observation: &mercuryv2.Observation{}, RepTimestamp: types.ReportTimestamp{ ConfigDigest: types.ConfigDigest{2}, Epoch: 11, @@ -760,7 +760,7 @@ func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { trrsMercuryV2[0].Result.Value = "" chTelem <- EnhancedTelemetryMercuryData{ TaskRunResults: trrsMercuryV2, - V2Observation: &mercury_v2.Observation{}, + V2Observation: &mercuryv2.Observation{}, RepTimestamp: types.ReportTimestamp{ ConfigDigest: types.ConfigDigest{2}, Epoch: 11, diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index d5346ad28cc..40a51b9d92d 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -21,8 +21,8 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -89,7 +89,7 @@ var ( ) type Transmitter interface { - relaymercury.Transmitter + mercury.Transmitter services.Service } diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index bc94166e566..429780ef2eb 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -1,4 +1,4 @@ -package mercury_v1 +package v1 import ( "context" @@ -13,8 +13,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v1types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" + v1 "github.com/smartcontractkit/chainlink-data-streams/mercury/v1" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -41,7 +42,7 @@ var ( ) ) -const nBlocksObservation int = relaymercuryv1.MaxAllowedBlocks +const nBlocksObservation int = v1.MaxAllowedBlocks type Runner interface { ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) @@ -66,7 +67,7 @@ type datasource struct { mu sync.RWMutex chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData - chainReader relaymercury.ChainReader + chainReader mercury.ChainReader fetcher Fetcher initialBlockNumber *int64 @@ -74,9 +75,9 @@ type datasource struct { zeroBlocksCounter prometheus.Counter } -var _ relaymercuryv1.DataSource = &datasource{} +var _ v1.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader relaymercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader mercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { return &datasource{pr, jb, spec, lggr, s, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} } @@ -90,7 +91,7 @@ func (e ErrEmptyLatestReport) Error() string { return fmt.Sprintf("FetchInitialMaxFinalizedBlockNumber returned empty LatestReport; this is a new feed. No initialBlockNumber was set, tried to use current block number to determine maxFinalizedBlockNumber but got error: %v", e.Err) } -func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedBlockNum bool) (obs relaymercuryv1.Observation, pipelineExecutionErr error) { +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedBlockNum bool) (obs v1types.Observation, pipelineExecutionErr error) { // setLatestBlocks must come chronologically before observations, along // with observationTimestamp, to avoid front-running @@ -204,9 +205,9 @@ func toBigInt(val interface{}) (*big.Int, error) { } type parseOutput struct { - benchmarkPrice relaymercury.ObsResult[*big.Int] - bid relaymercury.ObsResult[*big.Int] - ask relaymercury.ObsResult[*big.Int] + benchmarkPrice mercury.ObsResult[*big.Int] + bid mercury.ObsResult[*big.Int] + ask mercury.ObsResult[*big.Int] } // parse expects the output of observe to be three values, in the following order: @@ -290,7 +291,7 @@ func (ds *datasource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.T return run, trrs, err } -func (ds *datasource) setLatestBlocks(ctx context.Context, obs *relaymercuryv1.Observation) error { +func (ds *datasource) setLatestBlocks(ctx context.Context, obs *v1types.Observation) error { latestBlocks, err := ds.chainReader.LatestHeads(ctx, nBlocksObservation) if err != nil { ds.lggr.Errorw("failed to read latest blocks", "error", err) @@ -318,7 +319,7 @@ func (ds *datasource) setLatestBlocks(ctx context.Context, obs *relaymercuryv1.O for _, block := range latestBlocks { obs.LatestBlocks = append( obs.LatestBlocks, - relaymercuryv1.NewBlock(int64(block.Number), block.Hash, block.Timestamp)) + v1types.NewBlock(int64(block.Number), block.Hash, block.Timestamp)) } return nil diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index d8d7d39dbb8..72dc4327f19 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -1,4 +1,4 @@ -package mercury_v1 +package v1 import ( "context" @@ -15,8 +15,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -25,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" @@ -33,7 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -var _ relaymercury.MercuryServerFetcher = &mockFetcher{} +var _ mercury.ServerFetcher = &mockFetcher{} type mockFetcher struct { num *int64 @@ -71,10 +71,10 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg type mockChainReader struct { err error - obs []relaymercury.Head + obs []mercury.Head } -func (m *mockChainReader) LatestHeads(context.Context, int) ([]relaymercury.Head, error) { +func (m *mockChainReader) LatestHeads(context.Context, int) ([]mercury.Head, error) { return m.obs, m.err } @@ -391,7 +391,7 @@ func TestMercury_Observe(t *testing.T) { obs, err := ds.Observe(ctx, repts, true) assert.Error(t, err) - assert.Equal(t, obs, relaymercuryv1.Observation{}) + assert.Equal(t, obs, v1.Observation{}) }) }) } @@ -416,7 +416,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { headTracker.On("LatestChain").Return(&h, nil) ds.chainReader = evm.NewChainReader(headTracker) - obs := relaymercuryv1.Observation{} + obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) assert.NoError(t, err) @@ -434,7 +434,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) ds.chainReader = evm.NewChainReader(headTracker) - obs := relaymercuryv1.Observation{} + obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) assert.NoError(t, err) diff --git a/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go index 28688e3b17a..8f2eac59c33 100644 --- a/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go @@ -11,8 +11,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - reportcodec "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - + v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/types" @@ -25,7 +24,7 @@ import ( var ReportTypes = reporttypes.GetSchema() var maxReportLength = 32 * len(ReportTypes) // each arg is 256 bit EVM word -var _ reportcodec.ReportCodec = &ReportCodec{} +var _ v1.ReportCodec = &ReportCodec{} type ReportCodec struct { logger logger.Logger @@ -36,7 +35,7 @@ func NewReportCodec(feedID [32]byte, lggr logger.Logger) *ReportCodec { return &ReportCodec{lggr, feedID} } -func (r *ReportCodec) BuildReport(rf reportcodec.ReportFields) (ocrtypes.Report, error) { +func (r *ReportCodec) BuildReport(rf v1.ReportFields) (ocrtypes.Report, error) { var merr error if rf.BenchmarkPrice == nil { merr = errors.Join(merr, errors.New("benchmarkPrice may not be nil")) diff --git a/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go index f630b4522b4..2e50faf47d4 100644 --- a/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go @@ -11,15 +11,14 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - + v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" "github.com/smartcontractkit/chainlink/v2/core/utils" ) var hash = hexutil.MustDecode("0x552c2cea3ab43bae137d89ee6142a01db3ae2b5678bc3c9bd5f509f537bea57b") -func newValidReportFields() relaymercuryv1.ReportFields { - return relaymercuryv1.ReportFields{ +func newValidReportFields() v1.ReportFields { + return v1.ReportFields{ Timestamp: 242, BenchmarkPrice: big.NewInt(243), Bid: big.NewInt(244), @@ -35,7 +34,7 @@ func Test_ReportCodec(t *testing.T) { r := ReportCodec{} t.Run("BuildReport errors on zero fields", func(t *testing.T) { - _, err := r.BuildReport(relaymercuryv1.ReportFields{}) + _, err := r.BuildReport(v1.ReportFields{}) require.Error(t, err) assert.Contains(t, err.Error(), "benchmarkPrice may not be nil") assert.Contains(t, err.Error(), "bid may not be nil") diff --git a/core/services/relay/evm/mercury/v2/data_source.go b/core/services/relay/evm/mercury/v2/data_source.go index ec6cf5ad106..7c2d6424fae 100644 --- a/core/services/relay/evm/mercury/v2/data_source.go +++ b/core/services/relay/evm/mercury/v2/data_source.go @@ -1,4 +1,4 @@ -package mercury_v2 +package v2 import ( "context" @@ -10,8 +10,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v2types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" + v2 "github.com/smartcontractkit/chainlink-data-streams/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -52,13 +53,13 @@ type datasource struct { chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData } -var _ relaymercuryv2.DataSource = &datasource{} +var _ v2.DataSource = &datasource{} func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { return &datasource{pr, jb, spec, feedID, lggr, s, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} } -func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv2.Observation, pipelineExecutionErr error) { +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs v2types.Observation, pipelineExecutionErr error) { var wg sync.WaitGroup ctx, cancel := context.WithCancel(ctx) @@ -116,8 +117,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam obs.LinkPrice.Val, obs.LinkPrice.Err = ds.fetcher.LatestPrice(ctx, ds.linkFeedID) if obs.LinkPrice.Val == nil && obs.LinkPrice.Err == nil { mercurytypes.PriceFeedMissingCount.WithLabelValues(ds.linkFeedID.String()).Inc() - ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing LINK feed, using sentinel value of %s", relaymercuryv2.MissingPrice), "linkFeedID", ds.linkFeedID) - obs.LinkPrice.Val = relaymercuryv2.MissingPrice + ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing LINK feed, using sentinel value of %s", v2.MissingPrice), "linkFeedID", ds.linkFeedID) + obs.LinkPrice.Val = v2.MissingPrice } else if obs.LinkPrice.Err != nil { mercurytypes.PriceFeedErrorCount.WithLabelValues(ds.linkFeedID.String()).Inc() ds.lggr.Errorw("Mercury server returned error querying LINK price feed", "err", obs.LinkPrice.Err, "linkFeedID", ds.linkFeedID) @@ -134,8 +135,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam obs.NativePrice.Val, obs.NativePrice.Err = ds.fetcher.LatestPrice(ctx, ds.nativeFeedID) if obs.NativePrice.Val == nil && obs.NativePrice.Err == nil { mercurytypes.PriceFeedMissingCount.WithLabelValues(ds.nativeFeedID.String()).Inc() - ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing native feed, using sentinel value of %s", relaymercuryv2.MissingPrice), "nativeFeedID", ds.nativeFeedID) - obs.NativePrice.Val = relaymercuryv2.MissingPrice + ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing native feed, using sentinel value of %s", v2.MissingPrice), "nativeFeedID", ds.nativeFeedID) + obs.NativePrice.Val = v2.MissingPrice } else if obs.NativePrice.Err != nil { mercurytypes.PriceFeedErrorCount.WithLabelValues(ds.nativeFeedID.String()).Inc() ds.lggr.Errorw("Mercury server returned error querying native price feed", "err", obs.NativePrice.Err, "nativeFeedID", ds.nativeFeedID) @@ -184,7 +185,7 @@ func toBigInt(val interface{}) (*big.Int, error) { } type parseOutput struct { - benchmarkPrice relaymercury.ObsResult[*big.Int] + benchmarkPrice mercury.ObsResult[*big.Int] } func (ds *datasource) parse(trrs pipeline.TaskRunResults) (o parseOutput, merr error) { diff --git a/core/services/relay/evm/mercury/v2/data_source_test.go b/core/services/relay/evm/mercury/v2/data_source_test.go index d1030327e10..c9ae37ae018 100644 --- a/core/services/relay/evm/mercury/v2/data_source_test.go +++ b/core/services/relay/evm/mercury/v2/data_source_test.go @@ -1,4 +1,4 @@ -package mercury_v2 +package v2 import ( "context" @@ -8,11 +8,11 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v2 "github.com/smartcontractkit/chainlink-data-streams/mercury/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -22,7 +22,7 @@ import ( reportcodecv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/reportcodec" ) -var _ relaymercury.MercuryServerFetcher = &mockFetcher{} +var _ mercury.ServerFetcher = &mockFetcher{} type mockFetcher struct { ts int64 @@ -279,9 +279,9 @@ func Test_Datasource(t *testing.T) { obs, err := ds.Observe(ctx, repts, false) assert.NoError(t, err) - assert.Equal(t, obs.LinkPrice.Val, relaymercuryv2.MissingPrice) + assert.Equal(t, obs.LinkPrice.Val, v2.MissingPrice) assert.Nil(t, obs.LinkPrice.Err) - assert.Equal(t, obs.NativePrice.Val, relaymercuryv2.MissingPrice) + assert.Equal(t, obs.NativePrice.Val, v2.MissingPrice) assert.Nil(t, obs.NativePrice.Err) }) }) diff --git a/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go index 6b13b3b6ef8..33c5fa9a326 100644 --- a/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go @@ -9,7 +9,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - reportcodec "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" @@ -20,7 +20,7 @@ var ReportTypes = reporttypes.GetSchema() var maxReportLength = 32 * len(ReportTypes) // each arg is 256 bit EVM word var zero = big.NewInt(0) -var _ reportcodec.ReportCodec = &ReportCodec{} +var _ v2.ReportCodec = &ReportCodec{} type ReportCodec struct { logger logger.Logger @@ -31,7 +31,7 @@ func NewReportCodec(feedID [32]byte, lggr logger.Logger) *ReportCodec { return &ReportCodec{lggr, feedID} } -func (r *ReportCodec) BuildReport(rf reportcodec.ReportFields) (ocrtypes.Report, error) { +func (r *ReportCodec) BuildReport(rf v2.ReportFields) (ocrtypes.Report, error) { var merr error if rf.BenchmarkPrice == nil { merr = errors.Join(merr, errors.New("benchmarkPrice may not be nil")) diff --git a/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go index 3a58337b4ce..36b3a443880 100644 --- a/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go @@ -9,11 +9,11 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" ) -func newValidReportFields() relaymercuryv2.ReportFields { - return relaymercuryv2.ReportFields{ +func newValidReportFields() v2.ReportFields { + return v2.ReportFields{ Timestamp: 242, BenchmarkPrice: big.NewInt(243), ValidFromTimestamp: 123, @@ -27,7 +27,7 @@ func Test_ReportCodec_BuildReport(t *testing.T) { r := ReportCodec{} t.Run("BuildReport errors on zero values", func(t *testing.T) { - _, err := r.BuildReport(relaymercuryv2.ReportFields{}) + _, err := r.BuildReport(v2.ReportFields{}) require.Error(t, err) assert.Contains(t, err.Error(), "benchmarkPrice may not be nil") assert.Contains(t, err.Error(), "linkFee may not be nil") diff --git a/core/services/relay/evm/mercury/v3/data_source.go b/core/services/relay/evm/mercury/v3/data_source.go index d5e1e5716dd..a751149f378 100644 --- a/core/services/relay/evm/mercury/v3/data_source.go +++ b/core/services/relay/evm/mercury/v3/data_source.go @@ -1,4 +1,4 @@ -package mercury_v3 +package v3 import ( "context" @@ -11,8 +11,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v3types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + v3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -53,13 +54,13 @@ type datasource struct { chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData } -var _ relaymercuryv3.DataSource = &datasource{} +var _ v3.DataSource = &datasource{} func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { return &datasource{pr, jb, spec, feedID, lggr, s, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} } -func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv3.Observation, pipelineExecutionErr error) { +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs v3types.Observation, pipelineExecutionErr error) { var wg sync.WaitGroup ctx, cancel := context.WithCancel(ctx) @@ -119,8 +120,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam obs.LinkPrice.Val, obs.LinkPrice.Err = ds.fetcher.LatestPrice(ctx, ds.linkFeedID) if obs.LinkPrice.Val == nil && obs.LinkPrice.Err == nil { mercurytypes.PriceFeedMissingCount.WithLabelValues(ds.linkFeedID.String()).Inc() - ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing LINK feed, using sentinel value of %s", relaymercuryv3.MissingPrice), "linkFeedID", ds.linkFeedID) - obs.LinkPrice.Val = relaymercuryv3.MissingPrice + ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing LINK feed, using sentinel value of %s", v3.MissingPrice), "linkFeedID", ds.linkFeedID) + obs.LinkPrice.Val = v3.MissingPrice } else if obs.LinkPrice.Err != nil { mercurytypes.PriceFeedErrorCount.WithLabelValues(ds.linkFeedID.String()).Inc() ds.lggr.Errorw("Mercury server returned error querying LINK price feed", "err", obs.LinkPrice.Err, "linkFeedID", ds.linkFeedID) @@ -137,8 +138,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam obs.NativePrice.Val, obs.NativePrice.Err = ds.fetcher.LatestPrice(ctx, ds.nativeFeedID) if obs.NativePrice.Val == nil && obs.NativePrice.Err == nil { mercurytypes.PriceFeedMissingCount.WithLabelValues(ds.nativeFeedID.String()).Inc() - ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing native feed, using sentinel value of %s", relaymercuryv3.MissingPrice), "nativeFeedID", ds.nativeFeedID) - obs.NativePrice.Val = relaymercuryv3.MissingPrice + ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing native feed, using sentinel value of %s", v3.MissingPrice), "nativeFeedID", ds.nativeFeedID) + obs.NativePrice.Val = v3.MissingPrice } else if obs.NativePrice.Err != nil { mercurytypes.PriceFeedErrorCount.WithLabelValues(ds.nativeFeedID.String()).Inc() ds.lggr.Errorw("Mercury server returned error querying native price feed", "err", obs.NativePrice.Err, "nativeFeedID", ds.nativeFeedID) @@ -187,9 +188,9 @@ func toBigInt(val interface{}) (*big.Int, error) { } type parseOutput struct { - benchmarkPrice relaymercury.ObsResult[*big.Int] - bid relaymercury.ObsResult[*big.Int] - ask relaymercury.ObsResult[*big.Int] + benchmarkPrice mercury.ObsResult[*big.Int] + bid mercury.ObsResult[*big.Int] + ask mercury.ObsResult[*big.Int] } func (ds *datasource) parse(trrs pipeline.TaskRunResults) (o parseOutput, merr error) { diff --git a/core/services/relay/evm/mercury/v3/data_source_test.go b/core/services/relay/evm/mercury/v3/data_source_test.go index e03e321d093..4ff713abb21 100644 --- a/core/services/relay/evm/mercury/v3/data_source_test.go +++ b/core/services/relay/evm/mercury/v3/data_source_test.go @@ -1,4 +1,4 @@ -package mercury_v3 +package v3 import ( "context" @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + relaymercuryv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -22,7 +22,7 @@ import ( reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" ) -var _ relaymercury.MercuryServerFetcher = &mockFetcher{} +var _ mercurytypes.ServerFetcher = &mockFetcher{} type mockFetcher struct { ts int64 diff --git a/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go index 6b379dc1948..601431838d2 100644 --- a/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go @@ -3,14 +3,13 @@ package reportcodec import ( "errors" "fmt" - "math/big" pkgerrors "github.com/pkg/errors" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - reportcodec "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" @@ -21,7 +20,7 @@ var ReportTypes = reporttypes.GetSchema() var maxReportLength = 32 * len(ReportTypes) // each arg is 256 bit EVM word var zero = big.NewInt(0) -var _ reportcodec.ReportCodec = &ReportCodec{} +var _ v3.ReportCodec = &ReportCodec{} type ReportCodec struct { logger logger.Logger @@ -32,7 +31,7 @@ func NewReportCodec(feedID [32]byte, lggr logger.Logger) *ReportCodec { return &ReportCodec{lggr, feedID} } -func (r *ReportCodec) BuildReport(rf reportcodec.ReportFields) (ocrtypes.Report, error) { +func (r *ReportCodec) BuildReport(rf v3.ReportFields) (ocrtypes.Report, error) { var merr error if rf.BenchmarkPrice == nil { merr = errors.Join(merr, errors.New("benchmarkPrice may not be nil")) diff --git a/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go index 88416f7ea61..752e6ce34b5 100644 --- a/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go @@ -9,11 +9,11 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" ) -func newValidReportFields() relaymercuryv3.ReportFields { - return relaymercuryv3.ReportFields{ +func newValidReportFields() v3.ReportFields { + return v3.ReportFields{ Timestamp: 242, BenchmarkPrice: big.NewInt(243), Bid: big.NewInt(244), @@ -29,7 +29,7 @@ func Test_ReportCodec_BuildReport(t *testing.T) { r := ReportCodec{} t.Run("BuildReport errors on zero values", func(t *testing.T) { - _, err := r.BuildReport(relaymercuryv3.ReportFields{}) + _, err := r.BuildReport(v3.ReportFields{}) require.Error(t, err) assert.Contains(t, err.Error(), "benchmarkPrice may not be nil") assert.Contains(t, err.Error(), "linkFee may not be nil") diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index 90253808171..b6a2232da3f 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -6,27 +6,28 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" - relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" - relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" - relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" "github.com/smartcontractkit/chainlink-common/pkg/services" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" + v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" + v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + relaymercury "github.com/smartcontractkit/chainlink-data-streams/mercury" + httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" ) -var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) +var _ types.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { configWatcher *configWatcher transmitter mercury.Transmitter - reportCodecV1 relaymercuryv1.ReportCodec - reportCodecV2 relaymercuryv2.ReportCodec - reportCodecV3 relaymercuryv3.ReportCodec - chainReader relaymercury.ChainReader + reportCodecV1 v1.ReportCodec + reportCodecV2 v2.ReportCodec + reportCodecV3 v3.ReportCodec + chainReader mercurytypes.ChainReader logger logger.Logger ms services.MultiStart @@ -35,10 +36,10 @@ type mercuryProvider struct { func NewMercuryProvider( configWatcher *configWatcher, transmitter mercury.Transmitter, - reportCodecV1 relaymercuryv1.ReportCodec, - reportCodecV2 relaymercuryv2.ReportCodec, - reportCodecV3 relaymercuryv3.ReportCodec, - chainReader relaymercury.ChainReader, + reportCodecV1 v1.ReportCodec, + reportCodecV2 v2.ReportCodec, + reportCodecV3 v3.ReportCodec, + chainReader mercurytypes.ChainReader, lggr logger.Logger, ) *mercuryProvider { return &mercuryProvider{ @@ -84,19 +85,19 @@ func (p *mercuryProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigest return p.configWatcher.OffchainConfigDigester() } -func (p *mercuryProvider) OnchainConfigCodec() relaymercury.OnchainConfigCodec { +func (p *mercuryProvider) OnchainConfigCodec() mercurytypes.OnchainConfigCodec { return relaymercury.StandardOnchainConfigCodec{} } -func (p *mercuryProvider) ReportCodecV1() relaymercuryv1.ReportCodec { +func (p *mercuryProvider) ReportCodecV1() v1.ReportCodec { return p.reportCodecV1 } -func (p *mercuryProvider) ReportCodecV2() relaymercuryv2.ReportCodec { +func (p *mercuryProvider) ReportCodecV2() v2.ReportCodec { return p.reportCodecV2 } -func (p *mercuryProvider) ReportCodecV3() relaymercuryv3.ReportCodec { +func (p *mercuryProvider) ReportCodecV3() v3.ReportCodec { return p.reportCodecV3 } @@ -104,35 +105,35 @@ func (p *mercuryProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return p.transmitter } -func (p *mercuryProvider) MercuryServerFetcher() relaymercury.MercuryServerFetcher { +func (p *mercuryProvider) MercuryServerFetcher() mercurytypes.ServerFetcher { return p.transmitter } -func (p *mercuryProvider) ChainReader() relaymercury.ChainReader { +func (p *mercuryProvider) ChainReader() mercurytypes.ChainReader { return p.chainReader } -var _ relaymercury.ChainReader = (*chainReader)(nil) +var _ mercurytypes.ChainReader = (*chainReader)(nil) type chainReader struct { tracker httypes.HeadTracker } -func NewChainReader(h httypes.HeadTracker) relaymercury.ChainReader { +func NewChainReader(h httypes.HeadTracker) mercurytypes.ChainReader { return &chainReader{ tracker: h, } } -func (r *chainReader) LatestHeads(ctx context.Context, k int) ([]relaymercury.Head, error) { +func (r *chainReader) LatestHeads(ctx context.Context, k int) ([]mercurytypes.Head, error) { evmBlocks := r.tracker.LatestChain().AsSlice(k) if len(evmBlocks) == 0 { return nil, nil } - blocks := make([]relaymercury.Head, len(evmBlocks)) + blocks := make([]mercurytypes.Head, len(evmBlocks)) for x := 0; x < len(evmBlocks); x++ { - blocks[x] = relaymercury.Head{ + blocks[x] = mercurytypes.Head{ Number: uint64(evmBlocks[x].BlockNumber()), Hash: evmBlocks[x].Hash.Bytes(), Timestamp: uint64(evmBlocks[x].Timestamp.Unix()), diff --git a/go.mod b/go.mod index 0343f4e7159..c209cbca43f 100644 --- a/go.mod +++ b/go.mod @@ -66,8 +66,9 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 diff --git a/go.sum b/go.sum index 4bd90da600d..08743596538 100644 --- a/go.sum +++ b/go.sum @@ -1507,10 +1507,12 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 h1:qau0/AHvPwMR3p6gWsFWC4qVfEtSEALtBetTOpHA2IU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f95557b2bb1..4249cb0e9b7 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c github.com/smartcontractkit/chainlink-testing-framework v1.20.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -397,6 +397,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index b275f6b653a..d4b804101da 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1792,10 +1792,12 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 h1:qau0/AHvPwMR3p6gWsFWC4qVfEtSEALtBetTOpHA2IU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= From fb61a59360b8ea0ef7fd3dcf1444a5015cc106ca Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:32:59 +0400 Subject: [PATCH 003/234] [AUTO-7471] add notification to automation-nightly-test (#11473) * fix automation-nightly-test * add notification * fix test-results --- .../workflows/automation-nightly-tests.yml | 151 +++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/.github/workflows/automation-nightly-tests.yml b/.github/workflows/automation-nightly-tests.yml index 3c05786b666..9fa3746f3df 100644 --- a/.github/workflows/automation-nightly-tests.yml +++ b/.github/workflows/automation-nightly-tests.yml @@ -7,6 +7,9 @@ on: - "*" workflow_dispatch: +env: + CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink + jobs: build-chainlink: environment: integration @@ -39,7 +42,7 @@ jobs: AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - automation-upgrade-test: + automation-upgrade-tests: environment: integration permissions: checks: write @@ -74,7 +77,7 @@ jobs: SELECTED_NETWORKS: ${{ matrix.tests.network }} TEST_SUITE: ${{ matrix.tests.suite }} UPGRADE_VERSION: ${{ github.sha }} - UPGRADE_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink + UPGRADE_IMAGE: ${{ env.CHAINLINK_IMAGE }} with: test_command_to_run: cd ./integration-tests && go test -timeout 60m -count=1 -json -test.parallel=${{ matrix.tests.nodes }} ${{ matrix.tests.command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -106,3 +109,147 @@ jobs: this-job-name: Automation ${{ matrix.tests.name }} Test test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true + + test-notify: + name: Start Slack Thread + if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} + environment: integration + outputs: + thread_ts: ${{ steps.slack.outputs.thread_ts }} + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + runs-on: ubuntu-latest + needs: [ automation-upgrade-tests ] + steps: + - name: Debug Result + run: echo ${{ join(needs.*.result, ',') }} + - name: Main Slack Notification + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + id: slack + with: + channel-id: C03KJ5S7KEK + payload: | + { + "attachments": [ + { + "color": "${{ contains(join(needs.*.result, ','), 'failure') && '#C62828' || '#2E7D32' }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "Automation Nightly Tests ${{ contains(join(needs.*.result, ','), 'failure') && ':x:' || ':white_check_mark:'}}", + "emoji": true + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "<${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}|${{ github.ref_name }}> | <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}> | <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Run>" + } + } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + + test-results: + name: Post Test Results for ${{ matrix.name }} + if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + runs-on: ubuntu-latest + needs: test-notify + strategy: + fail-fast: false + matrix: + name: [ Upgrade ] + steps: + - name: Get Results + id: test-results + run: | + # I feel like there's some clever, fully jq way to do this, but I ain't got the motivation to figure it out + echo "Querying test results" + + PARSED_RESULTS=$(curl \ + -H "Authorization: Bearer ${{ github.token }}" \ + 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ + | jq -r --arg pattern "${{ matrix.name }} Test" '.jobs[] + | select(.name | test($pattern)) as $job + | $job.steps[] + | select(.name == "Run Tests") + | { conclusion: (if .conclusion == "success" then ":white_check_mark:" else ":x:" end), product: ("*" + ($job.name | capture($pattern).product) + "*") }') + + echo "Parsed Results:" + echo $PARSED_RESULTS + + ALL_SUCCESS=true + for row in $(echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")'); do + success=false + break + done + + echo all_success=$ALL_SUCCESS >> $GITHUB_OUTPUT + + FORMATTED_RESULTS=$(echo $PARSED_RESULTS | jq -s '[.[] + | { + conclusion: .conclusion, + product: .product + } + ] + | map("{\"type\": \"section\", \"text\": {\"type\": \"mrkdwn\", \"text\": \"\(.product): \(.conclusion)\"}}") + | join(",")') + + echo "Formatted Results:" + echo $FORMATTED_RESULTS + + # Cleans out backslashes and quotes from jq + CLEAN_RESULTS=$(echo "$FORMATTED_RESULTS" | sed 's/\\\"/"/g' | sed 's/^"//;s/"$//') + + echo "Clean Results" + echo $CLEAN_RESULTS + + echo results=$CLEAN_RESULTS >> $GITHUB_OUTPUT + + - name: Test Details + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + with: + channel-id: C03KJ5S7KEK + payload: | + { + "thread_ts": "${{ needs.test-notify.outputs.thread_ts }}", + "attachments": [ + { + "color": "${{ steps.test-results.outputs.all_success && '#2E7D32' || '#C62828' }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "${{ matrix.name }} ${{ steps.test-results.outputs.all_success && ':white_check_mark:' || ':x: Notifying <@U02Q14G80TY>'}}", + "emoji": true + } + }, + { + "type": "divider" + }, + ${{ steps.test-results.outputs.results }} + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} \ No newline at end of file From ea290bedf80fd5e575109f6d77bd7b719bd92579 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 4 Dec 2023 19:34:40 +0100 Subject: [PATCH 004/234] Fix solhint warnings (#11480) * ignore old automation & fix minor vrf * add more ignore and fix various non-automation * fix gas diff --- contracts/.solhintignore | 8 ++++++++ contracts/package.json | 2 +- contracts/src/v0.8/ChainlinkClient.sol | 1 + contracts/src/v0.8/ValidatorProxy.sol | 2 ++ contracts/src/v0.8/automation/libraries/internal/Cron.sol | 1 + .../l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol | 4 ++++ .../src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol | 2 ++ .../l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol | 2 ++ contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol | 4 ++++ .../src/v0.8/transmission/dev/ERC-4337/Paymaster.sol | 2 ++ .../src/v0.8/transmission/dev/ERC-4337/SCALibrary.sol | 1 + .../dev/ERC-4337/SmartContractAccountFactory.sol | 2 ++ .../dev/testhelpers/SmartContractAccountHelper.sol | 3 +++ contracts/src/v0.8/vrf/KeepersVRFConsumer.sol | 1 + contracts/src/v0.8/vrf/VRF.sol | 7 +++++++ contracts/src/v0.8/vrf/VRFCoordinatorV2.sol | 1 + 16 files changed, 42 insertions(+), 1 deletion(-) diff --git a/contracts/.solhintignore b/contracts/.solhintignore index ba2aac1fb3a..569c8c51df3 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -1,6 +1,14 @@ # 344 warnings #./src/v0.8/automation +# Ignore frozen Automation code +./src/v0.8/automation/v1_2 +./src/v0.8/automation/v1_3 +./src/v0.8/automation/v2_0 +./src/v0.8/automation/v2_1 +./src/v0.8/automation/mocks +./src/v0.8/automation/testhelpers + # Ignore Functions v1.0.0 code that was frozen after audit ./src/v0.8/functions/v1_0_0 diff --git a/contracts/package.json b/contracts/package.json index 1503c822ea4..2604a12bea3 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 376 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 83 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", diff --git a/contracts/src/v0.8/ChainlinkClient.sol b/contracts/src/v0.8/ChainlinkClient.sol index 2d9302faef7..ef7f7943452 100644 --- a/contracts/src/v0.8/ChainlinkClient.sol +++ b/contracts/src/v0.8/ChainlinkClient.sol @@ -171,6 +171,7 @@ abstract contract ChainlinkClient { s_pendingRequests[requestId] = oracleAddress; emit ChainlinkRequested(requestId); require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle"); + return requestId; } /** diff --git a/contracts/src/v0.8/ValidatorProxy.sol b/contracts/src/v0.8/ValidatorProxy.sol index 627af73b395..4584bb02559 100644 --- a/contracts/src/v0.8/ValidatorProxy.sol +++ b/contracts/src/v0.8/ValidatorProxy.sol @@ -167,6 +167,7 @@ contract ValidatorProxy is AggregatorValidatorInterface, TypeAndVersionInterface current = s_currentAggregator.target; hasProposal = s_currentAggregator.hasNewProposal; proposed = s_proposedAggregator; + return (current, hasProposal, proposed); } /** VALIDATOR CONFIGURATION FUNCTIONS **/ @@ -216,6 +217,7 @@ contract ValidatorProxy is AggregatorValidatorInterface, TypeAndVersionInterface current = s_currentValidator.target; hasProposal = s_currentValidator.hasNewProposal; proposed = s_proposedValidator; + return (current, hasProposal, proposed); } /** diff --git a/contracts/src/v0.8/automation/libraries/internal/Cron.sol b/contracts/src/v0.8/automation/libraries/internal/Cron.sol index fe72c412b60..ae4f90d0d71 100644 --- a/contracts/src/v0.8/automation/libraries/internal/Cron.sol +++ b/contracts/src/v0.8/automation/libraries/internal/Cron.sol @@ -84,6 +84,7 @@ struct Field { * abstraction called a Spec. The library also includes a spec function, nextTick(), which * determines the next time a cron job should fire based on the current block timestamp. */ +// solhint-disable chainlink-solidity/prefix-internal-functions-with-underscore, no-global-import library Cron { using strings for *; diff --git a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol index 5250fbda278..63952ab7ba6 100644 --- a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol +++ b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol @@ -287,6 +287,8 @@ contract ArbitrumSequencerUptimeFeed is roundId = _roundId; updatedAt = startedAt; answeredInRound = roundId; + + return (roundId, answer, startedAt, updatedAt, answeredInRound); } /// @inheritdoc AggregatorV3Interface @@ -305,5 +307,7 @@ contract ArbitrumSequencerUptimeFeed is startedAt = feedState.latestTimestamp; updatedAt = startedAt; answeredInRound = roundId; + + return (roundId, answer, startedAt, updatedAt, answeredInRound); } } diff --git a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol index 2043ffa6287..66fee5053ee 100644 --- a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol +++ b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol @@ -204,6 +204,8 @@ contract ArbitrumValidator is TypeAndVersionInterface, AggregatorValidatorInterf message ); emit L2WithdrawalRequested(id, amount, refundAddr); + + return id; } /** diff --git a/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol b/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol index fcf6093e3cd..947b1b0bf5d 100644 --- a/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol +++ b/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol @@ -245,6 +245,7 @@ contract OptimismSequencerUptimeFeed is } else { revert NoDataPresent(); } + return (roundId, answer, startedAt, updatedAt, answeredInRound); } /// @inheritdoc AggregatorV3Interface @@ -262,5 +263,6 @@ contract OptimismSequencerUptimeFeed is startedAt = feedState.startedAt; updatedAt = feedState.updatedAt; answeredInRound = roundId; + return (roundId, answer, startedAt, updatedAt, answeredInRound); } } diff --git a/contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol b/contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol index 9691cfb7fea..53f79b6dc42 100644 --- a/contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol +++ b/contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol @@ -16,6 +16,7 @@ library ByteUtil { * @param offset Position to start reading from. * @return result The uint256 read from the byte array. */ + // solhint-disable-next-line chainlink-solidity/explicit-returns function _readUint256(bytes memory data, uint256 offset) internal pure returns (uint256 result) { //bounds check if (offset + 32 > data.length) revert MalformedData(); @@ -32,6 +33,7 @@ library ByteUtil { * @param offset Position to start reading from. * @return result The uint192 read from the byte array. */ + // solhint-disable-next-line chainlink-solidity/explicit-returns function _readUint192(bytes memory data, uint256 offset) internal pure returns (uint256 result) { //bounds check if (offset + 24 > data.length) revert MalformedData(); @@ -50,6 +52,7 @@ library ByteUtil { * @param offset Position to start reading from. * @return result The uint32 read from the byte array. */ + // solhint-disable-next-line chainlink-solidity/explicit-returns function _readUint32(bytes memory data, uint256 offset) internal pure returns (uint256 result) { //bounds check if (offset + 4 > data.length) revert MalformedData(); @@ -68,6 +71,7 @@ library ByteUtil { * @param offset Position to start reading from. * @return result The uint32 read from the byte array. */ + // solhint-disable-next-line chainlink-solidity/explicit-returns function _readAddress(bytes memory data, uint256 offset) internal pure returns (address result) { //bounds check if (offset + 20 > data.length) revert MalformedData(); diff --git a/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol b/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol index 970ddf6b7e6..38b6fb57983 100644 --- a/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol +++ b/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol @@ -103,6 +103,7 @@ contract Paymaster is IPaymaster, ConfirmedOwner { extraCost = directFundingData.topupAmount; } } + return extraCost; } /// @dev Deducts user subscription balance after execution. @@ -116,6 +117,7 @@ contract Paymaster is IPaymaster, ConfirmedOwner { function _getCostJuels(uint256 costWei) internal view returns (uint256 costJuels) { costJuels = (1e18 * costWei) / uint256(_getFeedData()); + return costJuels; } function _getFeedData() internal view returns (int256) { diff --git a/contracts/src/v0.8/transmission/dev/ERC-4337/SCALibrary.sol b/contracts/src/v0.8/transmission/dev/ERC-4337/SCALibrary.sol index 47587e278f4..35d666a2d3d 100644 --- a/contracts/src/v0.8/transmission/dev/ERC-4337/SCALibrary.sol +++ b/contracts/src/v0.8/transmission/dev/ERC-4337/SCALibrary.sol @@ -31,6 +31,7 @@ library SCALibrary { hashOfEncoding ) ); + return fullHash; } function _recoverSignature(bytes memory signature, bytes32 fullHash) internal pure returns (address) { diff --git a/contracts/src/v0.8/transmission/dev/ERC-4337/SmartContractAccountFactory.sol b/contracts/src/v0.8/transmission/dev/ERC-4337/SmartContractAccountFactory.sol index 35719345ecd..bb0f2dbde63 100644 --- a/contracts/src/v0.8/transmission/dev/ERC-4337/SmartContractAccountFactory.sol +++ b/contracts/src/v0.8/transmission/dev/ERC-4337/SmartContractAccountFactory.sol @@ -27,5 +27,7 @@ contract SmartContractAccountFactory { } emit ContractCreated(scaAddress); + + return scaAddress; } } diff --git a/contracts/src/v0.8/transmission/dev/testhelpers/SmartContractAccountHelper.sol b/contracts/src/v0.8/transmission/dev/testhelpers/SmartContractAccountHelper.sol index c33df49d16b..014f296f077 100644 --- a/contracts/src/v0.8/transmission/dev/testhelpers/SmartContractAccountHelper.sol +++ b/contracts/src/v0.8/transmission/dev/testhelpers/SmartContractAccountHelper.sol @@ -18,6 +18,7 @@ library SmartContractAccountHelper { SCA.executeTransactionFromEntryPoint.selector, abi.encode(endContract, value, block.timestamp + deadline, data) ); + return encoding; } function getFullHashForSigning(bytes32 userOpHash, address scaAddress) public view returns (bytes32) { @@ -29,6 +30,7 @@ library SmartContractAccountHelper { address entryPoint ) public pure returns (bytes memory initCode) { initCode = bytes.concat(INITIALIZE_CODE, abi.encode(owner, entryPoint)); + return initCode; } function getInitCode( @@ -46,6 +48,7 @@ library SmartContractAccountHelper { initializeCodeWithConstructor ) ); + return initCode; } /// @dev Computes the smart contract address that results from a CREATE2 operation, per EIP-1014. diff --git a/contracts/src/v0.8/vrf/KeepersVRFConsumer.sol b/contracts/src/v0.8/vrf/KeepersVRFConsumer.sol index a18c6e03798..20fd806b0cc 100644 --- a/contracts/src/v0.8/vrf/KeepersVRFConsumer.sol +++ b/contracts/src/v0.8/vrf/KeepersVRFConsumer.sol @@ -61,6 +61,7 @@ contract KeepersVRFConsumer is KeeperCompatibleInterface, VRFConsumerBaseV2 { * @return upkeepNeeded true if and only if at least UPKEEP_INTERVAL seconds have elapsed since the last upkeep or since construction * of the contract. */ + // solhint-disable-next-line chainlink-solidity/explicit-returns function checkUpkeep( bytes calldata /* checkData */ ) external view override returns (bool upkeepNeeded, bytes memory /* performData */) { diff --git a/contracts/src/v0.8/vrf/VRF.sol b/contracts/src/v0.8/vrf/VRF.sol index a19fc39ec3e..f7d62a272bc 100644 --- a/contracts/src/v0.8/vrf/VRF.sol +++ b/contracts/src/v0.8/vrf/VRF.sol @@ -205,6 +205,7 @@ contract VRF { while (x_ >= FIELD_SIZE) { x_ = uint256(keccak256(abi.encodePacked(x_))); } + return x_; } // Hash b to a random point which hopefully lies on secp256k1. The y ordinate @@ -223,6 +224,7 @@ contract VRF { p[1] = FIELD_SIZE - p[1]; } } + return p; } // Domain-separation tag for initial hash in _hashToCurve. Corresponds to @@ -248,6 +250,7 @@ contract VRF { while (!_isOnCurve(rv)) { rv = _newCandidateSecp256k1Point(abi.encodePacked(rv[0])); } + return rv; } /** ********************************************************************* @@ -294,6 +297,7 @@ contract VRF { uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); } + return (x3, z3); } // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) @@ -304,6 +308,7 @@ contract VRF { uint256 z2 ) internal pure returns (uint256 x3, uint256 z3) { (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + return (x3, z3); } /** ************************************************************************** @@ -385,6 +390,7 @@ contract VRF { sz = dx; } } + return (sx, sy, sz); } // p1+p2, as affine points on secp256k1. @@ -577,5 +583,6 @@ contract VRF { proof.zInv ); output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma))); + return output; } } diff --git a/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol b/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol index 5acd3e74358..5dfb51a4b13 100644 --- a/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol +++ b/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol @@ -491,6 +491,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface, VRFCo // The seed actually used by the VRF machinery, mixing in the blockhash uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); randomness = VRF._randomValueFromVRFProof(proof, actualSeed); // Reverts on failure + return (keyHash, requestId, randomness); } /* From c68240f73fcddef7b9ab9d578ecd3ea9a346faab Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 4 Dec 2023 12:45:04 -0600 Subject: [PATCH 005/234] bump libocr; remove P2P.V1; drop libp2p (#10872) --- core/cmd/jobs_commands_test.go | 4 +- core/cmd/ocr-bootstrap-spec.yml | 4 +- core/cmd/shell_remote_test.go | 5 +- core/config/docs/core.toml | 52 +-- core/config/p2p_config.go | 4 - core/config/p2p_v1_config.go | 20 - core/config/toml/types.go | 74 ---- core/internal/cltest/factories.go | 2 +- core/internal/cltest/job_factories.go | 2 +- core/internal/features/features_test.go | 129 ++----- .../features/ocr2/features_ocr2_test.go | 1 - .../testutils/configtest/general_config.go | 1 - core/scripts/go.mod | 57 +-- core/scripts/go.sum | 347 +----------------- core/services/chainlink/config.go | 37 +- core/services/chainlink/config_general.go | 10 - core/services/chainlink/config_p2p.go | 67 +--- core/services/chainlink/config_p2p_test.go | 15 - core/services/chainlink/config_test.go | 78 +--- .../testdata/config-empty-effective.toml | 13 - .../chainlink/testdata/config-full.toml | 13 - .../config-multi-chain-effective.toml | 13 - core/services/feeds/service_test.go | 5 +- core/services/job/helpers_test.go | 16 +- core/services/job/models.go | 1 - core/services/job/orm.go | 4 +- core/services/job/runner_integration_test.go | 17 +- core/services/ocr/delegate.go | 30 +- core/services/ocr/example-job-spec.toml | 4 +- core/services/ocr/validate.go | 7 +- core/services/ocr/validate_test.go | 59 +-- .../v1/internal/testutils.go | 1 - .../plugins/ocr2keeper/integration_test.go | 1 - .../internal/ocr2vrf_integration_test.go | 1 - core/services/ocrcommon/peer_wrapper.go | 110 +----- core/services/ocrcommon/peer_wrapper_test.go | 94 +---- ..._oracle_specs_drop_p2p_bootstrap_peers.sql | 5 + core/testdata/testspecs/v2_specs.go | 5 +- core/web/assets/index.html | 2 +- core/web/assets/index.html.gz | Bin 420 -> 418 bytes ...560e37.js => main.8f602c136d4004a835ee.js} | 2 +- ....js.gz => main.8f602c136d4004a835ee.js.gz} | Bin 1190421 -> 1190411 bytes core/web/jobs_controller_test.go | 17 +- core/web/loop_registry_test.go | 5 +- core/web/pipeline_runs_controller_test.go | 9 +- core/web/presenters/job.go | 2 - core/web/presenters/job_test.go | 2 - core/web/resolver/spec.go | 11 - core/web/resolver/spec_test.go | 3 - .../testdata/config-empty-effective.toml | 13 - core/web/resolver/testdata/config-full.toml | 13 - .../config-multi-chain-effective.toml | 13 - core/web/schema/type/spec.graphql | 1 - docs/CONFIG.md | 110 +----- go.mod | 57 +-- go.sum | 347 +----------------- integration-tests/go.mod | 53 +-- integration-tests/go.sum | 337 +---------------- integration-tests/types/config/node/core.go | 14 - operator_ui/TAG | 2 +- testdata/scripts/node/validate/default.txtar | 13 - .../disk-based-logging-disabled.txtar | 13 - .../validate/disk-based-logging-no-dir.txtar | 13 - .../node/validate/disk-based-logging.txtar | 13 - testdata/scripts/node/validate/invalid.txtar | 13 - testdata/scripts/node/validate/valid.txtar | 13 - testdata/scripts/node/validate/warnings.txtar | 54 +-- tools/benchmark/job_spec_delete_v2.sh | 2 +- 68 files changed, 173 insertions(+), 2282 deletions(-) delete mode 100644 core/config/p2p_v1_config.go create mode 100644 core/store/migrate/migrations/0212_ocr_oracle_specs_drop_p2p_bootstrap_peers.sql rename core/web/assets/{main.b0b6f79f7f4a94560e37.js => main.8f602c136d4004a835ee.js} (91%) rename core/web/assets/{main.b0b6f79f7f4a94560e37.js.gz => main.8f602c136d4004a835ee.js.gz} (93%) diff --git a/core/cmd/jobs_commands_test.go b/core/cmd/jobs_commands_test.go index 04908e18ff3..5d9b3cb3f1a 100644 --- a/core/cmd/jobs_commands_test.go +++ b/core/cmd/jobs_commands_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hashicorp/consul/sdk/freeport" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli" @@ -368,7 +369,8 @@ func TestShell_CreateJobV2(t *testing.T) { app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) c.OCR.Enabled = ptr(true) - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} c.P2P.PeerID = &cltest.DefaultP2PPeerID c.EVM[0].Enabled = ptr(true) c.EVM[0].NonceAutoSync = ptr(false) diff --git a/core/cmd/ocr-bootstrap-spec.yml b/core/cmd/ocr-bootstrap-spec.yml index 9db118b77cd..058fdeb2c14 100644 --- a/core/cmd/ocr-bootstrap-spec.yml +++ b/core/cmd/ocr-bootstrap-spec.yml @@ -4,7 +4,5 @@ contractAddress = "0x27548a32b9aD5D64c5945EaE9Da5337bc3169D15" externalJobID = "%s" name = "%s" evmChainID = "0" -p2pBootstrapPeers = [ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = true diff --git a/core/cmd/shell_remote_test.go b/core/cmd/shell_remote_test.go index 6143d678a90..6c8b46eda4c 100644 --- a/core/cmd/shell_remote_test.go +++ b/core/cmd/shell_remote_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hashicorp/consul/sdk/freeport" "github.com/kylelemons/godebug/diff" "github.com/pelletier/go-toml" "github.com/stretchr/testify/assert" @@ -62,7 +63,6 @@ func startNewApplicationV2(t *testing.T, overrideFn func(c *chainlink.Config, s c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(30 * time.Millisecond) f := false c.EVM[0].Enabled = &f - c.P2P.V1.Enabled = &f c.P2P.V2.Enabled = &f if overrideFn != nil { @@ -567,7 +567,8 @@ func TestShell_RunOCRJob_HappyPath(t *testing.T) { app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].Enabled = ptr(true) c.OCR.Enabled = ptr(true) - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} c.P2P.PeerID = &cltest.DefaultP2PPeerID c.EVM[0].GasEstimator.Mode = ptr("FixedPrice") }, func(opts *startOptions) { diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index e438f4553fe..953f1feabd8 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -394,13 +394,7 @@ CaptureEATelemetry = false # Default # TraceLogging enables trace level logging. TraceLogging = false # Default -# P2P supports multiple networking stack versions. You may configure `[P2P.V1]`, `[P2P.V2]`, or both to run simultaneously. -# If both are configured, then for each link with another peer, V2 networking will be preferred. If V2 does not work, the link will -# automatically fall back to V1. If V2 starts working again later, it will automatically be preferred again. This is useful -# for migrating networks without downtime. Note that the two networking stacks _must not_ be configured to bind to the same IP/port. -# -# Note: P2P.V1 is deprecated will be removed in the future. -# +# P2P has a versioned networking stack. Currenly only `[P2P.V2]` is supported. # All nodes in the OCR network should share the same networking stack. [P2P] # IncomingMessageBufferSize is the per-remote number of incoming @@ -419,50 +413,6 @@ PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' # Example # TraceLogging enables trace level logging. TraceLogging = false # Default -# P2P.V1 is deprecated and will be removed in a future version. -[P2P.V1] -# Enabled enables P2P V1. -Enabled = false # Default -# AnnounceIP should be set as the externally reachable IP address of the Chainlink node. -AnnounceIP = '1.2.3.4' # Example -# AnnouncePort should be set as the externally reachable port of the Chainlink node. -AnnouncePort = 1337 # Example -# BootstrapCheckInterval is the interval at which nodes check connections to bootstrap nodes and reconnect if any of them is lost. -# Setting this to a small value would allow newly joined bootstrap nodes to get more connectivity -# more quickly, which helps to make bootstrap process faster. The cost of this operation is relatively -# cheap. We set this to 1 minute during our test. -BootstrapCheckInterval = '20s' # Default -# DefaultBootstrapPeers is the default set of bootstrap peers. -DefaultBootstrapPeers = ['/dns4/example.com/tcp/1337/p2p/12D3KooWMHMRLQkgPbFSYHwD3NBuwtS1AmxhvKVUrcfyaGDASR4U', '/ip4/1.2.3.4/tcp/9999/p2p/12D3KooWLZ9uTC3MrvKfDpGju6RAQubiMDL7CuJcAgDRTYP7fh7R'] # Example -# DHTAnnouncementCounterUserPrefix can be used to restore the node's -# ability to announce its IP/port on the P2P network after a database -# rollback. Make sure to only increase this value, and *never* decrease it. -# Don't use this variable unless you really know what you're doing, since you -# could semi-permanently exclude your node from the P2P network by -# misconfiguring it. -DHTAnnouncementCounterUserPrefix = 0 # Default -# **ADVANCED** -# DHTLookupInterval is the interval between which we do the expensive peer -# lookup using DHT. -# -# Every DHTLookupInterval failures to open a stream to a peer, we will -# attempt to lookup its IP from DHT -DHTLookupInterval = 10 # Default -# ListenIP is the default IP address to bind to. -ListenIP = '0.0.0.0' # Default -# ListenPort is the port to listen on. If left blank, the node randomly selects a different port each time it boots. It is highly recommended to set this to a static value to avoid network instability. -ListenPort = 1337 # Example -# **ADVANCED** -# NewStreamTimeout is the maximum length of time to wait to open a -# stream before we give up. -# We shouldn't hit this in practice since libp2p will give up fast if -# it can't get a connection, but it is here anyway as a failsafe. -# Set to 0 to disable any timeout on top of what libp2p gives us by default. -NewStreamTimeout = '10s' # Default -# **ADVANCED** -# PeerstoreWriteInterval controls how often the peerstore for the OCR V1 networking stack is persisted to the database. -PeerstoreWriteInterval = '5m' # Default - [P2P.V2] # Enabled enables P2P V2. # Note: V1.Enabled is true by default, so it must be set false in order to run V2 only. diff --git a/core/config/p2p_config.go b/core/config/p2p_config.go index 30693b3a259..4a7ec284173 100644 --- a/core/config/p2p_config.go +++ b/core/config/p2p_config.go @@ -1,15 +1,11 @@ package config import ( - ocrnetworking "github.com/smartcontractkit/libocr/networking" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) type P2P interface { V2() V2 - V1() V1 - NetworkStack() (n ocrnetworking.NetworkingStack) PeerID() p2pkey.PeerID IncomingMessageBufferSize() int OutgoingMessageBufferSize() int diff --git a/core/config/p2p_v1_config.go b/core/config/p2p_v1_config.go deleted file mode 100644 index 2e138285182..00000000000 --- a/core/config/p2p_v1_config.go +++ /dev/null @@ -1,20 +0,0 @@ -package config - -import ( - "net" - "time" -) - -type V1 interface { - Enabled() bool - AnnounceIP() net.IP - AnnouncePort() uint16 - DefaultBootstrapPeers() ([]string, error) - DHTAnnouncementCounterUserPrefix() uint32 - ListenIP() net.IP - ListenPort() uint16 - NewStreamTimeout() time.Duration - BootstrapCheckInterval() time.Duration - DHTLookupInterval() int - PeerstoreWriteInterval() time.Duration -} diff --git a/core/config/toml/types.go b/core/config/toml/types.go index c420d7f3f47..e944b44853b 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -13,7 +13,6 @@ import ( "go.uber.org/zap/zapcore" ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" - ocrnetworking "github.com/smartcontractkit/libocr/networking" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/config" @@ -1040,23 +1039,9 @@ type P2P struct { PeerID *p2pkey.PeerID TraceLogging *bool - V1 P2PV1 `toml:",omitempty"` V2 P2PV2 `toml:",omitempty"` } -func (p *P2P) NetworkStack() ocrnetworking.NetworkingStack { - v1, v2 := *p.V1.Enabled, *p.V2.Enabled - switch { - case v1 && v2: - return ocrnetworking.NetworkingStackV1V2 - case v2: - return ocrnetworking.NetworkingStackV2 - case v1: - return ocrnetworking.NetworkingStackV1 - } - return ocrnetworking.NetworkingStack(0) -} - func (p *P2P) setFrom(f *P2P) { if v := f.IncomingMessageBufferSize; v != nil { p.IncomingMessageBufferSize = v @@ -1071,68 +1056,9 @@ func (p *P2P) setFrom(f *P2P) { p.TraceLogging = v } - p.V1.setFrom(&f.V1) p.V2.setFrom(&f.V2) } -type P2PV1 struct { - Enabled *bool - AnnounceIP *net.IP - AnnouncePort *uint16 - BootstrapCheckInterval *models.Duration - DefaultBootstrapPeers *[]string - DHTAnnouncementCounterUserPrefix *uint32 - DHTLookupInterval *int64 - ListenIP *net.IP - ListenPort *uint16 - NewStreamTimeout *models.Duration - PeerstoreWriteInterval *models.Duration -} - -func (p *P2PV1) ValidateConfig() (err error) { - //TODO or empty? - if p.AnnouncePort != nil && p.AnnounceIP == nil { - err = multierr.Append(err, configutils.ErrMissing{Name: "AnnounceIP", Msg: fmt.Sprintf("required when AnnouncePort is set: %d", *p.AnnouncePort)}) - } - return -} - -func (p *P2PV1) setFrom(f *P2PV1) { - if v := f.Enabled; v != nil { - p.Enabled = v - } - if v := f.AnnounceIP; v != nil { - p.AnnounceIP = v - } - if v := f.AnnouncePort; v != nil { - p.AnnouncePort = v - } - if v := f.BootstrapCheckInterval; v != nil { - p.BootstrapCheckInterval = v - } - if v := f.DefaultBootstrapPeers; v != nil { - p.DefaultBootstrapPeers = v - } - if v := f.DHTAnnouncementCounterUserPrefix; v != nil { - p.DHTAnnouncementCounterUserPrefix = v - } - if v := f.DHTLookupInterval; v != nil { - p.DHTLookupInterval = v - } - if v := f.ListenIP; v != nil { - p.ListenIP = v - } - if v := f.ListenPort; v != nil { - p.ListenPort = v - } - if v := f.NewStreamTimeout; v != nil { - p.NewStreamTimeout = v - } - if v := f.PeerstoreWriteInterval; v != nil { - p.PeerstoreWriteInterval = v - } -} - type P2PV2 struct { Enabled *bool AnnounceAddresses *[]string diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index f0ce8c4ff66..c169c5fd228 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -339,7 +339,7 @@ func MustInsertOffchainreportingOracleSpec(t *testing.T, db *sqlx.DB, transmitte ocrKeyID := models.MustSha256HashFromHex(DefaultOCRKeyBundleID) spec := job.OCROracleSpec{} - require.NoError(t, db.Get(&spec, `INSERT INTO ocr_oracle_specs (created_at, updated_at, contract_address, p2p_bootstrap_peers, is_bootstrap_peer, encrypted_ocr_key_bundle_id, transmitter_address, observation_timeout, blockchain_timeout, contract_config_tracker_subscribe_interval, contract_config_tracker_poll_interval, contract_config_confirmations, database_timeout, observation_grace_period, contract_transmitter_transmit_timeout, evm_chain_id) VALUES ( + require.NoError(t, db.Get(&spec, `INSERT INTO ocr_oracle_specs (created_at, updated_at, contract_address, p2pv2_bootstrappers, is_bootstrap_peer, encrypted_ocr_key_bundle_id, transmitter_address, observation_timeout, blockchain_timeout, contract_config_tracker_subscribe_interval, contract_config_tracker_poll_interval, contract_config_confirmations, database_timeout, observation_grace_period, contract_transmitter_transmit_timeout, evm_chain_id) VALUES ( NOW(),NOW(),$1,'{}',false,$2,$3,0,0,0,0,0,0,0,0,0 ) RETURNING *`, NewEIP55Address(), &ocrKeyID, &transmitterAddress)) return spec diff --git a/core/internal/cltest/job_factories.go b/core/internal/cltest/job_factories.go index a9e403fb608..399e71ff216 100644 --- a/core/internal/cltest/job_factories.go +++ b/core/internal/cltest/job_factories.go @@ -25,7 +25,7 @@ const ( contractAddress = "%s" evmChainID = "0" p2pPeerID = "%s" - p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] + p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false transmitterAddress = "%s" keyBundleID = "%s" diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 35fd3a31ff8..7b29fdae4fa 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -37,7 +37,6 @@ import ( ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" "github.com/smartcontractkit/libocr/gethwrappers/testoffchainaggregator" - ocrnetworking "github.com/smartcontractkit/libocr/networking" "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" @@ -675,8 +674,8 @@ func setupOCRContracts(t *testing.T) (*bind.TransactOpts, *backends.SimulatedBac return owner, b, ocrContractAddress, ocrContract, flagsContract, flagsContractAddress } -func setupNode(t *testing.T, owner *bind.TransactOpts, portV1, portV2 int, - b *backends.SimulatedBackend, ns ocrnetworking.NetworkingStack, overrides func(c *chainlink.Config, s *chainlink.Secrets), +func setupNode(t *testing.T, owner *bind.TransactOpts, portV2 int, + b *backends.SimulatedBackend, overrides func(c *chainlink.Config, s *chainlink.Secrets), ) (*cltest.TestApplication, string, common.Address, ocrkey.KeyV2) { p2pKey := keystest.NewP2PKeyV2(t) config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { @@ -686,32 +685,10 @@ func setupNode(t *testing.T, owner *bind.TransactOpts, portV1, portV2 int, c.OCR2.Enabled = ptr(true) c.P2P.PeerID = ptr(p2pKey.PeerID()) - switch ns { - case ocrnetworking.NetworkingStackV1: - c.P2P.V1.Enabled = ptr(true) - c.P2P.V2.Enabled = ptr(false) - // We want to quickly poll for the bootstrap node to come up, but if we poll too quickly - // we'll flood it with messages and slow things down. 5s is about how long it takes the - // bootstrap node to come up. - c.P2P.V1.BootstrapCheckInterval = models.MustNewDuration(5 * time.Second) - c.P2P.V1.ListenPort = ptr(uint16(portV1)) - - case ocrnetworking.NetworkingStackV2: - c.P2P.V1.Enabled = ptr(false) - c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) - - case ocrnetworking.NetworkingStackV1V2: - c.P2P.V1.Enabled = ptr(true) - c.P2P.V2.Enabled = ptr(true) - c.P2P.V1.BootstrapCheckInterval = models.MustNewDuration(5 * time.Second) - // Note v1 and v2 ports must be distinct, - // v1v2 mode will listen on both. - c.P2P.V1.ListenPort = ptr(uint16(portV1)) - c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) - } + + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} + c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) // GracePeriod < ObservationTimeout c.EVM[0].OCR.ObservationGracePeriod = models.MustNewDuration(100 * time.Millisecond) @@ -743,21 +720,7 @@ func setupNode(t *testing.T, owner *bind.TransactOpts, portV1, portV2 int, return app, p2pKey.PeerID().Raw(), transmitter, key } -func setupForwarderEnabledNode( - t *testing.T, - owner *bind.TransactOpts, - portV1, - portV2 int, - b *backends.SimulatedBackend, - ns ocrnetworking.NetworkingStack, - overrides func(c *chainlink.Config, s *chainlink.Secrets), -) ( - *cltest.TestApplication, - string, - common.Address, - common.Address, - ocrkey.KeyV2, -) { +func setupForwarderEnabledNode(t *testing.T, owner *bind.TransactOpts, portV2 int, b *backends.SimulatedBackend, overrides func(c *chainlink.Config, s *chainlink.Secrets)) (*cltest.TestApplication, string, common.Address, common.Address, ocrkey.KeyV2) { p2pKey := keystest.NewP2PKeyV2(t) config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) // Disables ocr spec validation so we can have fast polling for the test. @@ -766,32 +729,9 @@ func setupForwarderEnabledNode( c.OCR2.Enabled = ptr(true) c.P2P.PeerID = ptr(p2pKey.PeerID()) - switch ns { - case ocrnetworking.NetworkingStackV1: - c.P2P.V1.Enabled = ptr(true) - c.P2P.V2.Enabled = ptr(false) - // We want to quickly poll for the bootstrap node to come up, but if we poll too quickly - // we'll flood it with messages and slow things down. 5s is about how long it takes the - // bootstrap node to come up. - c.P2P.V1.BootstrapCheckInterval = models.MustNewDuration(5 * time.Second) - c.P2P.V1.ListenPort = ptr(uint16(portV1)) - - case ocrnetworking.NetworkingStackV2: - c.P2P.V1.Enabled = ptr(false) - c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) - - case ocrnetworking.NetworkingStackV1V2: - c.P2P.V1.Enabled = ptr(true) - c.P2P.V2.Enabled = ptr(true) - c.P2P.V1.BootstrapCheckInterval = models.MustNewDuration(5 * time.Second) - // Note v1 and v2 ports must be distinct, - // v1v2 mode will listen on both. - c.P2P.V1.ListenPort = ptr(uint16(portV1)) - c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) - } + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} + c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) c.EVM[0].Transactions.ForwardersEnabled = ptr(true) @@ -842,16 +782,12 @@ func TestIntegration_OCR(t *testing.T) { testutils.SkipShort(t, "long test") t.Parallel() tests := []struct { - id int - portStart int // Test need to run in parallel, all need distinct port ranges. - name string - eip1559 bool - ns ocrnetworking.NetworkingStack + id int + name string + eip1559 bool }{ - {1, 20000, "legacy mode", false, ocrnetworking.NetworkingStackV1}, - {2, 20010, "eip1559 mode", true, ocrnetworking.NetworkingStackV1}, - {3, 20020, "legacy mode V1V2", false, ocrnetworking.NetworkingStackV1V2}, - {4, 20030, "legacy mode V2", false, ocrnetworking.NetworkingStackV2}, + {1, "legacy mode", false}, + {2, "eip1559 mode", true}, } numOracles := 4 @@ -859,31 +795,27 @@ func TestIntegration_OCR(t *testing.T) { test := tt t.Run(test.name, func(t *testing.T) { t.Parallel() - bootstrapNodePortV1 := freeport.GetOne(t) bootstrapNodePortV2 := freeport.GetOne(t) g := gomega.NewWithT(t) owner, b, ocrContractAddress, ocrContract, flagsContract, flagsContractAddress := setupOCRContracts(t) // Note it's plausible these ports could be occupied on a CI machine. // May need a port randomize + retry approach if we observe collisions. - appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV1, bootstrapNodePortV2, b, test.ns, nil) + appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV2, b, nil) var ( oracles []confighelper.OracleIdentityExtra transmitters []common.Address keys []ocrkey.KeyV2 apps []*cltest.TestApplication ) - ports := freeport.GetN(t, 2*numOracles) + ports := freeport.GetN(t, numOracles) for i := 0; i < numOracles; i++ { - portV1 := ports[2*i] - portV2 := ports[2*i+1] - app, peerID, transmitter, key := setupNode(t, owner, portV1, portV2, b, test.ns, func(c *chainlink.Config, s *chainlink.Secrets) { + app, peerID, transmitter, key := setupNode(t, owner, ports[i], b, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FlagsContractAddress = ptr(ethkey.EIP55AddressFromAddress(flagsContractAddress)) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(test.eip1559) - if test.ns != ocrnetworking.NetworkingStackV1 { - c.P2P.V2.DefaultBootstrappers = &[]ocrcommontypes.BootstrapperLocator{ - {PeerID: bootstrapPeerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePortV2)}}, - } + + c.P2P.V2.DefaultBootstrappers = &[]ocrcommontypes.BootstrapperLocator{ + {PeerID: bootstrapPeerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePortV2)}}, } }) @@ -1007,9 +939,6 @@ name = "web oracle spec" contractAddress = "%s" evmChainID = "%s" isBootstrapPeer = false -p2pBootstrapPeers = [ - "/ip4/127.0.0.1/tcp/%d/p2p/%s" -] keyBundleID = "%s" transmitterAddress = "%s" observationTimeout = "100ms" @@ -1031,7 +960,7 @@ observationSource = """ answer1 [type=median index=0]; """ -`, ocrContractAddress, testutils.SimulatedChainID.String(), bootstrapNodePortV1, bootstrapPeerID, keys[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) +`, ocrContractAddress, testutils.SimulatedChainID.String(), keys[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) require.NoError(t, err) jb.Name = null.NewString("testocr", true) err = apps[i].AddJobV2(testutils.Context(t), &jb) @@ -1084,14 +1013,13 @@ func TestIntegration_OCR_ForwarderFlow(t *testing.T) { t.Parallel() numOracles := 4 t.Run("ocr_forwarder_flow", func(t *testing.T) { - bootstrapNodePortV1 := freeport.GetOne(t) bootstrapNodePortV2 := freeport.GetOne(t) g := gomega.NewWithT(t) owner, b, ocrContractAddress, ocrContract, flagsContract, flagsContractAddress := setupOCRContracts(t) // Note it's plausible these ports could be occupied on a CI machine. // May need a port randomize + retry approach if we observe collisions. - appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV1, bootstrapNodePortV2, b, ocrnetworking.NetworkingStackV2, nil) + appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV2, b, nil) var ( oracles []confighelper.OracleIdentityExtra @@ -1100,11 +1028,9 @@ func TestIntegration_OCR_ForwarderFlow(t *testing.T) { keys []ocrkey.KeyV2 apps []*cltest.TestApplication ) - ports := freeport.GetN(t, 2*numOracles) + ports := freeport.GetN(t, numOracles) for i := 0; i < numOracles; i++ { - portV1 := ports[2*i] - portV2 := ports[2*i+1] - app, peerID, transmitter, forwarder, key := setupForwarderEnabledNode(t, owner, portV1, portV2, b, ocrnetworking.NetworkingStackV2, func(c *chainlink.Config, s *chainlink.Secrets) { + app, peerID, transmitter, forwarder, key := setupForwarderEnabledNode(t, owner, ports[i], b, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = ptr(true) c.EVM[0].FlagsContractAddress = ptr(ethkey.EIP55AddressFromAddress(flagsContractAddress)) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) @@ -1241,9 +1167,6 @@ contractAddress = "%s" evmChainID = "%s" forwardingAllowed = true isBootstrapPeer = false -p2pBootstrapPeers = [ - "/ip4/127.0.0.1/tcp/%d/p2p/%s" -] keyBundleID = "%s" transmitterAddress = "%s" observationTimeout = "100ms" @@ -1265,7 +1188,7 @@ observationSource = """ answer1 [type=median index=0]; """ -`, ocrContractAddress, testutils.SimulatedChainID.String(), bootstrapNodePortV1, bootstrapPeerID, keys[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) +`, ocrContractAddress, testutils.SimulatedChainID.String(), keys[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) require.NoError(t, err) jb.Name = null.NewString("testocr", true) err = apps[i].AddJobV2(testutils.Context(t), &jb) diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 387b15f76c8..295bb7fb14a 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -119,7 +119,6 @@ func setupNodeOCR2( c.OCR2.Enabled = ptr(true) c.P2P.PeerID = ptr(p2pKey.PeerID()) - c.P2P.V1.Enabled = ptr(false) c.P2P.V2.Enabled = ptr(true) c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) diff --git a/core/internal/testutils/configtest/general_config.go b/core/internal/testutils/configtest/general_config.go index 93d388b2d30..f076521b71c 100644 --- a/core/internal/testutils/configtest/general_config.go +++ b/core/internal/testutils/configtest/general_config.go @@ -56,7 +56,6 @@ func overrides(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ReaperInterval = models.MustNewDuration(0) - c.P2P.V1.Enabled = ptr(false) c.P2P.V2.Enabled = ptr(false) c.WebServer.SessionTimeout = models.MustNewDuration(2 * time.Minute) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 4b44c7a00ec..89ddc54ca7e 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -22,7 +22,7 @@ require ( github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 + github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 @@ -70,7 +70,7 @@ require ( github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 // indirect + github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect github.com/cockroachdb/redact v1.1.3 // indirect github.com/cometbft/cometbft v0.37.2 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect @@ -88,7 +88,6 @@ require ( github.com/danieljoos/wincred v1.1.2 // indirect github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/deckarep/golang-set v1.8.0 // indirect github.com/deckarep/golang-set/v2 v2.3.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect @@ -102,7 +101,6 @@ require ( github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/esote/minmaxheap v1.0.0 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect @@ -146,7 +144,6 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -164,10 +161,8 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -175,14 +170,11 @@ require ( github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.2 // indirect + github.com/holiman/uint256 v1.2.3 // indirect github.com/huandu/skiplist v1.2.0 // indirect - github.com/huin/goupnp v1.0.3 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/go-cid v0.0.7 // indirect - github.com/ipfs/go-datastore v0.4.5 // indirect - github.com/ipfs/go-ipfs-util v0.0.2 // indirect - github.com/ipfs/go-ipns v0.0.2 // indirect github.com/ipfs/go-log v1.0.4 // indirect github.com/ipfs/go-log/v2 v2.1.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -194,58 +186,21 @@ require ( github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/pgx/v4 v4.18.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect - github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/koron/go-ssdp v0.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/libp2p/go-addr-util v0.0.2 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-cidranger v1.1.0 // indirect - github.com/libp2p/go-conn-security-multistream v0.2.0 // indirect - github.com/libp2p/go-eventbus v0.2.1 // indirect - github.com/libp2p/go-flow-metrics v0.0.3 // indirect - github.com/libp2p/go-libp2p v0.13.0 // indirect - github.com/libp2p/go-libp2p-asn-util v0.0.0-20201026210036-4f868c957324 // indirect - github.com/libp2p/go-libp2p-autonat v0.4.0 // indirect - github.com/libp2p/go-libp2p-blankhost v0.2.0 // indirect - github.com/libp2p/go-libp2p-circuit v0.4.0 // indirect github.com/libp2p/go-libp2p-core v0.8.5 // indirect - github.com/libp2p/go-libp2p-discovery v0.5.0 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.11.1 // indirect - github.com/libp2p/go-libp2p-kbucket v0.4.7 // indirect - github.com/libp2p/go-libp2p-loggables v0.1.0 // indirect - github.com/libp2p/go-libp2p-mplex v0.4.1 // indirect - github.com/libp2p/go-libp2p-nat v0.0.6 // indirect - github.com/libp2p/go-libp2p-noise v0.1.2 // indirect github.com/libp2p/go-libp2p-peerstore v0.2.7 // indirect - github.com/libp2p/go-libp2p-pnet v0.2.0 // indirect - github.com/libp2p/go-libp2p-record v0.1.3 // indirect - github.com/libp2p/go-libp2p-swarm v0.4.0 // indirect - github.com/libp2p/go-libp2p-tls v0.1.3 // indirect - github.com/libp2p/go-libp2p-transport-upgrader v0.4.0 // indirect - github.com/libp2p/go-libp2p-yamux v0.5.1 // indirect - github.com/libp2p/go-mplex v0.3.0 // indirect - github.com/libp2p/go-msgio v0.0.6 // indirect - github.com/libp2p/go-nat v0.0.5 // indirect - github.com/libp2p/go-netroute v0.1.4 // indirect github.com/libp2p/go-openssl v0.0.7 // indirect - github.com/libp2p/go-reuseport v0.0.2 // indirect - github.com/libp2p/go-reuseport-transport v0.0.4 // indirect - github.com/libp2p/go-sockaddr v0.1.0 // indirect - github.com/libp2p/go-stream-muxer-multistream v0.3.0 // indirect - github.com/libp2p/go-tcp-transport v0.2.1 // indirect - github.com/libp2p/go-ws-transport v0.4.0 // indirect - github.com/libp2p/go-yamux/v2 v2.0.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -266,12 +221,10 @@ require ( github.com/multiformats/go-base32 v0.0.3 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-multiaddr v0.3.3 // indirect - github.com/multiformats/go-multiaddr-dns v0.2.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multiaddr-net v0.2.0 // indirect github.com/multiformats/go-multibase v0.0.3 // indirect github.com/multiformats/go-multihash v0.0.14 // indirect - github.com/multiformats/go-multistream v0.2.0 // indirect github.com/multiformats/go-varint v0.0.6 // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect github.com/oklog/run v1.1.0 // indirect @@ -337,8 +290,6 @@ require ( github.com/ulule/limiter/v3 v3.11.2 // indirect github.com/unrolled/secure v1.13.0 // indirect github.com/valyala/fastjson v1.4.1 // indirect - github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect - github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zondax/hid v0.9.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 78faaf6839a..5f0e22fba03 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -75,7 +75,6 @@ github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo8 github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= -github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -105,7 +104,6 @@ github.com/Depado/ginprom v1.7.11/go.mod h1:49mxL3NTQwDrhpDbY4V1mAIB3us9B+b2hP1+ github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= @@ -230,15 +228,16 @@ github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b80 github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= -github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= +github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= +github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= @@ -257,7 +256,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -306,9 +304,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= -github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= -github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= @@ -323,8 +318,6 @@ github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFM github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= -github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= -github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= @@ -334,7 +327,6 @@ github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KP github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -390,8 +382,6 @@ github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBd github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= -github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= @@ -591,10 +581,6 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -github.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= -github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -674,15 +660,11 @@ github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= -github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -698,9 +680,6 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= @@ -735,17 +714,15 @@ github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gt github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.2 h1:TXKcSGc2WaxPD2+bmzAsVthL4+pEN0YwXcL5qED83vk= -github.com/holiman/uint256 v1.2.2/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -758,45 +735,18 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.5 h1:cwOUcGMLdLPWgu3SlrCckCMznaGADbPqE0r8h768/Dg= -github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= -github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= -github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= -github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= -github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= -github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= -github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= -github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipns v0.0.2 h1:oq4ErrV4hNQ2Eim257RTYRgfOSV/s8BDaf9iIl4NwFs= -github.com/ipfs/go-ipns v0.0.2/go.mod h1:WChil4e0/m9cIINWLxZe1Jtf77oz5L05rO2ei/uKJ5U= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= @@ -860,19 +810,10 @@ github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= -github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= -github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= -github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= -github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -903,7 +844,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= @@ -927,9 +867,6 @@ github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZX github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.2 h1:fL3wAoyT6hXHQlORyXUW4Q23kkQpJRgEAYcZB5BR71o= -github.com/koron/go-ssdp v0.0.2/go.mod h1:XoLfkAiA2KeZsYh4DbHxD7h3nR2AZNqVQOa+LJuqPYs= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -958,211 +895,22 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= -github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= -github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= -github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M= -github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc= -github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= -github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= -github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= -github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= -github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= -github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= -github.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0= -github.com/libp2p/go-libp2p v0.13.0 h1:tDdrXARSghmusdm0nf1U/4M8aj8Rr0V2IzQOXmbzQ3s= -github.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo= -github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= -github.com/libp2p/go-libp2p-asn-util v0.0.0-20201026210036-4f868c957324 h1:2H/P+forDWBHije1WULwPfGduByUmC4fthndHVRpYNU= -github.com/libp2p/go-libp2p-asn-util v0.0.0-20201026210036-4f868c957324/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= -github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= -github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= -github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= -github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= -github.com/libp2p/go-libp2p-autonat v0.4.0 h1:3y8XQbpr+ssX8QfZUHekjHCYK64sj6/4hnf/awD4+Ug= -github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= -github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= -github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= -github.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk= -github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= -github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= -github.com/libp2p/go-libp2p-circuit v0.4.0 h1:eqQ3sEYkGTtybWgr6JLqJY6QLtPWRErvFjFDfAOO1wc= -github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= -github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= -github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= -github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= -github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= -github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= -github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= -github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= -github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= -github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ= -github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-kad-dht v0.11.1 h1:FsriVQhOUZpCotWIjyFSjEDNJmUzuMma/RyyTDZanwc= -github.com/libp2p/go-libp2p-kad-dht v0.11.1/go.mod h1:5ojtR2acDPqh/jXf5orWy8YGb8bHQDS+qeDcoscL/PI= -github.com/libp2p/go-libp2p-kbucket v0.4.7 h1:spZAcgxifvFZHBD8tErvppbnNiKA5uokDu3CV7axu70= -github.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk= -github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= -github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= -github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= -github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= -github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= -github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= -github.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs= -github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= -github.com/libp2p/go-libp2p-mplex v0.4.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc= -github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU= -github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= -github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= -github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= -github.com/libp2p/go-libp2p-noise v0.1.2 h1:IH9GRihQJTx56obm+GnpdPX4KeVIlvpXrP6xnJ0wxWk= -github.com/libp2p/go-libp2p-noise v0.1.2/go.mod h1:9B10b7ueo7TIxZHHcjcDCo5Hd6kfKT2m77by82SFRfE= -github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= -github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= -github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= -github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= -github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-peerstore v0.2.7 h1:83JoLxyR9OYTnNfB5vvFqvMUv/xDNa6NoPHnENhBsGw= github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= -github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0= -github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4= -github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= -github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= -github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= -github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= -github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= -github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= -github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= -github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= -github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= -github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.4.0 h1:hahq/ijRoeH6dgROOM8x7SeaKK5VgjjIr96vdrT+NUA= -github.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw= -github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= -github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= -github.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ= -github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= -github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM= -github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= -github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= -github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= -github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= -github.com/libp2p/go-libp2p-transport-upgrader v0.4.0 h1:xwj4h3hJdBrxqMOyMUjwscjoVst0AASTsKtZiTChoHI= -github.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s= -github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= -github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= -github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= -github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= -github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= -github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= -github.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0= -github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4= -github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= -github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= -github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU= -github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6 h1:lQ7Uc0kS1wb1EfRxO2Eir/RJoHkHn7t6o+EiwsYIKJA= github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q= -github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.4 h1:47V0+hJfYaqj1WO0A+cDkRc9xr9qKiK7i8zaoGv8Mmo= -github.com/libp2p/go-netroute v0.1.4/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= -github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU= -github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= -github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= -github.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM= -github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0 h1:Y4s3/jNoryVRKEBrkJ576F17CPOaMIzUeCsg7dlTDj0= -github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= -github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= -github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY= -github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= -github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= -github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= -github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= -github.com/libp2p/go-tcp-transport v0.2.1 h1:ExZiVQV+h+qL16fzCWtd1HSzPsqWottJ8KXwWaVi8Ns= -github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M= -github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= -github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= -github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= -github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k= -github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= -github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE90LU= -github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= @@ -1170,7 +918,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f h1:tVvGiZQFjOXP+9YyGqSA6jE55x1XVxmoPYudncxrZ8U= @@ -1187,7 +934,6 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -1209,12 +955,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= @@ -1222,9 +964,6 @@ github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= @@ -1260,7 +999,6 @@ github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -1271,50 +1009,24 @@ github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= -github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= -github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= -github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= -github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= -github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= -github.com/multiformats/go-multistream v0.2.0 h1:6AuNmQVKUkRnddw2YiDjt5Elit40SFxMJkVnhmETXtU= -github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= -github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= @@ -1338,9 +1050,7 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -1348,9 +1058,7 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= @@ -1520,8 +1228,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= -github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= +github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 h1:AA7vf29c6lFsZm+MtIEtXtg6VUOQV6waJo5MUuHfRjQ= +github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7/go.mod h1:WcuWFMskcGK0MYZuH5hEhGJOzdJRUFeNEM4PAKlejI4= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= @@ -1530,9 +1238,7 @@ github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1559,7 +1265,6 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= -github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1637,8 +1342,8 @@ github.com/unrolled/secure v1.13.0 h1:sdr3Phw2+f8Px8HE5sd1EHdj1aV3yUwed/uZXChLFs github.com/unrolled/secure v1.13.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= @@ -1649,15 +1354,8 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1755,7 +1453,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -1767,26 +1464,18 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -1847,7 +1536,6 @@ golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1883,7 +1571,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1942,17 +1629,13 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1961,7 +1644,6 @@ golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1981,7 +1663,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2054,7 +1735,6 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -2230,7 +1910,6 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= @@ -2286,8 +1965,6 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= -gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 5d8b1019e8c..b2f60384ec8 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -76,42 +76,7 @@ func (c *Config) valueWarnings() (err error) { // deprecationWarnings returns an error if the Config contains deprecated fields. // This is typically used before defaults have been applied, with input from the user. func (c *Config) deprecationWarnings() (err error) { - if c.P2P.V1 != (toml.P2PV1{}) { - err = multierr.Append(err, config.ErrDeprecated{Name: "P2P.V1"}) - var err2 error - if c.P2P.V1.AnnounceIP != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "AnnounceIP"}) - } - if c.P2P.V1.AnnouncePort != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "AnnouncePort"}) - } - if c.P2P.V1.BootstrapCheckInterval != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "BootstrapCheckInterval"}) - } - if c.P2P.V1.DefaultBootstrapPeers != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "DefaultBootstrapPeers"}) - } - if c.P2P.V1.DHTAnnouncementCounterUserPrefix != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "DHTAnnouncementCounterUserPrefix"}) - } - if c.P2P.V1.DHTLookupInterval != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "DHTLookupInterval"}) - } - if c.P2P.V1.ListenIP != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "ListenIP"}) - } - if c.P2P.V1.ListenPort != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "ListenPort"}) - } - if c.P2P.V1.NewStreamTimeout != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "NewStreamTimeout"}) - } - if c.P2P.V1.PeerstoreWriteInterval != nil { - err2 = multierr.Append(err2, config.ErrDeprecated{Name: "PeerstoreWriteInterval"}) - } - err2 = config.NamedMultiErrorList(err2, "P2P.V1") - err = multierr.Append(err, err2) - } + // none return } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index c7d9cc6ce5d..5df2b177bdd 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -13,8 +13,6 @@ import ( "go.uber.org/multierr" "go.uber.org/zap/zapcore" - ocrnetworking "github.com/smartcontractkit/libocr/networking" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" starknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" @@ -443,14 +441,6 @@ func (g *generalConfig) P2P() config.P2P { return &p2p{c: g.c.P2P} } -func (g *generalConfig) P2PNetworkingStack() (n ocrnetworking.NetworkingStack) { - return g.c.P2P.NetworkStack() -} - -func (g *generalConfig) P2PNetworkingStackRaw() string { - return g.c.P2P.NetworkStack().String() -} - func (g *generalConfig) P2PPeerID() p2pkey.PeerID { return *g.c.P2P.PeerID } diff --git a/core/services/chainlink/config_p2p.go b/core/services/chainlink/config_p2p.go index 35b80cc5f79..596a4fe0cae 100644 --- a/core/services/chainlink/config_p2p.go +++ b/core/services/chainlink/config_p2p.go @@ -1,11 +1,7 @@ package chainlink import ( - "net" - "time" - "github.com/smartcontractkit/libocr/commontypes" - ocrnetworking "github.com/smartcontractkit/libocr/networking" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" @@ -18,11 +14,7 @@ type p2p struct { } func (p *p2p) Enabled() bool { - return p.V1().Enabled() || p.V2().Enabled() -} - -func (p *p2p) NetworkStack() (n ocrnetworking.NetworkingStack) { - return p.c.NetworkStack() + return p.V2().Enabled() } func (p *p2p) PeerID() p2pkey.PeerID { @@ -45,63 +37,6 @@ func (p *p2p) V2() config.V2 { return &p2pv2{p.c.V2} } -func (p *p2p) V1() config.V1 { - return &p2pv1{p.c.V1} -} - -type p2pv1 struct { - c toml.P2PV1 -} - -func (v *p2pv1) Enabled() bool { - return *v.c.Enabled -} - -func (v *p2pv1) AnnounceIP() net.IP { - return *v.c.AnnounceIP -} - -func (v *p2pv1) AnnouncePort() uint16 { - return *v.c.AnnouncePort -} - -func (v *p2pv1) DefaultBootstrapPeers() ([]string, error) { - p := *v.c.DefaultBootstrapPeers - if p == nil { - p = []string{} - } - return p, nil -} - -func (v *p2pv1) DHTAnnouncementCounterUserPrefix() uint32 { - return *v.c.DHTAnnouncementCounterUserPrefix -} - -func (v *p2pv1) ListenIP() net.IP { - return *v.c.ListenIP -} - -func (v *p2pv1) ListenPort() uint16 { - p := *v.c.ListenPort - return p -} - -func (v *p2pv1) NewStreamTimeout() time.Duration { - return v.c.NewStreamTimeout.Duration() -} - -func (v *p2pv1) BootstrapCheckInterval() time.Duration { - return v.c.BootstrapCheckInterval.Duration() -} - -func (v *p2pv1) DHTLookupInterval() int { - return int(*v.c.DHTLookupInterval) -} - -func (v *p2pv1) PeerstoreWriteInterval() time.Duration { - return v.c.PeerstoreWriteInterval.Duration() -} - type p2pv2 struct { c toml.P2PV2 } diff --git a/core/services/chainlink/config_p2p_test.go b/core/services/chainlink/config_p2p_test.go index 21ce8f17e48..c23f3296ef8 100644 --- a/core/services/chainlink/config_p2p_test.go +++ b/core/services/chainlink/config_p2p_test.go @@ -23,21 +23,6 @@ func TestP2PConfig(t *testing.T) { assert.Equal(t, 17, p2p.OutgoingMessageBufferSize()) assert.True(t, p2p.TraceLogging()) - v1 := p2p.V1() - assert.True(t, v1.Enabled()) - assert.Equal(t, "1.2.3.4", v1.AnnounceIP().String()) - assert.Equal(t, uint16(1234), v1.AnnouncePort()) - assert.Equal(t, time.Minute, v1.BootstrapCheckInterval()) - p, err := v1.DefaultBootstrapPeers() - require.NoError(t, err) - assert.Equal(t, []string{"foo", "bar", "should", "these", "be", "typed"}, p) - assert.Equal(t, uint32(4321), v1.DHTAnnouncementCounterUserPrefix()) - assert.Equal(t, 9, v1.DHTLookupInterval()) - assert.Equal(t, "4.3.2.1", v1.ListenIP().String()) - assert.Equal(t, uint16(9), v1.ListenPort()) - assert.Equal(t, time.Second, v1.NewStreamTimeout()) - assert.Equal(t, time.Minute, v1.PeerstoreWriteInterval()) - v2 := p2p.V2() assert.False(t, v2.Enabled()) assert.Equal(t, []string{"a", "b", "c"}, v2.AnnounceAddresses()) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 2966a896902..6453fe05e00 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -410,19 +410,6 @@ func TestConfig_Marshal(t *testing.T) { OutgoingMessageBufferSize: ptr[int64](17), PeerID: mustPeerID("12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw"), TraceLogging: ptr(true), - V1: toml.P2PV1{ - Enabled: ptr(true), - AnnounceIP: mustIP("1.2.3.4"), - AnnouncePort: ptr[uint16](1234), - BootstrapCheckInterval: models.MustNewDuration(time.Minute), - DefaultBootstrapPeers: &[]string{"foo", "bar", "should", "these", "be", "typed"}, - DHTAnnouncementCounterUserPrefix: ptr[uint32](4321), - DHTLookupInterval: ptr[int64](9), - ListenIP: mustIP("4.3.2.1"), - ListenPort: ptr[uint16](9), - NewStreamTimeout: models.MustNewDuration(time.Second), - PeerstoreWriteInterval: models.MustNewDuration(time.Minute), - }, V2: toml.P2PV2{ Enabled: ptr(false), AnnounceAddresses: &[]string{"a", "b", "c"}, @@ -872,19 +859,6 @@ OutgoingMessageBufferSize = 17 PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' TraceLogging = true -[P2P.V1] -Enabled = true -AnnounceIP = '1.2.3.4' -AnnouncePort = 1234 -BootstrapCheckInterval = '1m0s' -DefaultBootstrapPeers = ['foo', 'bar', 'should', 'these', 'be', 'typed'] -DHTAnnouncementCounterUserPrefix = 4321 -DHTLookupInterval = 9 -ListenIP = '4.3.2.1' -ListenPort = 9 -NewStreamTimeout = '1s' -PeerstoreWriteInterval = '1m0s' - [P2P.V2] Enabled = false AnnounceAddresses = ['a', 'b', 'c'] @@ -1320,19 +1294,7 @@ func Test_generalConfig_LogConfiguration(t *testing.T) { effective = "# Effective Configuration, with defaults applied:\n" warning = "# Configuration warning:\n" - deprecated = `2 errors: - - P2P.V1: is deprecated and will be removed in a future version - - P2P.V1: 10 errors: - - AnnounceIP: is deprecated and will be removed in a future version - - AnnouncePort: is deprecated and will be removed in a future version - - BootstrapCheckInterval: is deprecated and will be removed in a future version - - DefaultBootstrapPeers: is deprecated and will be removed in a future version - - DHTAnnouncementCounterUserPrefix: is deprecated and will be removed in a future version - - DHTLookupInterval: is deprecated and will be removed in a future version - - ListenIP: is deprecated and will be removed in a future version - - ListenPort: is deprecated and will be removed in a future version - - NewStreamTimeout: is deprecated and will be removed in a future version - - PeerstoreWriteInterval: is deprecated and will be removed in a future version` + deprecated = `` // none ) tests := []struct { name string @@ -1558,7 +1520,7 @@ func TestConfig_SetFrom(t *testing.T) { } } -func TestConfig_Warnings(t *testing.T) { +func TestConfig_warnings(t *testing.T) { tests := []struct { name string config Config @@ -1582,42 +1544,6 @@ func TestConfig_Warnings(t *testing.T) { }, expectedErrors: []string{"Tracing.TLSCertPath: invalid value (/path/to/cert.pem): must be empty when Tracing.Mode is 'unencrypted'"}, }, - { - name: "Deprecation warning - P2P.V1 fields set", - config: Config{ - Core: toml.Core{ - P2P: toml.P2P{ - V1: toml.P2PV1{ - Enabled: ptr(true), - }, - }, - }, - }, - expectedErrors: []string{ - "P2P.V1: is deprecated and will be removed in a future version", - }, - }, - { - name: "Value warning and deprecation warning", - config: Config{ - Core: toml.Core{ - P2P: toml.P2P{ - V1: toml.P2PV1{ - Enabled: ptr(true), - }, - }, - Tracing: toml.Tracing{ - Enabled: ptr(true), - Mode: ptr("unencrypted"), - TLSCertPath: ptr("/path/to/cert.pem"), - }, - }, - }, - expectedErrors: []string{ - "Tracing.TLSCertPath: invalid value (/path/to/cert.pem): must be empty when Tracing.Mode is 'unencrypted'", - "P2P.V1: is deprecated and will be removed in a future version", - }, - }, } for _, tt := range tests { diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index 2531e7c281d..9efad05c03a 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -161,19 +161,6 @@ OutgoingMessageBufferSize = 10 PeerID = '' TraceLogging = false -[P2P.V1] -Enabled = false -AnnounceIP = '' -AnnouncePort = 0 -BootstrapCheckInterval = '20s' -DefaultBootstrapPeers = [] -DHTAnnouncementCounterUserPrefix = 0 -DHTLookupInterval = 10 -ListenIP = '0.0.0.0' -ListenPort = 0 -NewStreamTimeout = '10s' -PeerstoreWriteInterval = '5m0s' - [P2P.V2] Enabled = true AnnounceAddresses = [] diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 46d9dc2c239..c6387e4ef67 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -167,19 +167,6 @@ OutgoingMessageBufferSize = 17 PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' TraceLogging = true -[P2P.V1] -Enabled = true -AnnounceIP = '1.2.3.4' -AnnouncePort = 1234 -BootstrapCheckInterval = '1m0s' -DefaultBootstrapPeers = ['foo', 'bar', 'should', 'these', 'be', 'typed'] -DHTAnnouncementCounterUserPrefix = 4321 -DHTLookupInterval = 9 -ListenIP = '4.3.2.1' -ListenPort = 9 -NewStreamTimeout = '1s' -PeerstoreWriteInterval = '1m0s' - [P2P.V2] Enabled = false AnnounceAddresses = ['a', 'b', 'c'] diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 74d83035cd5..40c18f28eb9 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -161,19 +161,6 @@ OutgoingMessageBufferSize = 10 PeerID = '' TraceLogging = false -[P2P.V1] -Enabled = false -AnnounceIP = '' -AnnouncePort = 0 -BootstrapCheckInterval = '20s' -DefaultBootstrapPeers = [] -DHTAnnouncementCounterUserPrefix = 0 -DHTLookupInterval = 10 -ListenIP = '0.0.0.0' -ListenPort = 0 -NewStreamTimeout = '10s' -PeerstoreWriteInterval = '5m0s' - [P2P.V2] Enabled = true AnnounceAddresses = [] diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index d811a4461fd..271321a1169 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -75,10 +75,7 @@ name = "%s" externalJobID = "%s" evmChainID = 0 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" -p2pBootstrapPeers = [ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] -p2pv2Bootstrappers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] keyBundleID = "f5bf259689b26f1374efb3c9a9868796953a0f814bb2d39b968d0e61b58620a5" transmitterAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" isBootstrapPeer = false diff --git a/core/services/job/helpers_test.go b/core/services/job/helpers_test.go index 4151ed401c8..a7543753d63 100644 --- a/core/services/job/helpers_test.go +++ b/core/services/job/helpers_test.go @@ -36,10 +36,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "%s" evmChainID = "0" -p2pBootstrapPeers = [ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] -p2pv2Bootstrappers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "%s" monitoringEndpoint = "chain.link:4321" @@ -108,7 +105,7 @@ ds1 -> ds1_parse -> ds1_multiply; type = "offchainreporting" schemaVersion = 1 contractAddress = "%s" - p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] + p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false transmitterAddress = "%s" keyBundleID = "%s" @@ -125,7 +122,6 @@ ds1 -> ds1_parse; schemaVersion = 1 contractAddress = "%s" evmChainID = "0" - p2pBootstrapPeers = [] isBootstrapPeer = true ` ocrJobSpecText = ` @@ -134,10 +130,7 @@ schemaVersion = 1 contractAddress = "%s" evmChainID = "0" p2pPeerID = "%s" -p2pBootstrapPeers = [ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] -p2pv2Bootstrappers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "%s" monitoringEndpoint = "chain.link:4321" @@ -194,7 +187,7 @@ func makeOCRJobSpec(t *testing.T, transmitterAddress common.Address, b1, b2 stri func compareOCRJobSpecs(t *testing.T, expected, actual job.Job) { require.NotNil(t, expected.OCROracleSpec) require.Equal(t, expected.OCROracleSpec.ContractAddress, actual.OCROracleSpec.ContractAddress) - require.Equal(t, expected.OCROracleSpec.P2PBootstrapPeers, actual.OCROracleSpec.P2PBootstrapPeers) + require.Equal(t, expected.OCROracleSpec.P2PV2Bootstrappers, actual.OCROracleSpec.P2PV2Bootstrappers) require.Equal(t, expected.OCROracleSpec.IsBootstrapPeer, actual.OCROracleSpec.IsBootstrapPeer) require.Equal(t, expected.OCROracleSpec.EncryptedOCRKeyBundleID, actual.OCROracleSpec.EncryptedOCRKeyBundleID) require.Equal(t, expected.OCROracleSpec.TransmitterAddress, actual.OCROracleSpec.TransmitterAddress) @@ -207,7 +200,6 @@ func compareOCRJobSpecs(t *testing.T, expected, actual job.Job) { func makeMinimalHTTPOracleSpec(t *testing.T, db *sqlx.DB, cfg chainlink.GeneralConfig, contractAddress, transmitterAddress, keyBundle, fetchUrl, timeout string) *job.Job { var ocrSpec = job.OCROracleSpec{ - P2PBootstrapPeers: pq.StringArray{}, P2PV2Bootstrappers: pq.StringArray{}, ObservationTimeout: models.Interval(10 * time.Second), BlockchainTimeout: models.Interval(20 * time.Second), diff --git a/core/services/job/models.go b/core/services/job/models.go index 05dcab831f1..18a0cb79e2a 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -237,7 +237,6 @@ func (pr *PipelineRun) SetID(value string) error { type OCROracleSpec struct { ID int32 `toml:"-"` ContractAddress ethkey.EIP55Address `toml:"contractAddress"` - P2PBootstrapPeers pq.StringArray `toml:"p2pBootstrapPeers" db:"p2p_bootstrap_peers"` P2PV2Bootstrappers pq.StringArray `toml:"p2pv2Bootstrappers" db:"p2pv2_bootstrappers"` IsBootstrapPeer bool `toml:"isBootstrapPeer"` EncryptedOCRKeyBundleID *models.Sha256Hash `toml:"keyBundleID"` diff --git a/core/services/job/orm.go b/core/services/job/orm.go index c5f533c3d20..482d3d851e4 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -212,10 +212,10 @@ func (o *orm) CreateJob(jb *Job, qopts ...pg.QOpt) error { return errors.Errorf("a job with contract address %s already exists for chain ID %s", jb.OCROracleSpec.ContractAddress, newChainID) } - sql := `INSERT INTO ocr_oracle_specs (contract_address, p2p_bootstrap_peers, p2pv2_bootstrappers, is_bootstrap_peer, encrypted_ocr_key_bundle_id, transmitter_address, + sql := `INSERT INTO ocr_oracle_specs (contract_address, p2pv2_bootstrappers, is_bootstrap_peer, encrypted_ocr_key_bundle_id, transmitter_address, observation_timeout, blockchain_timeout, contract_config_tracker_subscribe_interval, contract_config_tracker_poll_interval, contract_config_confirmations, evm_chain_id, created_at, updated_at, database_timeout, observation_grace_period, contract_transmitter_transmit_timeout) - VALUES (:contract_address, :p2p_bootstrap_peers, :p2pv2_bootstrappers, :is_bootstrap_peer, :encrypted_ocr_key_bundle_id, :transmitter_address, + VALUES (:contract_address, :p2pv2_bootstrappers, :is_bootstrap_peer, :encrypted_ocr_key_bundle_id, :transmitter_address, :observation_timeout, :blockchain_timeout, :contract_config_tracker_subscribe_interval, :contract_config_tracker_poll_interval, :contract_config_confirmations, :evm_chain_id, NOW(), NOW(), :database_timeout, :observation_grace_period, :contract_transmitter_transmit_timeout) RETURNING id;` diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index eb6af3607f3..ef0458312bc 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -14,6 +14,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hashicorp/consul/sdk/freeport" "github.com/pelletier/go-toml" "github.com/pkg/errors" "github.com/shopspring/decimal" @@ -58,11 +59,8 @@ func TestRunner(t *testing.T) { require.NoError(t, keyStore.OCR().Add(cltest.DefaultOCRKey)) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) - c.P2P.V1.DefaultBootstrapPeers = &[]string{ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", - "/dns4/chain.link/tcp/1235/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", - } + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} kb, err := keyStore.OCR().Create() require.NoError(t, err) kbid := models.MustSha256HashFromHex(kb.ID()) @@ -78,7 +76,10 @@ func TestRunner(t *testing.T) { ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(10), nil) ethClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(nil, nil) + ctx := testutils.Context(t) pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) + require.NoError(t, pipelineORM.Start(ctx)) + t.Cleanup(func() { assert.NoError(t, pipelineORM.Close()) }) btORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, Client: ethClient, GeneralConfig: config, KeyStore: ethKeyStore}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) @@ -86,10 +87,11 @@ func TestRunner(t *testing.T) { runner := pipeline.NewRunner(pipelineORM, btORM, config.JobPipeline(), config.WebServer(), legacyChains, nil, nil, logger.TestLogger(t), c, c) jobORM := NewTestORM(t, db, pipelineORM, btORM, keyStore, config.Database()) + t.Cleanup(func() { assert.NoError(t, jobORM.Close()) }) _, placeHolderAddress := cltest.MustInsertRandomKey(t, keyStore.Eth()) - require.NoError(t, runner.Start(testutils.Context(t))) + require.NoError(t, runner.Start(ctx)) t.Cleanup(func() { assert.NoError(t, runner.Close()) }) t.Run("gets the election result winner", func(t *testing.T) { @@ -545,7 +547,8 @@ answer1 [type=median index=0]; for _, tc := range testCases { config = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} c.OCR.CaptureEATelemetry = ptr(tc.specCaptureEATelemetry) }) diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index f473c93b1f0..d3d133e7121 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -14,7 +14,6 @@ import ( commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" - ocrnetworking "github.com/smartcontractkit/libocr/networking" ocr "github.com/smartcontractkit/libocr/offchainreporting" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" @@ -146,16 +145,6 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e return nil, errors.New("peerWrapper is not started. OCR jobs require a started and running p2p peer") } - var v1BootstrapPeers []string - if concreteSpec.P2PBootstrapPeers != nil { - v1BootstrapPeers = concreteSpec.P2PBootstrapPeers - } else { - v1BootstrapPeers, err = chain.Config().P2P().V1().DefaultBootstrapPeers() - if err != nil { - return nil, err - } - } - v2Bootstrappers, err := ocrcommon.ParseBootstrapPeers(concreteSpec.P2PV2Bootstrappers) if err != nil { return nil, err @@ -181,7 +170,6 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e var bootstrapper *ocr.BootstrapNode bootstrapper, err = ocr.NewBootstrapNode(ocr.BootstrapNodeArgs{ BootstrapperFactory: peerWrapper.Peer1, - V1Bootstrappers: v1BootstrapPeers, V2Bootstrappers: v2Bootstrappers, ContractConfigTracker: tracker, Database: ocrDB, @@ -194,20 +182,9 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e bootstrapperCtx := job.NewServiceAdapter(bootstrapper) services = append(services, bootstrapperCtx) } else { - // In V1 or V1V2 mode, p2pv1BootstrapPeers must be defined either in - // node config or in job spec - if peerWrapper.P2PConfig().NetworkStack() != ocrnetworking.NetworkingStackV2 { - if len(v1BootstrapPeers) < 1 { - return nil, errors.New("Need at least one v1 bootstrap peer defined") - } - } - - // In V1V2 or V2 mode, p2pv2Bootstrappers must be defined either in - // node config or in job spec - if peerWrapper.P2PConfig().NetworkStack() != ocrnetworking.NetworkingStackV1 { - if len(v2Bootstrappers) < 1 { - return nil, errors.New("Need at least one v2 bootstrap peer defined") - } + // p2pv2Bootstrappers must be defined either in node config or in job spec + if len(v2Bootstrappers) < 1 { + return nil, errors.New("Need at least one v2 bootstrap peer defined") } ocrkey, err := d.keyStore.OCR().Get(concreteSpec.EncryptedOCRKeyBundleID.String()) @@ -325,7 +302,6 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e PrivateKeys: ocrkey, BinaryNetworkEndpointFactory: peerWrapper.Peer1, Logger: ocrLogger, - V1Bootstrappers: v1BootstrapPeers, V2Bootstrappers: v2Bootstrappers, MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint("EVM", chain.ID().String(), concreteSpec.ContractAddress.String(), synchronization.OCR), ConfigOverrider: configOverrider, diff --git a/core/services/ocr/example-job-spec.toml b/core/services/ocr/example-job-spec.toml index f8854a0a0b2..7622c45f212 100644 --- a/core/services/ocr/example-job-spec.toml +++ b/core/services/ocr/example-job-spec.toml @@ -2,9 +2,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "73e8966a78ca09bb912e9565cfb79fbe8a6048fab1f0cf49b18047c3895e0447" monitoringEndpoint = "chain.link:4321" diff --git a/core/services/ocr/validate.go b/core/services/ocr/validate.go index a780ebb0821..0524ed24d0b 100644 --- a/core/services/ocr/validate.go +++ b/core/services/ocr/validate.go @@ -5,9 +5,9 @@ import ( "time" "github.com/lib/pq" - "github.com/multiformats/go-multiaddr" "github.com/pelletier/go-toml" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/offchainreporting" "github.com/smartcontractkit/chainlink/v2/common/config" @@ -77,11 +77,6 @@ func ValidatedOracleSpecTomlCfg(configFn func(id *big.Int) (evmconfig.ChainScope if !tree.Has("isBootstrapPeer") { return jb, errors.New("isBootstrapPeer is not defined") } - for i := range spec.P2PBootstrapPeers { - if _, err = multiaddr.NewMultiaddr(spec.P2PBootstrapPeers[i]); err != nil { - return jb, errors.Wrapf(err, "p2p bootstrap peer %v is invalid", spec.P2PBootstrapPeers[i]) - } - } if len(spec.P2PV2Bootstrappers) > 0 { _, err = ocrcommon.ParseBootstrapPeers(spec.P2PV2Bootstrappers) diff --git a/core/services/ocr/validate_test.go b/core/services/ocr/validate_test.go index 0164fd82c54..4d8a9ae26c8 100644 --- a/core/services/ocr/validate_test.go +++ b/core/services/ocr/validate_test.go @@ -60,9 +60,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ -"/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "73e8966a78ca09bb912e9565cfb79fbe8a6048fab1f0cf49b18047c3895e0447" monitoringEndpoint = "chain.link:4321" @@ -92,7 +90,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = true `, assertion: func(t *testing.T, os job.Job, err error) { @@ -108,9 +106,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ -"/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = true keyBundleID = "73e8966a78ca09bb912e9565cfb79fbe8a6048fab1f0cf49b18047c3895e0447" monitoringEndpoint = "chain.link:4321" @@ -136,7 +132,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false `, assertion: func(t *testing.T, os job.Job, err error) { @@ -150,28 +146,11 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false observationSource = """ -> """ -`, - assertion: func(t *testing.T, os job.Job, err error) { - require.Error(t, err) - }, - }, - { - name: "invalid v1 bootstrap peer address", - toml: ` -type = "offchainreporting" -schemaVersion = 1 -contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" -p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = ["/invalid/peer/address"] -isBootstrapPeer = false -observationSource = """ -blah -""" `, assertion: func(t *testing.T, os job.Job, err error) { require.Error(t, err) @@ -184,9 +163,6 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ -"/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] p2pv2Bootstrappers = ["invalid bootstrapper /#@ address"] isBootstrapPeer = false observationSource = """ @@ -204,7 +180,6 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] p2pv2Bootstrappers = [ "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001", ] @@ -225,7 +200,6 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] p2pv2Bootstrappers = [ "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001", ] @@ -246,7 +220,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false observationGracePeriod = "0s" observationSource = """ @@ -264,7 +238,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false contractTransmitterTransmitTimeout = "0s" observationSource = """ @@ -282,7 +256,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = ["/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju"] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false contractConfigTrackerSubscribeInterval = "0s" observationSource = """ @@ -300,13 +274,12 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [] -p2pv2Bootstrappers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = true monitoringEndpoint = "\t/fd\2ff )(*&^%$#@" `, assertion: func(t *testing.T, os job.Job, err error) { - require.EqualError(t, err, "toml error on load: (9, 23): invalid escape sequence: \\2") + require.EqualError(t, err, "toml error on load: (8, 23): invalid escape sequence: \\2") }, }, { @@ -317,9 +290,7 @@ maxTaskDuration = "30s" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ -"/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "73e8966a78ca09bb912e9565cfb79fbe8a6048fab1f0cf49b18047c3895e0447" monitoringEndpoint = "chain.link:4321" @@ -341,9 +312,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ -"/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "73e8966a78ca09bb912e9565cfb79fbe8a6048fab1f0cf49b18047c3895e0447" monitoringEndpoint = "chain.link:4321" @@ -372,9 +341,7 @@ type = "offchainreporting" schemaVersion = 1 contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" p2pPeerID = "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq" -p2pBootstrapPeers = [ -"/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "73e8966a78ca09bb912e9565cfb79fbe8a6048fab1f0cf49b18047c3895e0447" monitoringEndpoint = "chain.link:4321" diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index e576e829677..22a59feb3ae 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -318,7 +318,6 @@ func StartNewNode( c.OCR2.Enabled = ptr(true) c.P2P.PeerID = ptr(p2pKey.PeerID()) - c.P2P.V1.Enabled = ptr(false) c.P2P.V2.Enabled = ptr(true) c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index 1ab9194c496..58c1e38e017 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -123,7 +123,6 @@ func setupNode( c.OCR2.Enabled = ptr(true) c.P2P.PeerID = ptr(p2pKey.PeerID()) - c.P2P.V1.Enabled = ptr(false) c.P2P.V2.Enabled = ptr(true) c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index e93824283ba..4a583e5db3f 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -232,7 +232,6 @@ func setupNodeOCR2( c.Feature.LogPoller = ptr(true) c.P2P.PeerID = ptr(p2pKey.PeerID()) - c.P2P.V1.Enabled = ptr(false) c.P2P.V2.Enabled = ptr(true) c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index a7d510ef901..ff666eefdba 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -2,19 +2,17 @@ package ocrcommon import ( "context" + "crypto/ed25519" "fmt" "io" - "net" p2ppeer "github.com/libp2p/go-libp2p-core/peer" - p2ppeerstore "github.com/libp2p/go-libp2p-core/peerstore" "github.com/pkg/errors" "go.uber.org/multierr" "github.com/jmoiron/sqlx" ocrnetworking "github.com/smartcontractkit/libocr/networking" - ocrnetworkingtypes "github.com/smartcontractkit/libocr/networking/types" ocr1types "github.com/smartcontractkit/libocr/offchainreporting/types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -67,20 +65,8 @@ type ( ) func ValidatePeerWrapperConfig(config config.P2P) error { - switch config.NetworkStack() { - case ocrnetworking.NetworkingStackV1: - // Note: If P2PListenPort isn't set, the peer wrapper will generate a random one itself. - return nil - case ocrnetworking.NetworkingStackV2: - if len(config.V2().ListenAddresses()) == 0 { - return errors.New("networking stack v2 selected but no P2P.V2.ListenAddresses specified") - } - case ocrnetworking.NetworkingStackV1V2: - if len(config.V2().ListenAddresses()) == 0 { - return errors.New("networking stack v1v2 selected but no P2P.V2.ListenAddresses specified") - } - default: - return errors.New("unknown networking stack") + if len(config.V2().ListenAddresses()) == 0 { + return errors.New("no P2P.V2.ListenAddresses specified") } return nil } @@ -139,70 +125,16 @@ func (p *SingletonPeerWrapper) peerConfig() (ocrnetworking.PeerConfig, error) { } p.PeerID = key.PeerID() - v1 := p.p2pCfg.V1() - p2pPort := v1.ListenPort() - // We need to start the peer store wrapper if v1 is required. - // Also fallback to listen params if announce params not specified. - ns := p.p2pCfg.NetworkStack() - // NewPeer requires that these are both set or unset, otherwise it will error out. - v1AnnounceIP, v1AnnouncePort := v1.AnnounceIP(), v1.AnnouncePort() - var peerStore p2ppeerstore.Peerstore - if ns == ocrnetworking.NetworkingStackV1 || ns == ocrnetworking.NetworkingStackV1V2 { - p.pstoreWrapper, err = NewPeerstoreWrapper(p.db, v1.PeerstoreWriteInterval(), p.PeerID, p.lggr, p.dbConfig) - if err != nil { - return ocrnetworking.PeerConfig{}, errors.Wrap(err, "could not make new pstorewrapper") - } - if err = p.pstoreWrapper.Start(); err != nil { - return ocrnetworking.PeerConfig{}, errors.Wrap(err, "failed to start peer store wrapper") - } - - peerStore = p.pstoreWrapper.Peerstore - - // Use a random port if the port hasn't been set explicitly. - if p2pPort == 0 { - port, perr := p.randomPort() - if perr != nil { - return ocrnetworking.PeerConfig{}, perr - } - p2pPort = port - - p.lggr.Warnw( - fmt.Sprintf("P2PListenPort was not set, listening on random port %d. A new random port will be generated on every boot, for stability it is recommended to set P2PListenPort to a fixed value in your environment", p2pPort), - "p2pPort", - p2pPort, - ) - } - - // Support someone specifying only the announce IP but leaving out - // the port. - // We _should not_ handle the case of someone specifying only the - // port but leaving out the IP, because the listen IP is typically - // an unspecified IP (https://pkg.go.dev/net#IP.IsUnspecified) and - // using that for the announce IP will cause other peers to not be - // able to connect. - if v1AnnounceIP != nil && v1AnnouncePort == 0 { - v1AnnouncePort = p2pPort - } - } - - // Discover DB is only required for v2 - var discovererDB ocrnetworkingtypes.DiscovererDatabase - if ns == ocrnetworking.NetworkingStackV2 || ns == ocrnetworking.NetworkingStackV1V2 { - discovererDB = NewDiscovererDatabase(p.db.DB, p2ppeer.ID(p.PeerID)) - } + discovererDB := NewDiscovererDatabase(p.db.DB, p2ppeer.ID(p.PeerID)) config := p.p2pCfg + pk, err := key.PrivKey.Raw() + if err != nil { + return ocrnetworking.PeerConfig{}, fmt.Errorf("failed to get raw private key: %w", err) + } peerConfig := ocrnetworking.PeerConfig{ - NetworkingStack: config.NetworkStack(), - PrivKey: key.PrivKey, - Logger: commonlogger.NewOCRWrapper(p.lggr, p.ocrCfg.TraceLogging(), func(string) {}), - // V1 config - V1ListenIP: config.V1().ListenIP(), - V1ListenPort: p2pPort, - V1AnnounceIP: v1AnnounceIP, - V1AnnouncePort: v1AnnouncePort, - V1Peerstore: peerStore, - V1DHTAnnouncementCounterUserPrefix: config.V1().DHTAnnouncementCounterUserPrefix(), + PrivKey: ed25519.PrivateKey(pk), + Logger: commonlogger.NewOCRWrapper(p.lggr, p.ocrCfg.TraceLogging(), func(string) {}), // V2 config V2ListenAddresses: config.V2().ListenAddresses(), @@ -211,14 +143,6 @@ func (p *SingletonPeerWrapper) peerConfig() (ocrnetworking.PeerConfig, error) { V2DeltaDial: config.V2().DeltaDial().Duration(), V2DiscovererDatabase: discovererDB, - V1EndpointConfig: ocrnetworking.EndpointConfigV1{ - IncomingMessageBufferSize: config.IncomingMessageBufferSize(), - OutgoingMessageBufferSize: config.OutgoingMessageBufferSize(), - NewStreamTimeout: config.V1().NewStreamTimeout(), - DHTLookupInterval: config.V1().DHTLookupInterval(), - BootstrapCheckInterval: config.V1().BootstrapCheckInterval(), - }, - V2EndpointConfig: ocrnetworking.EndpointConfigV2{ IncomingMessageBufferSize: config.IncomingMessageBufferSize(), OutgoingMessageBufferSize: config.OutgoingMessageBufferSize(), @@ -228,20 +152,6 @@ func (p *SingletonPeerWrapper) peerConfig() (ocrnetworking.PeerConfig, error) { return peerConfig, nil } -func (p *SingletonPeerWrapper) randomPort() (uint16, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, fmt.Errorf("unexpected ResolveTCPAddr error generating random P2PListenPort: %w", err) - } - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, fmt.Errorf("unexpected ListenTCP error generating random P2PListenPort: %w", err) - } - defer l.Close() - - return uint16(l.Addr().(*net.TCPAddr).Port), nil -} - // Close closes the peer and peerstore func (p *SingletonPeerWrapper) Close() error { return p.StopOnce("SingletonPeerWrapper", func() (err error) { diff --git a/core/services/ocrcommon/peer_wrapper_test.go b/core/services/ocrcommon/peer_wrapper_test.go index 209bc6b969b..854ecb153ea 100644 --- a/core/services/ocrcommon/peer_wrapper_test.go +++ b/core/services/ocrcommon/peer_wrapper_test.go @@ -1,11 +1,12 @@ package ocrcommon_test import ( + "fmt" "testing" "time" + "github.com/hashicorp/consul/sdk/freeport" p2ppeer "github.com/libp2p/go-libp2p-core/peer" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -17,7 +18,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_SingletonPeerWrapper_Start(t *testing.T) { @@ -30,7 +30,7 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { t.Run("with no p2p keys returns error", func(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) @@ -39,14 +39,15 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { t.Run("with one p2p key and matching P2P.PeerID returns nil", func(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) k, err := keyStore.P2P().Create() require.NoError(t, err) cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} c.P2P.PeerID = ptr(k.PeerID()) }) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) @@ -58,7 +59,7 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { t.Run("with one p2p key and mismatching P2P.PeerID returns error", func(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) c.P2P.PeerID = ptr(p2pkey.PeerID(peerID)) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) @@ -73,14 +74,16 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { t.Run("with multiple p2p keys and valid P2P.PeerID returns nil", func(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) k2, err := keyStore.P2P().Create() require.NoError(t, err) cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} c.P2P.PeerID = ptr(k2.PeerID()) }) @@ -93,7 +96,8 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { t.Run("with multiple p2p keys and mismatching P2P.PeerID returns error", func(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} c.P2P.PeerID = ptr(p2pkey.PeerID(peerID)) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) @@ -122,7 +126,6 @@ func Test_SingletonPeerWrapper_Close(t *testing.T) { c.P2P.PeerID = ptr(k.PeerID()) c.P2P.V2.DeltaDial = models.MustNewDuration(100 * time.Millisecond) c.P2P.V2.DeltaReconcile = models.MustNewDuration(1 * time.Second) - c.P2P.V1.ListenPort = ptr[uint16](0) p2paddresses := []string{ "127.0.0.1:17193", @@ -146,75 +149,4 @@ func Test_SingletonPeerWrapper_Close(t *testing.T) { require.NoError(t, pw.Close()) } -func TestSingletonPeerWrapper_PeerConfig(t *testing.T) { - t.Parallel() - - db := pgtest.NewSqlxDB(t) - - require.NoError(t, utils.JustError(db.Exec(`DELETE FROM encrypted_key_rings`))) - - cfg := configtest.NewGeneralConfig(t, nil) - keyStore := cltest.NewKeyStore(t, db, cfg.Database()) - k, err := keyStore.P2P().Create() - require.NoError(t, err) - - t.Run("generates a random port if v1 enabled and listen port isn't set", func(t *testing.T) { - cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) - c.P2P.V1.ListenPort = ptr(uint16(0)) - c.P2P.PeerID = ptr(k.PeerID()) - }) - - pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) - peerConfig, err := pw.PeerConfig() - require.NoError(t, err) - - assert.NotEqual(t, peerConfig.V1ListenPort, 0) - }) - - t.Run("generates a random port if v1v2 enabled and listen port isn't set", func(t *testing.T) { - cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) - c.P2P.V2.Enabled = ptr(true) - c.P2P.V1.ListenPort = ptr(uint16(0)) - c.P2P.PeerID = ptr(k.PeerID()) - }) - - pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) - peerConfig, err := pw.PeerConfig() - require.NoError(t, err) - - assert.NotEqual(t, peerConfig.V1ListenPort, 0) - }) - - t.Run("doesnt generate a port if v2 is enabled", func(t *testing.T) { - cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V2.Enabled = ptr(true) - c.P2P.V1.ListenPort = ptr(uint16(0)) - c.P2P.PeerID = ptr(k.PeerID()) - }) - - pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) - peerConfig, err := pw.PeerConfig() - require.NoError(t, err) - - assert.NotEqual(t, peerConfig.V1ListenPort, 0) - }) - - t.Run("doesnt override a port if listenport is set", func(t *testing.T) { - portNo := uint16(33247) - cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.V1.Enabled = ptr(true) - c.P2P.V1.ListenPort = ptr(uint16(33247)) - c.P2P.PeerID = ptr(k.PeerID()) - }) - - pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) - peerConfig, err := pw.PeerConfig() - require.NoError(t, err) - - assert.Equal(t, peerConfig.V1ListenPort, portNo) - }) -} - func ptr[T any](t T) *T { return &t } diff --git a/core/store/migrate/migrations/0212_ocr_oracle_specs_drop_p2p_bootstrap_peers.sql b/core/store/migrate/migrations/0212_ocr_oracle_specs_drop_p2p_bootstrap_peers.sql new file mode 100644 index 00000000000..d38370e3f2e --- /dev/null +++ b/core/store/migrate/migrations/0212_ocr_oracle_specs_drop_p2p_bootstrap_peers.sql @@ -0,0 +1,5 @@ +-- +goose Up +ALTER TABLE ocr_oracle_specs DROP COLUMN p2p_bootstrap_peers; + +-- +goose Down +ALTER TABLE ocr_oracle_specs ADD COLUMN p2p_bootstrap_peers text[]; diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 0ecb85f1e49..00297ebdb12 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -511,10 +511,7 @@ contractAddress = "%s" evmChainID = %s p2pPeerID = "12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X" externalJobID = "%s" -p2pBootstrapPeers = [ - "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", -] -p2pv2Bootstrappers = [] +p2pv2Bootstrappers = ["12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001"] isBootstrapPeer = false keyBundleID = "f5bf259689b26f1374efb3c9a9868796953a0f814bb2d39b968d0e61b58620a5" monitoringEndpoint = "chain.link:4321" diff --git a/core/web/assets/index.html b/core/web/assets/index.html index 601453b4eac..f2257bfbfae 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 24cc3068f6e66871c3c3479164b48ea3e892d6ee..4d09643ee1b9aebe70088e318891e5418c6f45b6 100644 GIT binary patch literal 418 zcmV;T0bTwdiwFP!000021C^3ZYuqpphX0Bp$f;S&$tGb5mU9RMQV4;zIrKOyjn_)` zVWe3n`|mrpHw%SA=|Ph9TJJL-QgddLp~HZpr{LS>I3w7$2}mGJ54j*|`TFB=wUO#5 zM+m6p=?vuJ z!jQ3)^Q(t0qIfeXM5f)bxkX4xL(F zOm+haiydYy=S7N=4M&2)cVARsOSS24>nn4)zO`KbHEh;5h}Bz>ZKDK@Kos5ljqi|NsoDxlOc+c~KSMyW- M4L7Cb62Jlg09%;KA^-pY literal 420 zcmV;V0bBkbiwFP!000021C^3bZ`3dl#lMQN#EEVk=(bC%I6YKCDiT7X^uTdqPc{bs zi9EAu_uJzn+m(=z5QjMNi{I~!$8O%(WcWCu7&!R0IgJRmZ2~d~Ge9O}EuX%B+I*Di zBu5CS<>c^rOqr!HDKf^g?Aci!w8hC8+$@s|7acqB8#3Tgzn>ZG*kk*3#0;FWczS5m zDmC84Um~N|l7>Py2Ntftr5G~yS`N%3-6}-^%Fhy-!Eoim-n~>2S(S2KoEcSd-NAvA zHYto5iQay=?^6!Ia{)`tpUA$@sM@Er_Xwk-su-0ay6Yi0f7IVnmpI*C*7e1&5Ak zJ+@6uC;3M@h=^zfCxH<x2 diff --git a/core/web/assets/main.b0b6f79f7f4a94560e37.js b/core/web/assets/main.8f602c136d4004a835ee.js similarity index 91% rename from core/web/assets/main.b0b6f79f7f4a94560e37.js rename to core/web/assets/main.8f602c136d4004a835ee.js index 6c9f23d1cca..de75dc2b1c6 100644 --- a/core/web/assets/main.b0b6f79f7f4a94560e37.js +++ b/core/web/assets/main.8f602c136d4004a835ee.js @@ -184,4 +184,4 @@ object-assign */ 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 nOB});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(97779),h=n(47886),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(55977),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 e5(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e6(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,n7.kG)(!!n,32),n}var rb=n(10542),rm=n(53712),rg=n(21436),rv=Object.prototype.hasOwnProperty;function ry(e,t){return void 0===t&&(t=Object.create(null)),rw(rp(t.client),e).useQuery(t)}function rw(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new r_(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 r_=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rb.J)({loading:!0,data:void 0,error:void 0,networkStatus:rc.I.loading}),this.skipStandbyResult=(0,rb.J)({loading:!1,data:void 0,error:void 0,networkStatus:rc.I.ready}),this.toQueryResultCache=new(re.mr?WeakMap:Map),rh(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n7.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,rs.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rn((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,ra.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(!rv.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ra.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:rc.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,ra.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,rm.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ro.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,n8._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__&&n7.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rg.O)(e.errors)?new ru.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,n8._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,n8.pi)((0,n8.pi)((0,n8.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,rg.O)(e.errors)&&(t.error=new ru.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:rc.I.refetch}),this.observable.refetch())},e}();function rE(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 ry(i$,e)},iG=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=iz({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(ij,null):o?l.createElement(iN,{error:o}):i?l.createElement(iD,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iW=n(67932),iK=n(8126),iV="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 iq(e){if(iZ())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iZ(){return("undefined"==typeof Intl?"undefined":iV(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iX="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},iJ=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":iX(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"!==iX(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i0=i1;var i2=new i0;function i3(e,t){if(!iZ())return function(e){return e.toString()};var n=i5(e),r=JSON.stringify(t),i=i2.get(String(n),r)||i2.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i4={};function i5(e){var t=e.toString();return i4[t]?i4[t]:i4[t]=iq(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 i9(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i7(e)}function i7(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var ae=n(54087),at=n.n(ae);function an(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)ao(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=at()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){at().cancel(this.scheduledTick)}};function aa(e){var t=ar(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function ao(e,t){aa(e),au(t,e),as(t,e)}function as(e,t){var n=ac(e,t);e.splice(n,0,t)}function au(e,t){var n=e.indexOf(t);e.splice(n,1)}function ac(e,t){var n=t.nextUpdateTime;return an(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var al=(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})]),af=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(al).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),ad=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ab(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=ap(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(iK.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iK.Z(y)},[y]);t=(0,l.useMemo)(function(){return i9(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=ah(u,2),l=c[0],f=c[1];return f=o?av: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=ah(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ah(M,2),A=O[0],L=O[1],C=ah((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ai.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=ah(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i3(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,ad({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,ad({},f,{verboseDate:I?R:void 0}),j):j}ab.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:af,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},ab.defaultProps={locales:[],component:ay,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ab=l.memo(ab);let am=ab;var ag,av=31536e9;function ay(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ap(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",ad({},a,{dateTime:o,title:r?n:void 0}),i)}ay.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var aw=n(30381),a_=n.n(aw),aE=n(31657);function aS(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ak(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 ru.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,ra.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,ra.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,n8.pi)({reset:c},a)]}var ou=n(59067),oc=n(28428),ol=n(11186),of=n(78513);function od(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var oh=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:od({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"}})},op=(0,b.withStyles)(oh)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ia.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),ob=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},om=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},og=(0,b.withStyles)(oh)(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 ov(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 sZ=sq;function sX(e,t){var n=this.__data__,r=s$(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sJ=sX;function sQ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cI}let cN=cD;var cP="[object Arguments]",cR="[object Array]",cj="[object Boolean]",cF="[object Date]",cY="[object Error]",cB="[object Function]",cU="[object Map]",cH="[object Number]",c$="[object Object]",cz="[object RegExp]",cG="[object Set]",cW="[object String]",cK="[object WeakMap]",cV="[object ArrayBuffer]",cq="[object DataView]",cZ="[object Float64Array]",cX="[object Int8Array]",cJ="[object Int16Array]",cQ="[object Int32Array]",c1="[object Uint8Array]",c0="[object Uint8ClampedArray]",c2="[object Uint16Array]",c3="[object Uint32Array]",c4={};function c5(e){return eD(e)&&cN(e.length)&&!!c4[eC(e)]}c4["[object Float32Array]"]=c4[cZ]=c4[cX]=c4[cJ]=c4[cQ]=c4[c1]=c4[c0]=c4[c2]=c4[c3]=!0,c4[cP]=c4[cR]=c4[cV]=c4[cj]=c4[cq]=c4[cF]=c4[cY]=c4[cB]=c4[cU]=c4[cH]=c4[c$]=c4[cz]=c4[cG]=c4[cW]=c4[cK]=!1;let c6=c5;function c9(e){return function(t){return e(t)}}let c8=c9;var c7=n(79730),le=c7.Z&&c7.Z.isTypedArray,lt=le?c8(le):c6;let ln=lt;var lr=Object.prototype.hasOwnProperty;function li(e,t){var n=cT(e),r=!n&&ck(e),i=!n&&!r&&(0,cM.Z)(e),a=!n&&!r&&!i&&ln(e),o=n||r||i||a,s=o?cm(e.length,String):[],u=s.length;for(var c in e)(t||lr.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cC(c,u)))&&s.push(c);return s}let la=li;var lo=Object.prototype;function ls(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||lo)}let lu=ls;var lc=sM(Object.keys,Object);let ll=lc;var lf=Object.prototype.hasOwnProperty;function ld(e){if(!lu(e))return ll(e);var t=[];for(var n in Object(e))lf.call(e,n)&&"constructor"!=n&&t.push(n);return t}let lh=ld;function lp(e){return null!=e&&cN(e.length)&&!ui(e)}let lb=lp;function lm(e){return lb(e)?la(e):lh(e)}let lg=lm;function lv(e,t){return e&&cp(t,lg(t),e)}let ly=lv;function lw(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let l_=lw;var lE=Object.prototype.hasOwnProperty;function lS(e){if(!ed(e))return l_(e);var t=lu(e),n=[];for(var r in e)"constructor"==r&&(t||!lE.call(e,r))||n.push(r);return n}let lk=lS;function lx(e){return lb(e)?la(e,!0):lk(e)}let lT=lx;function lM(e,t){return e&&cp(t,lT(t),e)}let lO=lM;var lA=n(42896);function lL(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hc(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hl=function(e){return Array.isArray(e)&&0===e.length},hf=function(e){return"function"==typeof e},hd=function(e){return null!==e&&"object"==typeof e},hh=function(e){return String(Math.floor(Number(e)))===e},hp=function(e){return"[object String]"===Object.prototype.toString.call(e)},hb=function(e){return 0===l.Children.count(e)},hm=function(e){return hd(e)&&hf(e.then)};function hg(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 hy(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,hg(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=hv(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 sx.all([t,n,r],{arrayMerge:hC})})},[h.validate,h.validationSchema,T,S,k]),O=hP(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}),sh()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sh()(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);hm(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sh()(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&&!sh()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hk,E({type:"SET_ERRORS",payload:h.initialErrors||hk}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hx,E({type:"SET_TOUCHED",payload:h.initialTouched||hx}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hP(function(e){if(y.current[e]&&hf(y.current[e].validate)){var t=hg(_.values,e),n=y.current[e].validate(t);return hm(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=hP(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=hP(function(e,t){var r=hf(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=hP(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hv(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hp(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)?hD(hg(_.values,r),l,c):d?hI(f):c}r&&j(r,i)},[j,_.values]),Y=hP(function(e){if(hp(e))return function(t){return F(t,e)};F(e)}),B=hP(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=hP(function(e){if(hp(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hf(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=hP(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=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(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=hP(function(){return f(_.values,V)}),Z=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hg(_.values,e),error:hg(_.errors,e),touched:!!hg(_.touched,e),initialValue:hg(p.current,e),initialTouched:!!hg(m.current,e),initialError:hg(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=hd(e),n=t?e.name:e,r=hg(_.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!sh()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hf(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ho({},_,{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 hM(e){var t=hT(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(h_,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hf(r)?r(t):hb(r)?null:l.Children.only(r):null)}function hO(e){var t={};if(e.inner){if(0===e.inner.length)return hv(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;hg(t,o.path)||(t=hv(t,o.path,o.message))}}return t}function hA(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hL(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hL(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)||sj(e)?hL(e):""!==e?e:void 0}):sj(e[r])?t[r]=hL(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hC(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?sx(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sx(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hI(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hD(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 hN="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hP(e){var t=(0,l.useRef)(e);return hN(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(ho({},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=hv(n.values,a,e(hg(n.values,a))),u=r?i(hg(n.errors,a)):void 0,c=t?o(hg(n.touched,a)):void 0;return hl(u)&&(u=void 0),hl(c)&&(c=void 0),ho({},n,{values:s,errors:r?hv(n.errors,a,u):n.errors,touched:t?hv(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hH(t),[ha(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hY(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 hF(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 hB(n,e,t)},function(t){return hB(t,e,null)},function(t){return hB(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 hU(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(hc(n)),n.pop=n.pop.bind(hc(n)),n}hs(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sh()(hg(e.formik.values,e.name),hg(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?hH(n):[];return t||(t=r[e]),hf(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=hu(t.formik,["validate","validationSchema"]),s=ho({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hb(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var h$=n(24802),hz=n(71209),hG=n(91750),hW=n(11970),hK=n(4689),hV=n(67598),hq=function(){return(hq=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 hX(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hZ(e,["disabled","field","form","onBlur","helperText"]),d=hg(u,i.name),h=hg(s,i.name)&&!!d;return hq(hq({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 hJ(e){var t=e.children,n=hZ(e,["children"]);return(0,l.createElement)(i_.Z,hq({},hX(n)),t)}function hQ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h1(e){return(0,l.createElement)(h$.Z,hq({},hQ(e)))}function h0(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hZ(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({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 h2(e){return(0,l.createElement)(hz.Z,hq({},h0(e)))}function h3(e){var t=e.Label,n=hZ(e,["Label"]);return(0,l.createElement)(hG.Z,hq({control:(0,l.createElement)(hz.Z,hq({},h0(n)))},t))}function h4(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h5(e){return(0,l.createElement)(hW.default,hq({},h4(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hZ(t,["onBlur"]),i=(e.form,e.onBlur),a=hZ(e,["field","form","onBlur"]);return hq(hq({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h9(e){return(0,l.createElement)(hK.Z,hq({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h7(e){return(0,l.createElement)(hV.default,hq({},h8(e)))}hJ.displayName="FormikMaterialUITextField",h1.displayName="FormikMaterialUISwitch",h2.displayName="FormikMaterialUICheckbox",h3.displayName="FormikMaterialUICheckboxWithLabel",h5.displayName="FormikMaterialUISelect",h9.displayName="FormikMaterialUIRadioGroup",h7.displayName="FormikMaterialUIInputBase";try{a=Map}catch(pe){}try{o=Set}catch(pt){}function pn(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(pr);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]:pn(e[i],t,n)}return r}return e}function pr(e){return pn(e,[],[])}let pi=Object.prototype.toString,pa=Error.prototype.toString,po=RegExp.prototype.toString,ps="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",pu=/^Symbol\((.*)\)(.*)$/;function pc(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pl(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pc(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return ps.call(e).replace(pu,"Symbol($1)");let r=pi.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pa.call(e)+"]":"RegExp"===r?po.call(e):null}function pf(e,t){let n=pl(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pl(this[e],t);return null!==r?r:n},2)}let pd={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: \`${pf(n,!0)}\``+(i?` (cast from the value \`${pf(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"},ph={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"},pp={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"},pb={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pm={isValue:"${path} field must be ${value}"},pg={noUnknown:"${path} field has unspecified keys: ${unknown}"},pv={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:pd,string:ph,number:pp,date:pb,object:pg,array:pv,boolean:pm});var py=n(18721),pw=n.n(py);let p_=e=>e&&e.__isYupSchema__;class pE{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!pw()(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(!p_(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pS=pE;function pk(e){return null==e?[]:[].concat(e)}function px(){return(px=Object.assign||function(e){for(var t=1;tpf(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=[],pk(e).forEach(e=>{pM.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,pM)}}let pO=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pA(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pO(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pM(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pj(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pR(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 pN.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pC()(pP({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pM(pM.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pP({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=>{pM.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}pM.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pN.prototype.__isYupRef=!0;let pF=e=>e.substr(0,e.length-1).substr(1);function pY(e,t,n,r=n){let i,a,o;return t?((0,pI.forEach)(t,(s,u,c)=>{let l=u?pF(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 pB{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){pN.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pN.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 pB;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 pU(){return(pU=Object.assign||function(e){for(var t=1;t{this.typeError(pd.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pU({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=pU({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pr(pU({},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=pU({},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(pU({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pf(e),a=pf(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,pU({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),pA({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pA({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pU({},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(pU({},t,{value:e}))._validate(e,pU({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pM.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pM.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pr(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=pd.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pd.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=pd.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pj(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=pk(e).map(e=>new pN(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pS(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pj({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=pd.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pj({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=pd.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pj({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 p$ of(pH.prototype.__isYupSchema__=!0,["validate","validateSync"]))pH.prototype[`${p$}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pY(this,e,t,n.context);return a[p$](r&&r[i],pU({},n,{parent:r,path:e}))};for(let pz of["equals","is"])pH.prototype[pz]=pH.prototype.oneOf;for(let pG of["not","nope"])pH.prototype[pG]=pH.prototype.notOneOf;pH.prototype.optional=pH.prototype.notRequired;let pW=pH;function pK(){return new pW}pK.prototype=pW.prototype;let pV=e=>null==e;function pq(){return new pZ}class pZ extends pH{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=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pV(e)||!0===e})}isFalse(e=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pV(e)||!1===e})}}pq.prototype=pZ.prototype;let pX=/^((([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,pJ=/^((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,pQ=/^(?:[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,p1=e=>pV(e)||e===e.trim(),p0=({}).toString();function p2(){return new p3}class p3 extends pH{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===p0?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=ph.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t=ph.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pV(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||ph.matches,params:{regex:e},test:t=>pV(t)||""===t&&n||-1!==t.search(e)})}email(e=ph.email){return this.matches(pX,{name:"email",message:e,excludeEmptyString:!0})}url(e=ph.url){return this.matches(pJ,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=ph.uuid){return this.matches(pQ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=ph.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:p1})}lowercase(e=ph.lowercase){return this.transform(e=>pV(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toLowerCase()})}uppercase(e=ph.uppercase){return this.transform(e=>pV(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toUpperCase()})}}p2.prototype=p3.prototype;let p4=e=>e!=+e;function p5(){return new p6}class p6 extends pH{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&&!p4(e)}min(e,t=pp.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t>=this.resolve(e)}})}max(e,t=pp.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(t)||t<=this.resolve(e)}})}lessThan(e,t=pp.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pV(t)||tthis.resolve(e)}})}positive(e=pp.positive){return this.moreThan(0,e)}negative(e=pp.negative){return this.lessThan(0,e)}integer(e=pp.integer){return this.test({name:"integer",message:e,test:e=>pV(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pV(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=>pV(t)?t:Math[e](t))}}p5.prototype=p6.prototype;var p9=/^(\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=p9.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 p7=new Date(""),be=e=>"[object Date]"===Object.prototype.toString.call(e);function bt(){return new bn}class bn extends pH{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p7:new Date(e))})})}_typeCheck(e){return be(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pN.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=pb.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pV(e)||e>=this.resolve(n)}})}max(e,t=pb.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pV(e)||e<=this.resolve(n)}})}}bn.INVALID_DATE=p7,bt.prototype=bn.prototype,bt.INVALID_DATE=p7;var br=n(11865),bi=n.n(br),ba=n(68929),bo=n.n(ba),bs=n(67523),bu=n.n(bs),bc=n(94633),bl=n.n(bc);function bf(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pI.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(pw()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pN.isRef(o)&&o.isSibling?i(o.path,a):p_(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bl().array(r,n).reverse()}function bd(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 bh(e){return(t,n)=>bd(e,t)-bd(e,n)}function bp(){return(bp=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bm(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bg=bh([]);class bv extends pH{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bg,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 bb(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=bp({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=pw()(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(!pM.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bb(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],bp({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pA({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=bp({},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 pH&&i instanceof pH&&(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=bh(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=bf(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,pI.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return pw()(i,e)&&(a=bp({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pg.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=bm(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pg.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bu()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(bo())}snakeCase(){return this.transformKeys(bi())}constantCase(){return this.transformKeys(e=>bi()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pC()(this.fields,e=>e.describe()),e}}function by(e){return new bv(e)}function bw(){return(bw=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,bw({},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(!pM.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)}pA({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(!p_(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pf(e));return t.innerType=e,t}length(e,t=pv.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pv.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pv.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(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)}}b_.prototype=bE.prototype;var bS=by().shape({name:p2().required("Required"),url:p2().required("Required")}),bk=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hM,{initialValues:t,validationSchema:bS,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bx=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(iv,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Edit Bridge",action:l.createElement(aL.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aK.Z,null,l.createElement(bk,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bT(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}},ml=n(76023);function mf(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||(i[n]=e[n]);return i}function mB(e,t){if(null==e)return{};var n,r,i=mY(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mU(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 mX={};function mJ(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mX[t]||(mX[t]=mZ(e)),mX[t]}function mQ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mJ(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mV({},e,n[t])},t)}function m1(e){return e.join(" ")}function m0(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m2({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m2(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=m0(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=mV({},s,{className:m1(m)||void 0,style:mQ(s.className,Object.assign({},s.style,i),n)})}else d=mV({},s,{className:m1(s.className)});var g=h(t.children);return l.createElement(c,mq({key:o},d),g)}}let m3=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m4=/\n/g;function m5(e){return e.match(m4)}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 m9(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 m7(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 ge(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mV({},i,"function"==typeof e?e(t):e)}function gt(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=ge(r,n,i);t.unshift(m7(n,h))}return f&l&&(d.style=mV({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gn(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 gt({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=ge(s,t,o);e.unshift(m7(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(m5(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(gt({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=gt({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 gc=n(98695),gl=n.n(gc);let gf=["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 gd=gs(gl(),gu);gd.supportedLanguages=gf;let gh=gd;var gp=n(64566),gb=n(68239);function gm(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gg(){var e=gm(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gg=function(){return e},e}var gv=function(){var e="[[TelemetryIngress.Endpoints]] \nNetwork = '...' # e.g. EVM. Solana, Starknet, Cosmos \nChainID = '...' # e.g. 1, 5, devnet, mainnet-beta URL\nURL = '...'\nServerPubKey = '...'";return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Deprecation warning"}),l.createElement(aK.Z,null,l.createElement(x.default,{variant:"h5",gutterBottom:!0},"Starting in ",l.createElement("code",null,"v2.9.0"),":"),l.createElement(w.default,{dense:!0},l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"TelemetryIngress.URL")," and"," ",l.createElement("code",null,"TelemetryIngress.ServerPubKey")," will no longer be allowed. Please switch to ",l.createElement("code",null,"TelemetryIngress.Endpoints"),":",l.createElement(gh,{language:"toml",style:gu},e))),l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"P2P.V1")," will no longer be supported and must not be set in TOML configuration in order to boot. Use"," ",l.createElement("code",null,"P2P.V2")," instead. If you are using both,"," ",l.createElement("code",null,"V1")," can simply be removed.")))))},gy=n0(gg()),gw=function(e){var t=e.children;return l.createElement(ii.Z,null,l.createElement(ie.default,{component:"th",scope:"row",colSpan:3},t))},g_=function(){return l.createElement(gw,null,"...")},gE=function(e){var t=e.children;return l.createElement(gw,null,t)},gS=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(gE,null,i);if(t)return l.createElement(g_,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mR.Z,{defaultExpanded:o},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},a),l.createElement(mF.Z,{style:s},l.createElement(gh,{language:"toml",style:gu},n))))},gk=function(){var e=ry(gy,{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(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{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(gv,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{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(gS,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gx=n(34823),gT=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gM=(0,b.withStyles)(gT)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gx.N,A.wU);return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r7.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gO=function(){return l.createElement(iv,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(gk,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(gM,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mP,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mS,null))))))},gA=function(){return l.createElement(gO,null)},gL=function(){return l.createElement(gA,null)},gC=n(44431),gI=1e18,gD=function(e){return new gC.BigNumber(e).dividedBy(gI).toFixed(8)},gN=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sf.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aK.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(ob,{title:"Address"}),l.createElement(om,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"Native Token Balance"}),l.createElement(om,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"LINK Balance"}),l.createElement(om,{value:e.linkBalance?gD(e.linkBalance):"--"}))))})),r+1s&&l.createElement(g$.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:r.footer},l.createElement(aL.Z,{href:"/runs",component:tz},"View More"))))))});function vi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function va(){var e=vi(["\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 va=function(){return e},e}var vo=5,vs=n0(va(),vt),vu=function(){var e=ry(vs,{variables:{offset:0,limit:vo},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vr,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vo})},vc=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"}})},vl=(0,b.withStyles)(vc)(function(e){var t=e.classes,n=(0,A.v9)(gx.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ia.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)))}),vf=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"}})},vd=(0,b.withStyles)(vf)(function(e){var t=e.classes,n=e.job;return l.createElement(ii.Z,null,l.createElement(ie.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ip,{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(aA,{tooltip:!0},n.createdAt))))))});function vh(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vp(){var e=vh(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vp=function(){return e},e}var vb=n0(vp()),vm=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vg=(0,b.withStyles)(vm)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gz,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vd,{job:e,key:t})}))))});function vv(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vy(){var e=vv(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vy=function(){return e},e}var vw=5,v_=n0(vy(),vb),vE=function(){var e=ry(v_,{variables:{offset:0,limit:vw},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vg,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vS=function(){return l.createElement(iv,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(vu,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(gH,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vE,null))))),l.createElement(vl,null))},vk=function(){return l.createElement(vS,null)},vx=function(){return l.createElement(vk,null)},vT=n(87239),vM=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vO=n(5022),vA=n(78718),vL=n.n(vA);function vC(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(ii.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(ie.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(ie.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aA,{tooltip:!0},e.createdAt))),l.createElement(ie.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,ym(t,e.status))},e.status.toLowerCase())))})))}),yv=n(16839),yy=n.n(yv);function yw(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=yy().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 y_=n(94164),yE=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)},yS=n(73343),yk=n(3379),yx=n.n(yk);function yT(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(y_.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:yS.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yE,{data:e}))}))};function yD(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyH&&l.createElement("div",{className:t.runDetails},l.createElement(aL.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yU,{observationSource:n.observationSource})))});function yG(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 vO.parse(e),!0}catch(t){return!1}})}),wq=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hM,{initialValues:t,validationSchema:wV,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hj,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wZ=n(50109),wX="persistSpec";function wJ(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wZ.t8(wX,n),{toml:n}):{toml:wZ.U2(wX)||""}}var wQ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wJ({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wZ.t8("".concat(wX),t),n&&n(t)};return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"New Job"}),l.createElement(aK.Z,null,l.createElement(wq,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function w1(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 _L(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 ry(_q,e)},_X=function(){var e=_Z({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_z,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_J=function(e){var t=e.csaKey;return l.createElement(ii.Z,{hover:!0},l.createElement(ie.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_O,{data:t.publicKey}))))};function _Q(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _1(){var e=_Q(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _1=function(){return e},e}var _0=n0(_1()),_2=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r9.Z,null,l.createElement(sf.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ox.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(it.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,null,"Public Key"))),l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gz,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_J,{csaKey:e,key:t})}))))};function _3(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 ry(EL,e)};function EI(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 ry(E0,e)},E6=function(){return os(E2)},E9=function(){return os(E3)},E8=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return ry(E4,e)};function E7(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 ry(SZ,e)};function SJ(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 kX(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 kJ=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kZ(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kq(kK({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r9.Z,null,l.createElement(aK.Z,null,l.createElement(kG,{object:n})))};function kQ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function k1(e){for(var t=1;t0&&l.createElement(ko,{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(kJ,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kj,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k7(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xe(){var e=k7(["\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 xe=function(){return e},e}var xt=n0(xe(),k9),xn=function(){var e=ry(xt,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(ij,null);if(r)return l.createElement(iN,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k8,{run:i});case"NotFoundError":return l.createElement(oo,null);default:return null}};function xr(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xi(){var e=xr(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xi=function(){return e},e}var xa=n0(xi()),xo=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(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iw,null,"Job Runs")),t&&l.createElement(ij,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(yg,{runs:o}),l.createElement(ir.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 xs(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xu(){var e=xs(["\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 xu=function(){return e},e}var xc=n0(xu(),xa),xl=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=ry(xc,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iN,{error:o}):l.createElement(xo,{loading:a,data:i,page:t,pageSize:n})},xf=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xl,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(xn,null)))},xd=by().shape({name:p2().required("Required"),uri:p2().required("Required"),publicKey:p2().required("Required")}),xh=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hM,{initialValues:t,validationSchema:xd,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ox.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xp=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(r9.Z,null,l.createElement(sf.Z,{title:"Edit Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:r,onSubmit:n})))))};function xb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xm(){var e=xb(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xm=function(){return e},e}var xg=n0(xm()),xv=function(){return ry(xg)};function xy(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 ry(xJ,e)};function x1(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);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(Tz,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},TW={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TK=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Register Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:TW,onSubmit:t})))))};function TV(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&&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 Mm(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(ox.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ox.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(ox.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(ox.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(ox.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(mR.Z,{defaultExpanded:0===n,key:n},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(El.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(aA,{tooltip:!0},e.createdAt)))),l.createElement(mF.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(ox.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gh,{language:"toml",style:gu,"data-testid":"codeblock"},e.definition)))}),l.createElement(oI,{open:null!=c,title:c?M_[c.action].title:"",body:c?M_[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(Mo,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MS(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Mk(){var e=MS(["\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 Mk=function(){return e},e}var Mx=n0(Mk(),My),MT=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iw,null,"Job Proposal #",a.id))),l.createElement(Me,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(T$,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ME,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MM(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(55977),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(55977),a=n(47886),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(97779),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(55977),a=n(15857),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;ri,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;nr})},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;nr,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(23564);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(23564),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()},87462(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;tr})},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__(55977),p=__webpack_require__(15857),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 t5(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t9("(",t5(e.variableDefinitions,", "),")"),i=t5(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t5([t,t5([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t9(" = ",r)+t9(" ",t5(i," "))},SelectionSet:function(e){return t6(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t9("",t,": ")+n,s=o+t9("(",t5(r,", "),")");return s.length>t2&&(s=o+t9("(\n",t8(t5(r,"\n")),"\n)")),t5([s,t5(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t9(" ",t5(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t5(["...",t9("on ",t),t5(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(t9("(",t5(r,", "),")")," ")+"on ".concat(n," ").concat(t9("",t5(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"["+t5(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t5(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t9("(",t5(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 t5(["schema",t5(t," "),t6(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t5(["scalar",e.name,t5(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+": "+r+t9(" ",t5(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t5([t+": "+n,t9("= ",r),t5(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t5(["union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t5(["enum",t,t5(n," "),t6(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t5([e.name,t5(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["input",t,t5(n," "),t6(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+(r?" repeatable":"")+" on "+t5(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t5(["extend schema",t5(t," "),t6(n)]," ")},ScalarTypeExtension:function(e){var t;return t5(["extend scalar",e.name,t5(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t5(["extend union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t5(["extend enum",t,t5(n," "),t6(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["extend input",t,t5(n," "),t6(r)]," ")}};function t4(e){return function(t){return t5([t.description,e(t)],"\n")}}function t5(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 t6(e){return t9("{\n",t8(t5(e,"\n")),"\n}")}function t9(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t8(e){return t9(" ",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 n5(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n6(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n9(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n8(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=e9(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)&&n8(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)&&n8(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r5=new(t_.mr?WeakMap:Map);function r6(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r5.set(e,(r5.get(e)+1)%1e15),n.apply(this,arguments)})}function r9(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r8=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;r5.has(n)||(r5.set(n,0),r6(n,"evict"),r6(n,"modify"),r6(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(){r9(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;r9(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===r5.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:r5.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:e8(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 r8(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 r8&&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,{})||n6(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 r8(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)({},e8(e5(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e9(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)({},e8(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=i6(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=i9(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i8(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,i6(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}(),i5=[];function i6(e,t){var n=e.map;return n.has(t)||n.set(t,i5.pop()||{map:new Map}),n.get(t)}function i9(e,t){if(e===t||!t||i8(t))return e;if(!e||i8(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,i9(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i9(t.map.get(n),e.map.get(n)))})}return a}function i8(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i8(r)&&(i5.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","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","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","VRFSpec","WebhookSpec"],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,pU({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),pA({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pA({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pU({},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(pU({},t,{value:e}))._validate(e,pU({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pM.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pM.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pr(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=pd.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pd.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=pd.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pj(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=pk(e).map(e=>new pN(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pS(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pj({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=pd.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pj({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=pd.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pj({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 p$ of(pH.prototype.__isYupSchema__=!0,["validate","validateSync"]))pH.prototype[`${p$}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pY(this,e,t,n.context);return a[p$](r&&r[i],pU({},n,{parent:r,path:e}))};for(let pz of["equals","is"])pH.prototype[pz]=pH.prototype.oneOf;for(let pG of["not","nope"])pH.prototype[pG]=pH.prototype.notOneOf;pH.prototype.optional=pH.prototype.notRequired;let pW=pH;function pK(){return new pW}pK.prototype=pW.prototype;let pV=e=>null==e;function pq(){return new pZ}class pZ extends pH{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=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pV(e)||!0===e})}isFalse(e=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pV(e)||!1===e})}}pq.prototype=pZ.prototype;let pX=/^((([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,pJ=/^((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,pQ=/^(?:[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,p1=e=>pV(e)||e===e.trim(),p0=({}).toString();function p2(){return new p3}class p3 extends pH{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===p0?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=ph.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t=ph.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pV(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||ph.matches,params:{regex:e},test:t=>pV(t)||""===t&&n||-1!==t.search(e)})}email(e=ph.email){return this.matches(pX,{name:"email",message:e,excludeEmptyString:!0})}url(e=ph.url){return this.matches(pJ,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=ph.uuid){return this.matches(pQ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=ph.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:p1})}lowercase(e=ph.lowercase){return this.transform(e=>pV(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toLowerCase()})}uppercase(e=ph.uppercase){return this.transform(e=>pV(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toUpperCase()})}}p2.prototype=p3.prototype;let p4=e=>e!=+e;function p5(){return new p6}class p6 extends pH{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&&!p4(e)}min(e,t=pp.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t>=this.resolve(e)}})}max(e,t=pp.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(t)||t<=this.resolve(e)}})}lessThan(e,t=pp.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pV(t)||tthis.resolve(e)}})}positive(e=pp.positive){return this.moreThan(0,e)}negative(e=pp.negative){return this.lessThan(0,e)}integer(e=pp.integer){return this.test({name:"integer",message:e,test:e=>pV(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pV(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=>pV(t)?t:Math[e](t))}}p5.prototype=p6.prototype;var p9=/^(\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=p9.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 p7=new Date(""),be=e=>"[object Date]"===Object.prototype.toString.call(e);function bt(){return new bn}class bn extends pH{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p7:new Date(e))})})}_typeCheck(e){return be(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pN.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=pb.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pV(e)||e>=this.resolve(n)}})}max(e,t=pb.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pV(e)||e<=this.resolve(n)}})}}bn.INVALID_DATE=p7,bt.prototype=bn.prototype,bt.INVALID_DATE=p7;var br=n(11865),bi=n.n(br),ba=n(68929),bo=n.n(ba),bs=n(67523),bu=n.n(bs),bc=n(94633),bl=n.n(bc);function bf(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pI.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(pw()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pN.isRef(o)&&o.isSibling?i(o.path,a):p_(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bl().array(r,n).reverse()}function bd(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 bh(e){return(t,n)=>bd(e,t)-bd(e,n)}function bp(){return(bp=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bm(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bg=bh([]);class bv extends pH{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bg,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 bb(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=bp({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=pw()(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(!pM.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bb(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],bp({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pA({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=bp({},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 pH&&i instanceof pH&&(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=bh(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=bf(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,pI.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return pw()(i,e)&&(a=bp({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pg.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=bm(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pg.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bu()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(bo())}snakeCase(){return this.transformKeys(bi())}constantCase(){return this.transformKeys(e=>bi()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pC()(this.fields,e=>e.describe()),e}}function by(e){return new bv(e)}function bw(){return(bw=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,bw({},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(!pM.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)}pA({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(!p_(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pf(e));return t.innerType=e,t}length(e,t=pv.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pv.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pv.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(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)}}b_.prototype=bE.prototype;var bS=by().shape({name:p2().required("Required"),url:p2().required("Required")}),bk=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hM,{initialValues:t,validationSchema:bS,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bx=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(iv,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Edit Bridge",action:l.createElement(aL.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aK.Z,null,l.createElement(bk,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bT(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}},ml=n(76023);function mf(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||(i[n]=e[n]);return i}function mB(e,t){if(null==e)return{};var n,r,i=mY(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mU(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 mX={};function mJ(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mX[t]||(mX[t]=mZ(e)),mX[t]}function mQ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mJ(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mV({},e,n[t])},t)}function m1(e){return e.join(" ")}function m0(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m2({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m2(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=m0(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=mV({},s,{className:m1(m)||void 0,style:mQ(s.className,Object.assign({},s.style,i),n)})}else d=mV({},s,{className:m1(s.className)});var g=h(t.children);return l.createElement(c,mq({key:o},d),g)}}let m3=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m4=/\n/g;function m5(e){return e.match(m4)}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 m9(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 m7(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 ge(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mV({},i,"function"==typeof e?e(t):e)}function gt(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=ge(r,n,i);t.unshift(m7(n,h))}return f&l&&(d.style=mV({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gn(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 gt({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=ge(s,t,o);e.unshift(m7(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(m5(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(gt({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=gt({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 gc=n(98695),gl=n.n(gc);let gf=["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 gd=gs(gl(),gu);gd.supportedLanguages=gf;let gh=gd;var gp=n(64566),gb=n(68239);function gm(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gg(){var e=gm(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gg=function(){return e},e}var gv=function(){var e="[[TelemetryIngress.Endpoints]] \nNetwork = '...' # e.g. EVM. Solana, Starknet, Cosmos \nChainID = '...' # e.g. 1, 5, devnet, mainnet-beta URL\nURL = '...'\nServerPubKey = '...'";return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Deprecation warning"}),l.createElement(aK.Z,null,l.createElement(x.default,{variant:"h5",gutterBottom:!0},"Starting in ",l.createElement("code",null,"v2.9.0"),":"),l.createElement(w.default,{dense:!0},l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"TelemetryIngress.URL")," and"," ",l.createElement("code",null,"TelemetryIngress.ServerPubKey")," will no longer be allowed. Please switch to ",l.createElement("code",null,"TelemetryIngress.Endpoints"),":",l.createElement(gh,{language:"toml",style:gu},e))),l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"P2P.V1")," will no longer be supported and must not be set in TOML configuration in order to boot. Use"," ",l.createElement("code",null,"P2P.V2")," instead. If you are using both,"," ",l.createElement("code",null,"V1")," can simply be removed.")))))},gy=n0(gg()),gw=function(e){var t=e.children;return l.createElement(ii.Z,null,l.createElement(ie.default,{component:"th",scope:"row",colSpan:3},t))},g_=function(){return l.createElement(gw,null,"...")},gE=function(e){var t=e.children;return l.createElement(gw,null,t)},gS=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(gE,null,i);if(t)return l.createElement(g_,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mR.Z,{defaultExpanded:o},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},a),l.createElement(mF.Z,{style:s},l.createElement(gh,{language:"toml",style:gu},n))))},gk=function(){var e=ry(gy,{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(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{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(gv,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{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(gS,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gx=n(34823),gT=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gM=(0,b.withStyles)(gT)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gx.N,A.wU);return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r7.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gO=function(){return l.createElement(iv,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(gk,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(gM,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mP,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mS,null))))))},gA=function(){return l.createElement(gO,null)},gL=function(){return l.createElement(gA,null)},gC=n(44431),gI=1e18,gD=function(e){return new gC.BigNumber(e).dividedBy(gI).toFixed(8)},gN=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sf.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aK.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(ob,{title:"Address"}),l.createElement(om,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"Native Token Balance"}),l.createElement(om,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"LINK Balance"}),l.createElement(om,{value:e.linkBalance?gD(e.linkBalance):"--"}))))})),r+1s&&l.createElement(g$.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:r.footer},l.createElement(aL.Z,{href:"/runs",component:tz},"View More"))))))});function vi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function va(){var e=vi(["\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 va=function(){return e},e}var vo=5,vs=n0(va(),vt),vu=function(){var e=ry(vs,{variables:{offset:0,limit:vo},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vr,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vo})},vc=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"}})},vl=(0,b.withStyles)(vc)(function(e){var t=e.classes,n=(0,A.v9)(gx.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ia.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)))}),vf=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"}})},vd=(0,b.withStyles)(vf)(function(e){var t=e.classes,n=e.job;return l.createElement(ii.Z,null,l.createElement(ie.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ip,{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(aA,{tooltip:!0},n.createdAt))))))});function vh(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vp(){var e=vh(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vp=function(){return e},e}var vb=n0(vp()),vm=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vg=(0,b.withStyles)(vm)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gz,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vd,{job:e,key:t})}))))});function vv(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vy(){var e=vv(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vy=function(){return e},e}var vw=5,v_=n0(vy(),vb),vE=function(){var e=ry(v_,{variables:{offset:0,limit:vw},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vg,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vS=function(){return l.createElement(iv,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(vu,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(gH,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vE,null))))),l.createElement(vl,null))},vk=function(){return l.createElement(vS,null)},vx=function(){return l.createElement(vk,null)},vT=n(87239),vM=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vO=n(5022),vA=n(78718),vL=n.n(vA);function vC(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(ii.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(ie.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(ie.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aA,{tooltip:!0},e.createdAt))),l.createElement(ie.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,ym(t,e.status))},e.status.toLowerCase())))})))}),yv=n(16839),yy=n.n(yv);function yw(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=yy().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 y_=n(94164),yE=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)},yS=n(73343),yk=n(3379),yx=n.n(yk);function yT(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(y_.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:yS.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yE,{data:e}))}))};function yD(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyH&&l.createElement("div",{className:t.runDetails},l.createElement(aL.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yU,{observationSource:n.observationSource})))});function yG(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 vO.parse(e),!0}catch(t){return!1}})}),wq=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hM,{initialValues:t,validationSchema:wV,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hj,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wZ=n(50109),wX="persistSpec";function wJ(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wZ.t8(wX,n),{toml:n}):{toml:wZ.U2(wX)||""}}var wQ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wJ({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wZ.t8("".concat(wX),t),n&&n(t)};return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"New Job"}),l.createElement(aK.Z,null,l.createElement(wq,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function w1(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 _L(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 ry(_q,e)},_X=function(){var e=_Z({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_z,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_J=function(e){var t=e.csaKey;return l.createElement(ii.Z,{hover:!0},l.createElement(ie.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_O,{data:t.publicKey}))))};function _Q(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _1(){var e=_Q(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _1=function(){return e},e}var _0=n0(_1()),_2=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r9.Z,null,l.createElement(sf.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ox.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(it.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,null,"Public Key"))),l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gz,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_J,{csaKey:e,key:t})}))))};function _3(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 ry(EL,e)};function EI(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 ry(E0,e)},E6=function(){return os(E2)},E9=function(){return os(E3)},E8=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return ry(E4,e)};function E7(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 ry(SZ,e)};function SJ(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 kX(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 kJ=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kZ(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kq(kK({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r9.Z,null,l.createElement(aK.Z,null,l.createElement(kG,{object:n})))};function kQ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function k1(e){for(var t=1;t0&&l.createElement(ko,{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(kJ,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kj,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k7(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xe(){var e=k7(["\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 xe=function(){return e},e}var xt=n0(xe(),k9),xn=function(){var e=ry(xt,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(ij,null);if(r)return l.createElement(iN,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k8,{run:i});case"NotFoundError":return l.createElement(oo,null);default:return null}};function xr(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xi(){var e=xr(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xi=function(){return e},e}var xa=n0(xi()),xo=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(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iw,null,"Job Runs")),t&&l.createElement(ij,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(yg,{runs:o}),l.createElement(ir.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 xs(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xu(){var e=xs(["\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 xu=function(){return e},e}var xc=n0(xu(),xa),xl=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=ry(xc,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iN,{error:o}):l.createElement(xo,{loading:a,data:i,page:t,pageSize:n})},xf=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xl,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(xn,null)))},xd=by().shape({name:p2().required("Required"),uri:p2().required("Required"),publicKey:p2().required("Required")}),xh=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hM,{initialValues:t,validationSchema:xd,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ox.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xp=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(r9.Z,null,l.createElement(sf.Z,{title:"Edit Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:r,onSubmit:n})))))};function xb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xm(){var e=xb(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xm=function(){return e},e}var xg=n0(xm()),xv=function(){return ry(xg)};function xy(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 ry(xJ,e)};function x1(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);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(Tz,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},TW={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TK=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Register Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:TW,onSubmit:t})))))};function TV(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&&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 Mm(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(ox.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ox.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(ox.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(ox.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(ox.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(mR.Z,{defaultExpanded:0===n,key:n},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(El.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(aA,{tooltip:!0},e.createdAt)))),l.createElement(mF.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(ox.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gh,{language:"toml",style:gu,"data-testid":"codeblock"},e.definition)))}),l.createElement(oI,{open:null!=c,title:c?M_[c.action].title:"",body:c?M_[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(Mo,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MS(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Mk(){var e=MS(["\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 Mk=function(){return e},e}var Mx=n0(Mk(),My),MT=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iw,null,"Job Proposal #",a.id))),l.createElement(Me,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(T$,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ME,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MM(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(55977),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(55977),a=n(47886),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(97779),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(55977),a=n(15857),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;ri,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;nr})},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;nr,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(23564);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(23564),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()},87462(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;tr})},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__(55977),p=__webpack_require__(15857),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 t5(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t9("(",t5(e.variableDefinitions,", "),")"),i=t5(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t5([t,t5([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t9(" = ",r)+t9(" ",t5(i," "))},SelectionSet:function(e){return t6(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t9("",t,": ")+n,s=o+t9("(",t5(r,", "),")");return s.length>t2&&(s=o+t9("(\n",t8(t5(r,"\n")),"\n)")),t5([s,t5(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t9(" ",t5(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t5(["...",t9("on ",t),t5(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(t9("(",t5(r,", "),")")," ")+"on ".concat(n," ").concat(t9("",t5(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"["+t5(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t5(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t9("(",t5(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 t5(["schema",t5(t," "),t6(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t5(["scalar",e.name,t5(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+": "+r+t9(" ",t5(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t5([t+": "+n,t9("= ",r),t5(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t5(["union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t5(["enum",t,t5(n," "),t6(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t5([e.name,t5(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["input",t,t5(n," "),t6(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+(r?" repeatable":"")+" on "+t5(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t5(["extend schema",t5(t," "),t6(n)]," ")},ScalarTypeExtension:function(e){var t;return t5(["extend scalar",e.name,t5(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t5(["extend union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t5(["extend enum",t,t5(n," "),t6(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["extend input",t,t5(n," "),t6(r)]," ")}};function t4(e){return function(t){return t5([t.description,e(t)],"\n")}}function t5(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 t6(e){return t9("{\n",t8(t5(e,"\n")),"\n}")}function t9(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t8(e){return t9(" ",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 n5(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n6(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n9(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n8(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=e9(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)&&n8(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)&&n8(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r5=new(t_.mr?WeakMap:Map);function r6(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r5.set(e,(r5.get(e)+1)%1e15),n.apply(this,arguments)})}function r9(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r8=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;r5.has(n)||(r5.set(n,0),r6(n,"evict"),r6(n,"modify"),r6(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(){r9(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;r9(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===r5.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:r5.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:e8(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 r8(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 r8&&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,{})||n6(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 r8(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)({},e8(e5(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e9(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)({},e8(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=i6(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=i9(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i8(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,i6(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}(),i5=[];function i6(e,t){var n=e.map;return n.has(t)||n.set(t,i5.pop()||{map:new Map}),n.get(t)}function i9(e,t){if(e===t||!t||i8(t))return e;if(!e||i8(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,i9(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i9(t.map.get(n),e.map.get(n)))})}return a}function i8(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i8(r)&&(i5.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","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","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","VRFSpec","WebhookSpec"],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.b0b6f79f7f4a94560e37.js.gz b/core/web/assets/main.8f602c136d4004a835ee.js.gz similarity index 93% rename from core/web/assets/main.b0b6f79f7f4a94560e37.js.gz rename to core/web/assets/main.8f602c136d4004a835ee.js.gz index c505ea9eeade9a8933c1cc213f6e6d36bc873d85..fceb7ce3cbe253bc309228ad9e7047001c11ebe5 100644 GIT binary patch delta 77848 zcmV)KK)Sz`)JTieNPvU^gaU*Egam{Iga(8Mgb0KQgbIWUgbaiYgbsucv=Awbe{CcS zqpw0^d^O~x33Jr~h0(Dk*;XPikz{94bo_%PNWvlk8Vf0!;`jZpabNE~$*rpH1{w<& zNse=J;>=jY(&(kSy1J^mma8~#q|9BTc9>UY692IuZ4c%FC8e|tQ&sAYUv6#L=?6T0 zr7inms==r-+iW2AIt*qSZ#vHbe;v;0xwJ{1FOvTF9J3{t8VIy@64(e#m<4}YnmCm~ zTyzec#O5XlDek=~pEcoPsC*kyj{~vDvmRoiU#cHj?xj(TD@$+@bG}$SLpr-M}I=Gu)l;l?OX;PvUAWY&M(m~13 z;(lWZ{6nwcg*&p-HvDoPvP%gVdl#LGl)6eK?mNSRS;39C8&`@EdX!d;Z5%bK6BNo9~#{*7~wGmL=iKbb__TVd3Bx=;f6>d(HZfaOxO8R=jce zTA?_Zs=PoRZc5ZNAjXQMK2tqpQ9n9K#H?>m6-j>jzaZXg`$Kxb$2j2jOjVUllv55N zRUdCERg?B65~`!{e}gg`#{URec$w&%w&vooto*Qjkqu`R2Ug6)#>kU}GeWL|=KaZ< zBK4DysUKAtDj{jJaxwE+XKXG`2g3SY(6g{M=%#Nv( zcVHX&0XGOwncMlR6BLrOa5Cip2^L%x`a{ zW`m=e3|zlyKzlZBW#+dF^UK<;Bo>UbVvomo#L;d_{OLixfCV=Ev_$NPFqW(atc{yZ zBnY`&oRRXZe<$T2GnRuoLRPZxChf!|BZW$9Ws(i*Py(Wouenk%zCH-l#g#|{LCe%o zZ_wSgm(SSATr5^z79XOMI(%CCKLwyLWf8+CondSij%9cshsV^X*h0BuMG&c0o0I=h zR+~yv(BSqKItS0$Ut_wN?TUHB{sh~bRrZ@JP5f|Ue*~dT*%rl$%!b|)n;HhuPh{Q+ zhyfdo&5p8Qtb-y?%+mVdktHtCpybgbYkvbXL7^qZEHQUr&5}zH8JDH&GsUOn2g%^= z#P3Q`NRw2N=&=%zBvnLtu~@V}s7%mt%G9HM-fE+BG#9V9(1G^oaUGiUBh^ZjS#arF z;EvkYe=U{Mk%_r&#|~^kk$E^h*Wn4bL0YHa*>L$R3xXp3jj6hX?l>#@;3XR>unD4N?}B^~&(wubt1&+Lz6x(Al&%SSdx zD+9BP%gF9(hSv6Mklo8Xak%4(@aeen7MahxfBNriE+SJ~9xhHFkRH|W)$JvvG4wVT zyzz}{jg7j-PPX9S3~L>Yfs(_fIrx!b9!LbWCf)Ptnmed#ujwYrWNyY;RiT+4jQSV5 z3ltP?Fq8I6YY51P6NrpGkdf5zvFh@G3YAewuVJ8?FFX{oZ&pm5#Xz1BGI zK4GBzMPQNWMg{~AK|A+G?$j8N&9hAah$hjntFewdbYWCu&mNiHOpc8cZ-q((4CBND z?*)Q7I)8iEE98_}`zCjH;AAnqE%2>~N1UL2CqVxOmX8YXGXDijfQugx#`lY5#ND3c z6ZPdQvr*ZUDp#v)*f;invc$I~W37kGpnI-~>%$CPC9ke_)>Sj!L`^ zKC^*m^$hI^2e$u2i#RIX?!w$?hj<<_q+L8y+fU0n*1qOGAabMTc&%>0p0e4p^hR%J z=a?;342IB`LTp58oeR#_O2sBxIY;r3x%M<6VscGX1+Yix=}Y^6&iBE-17fBp^Yp~g zO5$c-2}yaW-C-(2a_=zqmR7U2FsFKH4tQDn7h~%jMAhLy3A)9 zCLGY1szCI!g-8~Eq*AUhksybVcnqgfXD=COveP;cxN{t{Doy!F17SK=2<5A-( zd`0)e=zi+ifw*1(3y(T^_(W;=kRwr?_elm%#9!FE9T@{K3c_|i8@1ix)|Pe2uXI?% zo*B5_)$Ofysr}b~$$M@!&P9m`uB1Xj(txHBh#iQUHXiSP2?L@>MHlTn>_jF9I%<&V zFnBfx_6RUurlCTAveLdwX9WVU!qVKwhP+m|prr1@8DXQ#l{3IA>m1g%xAF>Y&O}K( zb`}!6w~(quQ=PFBN+FUG@lPBs!ixaBX!{A`4e3oWe>-NkBj~`4M8}-^AAdL}m`u5g zDSTYi1jsUfjK1KxW4P&(uO@FUWM-gPDvYdP;C6LQT_Hzz(^m5-Q+3t{>Wwj9#OH`~ z90*G!$95o@$vD5;QiZx41m8n=+PC%AeWKgDtXw5cm-a^bnqnYl zQk=;{0*dRLcp~CpYOl;OeQ3dQZ|K+!y9-i@lJd~p6l=I+mjM(um{HOZFT#$ zUPGUMh{K^d8e84c0afz&3D@gH!DQu7c^=L7$3&yPW!*vbWjO-ec#I|T= z)_P0nmzpoJ#LWHVUX#b)4@Sfym}ls@PH5N{J6 zGXdCgZ-~yR{i%(V-vcJcrnBiw-Ym2Eo^b`B!MU5G2WO#T&`;H{TjqWn26Men4;n^) zoQxLH;JfeSW{Zt7Y!W?odEurJm-1Rv0Gh2_MTT&^*p6YgT;0XElssv-2(Z(r8wE0JphpK*xbx7*RT;{EqZi0N@@ykijz@e5lmT1ynI!OGxp;CAKmBY1%y zt%1l1wRs8pC^1%*PWeu*E#*O(BFzT@9VWeWve*9)oU&cjZ}^8L6>p}=Jr^+};%cjLQ- zyj-N}eeDL%(T9!}yZXIQji!wB(^T&-?@Y&aCL}2=xIlBfc?*ImZyL49OpAGJtq^`p`AI zm{Hk%<-?~c+y=HUji{UOfyAZNYs~EOG`ijLT4j|e?F)btOMVRrCn_Gz7HO7EMvC9VYi$#zSFj`DB-WH9Q>x>Lwc9B1YsdG!Et9?P^MPs25 zZ;-aa{?OGa3JPu{0DR!xBKt&tJN6h+7b%Q+F0){TvPh_ZpyL_hZ)g}=&KlK>0Ml0n zGd*aQirICN(H=ZwIIK`;fB@4_OVlZql)Xwztl7AQQ^3to2Gx%2B`@m$Lk^Id1T)%< zM^Io&iMl9(vAQW|Ob+uMF0ni*i9`AX82K>d9?f6(GlMdsx z8$CeIJisiemw1^F9|512n3)hC0q>W%nGk+b4^uj?j#_?=s!OfrA*wET>~w(Jb@~_X zwdHJ}$*Qf?WYtNNRU=JSV~Hkfq6%+~*kZEfKUdP#U+tsUGg7dKj61AJxOi ze@$Q*A?NL_n0mPI<%OvV&EoMU60BHN{{#tE43$f;qcBVP0oX%5^sV!NVvDjG#&4t) zq%aWV(mlR8TyG;cVk@l4K7DuG>j5m(Effd@=Bd*^?BFu6#XkFEr&J_83atw53Hk+N zcs?MGmo^KScg5!exb=he`?1!wCz8`5C0sT%rU6eyZSzFi2T^`hgFIk4HrTxxIs6#e zHt6_5^7;JKPSlrdoDezz6ql!*5G?_Pm(`pQgIb@KElfo)0v5z}F5I!9H~)iKqsjP1 z4;?#Lu%X*OwIWb~nwQ8EUkyQL^NmQPODNgr)h zb^!byTomU^{knjQ1pX?gm$ID@u?ji75)QWj$-*fwmu;R9Ab-HazzsVCtII3f+7gMJ zfurS-VEFSA8^nuHdozJ|b{C`LTZPj%FFu?ezdt@LSkA=uTnEQpkh=ClF$B6cEP%8U zm|jrQkm67%q@3BxoN^q;^5nHO15uCzz*?~OE{`)NHKiDxI6d?Otxyz?N*cx}E#=R|9n~?r3)<>gd@~eaGl|-lxHP48pbn4G!_FX7>=GL;lKiB z&<7ja2zhd*PiuQz)spjy)G&$uh#e}sPPzYZq_kM)@_%h6B4)stIAPPs@0*Rzb2M3f zBxbQPt0ZEQ=V-J>VUb&pb3<`7A5SWJ$ckL5dO%tob%5mo?lCCoSzXC~v|lS4u>?tv z_+mAT&AI?|aro2JZWx^t3Q>d3_1JLfws`-Oz4Q*(w}N@Oi6rg$D~ZDokvaepT0&LU~ZJi7imTF9F_Xdy5D3N7TT zudRi=*hC9?aVIV0n{Qgk`)MJc{*APdr%5g34c9`xk;+Hu5RPL~zB=4o3wiy|(L!FU zTF8@cTF7r&$ZuN6Z(7LJTF9dfRgA(R?+AD=w{Og+q%~<&DhtNr)E`=~C(pGl1BAYy z+(q`$7UC$qBFZc2#ji0)hNF{f$9y|Z=K*ABYM>;T*dghwOwiKT1_^3o1<(z zTCZ$WREu_;8j`X|I_|VsDvw@c$n=x*HzLI*svu!cN=b~5sw6MEgeAZq&?*QqUn?+L zSph5(xlU3dmlKIxPQJ+HtT3h|joit!gnwB8(lVAKHFubdBPx7P4dM|^Nw`W>LsVBXqb8~?ADvA71wJOm+2HY6kvXY z@txuL(06hGL?2`7omt(m!*wMn&9$+Q7;mo7o)rjdaWo+5$I!`v3BmSLRut2-0n1Ma~C(9c9G~xm>G6W%9 z^)R*8b)Kq7>Gm9W(7t169#RBoLQxULbR?@Kn#C2k&LOa9*Ex19fRz>GF-T9`nQ5rN z!z#-b2SXVqs>~-U84#Ao3Ab(+!fZ)e6!h1Gb!wK|lY&nL@1Xl5NR1mErnsBIT z3{7(k;QXw@%nb_|$CxgrFaw+~=b1l-L-k`0@o)?!R2zN%Uu3#!e!zSpCG<-hnW&0` z|5)J_{8u<~3%|jCtS~S}Bf|#<2XI0=fIot`CtQa4BMsG-{L%&Mak6=CDqCfyL7MV5Y* z3!;=YSGp;#9l@hwY7D?8Wq(AQv8eULKaQLhC2Qf34PG?rMb4<-eH~58%Qc!5?1w7( zJ(d#R3h`eA+|E$~$Tta9K#pvdX!7&s!sfgGslul9#f8mxLfDMq^kPG`gFi;OYMI92 z&sI46qR5MceYdxSRPghe|GY@38h-kV-04T}AM9z(`&d+;mHu~nTz|`1TIz8<`V=3V zj6VG@$U`*xUGvD_ zE2EyS(?Y@bP8DYg)HY~&b%AsZwRJVr?%c_>ZRQN??hw8!WbQuDwbN5uJ90vV;D=Tt zg9p}S{YxW5ra=9RV}DC^T@w#Sr`ozE>g$@QuWO=yXGm5b{zd)4i(vg1_4OEW*EZ_+ zAVClQqW<8;T)i`_s%uAHx$_sbFHd4BcV2C@v)+eo~b83`hd4%~Lno=Y!)XDZtcB59DW14!yV?Lb# z4=gJ%EEf9Ki8Hhvaa3&t3@z5R4iqi>7M*LBBSdnTRw#~f#3p|PRfSnol`7a8ohQfb z@g#zH%j{MZh{ijZFAR_DNmIRHEZ?b1fSjW!ToA_@1D2xF1WTv^sRB7k zxh0-**Bbg(VOi};O(hY>yWau`>16%jSjM&E4$x7(l{;7E z>La2`wjZ3DPCqpJF!WM%1O#(Jt(Oj`5F%x?oASW!4~BSVY?*ATOC%bOdl@89T?~&9eVAP4@vTI4 zc!pUPfdIuN5uN>kJLNlG{DNV;K8ScdILrlUPvZ1J1t+NNu zrk*)Ibvb7GtlP>Nw|{_JpW)-a*o_-$b(piYm*A)nK>=--BB>Bae-;6V#TB5NYGe=Z zC}8!H4jAYd#T)AYo*~es*?tdQ0J^Pm6}YQPyorwEoz1*IVC9lcfXQOq+Oj3@4lBk7FuE{z>WT>{ zU}esMD_NOva`|EVQVN1K)T-n#zj&|avk~V zI)jCe`13$1L&}xx{urHKJK|wKTk|eiK45R_Oys?sUo(M5wX3bh|Tvq1s4+h zb>CD)5M|w4dc=c04FgyC!c~W7`a$HC!C4g{0ST78%efO_A9@J)@8$A_rseWBTcbMn zTa)?^v4=Sa!d?gHr>Iz9Ik4jgLEJ~O)-Zx(r;A=`e}SGLa@X0~;@>_}Dpu?8kX*^o z`}RWE_q!1{9KBGW;V0%uwI%1o)rlcu_sekN?wBzPtMO)$+Ihj%sE9 zhLWW#e}G^I7Oay|whZDvc5iRHY>W^{agps-gn;9%tuY&kGFcwBAJ;azy&VB1a`L3& zSzK&#P&Fp7yd8yqK1|At9}+M5v*Wmi4+#Aa>>Ntb9!+fSwY-ZM-_XJa zr|MN)-v!op!QQ}oK#8v9L5H8h&07y=mYv2Wf1e&`W6#ozr|b~kAM&YZCvkzp11@bE zhwMlo2YVgAJ~~KRaN4Bi6cGDv3Th^e#T;rhw*w*GRSe@ zdCS!Y!;k*$Ev!mFi~J=EEws(fb>G9>V29jB{$IUbPg}6xT0nc>mJT$mMWe6|{s^wv zv)14sqo+7qs=;^;tU7cm?~9lOoyEKQe?iQX{DzS~Bc@6zQ!;*IOPa&RaN~INc3JAa zQ}&E~%-H^)upWC2QcW>h4(D52Z*FgI(7f*)U4LN-!hPSSNqyJK(Sba~7CIKv!<-Myt~)ifN})Qw zDbOG^hHh%l&5=Evk(-3?LiMchf68^tj>`)Fe(H41p@sMEzv1>>J9L6j-j3i0J>Wz& z4={ofL>Zc$D}tk_R9TyVYYee z4UN4~Zvy4|7pr}32QO#if#vuTFz|8e$;eMV7$j~NY$*|0H4XpjHG8#Qf92aue7TvB z`bIHQ9@Ndy^rkf`0HEE8<@JULW)qm(74v2o<&pcj%tK}YHwyEpP7-e`F_-Onx6$jY zsIu^oI+rc;KcmjVpD=jC_`;z4!chFu0K8XyhT{fB7;>rbLtzD7&m|!d-s^<;ZSpWV zGrM*OOGB^YGj)}V<+@*a@NVq6=|vd#{2nq!<~gjAPQ$uefhj`e|oi5@srChxQP-+4N)E z@c|xcU-)yE?ynFefBu$rLeIx3;)dekL?8+VAFX(ifF5=f~_pOqcsV`EO z8F=9^#`u3#cFbs|iRaYP@2;&GZjkB6Qo|8HKRRkW-QLC8XvL=yCwlE!ed{KEZgG4V zja@H^$@qdc{a?gR3>e*oqB8tlx(a;FqZ6b)umf{wcSPUlhOdQ{Z!*ftO+x*TmcD>b?rI~IKeoBqHJU|z%xhThZK zra$06c87k0e-7QZh4k9Cy*B;$E(-5_C~Xg<(_V-y9b6;OqOP?)>`>ds4q}BJo34iT zHA(qH_habM)yU*e++NR$M}`6&NG2vq*PSs!uj9>tR8b{9&HW!bV_31CN zLnw`R@gIo8^mBuM2j&p#=-LFS?nhW`*wL=zmp{X{e^l<-B>4mMuBir#j}%i5xgPiK z!&~?q9ilZ0#a+!B+BY_p9*!uoZ=3$&O2&&|7~4)6?%ppzB***w63+U~p~YM}3;&{*Y#) zKcc@le`?r~#sk73SoiJ(5n^D{YaY9@ZIU8oVEWhAkUze`)&?EA;q?v1*VvzdA5hsK zz&pD$u>0;ub41sKGPc$6u^Yg0yEOF}4%r*+(3OpUu}dHEpE)tb565D0C6EX2qS4Y# z0F(=pi_=?oXw&Z}{G*@-T%&U;?!^37)XfnZe}9_dcTB&@T7ATxkKxe9p26A~(QI71 z!H^(~wchaGfOJWg&y_^O+1SNU%y9WBwD|?0?a-dY=sp}y5K=>TDCU!646Y4#D;pfQyY$A$*9MpHaZGOd<{>W9RkW(cza2kZq4$OWn zf3RSq@XLelkUz2G!a!B<$0Eo%AY9E9Z>Y+#Pg7n66J*@j?G%Rzxym3Dw zUf)QRobkmm<6FjO==l}rExN6~U+fQyz*>Z*{eD;X0ryxYjX|s5m_*e)5NNZh3%(Z^QOa)=WOt9vBem z$O1XL!$ZeoQi3~2cg+heG=N>SAHpu^J#zzCo*4OnY3Jfl0{!MSw#34bhclYg^gSK3 zvT8<7G^1(UWY+>Akg87?`mj2Hk_S6nHlTi6yqEB`5EliXtLQ=XmkhQLA%BfUi-J#5 z7K#HlSB}|I=#|`L>MWIA4-n=?3bP~xL zY&8Ok>jrw^oo{V0_tcRZbp+$7SSC#ANsmHHnVtny5|7Mk$--}ZZgeH-Ym~`mrp5ynNa^xfxSWRXe zfxFk)jozCT`!hE0A=~j5INA=HIiaeh3>_^ye3QqWFc2}`xkZwg;%H`N=zkGAOXi>^ z(^g_#^oUC-wOZQiCx1`1gS)1)=u$QdV2Cl8(2Z|YYm58FZzmFz$QP5h+4s+*4}cfr zBPn#l(HQ7F&uPMk>&fAIGSem#05Tkgb73q83o>$Kulf>*7ez*b|BQQqCbE#oCE%Mt zuRmF~ef*WMhphmX?AYqqJsXph{AsXflKjv-tm@LSMCL+~a(~+k0^pavVDF*@lR*z$ z$~PE3umO?`Y(p2DMzp1fwc=n16I*5D)5>Eb!3pFAu*KG2WrNf9FLHv@Dxql|HDm6I z6d{fXqI&c*=E)UaW+JZbWDnZ8(O<*m^xZ>4puWbf|7!*?RSbAwnscLmro)-Ow>(lv z+>M{BE{$aB>3zuAEOu_s@Ka{90+YHZXs zb{F@ltA8yhY-z)08jC-T*uQ!Ru^+vC(AVb@44j%h-GqUCBiW<==_bXWM#T?-RyG=q zdKGZxq*b-5yR4rV1)S1>^bhrCcApyyV#La>J+Wa+cz*_b(g{Y+es14b-Qq6R`#P8X zMOS3*O376n#3T)%j+Yc>`2rDqf5XVss|eU5Z);0?+UdBV6BM4ALkuRFjOR?cmcq$V zLHo|RlP)8xSg6W`RBwe@0b*USwUx20_co5YytZUo~2BPHctyV5;5AVh4$?KmsZGC7v*P`e_|EQRF z0Gt=^4^O@4eOLWH5H_FLv`XgtNKwMSP?W)Mzn3Pu5F`PTmrJ@3Toka)$R*@b5NF-) zUQr$v?zQsD1?79#rf34=kw1>Dzh~i*o z8Z{1ySz7LKmSIy~%Y>u&sR4+0#RTI3&r)oI@#s-NdF@n!_1o7cSkGdD^_w7A8cQEy zr6`vqKw<&M^hm7a_(7qd>-r{iPkyjie(?2dC4G8F^ywL%Cw_!~FZ0!7@4lFNEIa$p zf!RUh$bd-#xPj(?e?;)dz|_{NJCzD_{BtgC9r^vp1@hoaE1^mCOnrsEytt17!l>n{ z7JkW5ExcH%TG)@Gy-aN1hDCYr!E&{FwNhasX@{VAY`xHXR|+U#2b1^HMtb;%4{pUw z0y7814PJUc3MgRx0A9#GDzxT(98H#7*BFd@KC!LoGxrAl zF)D?|F8m*jWOGshk4*&dylZH$Y7HoMSgq(-96qb>=r#DMHO}c7UKJ`n{GnU;tu@Nx zvP^fMN0oA;aagSvpr!)-)EfR?p;n3hU&gy~t#V#((%VAgv&MQh=8Hr#IW$5+XXTvj zPwUuT2dzWqf1MjZN(QtI{(45OBEJ6@YUg^D+;5dpR=1(Y^@_gtvR>`zO&C_Stk(-w zy;jzD^?F&~hCgtpSK;sOu)M7|%hhV9tT*62Ji^CVU=RM`BYLo{Z{s7VrH)^~;PHz( zJf*7ZdKLacQN5{S^H7Vv_j$Af-!}Co)?KKS^&M&hfBr(Na8=hEoeFlRiLDlR%T*u0 zZ5G56ELfm|1^l)SJ=AyfU6=@b+tIfRbP<0rfu~Rw`qh**;;&Bw<1OL8Y{RwBqcZfj z(A0N3uunJwT4>{cQT8BVW!||7E(AO#ystTCE=KwUi!tww$ zo#uphf0J9*rff@~kwxAkhX0;_);|F7KD8i()~ zCV989w-ddn5h*#8kKwkUTsIDDQAzojs)E12JmaaM=U&4k3x_j)%L6!Rjdi2|D`_0o zn|h_ugj(^gpxj}10n=*wUb9h;Ur^hJ@-b1m=o&wXN=A?QCznaS5Fvj}G^#uByQ4QN z2%);ZQ{CM@tO8kSZbLcfN);dmE2Yw?LOGy5+q+n-+Su8~YAUt8T^vlUSqDN6%W1E= zjn!6a6(H4hy}7-&N6qZ)!it2|zs<*9--bmC6s@^iquRIY+elSw+s!Jr3cY9+>OkeF zH#>TLrw$;5>UL^X>ivIi73Q^}Z}06;vpYM@T?AaUS%(?NfmaCR+m$_N2PkS|mjGMe zY1FV?c;3YCv0I1G?#@o7N%im6fS}a$ZG;N^R<^gd@%dhL2fJ6TRcXR&)$Im#e+PyE z_t*%GZFjFO@1UyYAzi~h1|8hnYu0#&nz)7Ojb^h+GhMCHbO3*t53xU0Xb)#&x5oR^ z00aQ2*LHRq*dIP?FwUJ_=m=232CfJkFOH|8SL(YoJ+;bigO9Vii{Rg`Huer{K!CR! zJA8VoG#%SDSel3w)g8pL2w~pS-`@oeK}Rap!x|7?m`u9CA=G!MM@<-5bywf5B9a4r ztu}XX#%j%7n$dsy4&u=^;18ei-979f%tRe)fgbHtVDUDvhXlk%qe>%c)Bw>CjCB|p z-0uJ?&`ej5MZlRxMBD>}+G|k#O~3@`M|Bqv5iw@Bvdi(c0tmVb-_@zzW*spG`n8MS zLA!Nq2N1Z{pmu72dW1nuY!_y}g70A>w|T!RdkyOM_FjL5Fn70#paG1m@^Lk5h!Vio z?D28$)DaGV0Cil_yLxS}Q9lH@?d>#a;&=Hx6OwH2?8zIBKk&DP6NAHp?!#xzdaaIN zY!FQW#NDnDJ5s9w*x~u!UY(|4Z*PYXzq!Z1+uMVQ$6;X$r^>X(RHgZJ8#gw(jhxV+ zh2{o?W}AOtzTJ2UklcF@4|brDJ-u4xeLbxM^R{!!3&PlT8}F(3W{t~@_UBgrM`fd} zDjRL}Z@$r1?y%8Ti*vh6p2|J@=EH|mb7pxTs+vfQ$epJ&dRnO7VIKzJ$=C8lD;cpv zm{iG@J43%*=bQe&+79p2Axjy_`)7pWi)>a(iSV&boC2FwlbcRE@Ry*_&-&0 z)y9@X(kS_$Ebi2;axDeSl^sr3b3QP~R$f7|V$?pWfYcbRC7?LS{=?!hkze6lOC9-e z{D&M&A%0#He{pUrWqM10>&U?aL86T z9&CS|@KtSh01LgoJE6ZX@#%Yfy0We{kZ1r2q`hz(=3d{~o$LVh0zSWnJpB$35n}1L zK?JJTKxC+HPe634eHOc#n=NF7F|f1O$crp9gz zmKs&nI20vH|z7tTq{b_0F*a-_U<304jPvzj4qKcCixsmPutjjyBXNY`lY7 zby95(%C+kI>NZD;6~rb1opsd4+U=RWYA$B?m%#|B1dbR6>kW(lir`dQFyhx^A}m4esx2)JACu%?dkEw@sVaM(_4QxnMjE4Nc{C!O8FTj(}r$RzH9CH?Cj0is#fX>!VdY{ds+@e(2+l%lj(yu!eF{ zxmd9NUhY(lO@LbZ#Scp@r7Z`ud3PvA5 zv{1{MZE|PBctGyhs4$EO13#+S;BKOtdPPQ;;I8827>7Y?H1#p?426>rdG6 zuRT@J09sng=8n(qMrHZr=9qteZAUwVK_NDphH|rHBoD5p#-VTvUTcHkj;wZ3)>9<7#wDS?yIfzQD6`+dWO&dF2uojbMf$WqQ7Y;1Oc z9)u3Ju`##>4chF)4UJ$cL;TS}Dm8~c_*vtl{^KuY`;Ym&aUtapN)>5v%g7+idk{_w zuP|tWw0@6i=fwW{w9|}eC$yI@#tx=KoRX~`848`ZP7x6T8E)Vqs!+98)@ zawxzWNiW5(48A_UGLHF`!BSaorJ$FV@^A)n1iTb;FkZ;Bl&LuU5yrl{>ODOk>oR{a zDZyRSxEJ@7n%sv;P4^U;hBJvh$o%>%1SfN?;Pl0$rb(C0p}vIFlv=@)ufKw4@d}=- zmpC>QH@=*#abHJ;$!sNsn4^@H=V)scs7;!Q!k()gL&^I>@N%5^aEY@#qeFNhaf z`k2j%)4$?JJOM1QXp=Y4%VwokgHAq$%bnew>MmTLQoQ2HQwXIykKgEEk>?C@Aea-v|57ci)w~OXsR(!5_JTJcX{Eh}`VSoI+MfMmNHK zsC4j1reBrB7ws#ogFG5LqoUF)kYRdQ%Of5EuqleeCGMx59f<2i$*?fTJA9%v{91HW z3mx7{S9>Hgk)Bhi^sOy~)gvhwD!3S} zhEXh`rfA?|R4}ERXLmD`;h<2BWe(IEW4-_+N~Sm8&&mEON`TO<*wOGF`+%Qb%ahW} zs*;#HeQEmf{5Fn6BvBYJU`R=i@rpa1=B8Mecnbr>oO#(SntzjzO1rcwfQ@<0f1dLn zKnJD#^C*3%z)KO+Jx&X(E+Hto&%vLuM3MmnxD65vi1RVKtX!o)V;cX0Qs`}MQEqH# zD#W%J9Q~NWg0^4+Q zgmJe`|J3H%CFtW6xPF+8r#w=l#;zn91BN&hHDvNE_kU0em@vzmkjC!Z4ZV&$>zQX> z7@9`xIj%&^OUx$k{u)T-jO}HjCrZY}$s3Wi!0zGxAQCFZTzH4F0tsHNs699BA&3=7 z9pzVazKHR(3KY947+8g#i(tlJi9C8$K}l{2Sd1>;lgByGo-d^B5bC6?j{e~T&N(5! zuqE;U+JCfR2Q8qGK-4Dau&j;Dn+wywK9V88VE26|D&Yvz4Tly!eTJLhejC#a?Jr{- z?ohSVE6QjafQFNi2ue#a!eN)_xbV}b5M|zo>hSriL-8>%`8chowsZYsXbpMVJL(T+ z+so^8hJCq1m!(0@#Q=Idz&s3j!{~Y#vc?RK9e)bmpgcL3(2O6Q0mtu-j;x_MOGCi! z4lT6q_1;>Z?RM2CGB5-Oh5!PE5FZYcpF~fTPlBlXc>wy!vc^^pZ0<67NK56byf)q( zp4}Pvq}3w@G>*p&gIwUtCp@fO8gr6wFDTNv2NKxu1P?nVJds0Qne(uXdu-cM3MMDZ ze}Cq>0jz9u{MMp~@xUoO3o%~-y;n$H-2HKFJgG(Bjd_Ad@)ZT*3+=1{VeN~;kh=MB z@VOoDr^Lude6hhgmJxcFjocHffPN2UR*Ug4>_dO~2F7oqcRkDMLL-SWD)8g4GN5~Z z_IxdRySV2ekGUShEq^t+!X+55$FL2|%zsbTOrca={K7wqMo&-U38Jhghj~4sH_yW% z+M^-`h$&9`%?s0i6{0qHXcKaF2BC9}2ZdC&bM5weCooO6Ih5THPhXig@(FSUCdx84 zC7+mhJ>WrCBk425w}I&o&M}e5=0Ja6RabQUxVGf++hOR(ucoFQP|K9;!ga54EPv61 zcA`)5R{>@zu)5Fg^7-kE;79CE!5qmMw+Y{YOO?PYP_^0dcEX zD(PLEmZLX;J{LfKTd{z||zvl|{Agl0V%VVTTSiYt1$`O@?sK;j`hO?#h+h94Ekm23g$)PlUC=Ns$b2C@DEU(vU{*OCu57y~IK!#0%EJ&3c&ll%CL+bd|s8s(;)ffK7hIgS$$74-f8s zgzIW;XRiU*KjZV}PE$?sHBllXO#bGP5hfc&Mwt8zSO~)gzS>fwzXd0{_YM#&jxeVN zlbqDP?y(|`r_Lsg4Gl7(4&RvbB<^TO*+?5o@>kM~GHX1Xw9AYPHe-EvY~b1Vtl6bj zuT(Uc8P+zteSa%Ej z1RRzzyhPJD#7$p;QMMF3iMa|b{)WRUw>%%FQkI66SCSkupq#Z}ASf=)J~8}T`CTW+TurQeah37HD=!YitwW4C2eUvX?jYNqooYHq2Pi^^e@Ry z@q4vH;>klhV`e=@)7?QA2xAZbFw4quTpAI*r{s=rne| z=``-8)0kO*ADzZ5Na{3pxlUt8s4`}bys%WAhJSdxkxpappQF>*Q*;_LZ$s9mYnwy2 ze;CDXP{J)_96rz_--OQ%;loy zmMK)YziiZ~%1RMh*^SJ`uC{^on`Ec~|T-fz$C{Ehb9 zVPelE{D2)clv{02UUU+BZg1l~S0MI3Vb2vq6?<-Xb*>qnZ`NzU1Z0l~5@!g@_K}&X zAG&|z*@X*)X54m7ugl1{(g;ZJHJ;_@uFNG0WAj*Xf`V^ttNTdfJChx2+ViTb=2n*^ zihdj_#&uq~FxpcKuyjR*+yHSl$bsSHagmN=HaYg#IPZEaUoMHIG3%<}fq`%@lSS#i z`PqfrIoB1T$J`3n%dwXieQ#B|-EX>IX!%6>~#_J4ue!f|)w+VGa!|5JZ9 z(S^Y4_J-1qi1Bd?jd>Jizp5a3JeF^@@SlRUYE~<(C06dND`LQ zjlw|GS4EclvcCA#%cy=Tlv9L<+*8Ud&&11v*DZg4krCjY)KWvpyiNqfsO9U>&+?>2 z0neTtltmmmjYVMxC`N--=Fta8M!kPPqe$Q+N9brcwgX_oJ|j65K^%=aN^*VA1$H(a zUV)0}L+nIGAm#5yrtQ$A(oxlls)n{7yydZv&k6K;TsBj8wlI}guqd46x~2|g!Oa>= z7Z=SrF0PL?$&zM@w>S~0<$*kt0XTC*XIhs5@Pw!C)bbADeX(@4By8m@$Acw&*4`+1 zqR-=<4D{>mINnJi->Ew~YvEy6*6bS2gSU%lPk3mIm$lXqGJpB{*m5fdk4taq2!$3! zlvpe_&IHXVb~3Hh1EWaZKY*jPBp$}H&PO}T7oK{m_f7#b5Jh$5cGs{N zPlt8V^)U`x%HdH7WuQ-iq~JOHl9g!b6FlEd`bTmvP3i`!b(9qi05t;;w%Z|y1+BOU zm(AA@7=Mi#H&IF^q)fcZ&y9+<3;PbI21^#M;Nc&>MB)XD73UC&wy=4#7|2g03N|H! zUU;p2e>#CF+97{2$3$pvfW&?Q}h2L4)?SSYH2-2`so}pwE+5x4syQ8(LpM)05di;IXeYm^`k0O!s%hO zj%bO&`f_p*0~S`$Dw-`fxv=)JI$p8gGWps&9u z%74vWSDsi`_F}Oh0ugQB1T_OF1cqJ13yhBdw>EBxS(Z8AVCH0j_I|QoWhi|K79jbJ zgWpiy;v1hki91ZhG~^X5FtkCUQXv)ZMWO5)l~wKFqKPZetR&OTc&jizyuA`_!)NL6s;`g5gVwkt@?dD5+}#)4whQYfqVm z%6`~^@%V+rQ;p%+#jCh%>~>Be($%~XE3-x{ZJbmEc}>%c6%D5(-@LREOB)r5mhzgF zIhd9<3|Q}2)8gy~)*!I5LyF(VF@>r_l|h+Kqs!eoFebu?)9@Iv!-l;;%`|@WW`E3m z3g((Myw=TO-}OLVAE94>*Vipf=jmHIz8hlh8&WXiU6=gpQC)3lr^nAP#A8v2R@GZ% z+7S*IiU6R}pF!F46U35K=_&fzB5_X?eEs6|_`~th3t8Gk8}Hx@UD4ouRj>5Wr8m_} zp$>oomw`KOH7l%ukzeV8M}DPCvVU@DHLp?~k-8Iv5{5Q?W(8A_!71&JkBy4*pIvu! z;XbzmOmcPhE{VLO#gpTTgqisU3Ol7$@0)Z4g-Zp*iN!`{0&ix79_f z1!3QTVH}>jNb2EP54~8PI)66zio0g0=to^H7$Nyv(QqWK9`^G zCZEY_+7v!wgy(>VDK!yuqt1Gmx5hgq6G?!Xngh0u>fvk4d&w)5w}0a0g2wq$w*1Tj zR@8PWChN$&A=tqewnJBXogs4E4JwU|phDCv3zX{iDyT%S&S|^}0Fz;kG<-Ki1e-g7 zu_C}Is^uYpRmj#vBa!76P&o|p3Lwe_=-70gI$eS!RgTNx1#S#vZ?D9^Ua%0v#18yR z#y#$kde#Oq{u`8#*E7E^_E*K9=G)=acDn9V2a0?=p+G2pfE1#r5lBWZW*X|1nDU|c z!lRd~K3a&?-EHLuB$trf5Fvl6v2mL)TUB6;Tj6&e*7FRt^B6;cAaI6^c8O=n`p*7A z^xOsl&e8lJEU)>hY#QNrsI2L%sGpONR0Dij9dyd;zJn$@(1gbWd+P$}T3nPWR!h+v z+$%NJkR6>9qPMhz#;=3a>(3{E!8dWO3v_(UiX8WxlYkUsxdW)+S!{n^A3!LK^=?ZP zRYDJrgyOk5vWKu>ebe#FzUA3HEsucYXu4+~^y?qlQpSsH5NzS+V))Xy0P9(cW%zQ{LB{L1 zO`r<}@qfKqii7me&UJq;GzRNwtiWN`XFCZO~5jB6Jzo{D0RW_NqYEh(4 zTU#2CHW)S12CgTaO>rls{kt$%*&$5OjerPBkq<9Ou-^A_yP$ZEf^kGw-TmOwM$Me? z+*Qx@U`fI%veiL~!mw4D#W}T(dzYYQF2<9XN5Qg{SdAp|lK!YbA8GK7HH1fr@~_$N4#N*XF!iYBLt(+lmEwHe!Kd zvGX~{bms9!DOF%;Ym zu-!j><9?T`-w-YVJeS(v5Ho*1)L;hjWTsjvF}x=wLAsz%VHmMsg-R}xQNDZ~zhoV8 zfmN^`#r|4$upSgc*5PavTo4Xa7TPnD_` zn$G`y`}(?|XUjL}Xv6L1WjFLVzmPB>gH17n>)g$mJX7CV-djYmj(RUAk4G=76;^Aq zW(9Bu5QcCuELS7PRr)e%W!)1f8+=!d(XxYZksqBYttO%ii7%JC;1DDQienK`oR{R_ z5G;TDnCQty{KJp`9RA@)!9T=sYDV^HFiWXCi$A(;nIsM4|BAedbSaXm>AH?jW$K7H z*P6@=p`=lZN@vnS#Gj?&^~=X`Ea)iz^OA7t%KK$0AmaRRG7m&vCsS!4;x1-#Jgn(^ zD%(T)v69~*{pvgc!#pTt@;zi;@Yvc4R0MxC_nG8ZcRx(1!N7=Lx(FA2OBt&zRNf5CF zptU8BjA`gxtSwsxR>~w#t zi4kJ(r1Lf8ECkEnl-|X0^rI&-kwlu_DcK~Vna|H4k$9a=BeCptB9}xoUYSrL)twZo z<>Z=3y^;whgwQ#Lt5-n&}rU7QO+K1n7-NGz&!;)CV07NR$% z;~2Fnm1sOt@9ukZBUW}W7}NpzbY$&M@WA@~C&_CnRUc2vY$zB+eT zCDfknU@mPGQ%h`W(e*Z62XueEN7oKr16r}!4wQnI@X%YCq9P85?5PPW>?rJ^8AnoP zZf;a%pR3XzKa+8kS=gG@aspL#S|MtXX?XPkMqGKw<+_Z_D)A-rvxSKh( z%xsZDp|c`9TaRZspjCfw*hyQ}sr^;->{D}dJ^R$7yvazG)7&_CgT zLJXA~I7|nrm|4-vMY`7#i2-1J;rVjXuC%&k1IHyD)D^A^uNMp!2l=Ptnd{|MCN8eKNVlI$BkRm|(YRTNuXsz!NmW z5O>rBED0zfUzO0plkm+eu}aDCh=jq@M=^o(7)Aq^R84=vU%I?|uuIwqveqSUmf4)z zT>)rt?#Q9NP@w>5g{onC6hjH&=Qp^x1$$P%uOc*1YuqSDY3;bvegcc^S_na01cOteZzcdGsZ!`Z2 zW}p)7J+S$~9rUJ2I3Tduj2CP)sep<2*x)<6nBafPLd`Nd9Y=ZvAm|Cb!l%&kW`*a- zbAotUd<^V;;pFHMld?42f!rk|X}XETX<#plSRB47t&!kUT8XSYyhP>}pT2ejvJ6k? zX1GjF0c7(e@T4d`9j{fsOxsx?jlzKR83isBsSql~IwJWh3k-R#D~p_qI1W)CVm5{=sQ_xXfU$J_;&cKXDdE-uG^}<0U;-h@Or>A&PC-qQAp7ar{mEED!%9lT8$+AvchVN!FIwf z8?0R{f*dkml=KLyJOUrc#HG^JzSw{hp=yeLD-QDpdw54jCX7TF0BC-!H$5UDCHc%( zvqka>4Re1`F(?9F2Q&RsP%0*u9&ep5J=M1v@U&K`Bywse z$c|>nI!|F)q17Uho+$D4WgP&?fmD;Qwu@m55~h@>ixRMtE+{HTb_Am(2?a}R)++WB zxDorSX3)Wl(80?^2bgy`tl()0c4`NR9W&o56CTAK<>O#TAhZ3BIoJNuj!%D6eEhgL zmkZWX7c7QYWFJ(b7MHAuGSd+jIu_{Hk(ks$#cnd3(y@GZoW(NCGqNmT(A48PrDH|G zcbsdn8fg(HLVGlmdO%~EqY?Zdqa>y9Ye_#`S5T4s(7w|TS^p%vlkcA-mEOWnX@6yq z+Sn7u(SaLd0}T3|n@=Y{$fU}zGS>tmDH0@sEG^q$NSt9tWHkH3y$^85~p$?I?WwfpJUZvIC4wTq;F?VRh^ zuBBQ{#-hYADPO(%`uepuo9ow(|2g`# z@m#ylr3Rk4_LIx4Ay-O`PghEfrz9ahB?<8`S3*pL+}=;C_SE3iiITeQbcP{@#>P2f z)04dX(OC_lhInDYqC3J6;H894ANEf@S4xZ>t!eX z+#{R3$~?TC`&nC9cAt><@V^=6_bArnP)5ESn9uUF4oODTiFfCgqL= z1t3ztSr=pmtW9!9{pm@g0H?Lz+>lrt=l}b^|8EfK-%Mx5Q+&*6A8&7&iT&g}q72fw({dBc}5^~>km z9%k;BVv^2QGD#naNjgKV`YHT-p4C;`&*5dD293?t4?BO^5?nuwF*CU%IqdGJ82*@0 z3`-SLfGQ?OsE?dAI$=EGB%QDc7g8te!ZoRCql4muWh!68Jy@>jbr&VW&b*(#CVi7{ z*u=6(|8c%xgV_DpVKgzaSW*`SHIrV5O@C`$YC`t}IOtj2?A3zn$m9yHxdrdfh@Ef; z(xVrk6*qqhNDSlpa8ITSmlwZMme;d{l;4sONYAWZ6kb4mV5T1+Fu8Sca8Sg2#QKjV zU|XYyB^a|WVxXb)E`rfZWuB|kk^(87KfvHo2aaLw+p>a|%gpH7C#f!MdDEgdE(RD| znFmh=wvL|wK+gyas10>?^|4)2uI8u%XLJ7=6*7NO7tvs?*XqcaD{TsjNt9N%x#P<7 z7{_jJL&Vx6zHccn02W;-ZJNGs_nl$~Xgmm+81q2Lea_wp2+ z^h|%S5-9X^OerqoyCfXCY*1pCa?CQ3cqt_s$o^UfOQsG4?YX+zMQ{u`FFjZ^iTAz5 zJ{KM$yo`{FDHRg}8WD~y6x>B>73$pDVl`R?5n5FNow6~-bKznEToy_iC9a-QH(Tfc zKX>b1Df`pAzbL%r^yRn6hifAr%Gp^O`EY;wSe4<`;aj91S~Uo9eArq!P>TonMYt?a zw6$))*u~_489%ubgaS5;XmE})xX(#;P2fOq5a4XDY1BVh&c3bt6oytZ0JOXu-eqS8 zR?M{1D+17s| zX6$nj78|E|fKYG&?1FMu$>w-$HvOgL6l?VwCbpXXM2W4Yzrf}DRvS~c`2%xo73Y{F z*%;R-huf#nhOQO0GjRpHKdBT&Cgn&=eeveism3xaUV5>kaa6Q#$4k4SuJg*3p=qSg z7h{{e(TM{rqNctgbn+3(0R5yB$?1QmTrH5;kl!Oc6Ok;P^RNxPpm6-ao^t_anWkp& zip>+%0Ea@O=DtMaFl0>MGg&b)nR#9D`Fpg(qn;3vjZWjdtt}uMIs!Jw9v*nxRJLm1 z0?U}ORb!6(o_EJut}e>;5UFl2?xji@oT1{?@}wIELpn-gm{&q3W`*ayp2nU7 zP+nR?SdSN!1&#}lN#J1z%L6J@Mu!y=BsYo5H|iQYS%A2C8R;a*yV*>bxKUC3Oh5-s zX1C}f=S#u0hZtT+i}F!MdM|%k!@t8CZdR%lT*JS%P#E%U9!En{*z_M&%p;BkP%h6< z2dFj)RjQ* q7&!1oMM3noy>#<`BbQBrcE@x{C_qD~{ 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 delta 77867 zcmV)7K*zs})JT=oNPvU^gaU*Egam{Iga(8Mgb0KQgbIWUgbaiYgbsucv=Awbe~lvx zqpzZHay`;C#Ax+Gx7sJMoy6VF(zcUc+&=j!iINygq{2nABJ2D9*SN2DpX62*01{lZ z*iNTUpH9!jB0&%Y3WY*dp_Z#SZ=}p!qjs1#W)lCgA8ilj0VSoh4O3O>j$dwV+35nF zzS5TcFx6mGnQb-@dmRQdjW?a=e}E3>R4#3j=ZmC2KF4gyr3M16odh-l6K27mmL^VR z5Eq>TC$YH+LW+BD%4bcu7%J~Z)Z;)b@~nrL=$Gn8mOCo>S+UV4WQ~+(MKaofBez&C z3d>vWq_1*6gVgp(foQ}5aq>hwz<>$l;sL-a$=#f&M)4B5b_LT!$vDa=e^H}QoJQVs zft-3fnt4Yqf;OhxNY_Uuer$6Nhhy?QHcfU4BB*IRu@3Gg7$v!tEKN$Z0)$DNLpmt= zS=?_dfq&=~yl_W$+J;}wLv|?vWACCGoE1*F=(R;!^R7s75mv=yE4`Gjb?E ztwKNM*Gls&|4V_d(Mjl0CE+8a>dT`BmL9sxT5h|jW%C{L+FD=N#=0bYnNF~_IxHM~1HHVGXRlfR5l$WBV#OPW zuN8`usmcrF;ig1Q17fU5>NC|t7WJc(M9lj3RFUMT{|n;1wm+l?e2fEb&s0^}L^Gfg_nuGX=^SX%gPVi7uj%DabU$vY>YfvI3wgbXx^V} zDN;WPnfg(cp%RidOCG>~u870YYeId^09O^hH^o#xo;!=H{}YEdDA+upNzp@^%Iugr zc?Y(UA8>>4l*ugsMJANMfh|T(o$LL=RNp}A71BBe*oh@4e@xJkD^U$)eVfb(gdkOd zKIO<)tS4%xWTu1Eyt5q{uGs6^Hv+3qq%s+g$}ewkK53sAE{QN%z{gw z0(aEDe{QLqj!evLJ9c0Tip<05xeiab4bnOV&xXrqVGy_urlAJ@fIAo;$%Ni!D^j-F zTU(Ksmdbuz9K2%l#J6}BO%bG=yB-_ub0)uz2}N`JtE2;8_N}4*+%x+lneKsQ`tp&@ z(aOLq<1(_lnxVBl8)Ww~PaN*JB78coyhY~qf3E&Jn~TWQmWPYe2c$of_T4STGv6HPhIKx&)W1!@)X%2p5m_>)_}FxLK$Xgkvla8Wb}ssz4qX`4*t18bHS$d;4 zv~$dsDh5O7OCdHQwax|SYo%fnt(>EH$Xt7x5HYzXssh*}^z@~Df9LyP-vKexlX-gL zXeIGyUI|Hgso$GY_l->8!6(^Vz8F4!5?fOMa!p zBKFL{^{#GjtxN5{{!89-t8p$$L~tb)5|RcqjX>-`)U@$Bt@Al{JP6!W)Zb~}O&%t&<1ssHhZbAri~ zyO_eqMNNRLf5Yevo;!w{F8ONm=0au$ilxHH3I=Xh*VGkqbT@4^pE6ZveW2bL^F@4) zNXLP&L~?A0;!ymGI-B>{CoQCxvdqb?35`o3G{?|5_st`cD95O(24pYk@e)BQ%HrU0 zQG>7oTFp+_LPczY`r5L}$ei<5-yf8yJu_JPN~6X^ge&>Jy=o@ZH~ zqx1!d1#gqf=iD==GImVzoqz|mj6WIYcU!7ZmxJJY2v7UA-nvh8dzY1~r0LS$NMBP7 zB94gPF+5VUam8H->+_qXGNEuKmAZ$~OsdQye~Q``>ctN{L1_(fN1uQt0X5_!AoXH1@Jg&wGI&ZKbO4CA z36GfoY`Hf?=hXhxM#}F2lVj7_^dWa)v#OUej5gJy-x)V ze{X5W#o1qj$ygOYafWP& z0AE@Ip@oSV1Bey@b161@zcdGsZ!;fl>MPL>4lD%RL2sIb0|J}Pc)>=K3Ydt+mpYda zFn{9{EP7Nb%&~z&AynVQ7vXJ82uJVn_oKRng0Aa@)aTB_PRH{7xK5$KWf--jP_Vo4 z-9lb2QtiHWgXic&M~hv3U#P}Y@6iwZ(PA^1_VXN?WG#T-B%1qM%c&F*k|oxDmahu! z-dCZhfvD+d{Fx@8f(nJ1fHCT4)9--XH-BF$_?0NKKZxb;S}B>rLW`P#{_ahwSb`p7 zs)`DGs-IL!WY^>V4#fl0=?*P<=*=_#0KYRUx;sLB0L+MQ%wf(kgeybxhnfr^9-cmQ z%`RqCcHx?i*nn9E=EL=i?cab6uzD7t{m-F=rgp~V{1aTw3}`shE}Ax0$$i8|cYls$ z;`~Pf8|}C5(n_z{ocdz83(hWttkWunEs(xq@reV7_E1dA$a)cGeB|Z{QPnPR=bT!1 zIc453TVa3b>J$Y9Hxd9o@NSWPqJJHGjHrtg#ypo~4co-rI&C^SHTX{aUYluF88r6tyE{Do7%%}@r_j_f5b>i|OzkeUQD z+Kfj~U`ma;sDZJ%DQ8R$^Bpd+JSmAo`UDvHFytQ1ANSD>Yel(qz?0lhs(F$s&`D#Am;}Zj0CJ3PAjEV^;2^=34zlY!+QOA{Z7- zJ`({B$|yiRmxh`UO$!8=& zar`>PYsa@*nYWhU1A!(+xbvS+3#4?R1%q&e)(WmOoR#vd1V_VorijL3AO*wGvm_i? zzzq7}$2LNqoaxir9#^&GJR&trqCaAX%C1xHKO8A7*13Ftn~8`SFeXmeH1hjqqw^e1 zRv(F3tjsEjnB+Mctx;Iy*5lkz9L>j*iXO5em#Q9+R!1FRd4PKiN_tjTvLEf&ibgC! z(j&fD4P&z|09_pZG_@N>=Y&GkpmRMoT)HjZ|70({!}YBo-rh31TY#sJ$x}QXL}AZv zhXJk~%bS3I;D>mk1Ao;$!QfgsL`r)xblc`oqkQhfN2FVlIDZ*jQCyew^E81rbLwo| zn-l*$rWw3XehZU2S$Ma+ZzkYdF$C74L##!rPc55u1iRtEmBMKFXsw>RZ@b0eW(SAM zVjE(52zKdSmF{cEELe`aSuy)dh=(c4gRQL$u{+Lx1l?IA4Vg#RUq=gha|bQt#b2R? zeD$@pkQbY1AusNvg?#f(3wb{+A7V_FaOv+n_n`wq=0O z7nD1H1AExjjQQB?qEvKd_XmMd&8iyZ+b7Vx0sE~JoMS|mQ7E9&PYYdrwa{fl7*hCd1>`5t!@lloJMVGJy_ybx6A?9lZ zMk_0TMIzTpO5}1Pk;};!xttZol%$b6nU*kr3qV@Na-`-ClW|0q@2NpNqABSXFsK*5 z`slU`Ug-F!KD0246G0?T7ztWYS3JdkRy%ib^GChp6cr6q&xPH3Q?=rHt>QAB;)Vju zk1)P793T2l4uI%mOuaL!8+N#^1f{t)_7UUF722}`VJ(gZB>fmVIr5LUyQ~A=&ax+e zw!QR;wOVC%lYGo=U|{}eY$djd)d&E9fKrR1@L|Nia|;$|7Zo$GhyP?*W1mJ`Kt_fj zgsUE=*1FD96)D}G0}tAF49!D|0H07)L@^!7YKdlX1FmxjEZTLB9SdM(1$hk86L)4B zD)6w$vcs|U*AY!P z)HH^sIRUUp9lk#$nCI$PU zN`8-}#J58H7Xi0(lmPNcLKToBn5VIW5iv* zQNITXdhi?d2QTL8oncj7JMzk%->7|g5>vVJ3as8^N#6Av^*ev#?u)eY@HcAfDaGAs z%U!=wyYn}`$QFE8QgL_e-h`q(m?(Y$3Rby|52pE*dvLWIi6S0it#Q0Z0wfuOR&2-WKV%MM8Or(TyUi z{~U|z88_f+t{ZTFRSlC@r7(F;_T8FNsV2u7_3X!@wOse2QM?(nZAP_4s){y8LZ^^Ww^bDDlUCF@HOjF(!u$?RDUufIWP2vNQ7g_dO}*hU zpH6@WmK7Kl3w`Uv8QP9Gsx|_K7VBCEs+N6=&Na&sA~{TdE5|rulRtv0!mO!E75o~V zC&%C8Nd)nh*{vuLjdw6#7#`V^rh3C%D1-j+1iw)XK1}ng2RywmT$p`4b7DQXCfy;a zYAMkcG#*MU>|t?-cdyF^EdZ^<%n}brO{L?MUT@{J2D(2U+5us6u1<{tE)B!3QIJR7J&f8H4&Ztfji|pUi^TUpQsQO zB+o-k!W^=pXjyHu2W3;w9G|)zGkw-=<&4`uz^%{labN7lAD7su5JmxRmmjGRNPkuV zh{X+{n`&ea?52Y)hnsfJ1`+Rz0p~gv`$E_ht%R+iFaYU( z2N)MgCCPQ&x?-u)m?}w7~m4J_-T?CMHr~nW@F-c6`FfxUL>Jz|ic`v$? zL>KJXgv}B?ka&L3Gm0G`Lw^z*gc(4G_0)abR`+qwo%(rVYDyGc#k8kIY)cifos5X> zB}Ht$-zm6|;II3pDuO8M-qIr;>}eRd${VgaJkt*%uMEzr2nk5AZ}OxPLE~ zH+)(yZ?iS3bH6pI{}6kab0F+>fPRXG1(pLleh|ccBx?;LNOrpDm46oK2_ko$tu6lU zBc)-r4G+nc483nJgnhpoal_FA1sZ;0j#OK6PF$TBA~tS~(RPTY*jX`yqSax&&=1@Z zXYazef{zTE5A?(#*j|`@&cpTMDxCvG4)zW-`T3Y12S6poZsMKX%YS2&m-O;~TFQG@ z|GrxO^xsjd?B7tcbbkd9?7)I`GRl@g+{f@Xv=ynejv7Aul_QTlj#`|G>_n7VXi*=3dLYi1EFN z2XVc%bMp3*|&=GD^laH?@r&^R`CYC;so<<=n=FM5+kt~U3M_$Xh5w&G6CDh@P!yGOI zaK(_|M}-Z>O@DvedV71zmCheq?SqUP^q!KsiZhB+7HKMs9$)phx0GBhW+spGEEPSDDcCPy#<_0_DHuC@K^?KTZ{ni57`?hqT zVJjMib?`@U#h$eW2N^xZ*-{P0b70k>Q+aR1BkL*oxj*O1+S!GS!3~Mr=fB>XS?T4-T@(stB3$Kp$&_umVjLAaBLVB3< zf!TGZhE^$5$2SEUgvQWK?YTL!hcj}M@Lg!06@Om2j@fZp;onc4t~s>u-u*Y+zH5h0 z5X##T{GbP%XokGqGHM^LLrjB=OwUvFsajd~L(*S}cpYdd&38xJhUmw)l4Lv!cnuL)u)n%>Rrw3xC4k4dV-g@(V-pO9Sv;?HP_66k*7v!ViTNbUl}Z zM0l?g;9>`r}PaN*h5bIBJv=TX<~&<3CP ztb_iE`3<6M7gE5!==s0)_V(ZrI{Of{GUjiP@B~2d^c~&_@+eSVs5GHGF#GAEHjglu zF6KO9x|BSPOl9Pv+$YD5bSd{(>}D#{G`mXU+j|vsA;ri@WgL^1e#CvN(xsh&nGtEE z9oj?SX4A#C;{!a@zVLFF?5_|ce}0#BLeIx{FpwX#gt>3;)dekL?8+VAFX(ifF5=f~ z_id7xsV~x&8F=9^#`u3#cFbs|iRaYP&#tW*Zjk9>so{vrkB%Bow|B8MTJdSbi5`1a z-@1v*EshVPvFjx<86VK5|BKj(0i(N6RffMyH-V3Nbb{0ic3=+ejtE@9e-EO0k+$F` z^8HTBDemoV?=?%TKcoaN{Z470*wbF?QZw7YH3N{^P7i;0kUVmK!rQKifBXDrh<}`5 z=r!;HXutq{vgx%kJsXdQR{ZWKD)YLb?YMN+qpE(#wt+a&{Q>{6JMMkX(Ddp#>2847eDnV2YDcg6_4jyD5RMUD7CC#cBhfBiCS zE*`Jz(_dtVP#W*zKM;rM=LY`{%ptbXwFy$)kFeOVqg}@@e}-+Tf84W4@(1W$QwtU! zDW)89J?`Cyx9~bTL~9m`yP7q$Z)~bP98qN7HvPqwj2FQ$#L2a8I@XXPnTA8W@0mz7 z@xsOsjIM`zRv0yqpfav9Y#I49`~l?l@O)A0$-(ym{C;0dPrn<1u5Wc=Kf?=v!L^Mq z^=XRxLz<2Li2mZJe_=-&4+w`~-MbS+h=ECudF;xzNs5$#>0etzUVMXJ8+7P~w>KDH zV}AyIKy`xv@9fUN?ze($r%(WN)-XS2q5|E`7v*=EM{~9E-)3 zKpwn{MoTvVP%ca^PEXyTO~0S;kAfO-jn1jK6Z2cqHb-due`$)}G5sc6^$~kMhC>^B z25Vose zG_tWfwY*QEg;7WOjX&Tx4ZIbmh2wDK+#bIWr%C)Ke{kC&5`r;B@*aodF%i=-r(*ac zNhe-Mpc>DD0YM3|@e_^z6UTnCRRIj6Wu>^h;|7TEo9?+ZDS3UIsl6 zwn*4qk^FjLo9c&D-zQMOU-|(kn)ExcMiyQ0as&X792h%&+XrUY7QaIvwb%R_*iO2@ zzJu&-e*-0)@yif5GD$Igmr7w83EuvA7&_NwpLQ+(Q;+@*xmH$ce?#iU)JExB3mU-i z@ZSVx!~F#DHlRH;Ak0Bwf`2t|sb2vt&mh2!E(pDVKbsP*MixDq54droo6rHc52M=& z2h*g@_sNMhbUS>kATZk;Ehc<9ObFK}wjY|qf3lqTDG=tUu_<38Q~Mf0dSkgBwtX`i zmN{PDxE~R(ZzM|2_~MxHE#ouv{EG7y-B#Z(_J>7aEyB`%zpMLzdoJ?6k&$4l-{Z{2 zpw;j4x5k)HuF=G(8~rw3@7C&SZn^&Gt7n<+_oMTA_&QICN~144w}9E;fo0#m_*zVe52Zh3%(Z^QOa z)=ZXa4-ANOWPzOB;i2O(DZ!niyXJ)!8o(~v4`CPdp1A=mPmFxPv~zJNfqwHETVmnJ z!x>F#`ksziSv4akn$a}=WY+>AkeW|d`mj2Hk_S6nHlTi6JeTja5ElintLQ=PmkYKK zA%7o>76qTAEEET9t{k%yiRNc9{g6V7mHvh^_O|v`e_ve!+lVIP{PBP#jvLSui$aN9 zym@t6=p>Rg*lGk6*A4W-JKx%1?x`a+>IlYDu}qlKlOBbZGCd2ZBrY@H4hC$4#>9v^ zrc>}ih91ta&z?1MCl*FjDNzE`{tS~hBY*vd-DH}Zyjh;>E~7{rMLU~!qFj4g0Me)5 zaq;+!0tkbwem*uGqmI5Q*ryMz^+E6YQ!z}Q6_53g@6~zP3>rZ7&vTm?^1$$9x`GH# zW-C0ve8PZS(}aOIAt71NS~oDRuGPgONqgCeWdE3V!KRbTU}?1vd@byou%VkR4S&&2 z2BaI_sXb3#>189G{a_$H4#VIX3>bBiP~#nH^l z(ElQKmdrs-rme)f=nACvSk*oH1Pjc7{|YsJA3Cbr7Prf)mIKV2fXWl?_hYzsLzr ztAwU?)Qq_+QiM1ni0aYLm?u|wnTfczlRfy(js6-gr|%vb0`)a+{a-VHsbatb)0`Xi zGab(Kz2%WY;%@w0b!jA1Pk*2A7W4SdKWh-@FQ0zF0pR(h9YC8rDk)50|IG&Mk3IP^ zl+%YrQDdX7vAeiWTm5T6VM`l+rm^_bi2bXF5c|>F2fckR!N956(@hxIH8~6k07cHNhqGBsY>h^B`+keiZAbgj({2|v z3VBfd}Cdf7X563I?L)?X6ZWYY*?m>B;M#HvRh0 zcCJO$gZ@!5Q2?A5?+;JC=Y3cGJ`gsa*|bXL`$$p3-%ymnZAkMnoy`nrU%00Rwu|$`-x)2@{+l){ZtV^cbfxnigEpl>?zQs0|>bejpf2-!i zw1>Dzh~i*o8Z{1ySz7LKmSIy~%Y>u&ssV_1#RTI3Whu77c=RZsyml(V`t9oztY!Rb!Q}n4 zkskixgIh6^z{~-0gNGiF0t#3^fCsXV3axn`#}x0s|NF)ir^WxNwLxBJf6brtH3s9J zPi$-Y%)LQ>j7p)g3;#zW*_>3sV-o>9?;6^xS_7&bRx3JIhu7*mdJTSRjdLo)t3t(x zKXePfwMJQ7mg(;Es8Vh;4y*M7v{ayc%Xn9=RnF^8dRl0F)>zNRe357- zhejyqtems`X&u|^pmoT+e{%y!$$+-OU(cvj#QXn3?Od;t`>ry|>NfPaUeWhn)~g-8 z3B#(E^?ISI*UI{?UN7t0@CWYnD*W9Ymbdk0xmxX%^#(kLA}q!Vd+-m7s9;;)#v*8? zj&H!=@r^o^Qqy(43V)%h-qi8)(2BnId9(xXHuWa9U8t1x9r_0Re}%8YRb6j%D%hPS zezm~AT=ntYWi}-~JltNwTS5vl#zdj9&e+mC( zKV17fDnow@O?|ghu0nSKT-)Uu)oJ2omEQvZtI+lOki$){bqF*y9Dlh6eXT;Js(=Z+ z4nU(TtPfDrX-;^Te|cj#yfW59coz`nHTrE1%hd+ds|o}nY z(4Y8hWc#qbtyh~BSpD1h zf90;$IE242$-9lco#;W0NXelrhTDR2-8ig8HDxh11%H3XJ5xi?y@p8^4rlz92XN9F z>qr4M(m1R)^-7}&t>Rrlxx?-Prq%SlW}_ZIpl=__Vxn}>HNFznjEeaymq@-4A%CA} zRCnNaM{iaTLUnzoy1RW?1+voIhI-JIDnJZYN~KYSdO&@)cd=Tvv9pcMRBC&>IG9?q z4ul+*(_VEOo2}F;K&tC{b9--(KC`n6D-u@!HXnO^8x}24wB~M&THmg3BUP>zn z=tZ+o2P#Ls+0pAebpRnWw^OT9?|*lzFs}`LdvAw6yR*~WMZi^?b(nD+c!fZ|UD<>0 z07Y%=5@72)jT(Lz%A5E;cIyznyR%biQv16#ASg9`8=(TfmF?|qEZ?i{VE3xEDouE; zy4|4e@4ztN9zOzO+uf_nJ7}tTNY}8BK?nEtnl;{`CT?MRquFfIOjoNk9e)7kL+no# zzK651TjTv{00IEiYdbp)><^zc80XF|bOfki16Kr&7spf4EA?HPo?2zM!N*zMMeuJ| z8+(T}Ai&#=9X>r(nvU%nEKNj;>JDOAgfQ>v@9zSKpd*#)VGRf`OeWpn5b8VBqb3Zj zx~uP25y^qRR+~FGW3}ck&3|Zp2k~ed@Q2U%?jH6KW}=R*K#z7Ruy`BTLjq!>QKb!Y#s<+8K-}#bu_Lt_fE~*B_UbeZdwV;C_{}~3-rgQeJPr%LaH>peOjVjs zw{c^m+sFwGzR=u&(0^w-|XXZ zqy4$n|54d!tI9@O{hM#Jl{;*-)#BXllBaUdzWMOs)SOw~hpHwLBXZ{{jYpz@ zpI5DSmBqvU?tj7732)VQ2e8oVyA%5R5=-A>>B_d&K%xO8koLlDn0tL^cd`T23;6sR z^7K1EM2Mx|1`()U1CgP+Jps|F_E{W@aFS7L^_56}{klRj>VI>R(YOQ2s2AsdB6S#< z{&i{*m>Rn=keh6>nNi(lJfC!}$_CuSvf5<$);q@ze1Aiu0I2Bw{Ki2`*u_fhTPBtD zINH!hVdEXts*`GSP_9+qSGPG*tROZC=&YkQ)^5-2RdX@BzYIo5C2+(rSZ`STR|Kch ziqYb5kduIAyJNztM*RT%5sMubH!O_~h_gU2;18^WS^@bgkh7uIF0c$kc({WCZ*AA) z>IL~WaDRj#6rmivEB;V<0n6Z7Z3s&ZSp?t{c2R;(zK()72<_Es2PE}f6xSQ@<(<7U z(v!*_P?8#Tn51?=o%lzwWKaB&<3RCzJDSMqPG@_knkSR1>$=(MH@LsccCJFv56mFs zh6#jv+$@1`Z(pS74tR2I?eNX3x2MM!$48p6On-0PWFjHDBk|W`DdlIBOdGmM`L4C& zv$Hp6t6KeieXAlpaMb46yVtKzUcXr0W^H|&Ug!va58meayYsilua8#s^yl@h`k{|I zF7K<*!y3v-J=GXg1d^7V;lyp(bRGtE-MiB zG@hV;&5DGdhOYH!;T1YZR$$sgKVhm^|BsVrpqMVE4mDDRykD}EA2aOc95d{#vtYDz z=-tE2`V)5iYflw4fR>iBx#P3DQC&W{Ie(^K+Y){2yAlH&Svzl6$Tl5M)8Ycfc?SHo?uS#CvkQQ9qx6zTUu=Fk0lHXg$WSeM+YMmwtB&|AKkW5Km#Mu_&tr> zlwM;ZbG6qS_jsK}WM22rLWg&sx}KZ5eQAFB&3c8|+?U&EW~^iZIQ@$h^UM#0X;#v>ktn zhWX?V%_Ep(ED6n1tgX^4$C|3EHTVoG^|duc<$6z3yHcxhx6<&ua~*(P}A}5;!>>_zXP1-#1+7oK*b2bEoe+ zvX*lP8=GCA2cg4lYz%HegEl+yheoiKA^zwfm72pJ{H*a&|M8cy{l|RXxR7!PrHVAT zWn>WMJqV|TR~R%wTEEA%b7Fse+G$3#6TX)%#tiDXp_0T2H%E_Ctk& zL<6-Yw`9f;0s()2Uge}?cc6qg6;Cnon~Kj6z--dHlf9bC@5G3*n(yJIg2hQjBIUf$ zO1HaZk4d`Cn9VVAVKy=@%~5dEfOaV?T||{ITT=xq?h7X24A0F8OQv}V5zLPQqW6Fc{l?(0$z$a7_Vdw+Qb)(Nl+VQ zYvxiW)9Z4>a7mv8=|HzE9?_A0PR*Gc26C(9tGb`Fsz-N}u_C$3jX8`5%2XWw2xDJe z^`0J&b(w#dl;Eyu+>3ikP42^_rhAG^!f|I#caQb3W)1=Gh&|X4nO0D3@ z*I&W2cm+?^OB@@D8(&V=xUZwaWVVt*Oi_IJKxATu;zJw$9p{S=A6AMF(?3ys=-=0u zVRHTz9k^j1%yHeGgqylE|A`LSFCXGkJvokDsRLw8SCfkqxvjIJ02c`oa0w>d3=3@uVOgY5Yv?`7peB zHn)Gb!4FpKt40J*rb3gp4o<5A%Y|nJ3QGIV_rbpN-FGGL(z$9`@JFs7PoZlkA~$<7 zr;t^W(T%VlDjhtM=~pH3Mf(ctAdkk*sH*e|WSAb-@`y(OY>MJ=iTkN%2jY5BGAzvT z4zDO5el0qxg%1BpS9>Hgk)Bhi^sOy~)gvhwD!!$F}M%N(dT#(V)tluU2FpOgJn)BvGdv7_NV z_5nY=mM5i`RW&hp`qK2{`QJDakwjs@fFUJ4#w+f4nww%>;w=mibLM5UXn#&VD&M75 z0c^}`{_~vw06Hl3pGWCC1zw7n?r~aRbqPV$eGdMNC6Wvvz-^FVK%9@+W#uXb8q@e2 zltOQ7i*jSbr$UTd4RGeUv!fD{%t}mqWsd2E+gt72;_v2QyTd>5R8GHm<;i~+4~%#A z?MVq}?18a+Tiw2`*Gh1CIDa%pW2;-@Daal_(b4Jvnn?|H!MTUuTKR@tY3zjkUJ$Jx zss};2YE)1?Ro(Z1fMHf#=h7=zuN0K^B2qRvElSfYkq7V6;^Agxuw7HevcZ_i6nYor zO3QQ1A+Sv+M;Le8^iOTBU4lMNf$N9ac*-L+YV1m)F<^*8Q9~xra(@rCfC;m_32E%k z-O%gEvz~e8fuU){p5scyyu@ts?yrGV&e&cidZJ`poV*cP3+x{54(qmdK-56_n(bfW_$YJ$al1?fF954xvuU z>gXRn;G7fk3tJ)&pnpvpcF+O}2}Esz4$Ioeyty#_>mwNg40hjlq7jZT-Ee4O=`-8} z_uH6eXnz^waEGR)UQtHd05qJ8L{M6a5e~aV$AzChg(&kz)P~O=9g3HM$;W9uwVmr9 zLu<&>-cf%r+g@I$GwjP9x-1QHE(Xx!0p?-I8%Ec|kTqs-?0-=32BqX&LNhKp1CHMv z9a%$jmWF`c9a?DJ>%Fx++wH0)GB5-Oh5!PE5FZYcuS6xvD?!x#JOKS*p&gIwUt5+2qrjXBAu7ZmB-0|{(+f`=Uwp2#7u&Ux6z zJ+^Hr6_bh!5fbRX-^R+1N;y#Fc=X$KS{M953mu0+0!+$m~I6ql4g?e@I4gV+_J(b22 zM43_m|SFYpuexGt2{2QEh&CG4E^}g)U*To zG9|!p-G6HwOH|NK^eKKTz{~|!_t{+@KSMAJFeawi3zZeN0&$iCjjLXt1AcEnpZ`+0 z5vnbN+P#2+8DxT}Max&AOj&AjpXGv5QkV_5IO<~PVuz`Z~qsoJ%E|cZGWG@xyZgec}W^#MP2jx>!@p9tWnpf z674U%OY|w`MM14BE_bZbqvUB&Qo0iq9JwaJmU-VVubA7yQ_$9<>OM*iI8eJ~OTl*k zQPIWI16y}M1S^(Gde?ONpQF6^tST>l&lU1Pgr(C7*Eym@jDUkM1!OgI z9smiVHBn-<5-d0GvU0^t_^IN;NLG7PrGH9(%f(6|{6M9o`H~5=trL0}MdgBwTR=ST zsY-FPD3srW*5olz5IrVP_l@)Ej@ew~#xwGb@pE6B#fSN>a|jr4ZJ_+<*=BiYwymLN zP{ye;R1R|twcXzG{1L$iJcBUFq~>^N*qB|?F`n@Rn?<|C@Pn>z89f;(^l2El&wt%c z=%4U|i|qG;*Jv)F7|0&PkZVO62+P4F4e!oQ4y8dvaZt)JpI@FAV{);4T*vYy?I;A6 z6h3I>dF%$qSq+T8qx@UX(R%^msp5|c)>cjSq~GhQVG3DSAY47uF9cYt7V)HyZ4j~Zz_bv9{iXru{k_{N+kc1PRFM%s9iKa!@ES%c!Ft!8Az z8SB7fqtCu)%`UZirJ}*iuz$AM?OU2$&nS-G*Z)9$RUm@Et^jzR|T>RQVzj#?HmUMLCMt@=rToy54udI<< zOq;MorzC)M03YT@z+o9QFi;L9ul+u+gW}f_t@S^1?x0(Qv4{V0k4&Xoc&B!X?ibuLeVcDujf%Qa z?R_1c#_k<-8av-~8u!v^%&fnUPGc4%bsD=|r?Dec88b&-SbwTcLlkeM)7bmx=rr~e zoyN@DkhSUB=FsgQM)4c~T0o`0ln@LVClEB#H{o?dc=k6c7zzSKa?VfZhj<{bZzIL4 zVBCjPQQA$47@=mRaz!TPGds&x%)ETI0tF4Bx*>%DF&3@^M37ht$#s~mS)eLvG>sHB zZp%bPjSoN7E6GV50)Bts0YTDH$}KTmn(WeJ4s(}p&gN^R?^1o?aK%PWdw=xsfLbvfQKAJQ*K)MFz&*u&8wdk-|u2MISCzZ-vIet0|+%wcOHD&qsU z&3(uO!CvHv)9%GGg$nnfjXtWfQUsomZ^zv2n)ck8iNaxHx7k$o+?o4T_S~7j`JOuq zzwNpA+jBdAqdj++*mDU#V22ImR@;*ooy4Bo+j!3vi2YC4bHz}_p4(lWe}?Cv^;$3i z+2eu48N#xCWM+TrhwgZG;R2xG)|P) zWqA<#{DBf(!g9J%Xo&i%XmelIKc9LTwNI6Ds_-y;N}c7|d3iv)<#5(p0p_7*|USPh)t)lC=>z3ZLrEbA_0HNs26Ay37q5zoe#%$08H3tB&Q;< zqcKNGuJ5_P&Za{yP!)ZMoyZ8J{MpE~9hy`+s#?+1(DsA3JRb5nfnJa6X4=kHrWy+t zg}7YzsY6+DvyY{#i_bYOu8%bdlxB(tIT5Ml!99~fIdj8lT9*Ovgs1M*@($s7v2?X0 zl;tQa$E$qS-YB4=&tsqr^y}?721+5{(K|Y8;h|X8>>AF4w~J^`cxc?0u+|VVZ#n$f zaw`UpOK<6@g%(AbSS&Wq4$UccGS$=rBTL>tsH3$cM#i$vM?1?Go_cEcPC+yhJNlgl zAhy8OGq7qjQFCvKrJ`&SC`>V6GN3T-@bWrIDR?GU(y&bMRAYM=oq}gG^0A#wRAtLl zr4EtHmr>Uc9)Cw(pdYhYv81EZCy+OwB93l#`<5RQ5%`qb#e&T05&lbd*RU8*k#*Ab zF*aMu2~r7fpihCM;5qz~m1yY`Jl{?FOmfdn>ISNHlobsCH3JZ~+aZVrt+)z9DTYMW z4*<5kMs>RZU^`wVzFta(znICF8h@gcOj4P6l%FpZZGRW`9Zn6FEL_3EKYWSA3l zY-nSjDv;~SjnDWWUN#`VuTa@*M}?p|-_4K6e~{_4?8d(zmO=^7>->$itEO%=oB_x%_Xrilf=@Gld2%^)AV4) zhf|VoUaE4@ANWqMEUGlj{ zb+w_L9zVYj#iA0es<+6rBOEY10YIfcgSzJ@h$X4oQ}nY%;-0AZ`o-z-hvTCcvbKpf z-oY8VqQUd3Ug@DrZ>pC<9RL+B19#kNR#*X}!O{hf21}P@<T8U?eN@1QV(T4^kRAH*xWBt`m-CD%84HR4uAMzM2Vc- z?=YzeT#};l$m)BRWhf;wmtU1OEg6AW`&YM)|I?sJMwUpISl*?Aj$>k*mRycU4kSvj?3T$ zZVY5^uf)V&un@$=4*W~TLhg`y)&?^E8`O~3zb^Jy#iZuj;na4z?j}?RihMkw;3$58 z6r!jRNJcJZAL^Bu@}hXdqlc?rT8P!%ZRH0kmx|mFA%B{&ahot(RbY%;;ddUk^9;4~ z7(;;|aE6R_iD%CG&i+C4+y(;9(flARuX(F%8sT@Stm&+1pOcW(2l%o&=#WzKL61pyOj!?p3U;oIKGG1hZ zU<*GN!gsZUbLoPE zCeJ+7jq=XQkmjm`jMs0QKo<()|9Z6)2kxJp>wjL#%_chYijs>b&K#SJ&#F-dIhBqL zi9TaIljvYEZ%&Ry*inm2=vB|9+0ob0p1r<<_U!Fn6t{79_DyYfKegS*zmeMR`L__1 zYeP`VCAc&MoO6blG0p2>@k3FYUr&6S@={~YD#k*fTBmwSj6E1tMtKsrn# zYJWt3Q#GQiY%+7zqDY;#wlpAZFlwX?Tu(Zi;!aBYcVVuwLzuK10TGlUA6}4Pz3=6A zK`|Z$g{rzwjKlTYs#`E|F6uyn zoh*G`q|!pnUVI(4`~O3-jk9bUD2m7 zj99TkH5bV!U%rlSvW~dGCRmSRe=R#$4~ik{a5f4qh>gmv=SAjgb3QafJHSuG;jBWY zZDIFXT1gglN(=6%N=*w*=l{NaeO=JAy_vMK?g^9)zN^M)*+IC-m(G;0CZY?8 zH!;k+Q{^3W#Kg4iqM)qkiOR3z8Ke}z1I1S_f zid>6yDH5&ex{jqXokW~}P3DSFQmaL^GpQot*HSV3WpNw}I?4&XB%HePd|6tEIA@&9 z4UyN$RBDL0i?> zBuAZuS)4869y6+h_|9uvWQm9y&V<%p^Tib*wezdwMCM*uF_RWz#k?gGM95+#ON1=S zrizFrD4j1td5}t-5KUetbHoxz*X{`FxW8W#%E?R-5e{eKtgj!qr!FrgY7F9YU!tz;*b0cg-LaqG1D)}5@++Uv7Vr88uv%*ryrhju({0{-{@`MzB0{E9? zs)+ccW$7w5o4yp#)od2oEjaFJf*vj>s5z8!?N)Hdy)%D@P;+*t2dN~yh=-WSZ?USG zWPaaSb6I}hm_gp&j6p`%;Y3Pu3}=< zU0xdPf{4@-S5AIMMZD#!b9Ytt?b!|{-bVSg#HJQqZ-3KuK-YV8?a(!#6`SoqEqDlp z-pbq+ahzmN&17Lm@es{8#4>Yp!!7$<$Nu=446w|?)~uEj!K%{=QT5$}pXCv()+uN= zPQiVr1KYivhyqjTG>Zs&d#jbp+6G6Nb!p0|gnS{_540Oh`X? z(-ZN$Fn`3LmYawh8ie_$m|%Rq=@2Nl%pxZ_&3=)e_$Z%K&I}A6c<$><` z`Lj6CpZTJ?sVG-93FUqt~wH8)qlPhD<7 zhq`xWGMDG4qLf?`-Q-U2sk`$*n=cQ~=2 zh&w((>x!Or>MPcODVNuvMM)8q{0mV42y0pp?Z-!C*sxITTQaEva})~(7e57~G--q` z1Alx%&A!IewB&xa66w~dmMfC`k|R_+hHoJ}?{{)B{TqewoF+qf(!#pEoyriN)2_To zgz%j9zWT|9=*cE07lH`=6HYF~P`Ty9bdU0TRb_|?cB{69@x2c`K_d)tN1uQt0X5{K5}JMzzIi29DH$G-FerT#6F84yn166d z)g=6-FT4l4q>UhJUGmQ|n^U_h01eI^dAb)W6dA2hHSCtDN?o%Ge9 z*yQ+0<586-w=6V`xR%F*m&0c(SNq5|+fHclYC8soTL7HU$L<(p30Frv;Wu$r>4y%G z!@sN7 z<@fewo!;U@pJ>)*!0q8A}bFsk-5dIuib!5#uK_3E|XJa**pn6DN0YrYn3n4b{0sZFd%(KfeS?{gi5iF zNIuE}Lmv3bD(51=gffJWNq?}!Dw_l#{sf4$S1A{!qgInm`=~uY{!Qq%O^Sut z(!gJvit(BNMh|XgQrRR8oHdH1l}UH_IkG%X1_9&|DMU1@1)EM=ndmd5k$Cq8H4-4b z26+EeKdGPfr zf~o6WmtAK#L(B3`E95cUdT@d36F$Vca7|({8^e`U0JU4dSh{|3I)RRqaO(h?)hjKF z0{M|Ae85WGrIlVY?teLgc6G$tjaZ*eTE$Tdq^;D%ddTsBkP}9DJ>aS6qI#Vu)@X;* z@n{1TZ*&B$Mv{J6VKv5JJ7Jd%)-D!74w)}XdIVJ-nh#{+Qt4`6Y`}>aHbuV`$A5!8 zyrUx%Mj{LVG(QNO9+8leEc4Ygl039cnuutIBO_v>8NLSiVt>g)!yHr$ih$R_O#c*= zipizNzs{GQ>huhFTB}qN$+i<@M>Axbr?9NhoRLURlo9)~4uIr9s!3Se#V`g5Q)<*j z4cJK+6s{vXg3*#pgC#a=75fR?h#gro=-@@@;N_wN%)1;`@U#RwwFAVCnQxT|j{=hN zaWEv1*?z~IYkz-f$EPVieq5Z(1#77b7Gp264=PcMYgR;^=?E(w3-s$qOzNOwHyLN? zSUx+>Vi_hNSr$oX>T#XYv7+ER&NXF?w1^X-J(@{9pfSzS2!4>^lT!G#q#v#;s7QXy z-|2^}>}gnz>Tp1Mgz}Hw11Ny{nXJr825B}wD+8b#>oaS zW(mu6zDaC>Yh$9L(;-)5AMhW8l-X-{eh0?#KeX=JWA-*-1Zl)h2f{Ph60@nN3rKES#wd#cm78TQ==s}2YZG4{pphJA*bp4Hv?-v_b7JP&a!|s77-5XjrWlZR@x^s6) zhJU3xQYUyn$3m7-du>f+-oQ=#s&hbcE>*;SoCGmsY)?igN)t1u`nM4s>YpOwLB zEguWZW&>^)x#vylA(yd9y<E$_|AHzbuN~r`B(6mOqy)G{rbG- zS7bK(%DKT=_4^oXQ)*_g@4bpx@!A5JPg!mvVXby zVJBOI>xVIzCigIh-5nLf9}|jUsbUIH#pDR}k+Vi8j7OZL6E@*O>V#dmCRJ^8P<*gV zY|`!(hITaZ>>vB=$-%v zJ&T*YT5uhiT){QB;Qbk~6YfBI^nU`h;zj|9VZa~m;&kEi>Q~D0dX|v#TQW51nbnKp z4yXlYx&VR6t&4+$A|@)aTFz7OmstRl!KLLQA5g1S#>g?)cyQEyrQ3uZE{(m(pWTGyj z!CJ4?k-=En6fKh|t!{J2mFF>z-QI?XwMTs4QXT*-x>DLSec$do#SYMT5Hc|svidbP zh=7P}XCnTqRPI>`4iDM}#T}muO_Pk@gBeW?} zdaQhHFVJs1*>6pM+jqW~r+?t2XM&YLF{xupaT(tw;m~D+61$XRmKnxNDbYao*E(1- zbs%WZ)zvPdX2^Nz!Jf#d=hhah(JF}0stV|o z4K|(&7YpFBQ1UBr^_05VLI?P{TlY%YpWgjNaWAJYzlB6x8xm2@&VSO7h||Za46hE~ zBK^>+L5SnS*2;leJn}EXWqAg!bpysOCI`&;$(@ z{e$J~+qzd_Xe9$c%j@A?c6MOJEL$Vax(|?5D=I4lKlGxNtN#%u<(}0IJ618r-VFpL zw0@2=v;DX2jKkoMCx1^c4O_&FeJ;Xc<1`Nt3NCHMP-xWLmxvsOjOlwO8zv?*uPZ)(j&^v|6C$$F zX`HvU1!O}@; zbi-gsM@bA5PRNX{P~Pil>^T7Cr8R{0ctPpmxB!_19(J%iph{(QSRp}jlW={buCbE^ zh?|$HPJ+CfO_7Nk6$RD=bkJnli!O4$6m@%uafh@hQGaEm_o6lYJFMYmrCPx?{A&w^ zA>ZZ!HZ+Az|53#};#dIX@(gu=YLifEI})ZqN0IGvwzhCzTiiWOdm>cjmjm$-O@Afqkt>~ffAv?Kl(-?;z_g@rWN0c% z#IUjXFfQNn3NGKu+U0vYm+#h3<6QAj#-0cu*I}o5?kQET$`})3IC8v9|g1(6P23rDJ=Hjy>b#t8I%5Q>J75b}l;h z{MVsl&m}t6c9+R~ZGRap6A|XZ)~7PLT)op(M_&Q;0oc09@g9bbyB3;a*A|&x=xW^r%10;q}M6Z`BJ3YVfHT&^jP*UGk-R- z>|b;-Rl_^S_&ZDn?gk}!LH0s2B{-&}o;8ea7+!FL7F6d@VFDHU1lyO1psaYxaB4=d zTLU@5@$~kDXT-wRfjd_(Q^K%B{(s)4xwRw&i`8b-bXcRF>=89s8)+Ua>5?(&lO7q8 zDd`ZJI!ms}RWMJ}SVCkwNQi8!^JATwQG$l@6FWX{6HAwpUu$iV2FYKqRbn}xZ8^WT zLbeInW%;$PuIuAOel6WMH>ey|naK}NR}Hc+w?w*aKghG4)%0%Aie#ScOn(GO%}T-U zBs~lYVioko^L{cLGQLa0lvjVJsJ90MlvJx%>to;tJ}MpiIWcWGebB3Uz8z8ayW#>WD&}AoA`i zsC81iOw>Aw*E*<8615IQp?^V8>-Cc-S5KZ?bFTNVt04XvR6YcgD|HWw`;pN@$El|x z0-c!;q-!?rfkxmLX!KqIjjkZnRtws1!_e_xwe=y*Ko-Nio0@?v2MC#Ixx8t_tL;i= z4}<3MUZZQUT@^HC3&is-~-}?0=e~P<5uFf>*o( z92;0+`rg*&vn|P@&~87B6{o&}6{oqj2i(pcF#3t?0rRLmpw8?8O<@LTiHlLh9?;6K zoVR}+<-9E`=ckI0FV&f*9h&J-y`iVMRx8iQ&_v)=yqZ|{oA3uvV#}syn3~b!-Pw~1 zS}^)(L98y;-3Qf!hJTT7?$HCo+KnW9K>Kw0xu-NZ9)!QS0_Qg6RN$D4&?p@`)9aRV zIJTx8$8dXuLdI5Y%p`#)3W!dFBfBO7E$UK02E1#Ty+<0Mh@Vyl;-^6oKNV=ADmVkb z#HL5VT&|*U34RHOni=P1x_%IjWg;GKQyCApDaXTY(zsWQ1AnB99tJ;b54Rk5(H~F^UEE|Tr#ZMlFAsw#3kdA+ZTjA)T z-3rHf+zQ7lxfRaVocwm2{QM{4gp! z``XODHnXqI?0;EicGCVMr@$Y^n%rQp%fLl6utORnjwlsl6W9vS!pX$3OIJhK!enan zFcr1B^WQPapFeo%fQ&k+mKTJc+H>5i-XLsOe+-mE8(O`dX=?<<>_%|c9D@MvS)PU& z+C9t%XP>y?RZ=~plJY&KY9yXY8p=k>L-uC!DaP}2Rewj@bj=Agt7jQwXK-#zaiBs? zNZZ;iPv;ucH;>=3Y?e!)?LHGlqBoL*(xD9b!suBuue$Z$aqa2KaBcX7nFLIU`ouoD zQ!dNB-BNe9df{|q%*sT`X)}# z7~@Hs$baCto~l{)&~z;yCyK)0MO%#x+fG#E&g{2NCt8w^#8yh-)5y0I6S#;3m$qL< z&)^P^@1p5G27{ZS05HfW&KwZTfCS%!$5X^SLUU#NO>LRx4)P+jh7N;( zHML}Ck5L2pQRLiGy}l?EdxuhVkmV`InyCufpty;{MU#i#kfGuomRSr%r?nv#?Z6)> zI!SNGpfk=J0j@TG*Q*03@+#od*ORz;>^WmB=_Zn*8irnQWPBNyo6-g7p z0c5Yz2kL_?#&ejFZzkX9zAWm-IWcKMC4agu5GEyM_-=W=l5vLC9BMmq2;Voz3z7Nw zC5aU+0?Sg9NU$U@9-V8gs!>w8!Tw7vQyMSDPFd?Hib?0MDFBr;mgE<@SFTq{@mm(l zDx0rlDxc>Y2A=YC3>A4*tpjo^vT!W~iNYqB8y(ZXGEIABq$pKJ-Eb#JrIV4Pq<=0* z>>3m#pm&9X9!LcR6nk8&r-D6FerR{K4*`#YF2ir6#|4dq+epI`;Rgjf< zRPr}g!g5AAQ8^r%-BHH@S*?p#z<*x5AzarLX4|o>Qho@nri!V4khWXxuMqQT zDqg7FAUDXMtHFC}3^aL}djd}kSS@Du{KR_DsYL8!|vNli7oa zViBFZP@-9f1e+sb2Y>vD{g$|eBB=P&(qo(2?~<@9-4$NYO<;I)ul4p{=msb4-;>q1|I{OZZ(7#?S#|^D z)eRnnjMQ3~E~wQ^un1T14$4KAFpDaV$9BD0jG`pJdzT3(xOc_h^EK4j)i!Zemcvce1S&54j zf;Yusk%TblaeusT82-XLG;k9U;mw{pr|sD@$9g8>p?~WRf<} z;krYAT7Rg#*1hk^9Cxqvx)FuuU-2Lg5arw;ifL8@VZ-tCx+9XuyEJ)xS5LH&vaF!@ zb}VAX339}plpOK2Gn8j~{M=k|P_h-|5}T+?IjITUrqG%!hsuGK`jK^4t(oGQ) zP=CGY#sZNCu6g}=w_FB!&A5b_%8Xj(wj9v@Et@79)~88_U46Jb(U4a}4X;7KLfUe= z3e8MC)ArOj=|jyKHd_vr(vXkGm;~$fh;ff@tZ(1=)Wd~9oWMRwZ#8q<=Qu_973_1o ztK2>}THEK=_PMovZf&2-Oi!)Pzkee`t>q2|^ zU_C^{W6-DXExshLBA3vI=TOvf1@f%!FzNCR8(K zLbYhxWdQqF5me#xHt|6J3CyRaz;^6&YGSGZHv?;A7p2#6r1kU;Z9KhFjyJ8Gx`HK@ zvi0!p=hM*L(29qr!E}ZTGk-NmO)eD=Rg0Mw*v*7W&u^=22MVXb>2e=RBiX5>11Qov z(eTJ#T;>c~K0Y~oAdO5P$N_bFoKmO93B88Ck@^ArJG`Ix)%pqQ$gXYrxZX{D5{o2~izwoWI^LX`NEx-xIdBms09iZ^YDVQM76;X>f#iX4 zP=AX!sJ^)KTHNVj91=*Ly~N3Xz~F9~6b$K~eHL zK-{hc(G%$@$~Y;-0>Wr1Td&V_kac+wPc1a|sZ4hoF@lI97=L)S>M%;cK%31hEwai5 z8QZ(-$SN|#p_8XFu<6p71a+jFI-BEQHplAO9H-4@Ux(pJoMzI)6Az4HVxpZ|Xy*ei zHBOyq$)uMg4zS+CiI$?mViTQ8DfmQpby&N3C)!&wqusDG`DQc}ul!Ym^9JO&4UmyZgQYbBq}wUTah73(O=25T!#uqw-JOiuZ0xqS9#vV%(VYei{)t*m5# zt$e=)_Rr-#`aRjBq{LatBXU+&kvS{(Ep-0Ayz6f>x_=%BMRM1JwtwjLTY9}suiw+_oqKX^lqobveOJ+TR8!ElRezrHa0?&q-BZK1V?LK>58`ua zn)!V$O@Hk=xRvXmY5zQ~gQgpihUohE_N^(BrJI(xuw~Z)zn#mD|LoVX<3E$^_)SBp z@_0k%ig7gH8b=i4m+C*B>9?H0U~I-8x^gI#Reyu|lA@ou#}DJ5j2c=^(P)E4Pp?+r z)0s=L>Tw2e%WSSA1zFwq9P7=R*DAP)tVO4XwXduM0 z0cEDOl;c55A#;kwyhx@AoZwJxHNJgw^eLK9)5BMMRqzTMPAaS{k6vN0t9a5aimDsz z;eQ6gpc4jHinqChL($O*CSdVTqN8SPvV$5qTq)C+u*(|ZhTag?v`2>K7<3FCR}yO{ zlX5h1#AZHm!ml7s@UC)kV!93sSce6y!vfY}0ht*W&Cc?S3!xAga$ZF&;{xco zD#-!_8-{j)hs1&&5iQ2g%mFn43xP85(_~^K3DZzuf%627%JK}2%IY~9m0vAO!+-j| zERD)7(=@VEF)H_vi}7xiT#Ro1T#Q+EE=FawWQTiN8q~$e{Mew_B<(ezqo-MG(Ayo+UmpfBpt%BKK=U%PaGB2JKY5a+ zfB8a`8yem**CoE}KYFCR51*}uDt`ym;!7XDUh9Cj(*du3A|0?D)d5-7J)e5GsEdoM zhz?lKuLJ%%S@%sj>%Q4c`l4X4B?*kmUqDn){(_F6kmh1!;IK5>#mGSU_{C5%gV*c* zJ`9N-RUFxD0KxEyJ1cb$isGZN&TZIGAVD(D80;*1J|Jv1zcg@->G1stU4N*aV&TBX zGix+TEwb@mVCC#HGY~T!w)=2U1Ib`k>z3dZ)AsOm4Vh8T1fzz=Iz0^0(mtFWd}zIC zTpa&dKhmH(9IEW3QPi~$wlRan>so)CJyMvCP2?{?#9^zj!=k1%+5;`SJ(x@&@OYL- za6;hQZV^3~E~69LQ;%k`X@7y0Z^0=8@IV_!%&3hcl))5~!3!vZr=$!*uLXLrkUQXu zxGPwb2SDQ~D4)}%CtxA)8c467Edp}~TI3B227Qk%WI88RTMi;_jvj_j%~#-4tsh}$ zJ$-09Yb%ePwY8F+wXx>0x8tz~KM{{TiSpPv)OtmZD&2ok%iN223a_v;DsqTJ6S0>gy-s1-K=Ds5Q-RBL48*bfr#<< zb3IJR0XZAQnzWpaE*T3-)I5$^bNYcbr?1Z0=%=F-e@D(nf2Ev_(0B)ZUnyy$lAg8E z&7HN;EoLDcyZSinPJh0v4L?UBP7g;aV^U|SQ+oR}oUUq0!w_EO%nC^A+a%>EB8H2p zbDAC_%;q%2NH%}dn4NSKgwy20s&+x|e7O9ZW=D~K6B>}EL>tVmX`bZ-YVI0tkDs=V z2hI0->}X@{Np);^{zi9bjeDB(WRVAdnSwR%Ay_kQ%Xt!fxqo?<1AMtM#be&w2TBFv zpRV5U{)GU`%aer8O_1tYmXKYV!jI#lW#=8UQmbYQMN?|*@Q}OW-Dc9bD`nD1Ds1!O zVax_!S1=pA`VnS>KOWj_@G6hl;MGcIgO_Ww!R^cjAATaU!JDYr;43p52z;34V{!2z zVm5f3-)wO4>wlOHE@ZR8-)pnM+H9~k8?4O+*=B>+_W+0sME&0k5cj-t9u2qRH`0Tv zc!4$GZN5+{rfoe4)NRJ6ZWCD5JSR7-%6|`#TTmSoaaDi+TY=m*KRr0&Li1f1A@M1W zxB$c=^9*+OJaGm))!~<($KHImG`YL^G1<5zS^{sSXnzLZN6YXLvii0>GrjpSE62O} z7ba}u&=yJaPkIeKAt)Ix^upPBHyk{1NiZC?nii@p?#bM{+ImT{&Bgflc$H?(kHgY^)8#&fS zj2f; z<6`(7KLS(>a`iHx+UE0rCQvP#*p(n^BWp(qPJb&baZwmrwzPgI46Qs-=`gg3x%%hB z(DG?q1%OsHjZx@XDDlq%m~FnkTY%Y-t3+F}Ja$^W_X^$uEr`R_q(+ups?DxFdKx#d zQv>u1bS|gUZL%7+PWj(2! z5ZY|a!-)L+Hg9cgX&kQUH$OQ!DqjmbtR2yu*lFlwqW6@e)hlAY$3>Y$!MH2z7p#2syDD62A17&rq%8LHcQ^r=%SuKvn_<8 z!$&V2-*+a}N@*wlyhAN<{JFL{ofkX@ZF<4_i-lckTjer{O%p#fGV0>Px6Q^`Z4(~gg+|R^ zqkb~nb2MHa?g^qJPfTsS0cjt4o_T6coP;DUC>1UP6pv;>bBXp8D4DcR zZCL1ZwajR%lh5JKSI@CA<`*Qm&-Ry8#&h`=HwartE}@+`L##Z%gwstdyMJeO(Y$jQ zCkTm(%ECP;jjZoft&avk(H+Bso3dbLe+i;RY~-7*}O z`_=#V+y8psHj4k-gojGVP2c%2or=m9d=#$8sC}ED^Pc6wr?0D8e{9aRw8(QoAY+tk zu>it7nWEI+%Zf<%m?l&jV~E9qsTJ98&?2HdPzpSbG^_lT;bIZ-28{>cbpOQc`^lCQ zSRnq)qmJ}&5G)%5Vt+O|W2ZZUEesD^u3>w9#|7%&HKxFn#@64a2$+$2D5A(gh_r0z z;@FsaW{*`b3d53cXd3=?iGOjkM4!<>{ZU;(x!cPrH|q#p;vSPt4tyH76!LCNbypE_ zu6f%`=hhAzr{^d2R{cn$Hf61TanZQIUx8_w-oE&7c6NOBI)Av0)TU%RS9&op{YqFS znDlNF3v_m;=6|-MTj9&MZ+?kS@9HAH@s3Kq!S-zEn1e4ZZ?vu^!(?{J%@qwR+1=)= zplYi$0}8f#Bl8zrR+-|yTsT}{VKo162iOqM3*?4IQYjlKCd4J8IooL?P1`yQ+N19n6QM5}l9> zd9#%U0kjpw8wMNAwK_8jE(d*sPJUvoiwJh%{SzvRQpxU6A=#ZMBsVkx{Aw6|j*DN=XTRp`3xB4lApwGwXzYi~EM&;bWgfSL>uEvK zPa;SfwwqLlL9xmwjE!xV1s~fktE`2J&+BpjQL-<55Q3Q0A_d9%Ujn(w z#SYoU1TPvIUgOFZU1J;x1RoSI%+D^g7JvRzTm*trF~wyUcIa%i#xik8Zt}kTqk03H zwHOvy2)No_C}OvGiGk@$yt5^_f<0_s2)1?snx_! zH-d*&5jWj$X(l{8qeB985x#6P;*XXD>fK*ZpX95Wt?zC<+uAIW(H}Lt1eEg?T)u?c z&$qUoJ%{TL?Dk8ze1o^2ZS4Xd8a-yW-@xVHwVTC@(ci!Qb&tOvk>4XunfWNYu5w3~V3{+Xv zjEHW)XmhbyrSn0p0KXPG+{oE~aPTk^z6+=sCUKK~-rN z{_SNjV;aHr_@#*8iZI)fYxZVcQ&*%62KmTPoUWXN0a@^GwH+B~2XGivE1`Yw@q+8W zfhpLg3g%51fL_KrgE{9c=*bhc7S2LBlNvqK%@foDnZsw)yT8=JmPrbBF=$rU2O3F# z>-clReFfFc-P?ttTUUP^wgeVa0e8oLXegldQ~{zR56I5UM&&*EtWQtIWpTN85h}dB zxUcB@Q9X1Nh=771f~6`2$&zrdSLKk@#Vv#9uRoxG7F$czv^5qk8x$Aw1nlnJ*^ni7 z2qsS^2GSunEKI&Cy>Q#n8J1j6z3PXhgHPlJ>5Al$3j;S5GyH!xSg6IU+HCA;K&CP} z`jy-Svb&^35@LT+lIGrrZ37EtY5gt16@V{C#%98^~3 zAB($VGohu01C93BF0y!Bk`|eb)Ylw`_eU9&7q0)g`|#`E3Z@mlt`vpS!GLZhJ;sDZ zS)@}6i>eiDWv*a@u3!rVwz)c!Ni0WOtWjK-Q_P~X_(gxy`WyI-d_I(u>9Sx;OG52p zD=)zIQag`Xlz|v8+6t>j+YpSJ$SsF0=91uU)gqkD*fL9wM4EDjE+PaM%#)FBU|C(l z+N#`*ghkJeQe?&nI)LtYC;2$fr_!xP*8{qjy{e;rb`P{c326QiU{3^h`c0{}JrY1Qhf6o9!%a>gF@YMpCn zx;HeYrhbF+Pjy%I!Z5jKR;X? zXLNHIbg`;ICoip@*(-Fe3;xgq<^Xe9V8FR<0T~btXU{b>(1O)RX0`qX`%Ls&y21M% zo{8}Th9GFpZ3aS-ORY))6q`r^liYu^Jcf7R2Z+hA^EI}~S*|n^zNwN71!}6G8oXy$%=lBq@*fop(JD$oi_AO0g|zzmdHcQU67u01HFHmXd$*Z~2?v zp<~-7ZTJEF_fmQ{rXFVsc#}*4Z$JgS2~~S96(vz78$@R9`av?2=2!VMljiLo02n*3 zKQLe{^l~|1Y_!laz}R{72Zb}tN9$a&Tji3?=RZ#_*}S!mSy>vhBDP>^%*y;U!X+tP z{PvA+Ah-}2dAKs9w;B)6Tl0S>0dgMlDR=e!>Ou@LdWaH zLg&hwQCv5QX-7)^C|g5P=txNr-j)61P^;53(`unZ%e-=O9cJPV^v8ejhw|^lccNd) zg&#aW%+&Obsit>GHNB%0<1}+x`Vx@X3K+uk%RCsuAkR3SK0gM@f?)~cN!}o9R6oxL z2+T#XgOLe4c^v)Ta!0?%9sS*!I4i{f`csOi^}^vwH)ZN0zExkyM+7OX_v1FA@KtXF z1Y1>mN^5L$lkZa-ams(tziL)*Q0l1quYheN)WwC5*3_V-A@K%q*5gfAqAL+G%|l38mw{YJZmj#&t%o% zQPvW(jz@5a_fHx{y)UqJqi;yGAzD6A-e?K*X&d_0YrS6-%E0F>n1d*CaKY7k;n)$W z#CAHiu%r(3S49_|Px;Bq-O!*%Ps*O3)ogv|S!HWmGFQyc!_4jZ`OllVP08j(#Op=R zXFop5aBYPrxTk-YF}gG;;GoxYvr6?Qmc68m({(pbfjn78Q0@wCRi8C{!r^=|D+hiZ zdoLZw2fYY1p)ZL2TU95WK6&E9HilZ#zgxmxjCCV`o%DmY=e%-$PLKAwv-Ujtz6J$H z*#BdiR7st)N~A%R2b!}=>YUxWJ>NM%8FTidnsn$)fbM^tk7onR7C_n4zcO}arB0Pp zbCp+4&7Ni0tG(fNXYTbXue@^HD+3LGNL_$iu>q0$PUc^8;uU$b>D0lP@3&5ehb)}~ zsdPi^+$8EGGLBaYr`oVi1x}yo%^2EdmpH{m6LCqRn*?<-kY{K_4D~jNSi1|euS<*! z?$rBzOiX{0L|&%^L*@dNuel3?%z&g6bRU5zA_{}psGH2Ur(v|jXJ`en2)p&AsB*~y zE4*Q#;bi_t5=ChKbvZsHEC`=P!3Jmj*-xI>G~my|X1s00NML4o&5+R28Kzbm9 z2U3=||M^SRBVF`6u(1VMGJM>{$6YeS@J4o8BAI`NPlxz)NILkTu1lXz!lxa4+93n> zv_cYlcssAcK3+e0a-AK|!Ma|xNYv+OC_+0fLb1h3hIZg6^A%^+ z`Wa?lK8PUl*20wubOj?rj<_#5U5LsT3PSz%wCat(tAfqoPi^NT>z$eI7=ECzkwDTB z!3BTP4IW%@h<6I?mS>Jl7PpidW)S$}V<86(dwgA}e>_D4Am58{BcMEr@EN}z1xeE2 zZH>b?)9VGHe~Nb=a=}+cWK8g9WJK^r>L`Q*6^w_=R4jE61^>hbEdm|O6L=xsiH2Q} zSt8;%Wi7|Y*W_c1=7!?pyGC{XN-?Su>({A%BYx9-I(GS;)1}-5f$9=W zR(L~972XLaDp%+c9q;%!pB$gPD@=`R=C8>&)S@+a6LvI@om}1&gb8yUDD|VU>bHMN z=8BH)79Y<3cs4(EfbBVMAPl4CiyPbQjyld z#kjXIB!NX{;U4FK-37}=DTvBL`F47wy@}mk{r+{Z3RR34N8}NeZi=W(_(`RN98yz! zWcj6_PmH4_#Ut#_7gb~ySfiX6f^(GgC4ofhaJy%WH)a+__%+pBei_r zG)Rq%PsHT*6Jhm&Nw9i20amXVq}JXs@vG!#43&^GRDKQ4P`PK$P`NM8P*HJ)%AIkB z%EPdR%0G!UR8*{?^3P)pm46@BP*Jmn%Du6M${q5CN(OJZ%rIhc!xcTpv?lC=_-j$T zdyaC)`V=#A=hCKjSE9oT+x~wtddyzQr%SEY7?s;#S!?(^_t6}MOJZ4T*emzbn54I3 zd28r9t0Y*h#0%r@6q%GHp9zIrJPeU#TXa0Y(mlxUKPd=j3S}QSPhdT5Tm+Z6$wRZ6#i9MNzHFk<05-dThnF6t3Cek-`VdpCz2F8m!q~&Db{> z>rF@d2)s^+1%}bx!&}--t;XbYQ#2)dgL&Ar=Iwjl%TJ4Bc=T#>ve&e*# z+9K0Lk&fy^Cl4`QXRH-7U&tny`~bvMucCqkN9gK;%s(h;YPUb&Ik1ejx`MV^GHo?v z+Un$_t==lMb+&&BZSAf?Tl*`}RvH;94tS;LJ~XJa7gAh5h-b(mynZy&A)vubf!xtdmJIuKD8UG%gAIlUd{@i#mIWR2aV z?m){`lzpir`cg^lOC{EqN=jcUvA$GN`cg^srIOwk6-$3g)XtQjQkRxZXIE+Ov6LAz zm6xTxH=$+I`CR&Yw6K~9#$W2k3M@FYTa`sJPN-m!j1wwYWWWQ}EOJom@Ato#McRx- zx<7zL(xm>o*;M@pb6R>&iVA&;B}JhBt;$Vtc}2ZYBr(Ny8@Wta9pgk4Ip&~F|- z5%Y1FHS~Wbj?O0s#<%HWU>h*R=aWlF1rpHJY_6prEsjN9C`jCk-w30;+S>^;OMKK{Lpy&ZKmLT2YMnI#*4T%)TKUuk(PsV9m{-!_~Moi`GAx8hD{31rz z<)kGf*z#{mh;w8RJzk0%KL0zpxZ=3s@(NO~tN4Fn3bVh0ysucsS6Z%DH(a4nQ19%8 zo<0PBFCT)}dG{AAAKb%^XN;o&nBs2wIGA(abZuh{g~eiU&57fiA3liRG6*I=rI=-_ zuv=pTjXKS#*$u}fkrQ`mx*qf?eK=k(AC6C{DAd6%O571nIBmu)}1`4fU2Io%S;gbND~Af(*(i4==I--`}Yz4 z{YihmrjLH7*PrmwU+@I}yrp;E;ho>%-}_{8VLY=3Q;LH~-!QeOEF(UY5%J-L1Vi%{ zLklI&y7@3bH02e5XevJfh-T}dfoLjufM|azD*@4Lukm1RhX=Fs6XC(^Mv+X)49R3$ z0GRBFi=7CP$@7O7p!f14nQZ<#NG6*ylF73*lF1s$WQ}C9Ml#8VWYP*x%m9KOLozv} zT1krCL1*Hu05r)g%dk!Gg#fLjn_2e~gp*U`XgrsFI)t*{@##(17sA9NvMnZJ@($s*>>YJsWg}Bphk-uA?!=IiRe5qPq zknYf$ir*L3VCXA8%)pL8;j?`T*AG&9;9eqmZO!N@!eWs7>p@Noa^=oPxtDX4nL%5EB{A5zGmw9g$p8ahJQ*f% zmJSoxt<86J8V%M&06hKCW|35iq+jczonZ)nHZh1JT8N$J1;skGDHfZ+pG^$SNGWz% z8(^_3_yfh-5yF23ypv-{v@n1e@#>ngXmrKuS&&iWy$POHPg-)xi70tmQ=jejs8;JN zNCTVcczVlKU>?2=Fb{vpi701fmpc^YjO=p9HQlexx6|B zjjjw6SSA%hwirxcL)UdT0TW2Y`y1Kab$nEHM7T)RR0PFIc`1?5I5QqJcRqZ$Lc32*Mle$#E22z?IT3vTYmeusbWMMIe%@fW4L`-wZj z%auq+EA^BZIWwL$n253#4X(sGT1j@aqUvZR*3n9`qZMUGD~XO)lpWAe(O9H} zR{gI~Y)%QbBmYG-9!CMii_!9`C_8+C#aSfFD}w7-ah8UnD4&d~qeo+>#g7ML8Gp8~ zzRf89g17-L=VpHlm(vPxIem0{byIOUrDIYvO>4!B^HTGe)wHT7gK|Pg%pP@G26?_b zT`m)t)02QUtq_ zilSYT@;lTYm5T({%|g+nacYLaS$bpZ;w4P><2k;>$ct1kxxlUl7zMoPf$QMWW;ksw z&|8=_Y9uG7D8X-tHx%8Pe8o?&7x3uo#7qySqIZAEm+LqtJX%IpdE@yBk<~f0rV4A3 zl-=N=HK%$rwruka^Im9M<>?%eAp!u(YC$7`;;k6>$*cITklhEXpz3UwZ+GDP?asmU zCMrY$$>J8rh+JX~mt>v%_V^_&h(I4M`@WE=R9|m(VTAgFsB9G^EhuVkjl~O*j0#oB zzEpp*B9*))s7Jz38eLG_AC-L1I1!0dF;c^K{0QxGJkLpy^t0^sBb{NDggHGu!=@*n z60U1#4DLX+<6c6|NILrg#Y}!+h$hZOm?d?rs^|k7>kgyDr+P?&Tj85$3)+g-ux9Jq z&nnON;Cc$zdz)L&ci_51@9*q9hwBO6-`aoN-I0QKTZ0D)m~LI>514LUuj77i75Ce^ z`g!7hTkS{$FI^wszVWGti@LbDlH-2)?L0xad}03zLAboDEC{#NO!^&E@fcLHi8=Zg zcxEf+Q<+R?;Te&wj*ucg@08157MQTAE;xx5L~s}pdO-sYwplR1Na18^s$F?Oju(IE zUJ9394%Zv@6q=Io`L$343qw9MZvsm}ma6Phq?d~%{#$Ffw2JsxWeaJMRNYO4sz!N@ zo~A3+iNlIL(t@D@!T(75kj9w8TnW#>k>z1DVH2u6Wp2%;@xAbeM&8PmSPD;>Bd<7% z`@hmA$eDtc!Q)KSlb2WXG44n6aif2lj~lXGN1^$g(PO>hhk0T8vbKt{Rg8F!zWA|AqbQ2wHy2hE$OYisD}C4esO+S_;7!5vTV#| zuC-tbtJO-P!%TIk`r9LrZc!0-83PC?nimsm=AZg@`7qzxX;7@F#*yR<~=*FF@z3rXbh3jb3u?KL+3Ju zoJg&UVD5HJXiW%-r};yT0B3(fC9m8D^;C*76R+o4N_42n8n&|=Z6KCvOQ#$C8T7yLG>^|RsxIeIA zw9&|;F}zeM5j+Xd$Lvip>k_ECtbF0%5_EAC{V9qC(|TK{Ybvqc^8bH6TmC2CyBt0= zv0>vTji1$JxbU&I^G5KRitHw{)f4T|AR_9MsHw-XAEoQLax-rS4uOv@0>VS zrh903W_Wb45@s|~vSiua@eK9_MUWw_KU5ipNzf1t=MFeT>!pA0BP5axlkhS^#cHir z@EXvv&y-yv5|n^|BRs!^t}42cBt$KH-$tzSfTqT{3<88u5)``+Un+Ud#MIp)TNNJ^ zzMHQr2w&KPGuiJ@rcMW|=AzTVfIpi^MWbajjTZ814i=ihA1Ksi2-2s}e?5UNcEi_g zwM_V`g)6aU;ZlFBShx(yJy>jsctdH!Gr`cW;d$Yc1-694jV zzY)L0kokD1U9fpT<`LJw{U*oDg_{V91V5r>f?Wqi#C5cU*nCicU&d=-8tlNhzXis3n(0v6|fi)?d-wduqbxFED-$!Xo<>?LPJHBU%7mXnhpDo z6;W&iS0ffI_by5|hq2(kpjFZJW-zvr_|p5(43v2GyTZK7c%{hb?~1)0X(TN|VrRkd z4Z1-e^5YgXRUp)NH;FIb;h<*tC5oGf97bW$rgDWTh55BR90FsfnA#0mL|F)dYISlP5+pdhPFU%@~T%v|(x7rm;yk2pq-Eo{L2(*}%C8 zio1w>(7XV&h8CjhoXlJcmB}Dzg`Qv4dY5v<8jDrVy*n3q&*OjF zB;!H#`C7xNCh7ft~IG>(5OLwalL z!STg;OL93RKK?omiI1{FVrPvnzQz|{7aWhpfr6ulWQilm)JJ8d908R9eB1=8F^+WQ`yi12J%E&V zGl7(!Q^4e13Yff?V%!+*GUI=SWa+SF{6se$KhdhqFWCX*;skOlFnD-yO-BKAI1q*q zBJjBaB5)8u1R@7_@)$Z z0b@%TFyjydMy^lV*3t5T(nd?5Ez;1hUhCsb2pLdD!ja7hAtR{)-DMy${r%Oga8V1K zj~2LOn&~^tdgL~x9yNc_Jxie?k6!|puJ6^ zQG1~x^{};DWQeGy_EVHaVKxDLBKqax}v(-RbA!k9~`&b?eMTqZ; zgOa@eOAEE7sI-LC;hP)g>fZcNRy12uooRstl0!e-DKzQpu9o2v)J zf`2M?z+MjQydY{I8`cjbV{{xn`tz0JPEv>HYf6sJ8O1L{m=Y&IC$U1*Lb_C{b}3jg zD&WNftfOansH(Pt6N;2lsWM1sN8ogdXrCKyzR>g2=@Fo*MINWrv9EfRBxt?;JuCFp3N6 z==U{#R#H{7VOor_1~WMxf1=xsMh%(c@!`O zSh&wJoZ!q=s&a*kClQCZ!qFA^WPk0Azm+%swDwr!E6~Aj~TB zdG&vJDg-iW5o*~+2I{@C%yG{{|B3Vnp9qhMa6E-vnEhr}BzDd12C{36dy=9)lGDp9 zAIa%8`be<(Ae&z0Ob+CqZb>kMX(v2Rc@q19FCD0esE5@}3R8Fx1KvRBW~}=>`;bN> z<;cV(2VJ7sGSQBpwmdDQl7G8)!;=iEF}r_HL5|hc2*L@9XV40Nn)3Rl*6T%dSm z1$SoKSjeEZ6ev|Hd^)%|!(_U`aUX8ui!+baV-`Hv0tJ*pTxfp5UTY?yM}W|= zPXNv1arAHrjLQr@hjrlxfmf9fijqn}LBE(#r0f>Y56I{j`y%~<_!{lj0et@2nH7J$ z8Nav!kqRj@`*^ateP|da`tS$s6tQCb;I?6{hlQr_2MTp! zX0X1_0U)a;%KSdArN;Rr}fbQY3@Sx`7cn*_nO3 z&W+$ZLCu$uo{>=LG;=2IElX2V3j9<}5VF2!!p`f7jIk6v>yFMdel*G~M79rT7?YCK z!z|AyPlCKYOcf52K7;(e$ldSNOEO!^37TjsU(@Qg*5p>%%dnDeHQKlB2BUuz<3o_ zrEu0IkgffaLZ;0cBbLM^(41gAmSg|OfIrKR13|B*LTp~->5Gwe)`OyKM0#G3GE>!b z8Mk2Bs28Jq*i-Id@75eIeYZ~WFr4sgisA}mBX#*w4;Cp!0r6DRGZ=s3ina$cV*qk0 zC?aDFHo;S12D3!kt%f!&&)E_B}^9X?w__lcX@Nal(>M*rQz5^ zCLz<2!t57!5_5sd$IY^LxJ)HzUDbvsRxc7R#JNn>O+MlRP}O3O7JZIWE)r- zCMnnx5i+!58iUu9gL8lC@WB~ooB%RqCxAp9AhPdfB25B{$;f-86GMze6n*Qo`S2#3 zQD|yta#MpwQY{YW#x=$yDoqY?b3-IJg53&ZN7(PL;UlWQL_%&;3>{MVX4Hfc5jPZ; zi&3WKVoog=FdF3a8veCXvLJ>BG2W8Y{SD4t_)|(8)u`0v+Uu1v*$IU-a~F?I^mPqv-f2augj! zJunUC%s3Q|7Xg1qh<%dYI(vBbogV>_6QN(iqjD-G9G+gRBly-4eCr6lbp&6&gu~OX zp+3He6aHn6>w54#MUL^-A$joHoaLgfn6ez+@}@r!@uUoLr<<#FOG!Jy8Te2q^6 zDJ;qGT=Hc_x@U4S?_n-pEuV`&GUkFQFR@-FSFNiomR!HQU&>k)Bjibu2+2g)#XSz& zAIpdBWyY{^MK5jKNZ~QCz^+*>@Kff3Au`l228)ot#$HbjtopnEsF8iSd}Kdlj4V@| zmoCVzvRHrWf>hS}j~ccQnHH!w)B^PtEl_j#_n7{CMMG)+rq_Sa>(})9CB6PcuRqZ1 zcPYt)9~ziU2UpX@?*}c|J-*5vtCd5{6Uz8igJHvm+Omiud~bbM7d9y)NG@e+D%Pr zmJSVB!jFq#`g0<~IUftws=l1nOrzb{N8`!Nv254)Tq?u+u~; zv6X+GZyb6E($WjabxrTwZi&o-B{EYlky+Xj>834_73P1IXa1Fee%$g-Fv9XR$w#(82$p*`B1%cDtqzRI|+csbY1?Dp(nkh*5-d3w2wLJ5hhN zdMen7({~c=#9vBBu5f?)asl?@yuZesEP?0c2rmQ$96wH{{A=&7gnP@CtuEy!+?(P|N-^ zo+xq#Ym{3H+hM>4-i=3Ugyt){HbQ^pRutemEuRARgAPO(g~2NT!q{urV-8zJU33aX z`1edj_$}3Hf2UgQ`<1uf(o;iL4z>7Re0g@7{VkE!PyZq}*j)t-_37rrFSv5J;5Ji@ z<{a$BD&}x`rSjX!C4G<9nsVkMt$CS0#riyp#`^5C zSfA%S#HZnlSf4#P)~8`UIPc{x$rbCf^XtU=?8LLD8zBpZSyBkao)94}7%pkHaGWg? zhjPoRLU6;*k}n!A$`{xbSW4DCo^9GNRy*6Y;UvyBgU;N0G~aKociwI?eB}&rc3v>2R5y6Rw5nx)y_zC_tKtNe(Dn|^-25ydlZxM9CRPC zg5)Sq*GZm&$i(uLYv6xYS4OP6&qQz0jN0X#Ihm7`YgBI1g-uL%YfcRt(Kcblu*od+ zQU1oI(~YG8rX~TNZj&2K_M_XF%Jb%eo)({Mm&=xU&?1`p)P*nlRf~c)oty4CW-qz& zuf~*NE_`;s5Zu{Sdt)aPA$lT+NMS} z%NR+KD1kHp3zW;Oh>Ny>#yDaN;A5!!Aanh8C7}G=-t}lKPp6{-No^2NI>a%CC*vw^pwXx8hbrcnu1HYiZ#9ZIvvTviW^1#0< zm&q&8;+B7X5jDNL`d-Uf_BG$RtAV8%yvshvGUD?2&gd8KrfshcuW;^K%a_`~@MyWa z8WjBS338_#AExDQdqS=B%?snI@Wz}gzc>&E#i2R>{x2N?gUbF44S|S>anpwv<2s=j zH;o%L>i)3h8n(w$>=QZK(Z@k_ZKhoVd(EzJGqZmyF~Pwvy3V+35Eux*GUq~8w7a1} z#24ohNp=$7;ox&P0164NlPSSXs07!c7Lvh=9Qfg#fSNo798=@A%6wx8>vzsQg7Uh& zeE4;F9j?pERP`dd0`A}(Di<|9>qpIzemxZ|Fr%@KOEnPTaj+9 z1!*7fYjQ(zBddGFw0qS(wW;SjT#fWY$V-wR%J;7C+~Q3xdFOO+0*_*cxI{gZjfaP2 zX@syWjb=8xPxofrhVO^mKIG_vO)D9CjO>49f~+(s$O`ri#!QW{q9kS11z^c80NL>W zu$U@&c|2wAw!*;6i7xSz&+>fhhZ*4mQ$h|q_$o$ts!-;!!8>_MF>O3YL8?11G1co! z7+qis6M+e}TCIGKHhmC5wLhF0b#UAN_y7ApzCdcG*oQKuFI<}xmn?S&V(5T=s$+kk zQK!7vq)>qVEqnsWHbf1D5to6bbQx@xNo9xZl*wkf%)JM0PVa#{NI9Gc)>0>enXhs; zQMF_>2Cg#WGbZ12=<(a7YW2b6<0 zP;r>Uzmt1#TU2&ruf@UYUW=~L9h!d|hTYrX{)_ysi)H?a1T|`r8>|%w1+TMeBpnlo z_1rl82e=&??eBFvoCIzMY4aXGNaS4OD1YQ!<7^!{cdN*`#`(_^IoG&|Fb%pszJ23U z4;Np>#d#!h?rZKaHomZbg)lbW)y2ZB*`?v6hf9_c{8x(bEULYrT*Z&eXgPn=YB_pZ z#ZLUR3bc6qQOiDl047w5pDNSvDYs@*SS9#F0gosObqb+O*Q{>o$#ggIj4j}`NXC`o zDF%`)NDrhA(+yADP~lW+6L_XkNPwvHRs%ZA5Osl)_mGF+c)E(?*4%RG&vUN6#S-bZ$GWlzsEgAHIA91nvQGr2BxYN zo!vaGKz!kW;_bmnj<=&3r3%Q=XE`eQ>U7#7ZBo^b(~tQoQN7!~&hf50Q_kYNpW|K7 z)>Q9~j2!RsfWV|M(#!F(tP`o~ILxXe)F2&)=`0}kj7`7Lc!|Q>v~+*Tz6*8b-M@ek<5WAg5VUc4d4?9oSZ z^qyXPBHfqtqD4*`^x~BGJM>~i{_^R?oV@MQi$ikqj9&af>a6ffZa}BdgEd><-L33B z$GBBn-`?CSmoZk=F4=z!dKDf&dk(zg1>)s_I_6?>cd>MQ);_;zyuPS6n>Ed{aX}97 zs2+`2C61ilK0Z5aoZ`h{IY4sTvSD2DqRT+L4K_Ja4e;Vw{SaRZ z--5!n$?Wt}$(I+$N3ZK)DYnh!63vr({X8t-jZJfk&+8W#jf;Q!QH`DYfOzXx3EF*5 z;oNIlmmlfa)99FWVLILUJ7%-Z7VhzwAGHq`VIes)ph+)Z&NV z9z6H~l$Srn(S(23(xMttai5*@cSZkuDa-j+xyAh6FC*pmBwX6|8kTkqOS^`pUBl95 z9xPw4snp&Z+?!ROFvBQ4MW#Dqzv!M{kf!pzf_%0*&^Ka~+Ruvn$CMT7b-00PSqn1C z`0BWHJS?3xTF0;cXg5C`9)ct)1uK~Jj83nLrdh9f3P|&;WQh}4L2qE)Wb&gT zEeI4e6l#AL#S&ZT^7_HE)Wi}Jeyz;F&k4M0A|rt9n4p)(t! zZ08pY23r~yDtNPwckO{32;v?+{;q?rQPV9IPEn2nS&yCOitsv3kO2#02k6w$@qA#5 zBsS8jR!}d{P0-J()aQkI3dGFDCxT6aO&Ck{U~_sVMGKX7f#V(t=+N%if}8jg=M9u# zc*}pdI_{A!JoK!AZJ?DK^gauXlkOV|Qwq8tonP?~~^X#SF(25mc%+V;V+{XIzyfK`IZG*RYO1CekK zot}B%>*Yv|XI$^$n?wkS8nb8kHB`03FJb+5jcYpi)k9t{SGcLQSZb3W^s~_M9Rq(h zBwzp%-ug-^d(q!XZXC3TXQujqde!-Xz%gMJN|PG(9yD-}LKHwrzoOV7kaTZC!5 z&Un19MVJJvG+6b1$RyklvRF`YpD_bY%gqEW_ff~D9b|?8c*QOy?kcFe9b-Jj@F3Ob zU?TRgH;j*_OG&bV{_=5x@ Vp}KjstY5Uyp62gsVnD(OnqoD1(`WoEC<1>1WxSf^dE=~E zkJl5wo2baCX{iriBrWK|YhZ@vbFS+UzZNiauU3BH{mXpe{q|eY|LlDU3&W2rv=+Wp zEM!!i`E#=XIw#`m!AGfwz2OiV3FV?kJX{uk5*RF{hcIixT6gxmI>qBf2xJ0sw=wuJmFYcG#;8IOs4j^&X7VaM?J8?Ho$&^vICG7H`L3iwjfv3my2vWZ4(c zECJO|n1)@Ok_l9e>rJ0VKCzvOj4X2jGX)ztT#@f$d;`e}KmS>y4=7LksG?=>KyG3F z#`fFB%l5^Gv!;J027Wv`q6f$W&Gn?8j0$_igLS3fu!Cl+pIza%{6hG7120V=2n-%& zL8#9*)7wlv!C%Q`gh~`s|DO{e0lu!BMDF;v1vg`9{zvAOj$LbT}D(~)zI5QvZg48 z_)1joQ-}+;D58s3SQRz#asK2V#*{_ z%f$)9H#;|6W8%H=k=>YfPuGlIk44Y`2V$3Kgb;sEp-prykB_iG_>1M%fDmC)?;y=P zLgR#ZFSv=X1wW*>j1FjijxE2#Z)t?tf%KqIQF_m`Q6cQf@9|sGd;CsOaF@w%+P?Oi zA2`#)tT|h?irk_e5#TTHW9F~f%+rWCawQRmYa|YMO4o6Z#k}E>8mY_SatUh^+Em(R zoH~CViv2h2gVJ`y2dVq)iaaXId!v3IBh+LgGQ|>TKYTAfe$i1r;7x>A zQiu3J9^%9M7~*3gJc72?wm@Ce$Wi*}9;S{i-IJHtqbu)iZ+JTJzjU@8J*nQ^=f6Ex|wY5dtZplJAb6n{*q0)2EEe}ue=K556F zi1kl@pPs~@f|vj+l)X9#A1=rRz2%lZmi3zs=*B$a19alA^ogfm6v-=kc@CEgvbptq zFH4{Nld6$DS&e*FdA^N?5nJD?Y;EoqX{_1o{mPuW@DbY|?8M zWP>)tCr@Wys!=b$_01OF`zY@l`T@)*&*dp878g3N>t<$^ndtNTP(CX*-Io7uC->v{XI`8=x+n>(}*o(UpfkM<(>*urZ)%*J-3v*a@?RJ4!Mt=r9A zq>4M8KEBj0ELg;5^+vSV2Th?A3=5in%OuMIqte`hDll~T>#24K zH`&w7HnW6(h+J`X0uvA94^%jES@ZOw-urBSA;Um#b7Sc;Mt~$S({C1JAg7{T0)LtE z%$Z(|5-7HoU@)9HDCC1OqOPrdDQy4aE2_OB>^a4-R z)x0S*X4@;^PCz%8O%JU_bO!DJ8JX987v{n%U@0I%hz|Up@R#OCL3bvI^+EG4w|2`t zbAUu*Q6y?~e9!Gtken;4=MU+kADY%+=o6Zu0c*gKmbQm4z(>i9@0v${wVL z+m&5xX&@$zX~{0nHDUsPRWD1tD0%Ea%WvPH8@6=+%5ZJV9^icu|4T(ck#%}v_WeZO zF5wR^==dhGdI|rSimAo8y$vhP^IO1Q7CcHe@lM!Q#`S=Hk9ZC7N74Ft*OE2bk%GJ~ zn>G!pyaG6x4jJ!Nh+{|BIR6S=hNudr7bvSy z!g(MLre;X?=!`|`MS)?Ni{Qn-g`H8AfNWZW+%<}SV&B%a6UHCPdGHk)56Rvbgm0Px3MV&1BC8^ zmKabC?%yzgJ3N!nt3G`?fcDKg$mXAVAguUo-F5wuKl1nzBK;?&GgTy;;=jMY@{jwb6n+-!4DT!9>3<=bg{?_I8;Kfiptno@k+9 z%#-s`WS(2)@@|=>>T;Hf>~pugQz;|+1n&Cm*)z}(hxmnOmGX0R1ll@H)-Hm?4K)Dk zFM_;{E!cwh2O;ymq%3*Bx_AvgroU;=`@6lpt%})yfBwwe+kU?FYaOV`SwphAr<={g3uYooG?PF!fFz4e=Cx}w znI1rY(n69+GD!=qw9rzLnWWWdbKl~=fL=lF9=`wrnXLYqIp^FNn=U4R`0odw2#_YxR0uRbyj`Q=8LDKYdfkM0j9K715*N- z7y|Ddrnj(zhdH-1fn~&ZhCm|$%sg^~HK9$r265`u_IH#H_fGPNuv&!NO6^-zqgM!j zjFKrtCr^gNOM-%9Iu+MDqZQ|Zjm43ox+5ROSLEIQS#5?#+ zmwmW(yv6eo(?tpNRH;D9HQ{zpKUHv(2;XF3jXo`&&E|T4&Rr%H zqRkEhj{kZEPb`N5mz%gPRoWTi9kDZGlX2FJ)B6a*#GcQ_68Bjc=j5IGd97zsddrLP1mlKI}=zRObk zRO-K76BkU-nowLkW9wh(0FL($Uk@DJ3Uyn>oE`3R2Y(QHn$M@Z`d)nz+w$`{T;q?_ zl@^qY>kl4K#Ywx}c07Si#@^dh8@c-UrL_mr=yjtJGHDaJ zC?z`oinzrV4b7VKb(9aYcnXUQd6Bhs=MkQJR(GR?l}3!s38M8FCl#s>C&sq8)cA-t z7K@tT3E+l>&4gkK(JR&F7LRT&`P7w~%M#ioOGj&#skSBas*${Z5(ieUwT9ec<~Xa{6oBcyB88iT`Ul+42Xy+M#MHR zNhFqbb+(|oqOKeD+~}ASrE}_|&s;5Q*GQvKaU`}-77pOk+mZ9kn%Za8$DVsI5P^)9$PRuA^FW+z`t{6 z%f^~xqvlxGENou#hXJ*D=| z-%4uVva{fx2UqFL`|hoY7c0DF1MN@EZl$!qmt1V{m)uc*Ixt+ONs-(A)r9@o;D6c_{wx0mv{W~fE%OFb5g4dw)-VL3{kS60`?_*3OJ;d3YGkvJ9B}LpG5X{bhu|UnDjUeyNTO!e(y=o%atx zH{A8r?aW6MI|nFsH2DRE!ohnZt$)_Q8l~o}t(6MHz*ZnYa=R3za*FCu${AgkoHC2T zYek3j7Zy|?^2n0Hv{sx2(z06^S;#xwOj?6vm7`XF1N^2y%xZ6OyA&mHBO%M%q?^NA zw{)xV$kH%~9PlF^c_ONmJR3wzUBre5UVu3vt+I;jt`%OOQ*Ogs~pBbiivm=XewRK)axCbpQ&AJrcG* ze2abnrGt9^oTTyS%;!PWjnk zLyHDbO@;4_l(ed93J8aT$pr|+0<@c5xxLntN zlH*HDTG1}s+iGg#__LhB_+ZOpsY1Pm-Wdle^iC>!r;aJ7vzl)mHf}&Atfg19jjafL zI=%VUlDbxY`MsIr!~543`pvhN)V1=PKa+H?wuWu;T_(sc^{j43$u z9S`yI#fQTk9<0aq)<`X|uT(nmTu*U-R84YZq&Ys^*+1HORjSc=XHR)#OKO&?fyGox z{R((+6y(5zIp@J2ljEORvhfejoY8`@=<@ViE@T;Z*zU9!9Uo1 z(d}+<E{ni@oUHEdC;kNB+GuPg(y%Dh)z}6(%Hk6=tI^$(qZ*FXCbn9p6pU zi!7X8-bt_Ks^>GBWeQ_W|DK0`!1M3P3H<ysE87Rw*o{z(ESCpHzqpSALcBjM8$&VgS zwlSJ|shX^P*AZU#+Q(0p_vX!;mv6d?P9SLO+(KtK>TQSGBv$4gk4NEuDAc((HrCf6 zH&NsKZnWMT_r?v4)>gH?LTrF=iTQ~xQ-DE@7Rm0f#H!XJVn2M1OeXA68~>^dmhvz zr?a-*e%cd;c)ST6=B&0p|AQlwP^)|OcIC74OF@S&t6lGaIhq3sf#ctUB;1EV{F+;< zp`RSJ@=bUhPk?)$;vq)#l!ME&m+14e7Y>i;@eQP7hv|7*r0mORauea^mF+-56P5!{ zJk6slhN*Wh7|O81nCm{}%lsZDv8RhkW$v2k|UazzH{5jRySWRz#aW&5$RZn*v{)efIndCfe zcY2HKUSCxAdixO-uXolrw?Dn&4TCYe8%7oHsNi)mcKqF51Ty-Xm{q?#j4oyqSj32= zdOerj#Z1DRxK(Z7dbxpJD5&?Cg4WyHn~x}K{ZVBt>2;XL*uag)l=T=k1@75~@-`n+ zUbnrxwDom=>ws*TvIwSQt(?tYOBRXsf;6#5G-)}LZ#FP9R784i_t&Tg+Pk@^l1K5O z7IV!~9@?{QsfaYHQF@D)Uwj2~Ih4ZcB30eG zC~8r4n+rOiRNcm}Qr*@<%FX(oFRf4Zl(o}EecQ6W?lR8YYD%Q)+m!XKH`n+4G4-v> z{_HKSZ)-_?FR-ZIW9r*lTHn?q>f>$OSW@AJ9KiLZ72*Ir-B?f|F~jB~D(rQZR=6!j zXkmqa)SKH)y|tmIO;1keUsxdx&x^-Yxbdh8Uo5MTdaL`G3b&S4_*_=Fxu8PTTZ<|r z27NASUR)t@;ldiXWOpsAalP}n8oSGC+?M^dw8q}j8ecrB#^aIT*b^qcjskZe;RJqZ4WR)Ass>J?W z-+WA!z2#M^<@)LNf-2YLFfFPQ$9iK!)V-|A&1F??$YEMq<<_DqPZfF|cTmJbugmB< zLioHnqX041!*F$5lL_<*1&*S4x3^6w%1h5l`p8U8tH8jwFIoXiOD#M+8lpM8o3YVMYAX$QKU}wO4J} z09*@Tij^(_UDU6g-e>8@JDI3MP%EObuVFMAQ95dsxtN@toHFA|2ThDtIs#}#-FIMw zRMptDe8=ae0W^P5E8VZg(c@PiCwTXNcG}NOGp0-;jl_s4?Y}E(D{$%y3Q#IPlK-;=WAVj}Y-HZKR_|jG^RvJ~Amo&?d#dQ4r(I}?8p#@73`G-w zK5|Zyu!wJ>)-WAKr{?zu~)B4os;H-JED%eX?#ZFw1v4x=AuQC?7u zj;1zjpy>9u=+X}fa4gyk;6&SnzAf&m^h6Exoc$K`97Wkpls%S&-QReSFoehiwC%?Z z(a~=+L}V^>D8gU=8fGPh$%F*n0@zb+jLaJ5th zz~r8bd`c=I5Vx;!$-jvDdGG70d~3%Fz8Ov3N5ny%r}-6|%D5fXN;YGT6!JAfGZoW&*0KFBhf- ziHz`=o{r3w%@g-|hoG(9QE>V2z+-QF$YB9zq*43*0fH>xzx0HDFYt(e@L3xA3j=?e zqx)#9xJvIxw=W(^#(HE)~_AnJQ0;RUNt^ zwGUmCC|s(;qUxDaDGl)_B}&ECmO|F69B+P$D0bg}1`A@|2lpJb50+4LbTxVni0H1y zz_Oy|ShFqto->RFk50L0JA87g+@!4(EL-gyJs+~>3pN!xnD#HuNz&8!wJZb(OoP07!7AA zM_@sJB!#M<_C4flEFG$CFshya>Wr_ePxcHHl8rX&d>3 zKj@^=>X}$Q4&eVhoo6@-#3!+MdI~)Nt=xiT5nJ`}&=bEr?>-GWs@ORcw=OF1DX7C=tyjv=-`2HDkx=xKxO!Xn=h|Q$EQqmsG4Bii@XojAD z%mg1W(~=&VhjQN(f%Fs-GWYJG%ePn#yPTk-)JxEB;R&eOBpwjl9O?SUp``YlaXj!15QPtO-jkwL7YixMj58${Euf_H&aAEW%A5iEa&)7ES zgWR8_0{1h`;tLGi#VG#NE4r(Z1YEfjLtkNI)Uo)oZaE(RKvyQnsnOgu7UBRxkWyu z1Y4Yt7uG_zKe36sW)ruogRoiZ_7`@7p9_MsGQnrCg!2S1d|ls>>YHxImz17jOia&P z#;dlvYV>` zY5v`NyrM#^eAiJyyxRJ_(<@mD@J%{q$`@uT?d?;&5MbS~$GM;Ln-b$jhj#PRFw1d8 z7up1@4;FxsoKu0sRE_|D3W{im$j6I%@2i|UG_vEVfM$pfMSo7Oc{dupM}vXBj?NSv zO7j9X`Hn3JC-miq9th!w2gj+j9dzsGZd}=V8TsCQvgxcmq-3i6*sJOkUBi|B%iR>80IgK(XUPr6x;kLG4sq5AE>+Z zxP}KfYi@xogRHB622xQG|Dj^tDfww=+bge}zzo zUj4jb-q+sdsu*;)yODV9z$Pc8!uPY!%)*bmh#*E_aBZ$bmZgRzZ~xz?(R4sf6pg>v&|P)*=NY9L}HsRPbJG zY^|@8)4m+|!o0JQcN)NN{3aqnujqU9KgS5MG!KR(3TYe)>(LwzN0fm87Gn-8#OEvWN}^Ky(;i5X=Ci2il!YCox8b96^uZ$vKSN)2DqeJ;`C? z3fnpO%W_g0j-o;-!^S}dS+}6}jvOv69=Iw`r=c)O7_aj-%zbCABBmo_w+Km{d8sw8 z?uL_i*cwH6WEdsGJ7+ES=0EExAww+D{LtALy@*%n{pG6puuqCx8J*46?)nv%%7ih0 z4UTc`4}Jc~k&5|9#W+&&sbOH;w1)xhX!H5ja$D#UR$sv*a@#H}jMELv;7;cZn9FWYTuO*+n&XwJ(ldwnDsE%U zY0!80aUp2EP%~^|*b0Klw9{mgh9h(2W0d)*yopwGGzn0`pdY3av}bwtPj35v4+GCX z`KRxXzE4j7;GTS+e}8leuYUjR;+j>hxP?sxFlw1#JmRJ=Cy)%5M8r`F2w4gLS#y}P zmSA*Ymbfz)yB?{fgN7i3-*{pBO5Z&P>h3uRNQlS}xXSS+cz8Bb2=3?2a)?Wp6qGJ|QzCkf`znV%|`DK(W$_zFj8OEq`dW7Z+SB zu+?khx$fi$Bz$-gDf3dHZWe}tr`Xn_Ga%z`BNgwTO#_)E1;Z-5z1H4 zhG222uQyU_%SvlZzqJ0qmR4T;=|v^OMMcAM&DmyeeXENN#m!Q8i~s68*Kw}o`O5mn z)>g->X$%X@|4qjU^Ae(ew7fIU@y6i9hPHNg7Ug^CXf}y(Pp{vL*%YWZ=n=HJ^`g4h zGi~A{qqIS!;gX;ij4}Rsl~dxEU9njs{ zSO~w`FN5fb>Aix(^4YfZba*T9Ru81U&G3p_p{(L1Zc1+oGY@sC+VFM0xC zx!v{d<^}@0@xwxYw`y9Vy1Gp|I{HlHtE&|YOyk>0cxv}%YE|Wy>ktIfpWo4vk_#h| zndMrMLh430o?HKcQ0(neIizJ^%-R^lTIm&W`!Jp8mr6ZuCsA<=l9)b`-w{9gGGF7~ z)DC1%ZiYE}P@tF)HejIkaKkV0Oq-Z79%8f|(8hmA=RKv@-B)@ypfCy zH*v7vk=jYB_io+J=0d%gSh*%^$!s4dRlWa&>)Lg)Rk!4t2? zSrq*g=_>@`c4?ZTszXH2*vg5Ex55%k!%$~hiVV7_Myb}_=yV@3CSgsb;<0&=kG0-l8sveh@e8S6>Ll^OmD4WwS-9lq@bGH;hn@q^b5euvVUbN11s?9`ftkWX);0c@(uSi(;Dl&z@bx#pUcA z?b)7%v`msN^yB1NEL-_>8V;{OiOD_3Pb&IU1}u(K>o|R%PVrcn>sKMm5oy5O`(|^4 zjp{m0-}G$Px6<17XNGq>*wRUG;*6s68SHsr_k;i9p$z=tTMp*rG04)wIc4L2V4|fr zCN*jq*=^gQR-GpTSw9xj(iJB_Ae9~-!XKFXT;M*>E!@Gdn3`_%?Jlk+Dj^}z}s+|X*aXRceI5}tMr($~nLe3nz3%Asn z!dF_#WUuNU-Lh++Q+6%&O-HCV{JO{1ZDxv^D!LFrpG{A$Pr0qcO>klEOYasl1;z0U z?v@VW-NGTfTQG!o3y0A5^=`=!va`~0>@MV7xwRHm?ofMe!uJ?IH|!vPZkZzzIdUxT zqvXcF-#>W0d$zm(b!li}rKt{8Bg6o2OUF0^I0C|8rFHoock8w-YNj@Ob7(6u@D(aI z9oSnKBmGgg>8kxDJ2JibjBD0;I+V>$g9QFD4rQBe`bx%5w7~Z^pZB`U4Wmpm zxsA=OCL=>PUbu-7_#o9SjC8FT*lysZZsIq@8t*^D_lB`kd& z!w!r~9dK&2f223J(TTj5Yk%jRHrupf071*^2-r2B!tf5%FX@(ARyG^1+-g4G`eyq3 z6F%8m&zR#FA5MQyFDf44g;i{o#Fe%$ton)fZC>lJn{iP4QvTwJHkX<+64=XSe5_>r z&pn7L9f_iUncWY0dsf5~+1>5oEHCgFNP;|}W*6gKYHzdGa3*WH*ItoYYsEfLhmUkV zB+*B#F&eehR;&djUR*?3L>K}xWK<;12EX|rjtxJ2Q3`?+TUNZ#;K>q$piH*~U@9IS zGPhH{!xVi2yH?oJ;X1Croxq#ZKy2C424^_YS!C{iM4{9J5mKGIcSXqvqw*#rrc)fJ zR}t`(O2D5*2H`lC*qpw2%Pf{URIIOS0Htu`%i^VfROqI>FY`rBF}$-e{}rq)l2qP2 zqhSgEiAd!^CS3K>$?Q4_V*W(>Y>@JYX%^kUu9|+MQ)|W2_z}%SVthpw4%S z9rHwS`R6H5l%xA>-+THr*T<8*^pxeLE3qbjGfzDERmPobYlVjbv4zqPPhp`G{}cIJ zV~$B&v8F(5WRk*!)qe-?(eMU}H`1sp^@LUjV)-)6UV(($DS)|!_g#10@A5_3{JPc! zEgic>`d*>gjjGG^fPTR}qt|=>+FD#OkIzM3=^gkz4kFD@=fO($@Bm$eSBZE9qEy`+-Pxfl$(m)h7GSNwK@5JP zR-o4H?~`X2Onmesmh03Wd*V3GleL+DP7KeG`Uy}1AG|gCE|-^g-^t}=+pyc(Uf*2k z^mSReH7g&8-|z42?fMR$$~)}!!K)8@yZgt!^IBZmIPBp4E{|0STjMtr`}SbpcMhhw z%Yw0gjpqO{KY0Cm=kV>$%Xhn_l+xK#IeiFYw*j(fh;Q9ZZ{r7x?LDiWU$Kd%OSc?f&kWOy@gq(a|iD z$&3-~h%~(~R8v!bHp`^rp0 z4o7ES@4Wj^F7RzKo$*dDr#{|2e6#avw@v}DN|cRZc2|k`uy5^<4~eBSkSpp*kxQ&8 z>trj2s<*MJ>!_vn{!=Vf%AmNbJdDI2yMH;}-9IweV^^B^lt;RhP7bt@)+E+QtI0Id zRtCjLUK$f4hcP5ZYBeH%MoK*(MnX9rB^s(-Tq2{?!#W$k)$J}0u=%C9B;$@D6XR6v z#f$B&b$*jbXL6p^)v#gb16u{|P-wp+E1~zy`&IW^1C9?d_$(%(1%dGy;adaqf$sAu zgseIrM(xHS-Pl1pQVcTNmM6q`G|ZYJ?PhrPg7$=!12r>N@9JuQUP8b{lVW;@7>_(q z?67I4@k^DSFSxN&E93UupQHnoaU1Vx)6kWhh3>pxSN}naaiJq$rlUI!k9UsXcsyCV zbr=mJn7JcgpRVu=LOi-;P8$zbXb*x`<#I1YSBzpSC6O@>#^XFH)UK-ZmUc9scLXK) zfJfBjH0YnxeM+i-9QxiRBSq{q&0~}&U}9ARd!|*V`qj~%|miDe?eB;3qnu7spxDA&QQ39F3w8isnT(HF{r=toj?B3 zcto4f?TyV|&FWlR-x|*KPwt(a-AR1m_|EqqG3c$cv-ci<9#-*JIGhy`|I)?hVVn*5 zV-FuMvhYTH?%*>&y2i&%d@SP0NPJ$$=h^jyKW!kw-og8C=_!1HPs3;wi?7@8ynFm5 zJ&ds2U4slvysOCw@zEgTIXY5sqDWJr&FpD(=A0y-uiqTOMJ@g_5QSOFhqS&clqi7mFQg z{)mu2^yB$+dIasC5+jAfbb6;*YdFiYDB;go^eEy~oFjRo7+rzsd5q;oM%yuSq}z`D z8X2v_dZU7mF>nuI^jQl1%UoD_nqufw6}Y)o|>4>O+Xe3KqWqf``$ zeUzN%Q=S-Gorq5%)}H7=#jw{-s@pQgK~7a$#yAwICLUulJ83o=^TI;@bj@p}Cp~j8 z#X4q2 z2VyVGbO$1JGou5sUfBmB_RviA2oBAiQ4$h-C+7rq-knN1cIHp;^kIGTdFRm!NC)zN z{(t|^{{;ngd(YwbfA~-TSNy$>@BhQU`)}zt_RjzNU(nAUz5XBQ_d5OlU+DJ+{r(^6 z_a^=RpXm1%{QmdVTee*t~b}I6F>f z$VxD8iWa?>!VsC5q7jL=gaqo-ANgt;q7=DzFmR{u+;`^mp%q@FH1{r}%y^xD(|f|m zM4)G#4|Ft{#>i&8&CzBACSaHqk(F-wsYbf~U{bH&)*B$cS=u1JENg}?ywVbxpBi(W zCb%lpo5DZT8X{kt&5-Zvji4`}CgtK)opAhM zp&MT>BU_YNp_+0M(WCILLh~JetbtL6?@KhN{EeWuNUzh3;B77BLP`s@@OBxoyow5~ zn2$)kgdGKDsdOa0wvmA!EOg-OWrT<_E0j=9&Zlug3qp+;@xjV{^vTA4^kFIg$&#xK z$a51}u*RmiIGbc84THqfTFBuamXRZ>s*xjdBgi+d@*sN`O4h7x5v=rovLsjO4Q~S% zoLO5?F3D4Z5t1&EiiNFG@wOxn3!~bSFf3H{B}rIhd3%`{9OyhMvNti=@_-r9%F)B(tt66~c>#3Nh)wK8eF+N+UmBa(;ZQz>or=anRtC^N5)) zXL3_H_=%dBgw(13n4|xHn4|xgqyJpzD4(F>Pqi>JTL>J(Z*yfGKj1MgCKPH*tfxBG ze}|nU7#g3ikr2*q_;`!DBQXO$;d0Nr8=o#6-DQ;3(M`tZ+!2)1zfv1`iktK@4MKDn zREQ~}=@2X|*s{b^7JhQ(CY?0AWm5SNmB|&|&3A(pDyv&L+cwUBF(pG~&tctfm=n9K z>f6+OV^%)C`BiP$rU{ohzpAdb z#k{Cy&nc(y8Bnl)9SPIFEzLpHAzlk!<*B4|OVV}{M8W6M?5u*4f|nvu+k`HP>U8R& z=q@Gn(bu?`GZ)iyCwZm}a?8PM2T*lWR9ip7x|M%m-&Uak)sqOA%kCh2S1HCABU`owF2JdF0fw#(kson{64To_ zRM;-`iP=XvW+8?`o_nrmPZ7}7G>m=)tB_Ky*LY`Snx*uCk<)S>%Ayzj>Q zd|QDRLD}VhvDUNN#muWYotc46k-r`uWa#0#N0@TOvsJ>q)V#9K+PtARFRw>9MP0WR zN7nP&!^?Owf+hRmfy^5DLx8N*)Mbtk22Qo>5MWb6+{?gMM=d@rKu-Y;i%=ZNZK}nE zlN2n7<|x5|{=ixv@~E|Yv?pG*^1Gx6KQgjoh(f%7@LmJf9eEhMHnO)ll^paaUn(*p zu+BM>ZHs~h)-P?FnXR5yxl!_7LI=slZr3tKdF-LHot;e+G3HSl!;9qsq2x4=bwe*s z1LqlxS5)NB9D6lG;i64~Nv9BF$=6LT$7`TY%IrI^t*7g~_3jqBjN3jw@AkG3Y1#H| zbL$0vf<@X3?W{lVVze)7TihHWE^PS(0L;YtaeMv6`X+`Hu@^e=*vL-kb`SC9FYO;U zUu!37m5qYXUQpC9jE;&3;{}L@esl0DGYUzg z4^BLYaX|&X@(W0a2$5Ld1w~fAAQ;761oM%9CZfT0U%2@aYuwp8dTZnP3!`PXi=%AS zLksFx8OC?Zq>Bfvq!ROFbycIEr0G?RAWjYojjrLq>&_kq-#I%x_;9?7CdiopB6psl zbc@LuI&6cyi^l@PKW~e@(T8lpdp|`umAm$zJzUtK%oL^JB+a7C(-G>7t0Ch%k-$BF z)}iuTMkBfB_*rLe-kw1%{lvXTvV3R%;P~yEZ_joQ4-XE{_Kv=A*mZv4&k#bw2za+J zv5EmIiD>ak9SVhGf>j&JFh+)ZQXp<`g8orOs&X4vOOXM^a!u4c`={GHANcLHXCAuq zn86X-t#8vAxBo$b62b=LJWq#LQPCEE_+h*DIkUCCX*w1k{Y~Q4UM-1NdsK?K{!uRX z3)UOqEn*2#E5DQi)mkEmMim$8#{_%L$xi$473X!&nCMr8!~T)}J)nP&O`q_CM@M)) z`i~~U>ye1?icQ@KPHxyOD2k`-9g3gx_J?=+P)rt3Dzr})X*lR`A^6wRELMiNA=PsXY9U6{b=qrRE44C(geeY{=WU)9NChX?wjC$3mU+$?ESg9 z0ZQT5fufB47`%pM{>OGcJI{w%d>*;AzW&ir2pZSz{me5v)W zmhUVHHVZg`lHgGiZf!V72+;KB_SF}jtB8KW;SJyOz#$n%F`-TB6n8x%ie-!G^jc>G z9JvEM1k>Tk#2wz|i?la!Na0r;{_G$a%e4K(^Xt|3Uep;mYfEZ>6sF@G5eqN~MPk?Z zIk2m?bI(jngI^=ky##Vf5Mh)rh#vXUa(rRFVpVk<*kuAw5CR5hS5O&f$5m`CrDWYF zG?Iz5pYHVQ08>)E`dsN|gL@e^)EB?H!aFxQ$=23RK`VWXcEelS7{>lL)^4Ks(K<^M z{&Q7YqF-BS5luCJwiV*dZ>_RN>AwiKm?*FN#USZ5$C!v8fi|BjsSFOgH=tqX(wQc* z#OP>rZvY`Rw1v`%W-g48^k3p>@!S~FPps*O@Gwy6&=JB}i{^b^&I)`k#ifI;6qC&e zPbcuKO9}bhiN-+$AC#1XrjF=MDb`(dN$(J+A&znrMlG6umZE?mgp+{TSqYM+AUO5V z-iaC8Mu$pQ_%P;hpO=ZUz|jjr+jjVAn&Z?5*{5JRhTD5C8x1c zstZC##b^o_1O5l^TJoqkj<2J1R+y?1Z!oB9Mt%&^dS8uvQjPb?(cMb0ZV?6pnwN^Y zGyCLhh^#e#n^rE_NUW-lq8HeTynXA7kP^1<{4%I8X5aVgbA^L(!Hftq_I^ zuYm-3q&iELb6z`Uo=k?dte_)L^8@4OukWuF>NpJyE z=$fk4eR;4IV{c}-mdRLx0w#+aYs5MnJz_U$lmv0;g{W54r7ZT$goFhpfG^ElxGzBL zImZ{djBxjI*g_h%(3{J42fEz@;x%8j%=kpuE#=-edGttj4>FmhL+SnbY&?##O2K+m zIgk=RohyWbf0L*^97fC&6|Yos3#xSSn%z#S#ho&knssf9m`>NFhMONJL|4t2E&|D= zI`#NVH4TJcVwrvUD?vT^C4tzV@DhiS(LNY%llK`CMx#a9LX>Z7V<`x=PEVR}X4;pcIMLFidcTKgVB_3GOQM@^ar0GjCy`ig#gGqFsZH zLQ4&We?3=FEE;PI3u{zIdHcL#I3_DdLB26Z9`GYZqw1S3(@YMSZ+bt^d8G2_%9Yb! z6G|K*>`>iLDff5K+{7neMV=PTPJ8I?~h?e@!i|GEcNb*Ti-4TnHrBa&@-6mB3o1 zfZnoXEpZc)DVRvK)ohbYhay4sfH4shB_>=_1390>6VT!R`S%SWa^p$4V6DGtCuPxS zHjI9&P7=DY=pRdP%g?(kM$D_Q1gyZ)AT;N3^64GRr{79UV4=9TUI`x__}yt5Afi}d ze}o^dbaXCoLsJaO_UZkM;|V(CijF)WGMI5qx!mWyk1?9@xaqEyBXk5T94KhSSf^-i zXBjwt>seZjR2U4x_N@b1utc49h@jpgk~f06>Hg17<<++sH*+ZBdWZ`cyiPSN6k^OZ zjSi|Cyls({kBcS!8=8=-uG%aWMlX4Of9l5K#_S|qTf;z^bETmniDt-fe%MkL;j&oD zhr(nM7JI*)q%BXB2HsatxF@G<+0sG>hBe6>>Vs0z$su2$gb1(vs|f3&iSQ8x~4VabUySGCydueeD7dYsqBdxE}PJQ9A^ z(WgTqV*8MXj-%q_p0_HS>$e@nFQvD9$c%?9nk0(2U8SmDr)mL*nCF_vjp^97s{E4W z#L$%%^R)NLox?#tlM3jk=p2l+^oY(8yG6QabGqw&46z!R3f4+FVnW!i| z*g5r(;AWDgZKiS777_>t!!E=FSSY_a#{BZ$f3^_N)tLo9lMd?;qF2aIdC!b8;Lq*o2iJLncqE_(7?}O3 zEQmQR_1p2vp#B4=g?~n^Cl$1RfjI0uLCk*#&EXI+eLvMj<*hISb#pRFZz2EfWt6mL zXyt;+h``I_ClH{9W_Rt@K{B~hUrL$VP={*D26U$la8vaGF0wpkePk?&CFJ1jSVRDF@P46dqD zG)S;9Ypa2L4OTJBe6Pzaq2#KPjI@VF+7foIE%6UVVujIbOZdh}Xkho+l74F>RWQJ| zgfESRCOEGx?FS>R#`d+P{L)CNVS#Nae{ZDJh{Cp%KS`K;zVG|N#c*=aC%~K0Ng-$7 zKH1l}Lai0|e_@#Zkf(oNNiX$ZmF^`b5c>s~`mkd}l-;}`>@kvs(bg#rja-{D3jC<+ zh*g9Z7#CT37`QNn?&sb~Vv3KxkuTKwKG?~wlf{LEA+vsR6X-T1iX|%l+~}Aum-Wc+ zs|j%08~2R0MgPh1C7#a{R;E;uTh0?@!!Ek%2`vBPe-@51d{oPS!b1!^K`3!YEM)m? zimDJS$mAHMB0x%iC@iN^Uyi0q=}nY@8XS>E+nU7JaY3t3X(*vM5j5WD$zp-B>z6#+79}{Cx%N0=Qh=M|nDo9-k;I)zV1*ww39M{mIzjk9t>^bt1 zMjA?RzZ>{mc>QoKfIKow%9G1Rmc#!ho|Q5l#o!ww5EjM$jZQ1M(b7qtW@4>>)LMO& zY)VU{_?46%9X-w2eU665B5;^*vB>>e@#xohy-r?|U)zkVsxin|^rL6Exko=%JOQ{S ze^HpT#Th(RuWS0U<@%=Qp<&9br6kK@@w%$kCWjM{re?&Zq zAPKC-w~&hL!fER#P`q0Ni{f1TIB{AR!v3>oP*mRLF@^IY99|hQhM)2^;@L3G zqV{ERJ^3BvE?Jk`Dc{(ZTe`}D2Z?2ia%bD^uc-d>YPwg`Dap#RQ<5hIs$7SHvGwf1h)eg0B9WH1Zgcu@+79`#Hq(xEWqj~tnWhf$zYcTSCJ)=R?bSEvBQ z{|c9H7A`1-trYUlqj)|Q8M^T^$Xay%R65hxG?v4j;b2-He3&f!EC!8(f6T+VJv?C& zLoQFt4DLiFdU+|Eo$`vdWtBDmLIsVD?9Hy#-NqbVW46ihG~6cSsa={xO}*Vz+-A=z z;eh!XhrfZN3f>sb1KhGoQrbY%;(|8U%WUw1u0C+e_st+=9g4?kkls%|%W94Xurb1; ztObYCu{dSps&B0MkuoVpe+?fVoa-6i6Ut}PGLGKKbvT|ktOvZZhlk?f0aU=4F81^h zT)*jLd{+i&tZ_GITZ9OZ(~h+ynXFYZ2SnJA&^+EX_O%}kU7pzj*0l{dA> zSK8DJcT#e@m{?Uxg>yuQI8feCc$KLNmFlIL$g3QdZfi2{)i&SPe`Vx&WWx02iH`s( z@`1jop~iz?-?(kSXGXmVZ|Uz`oBx$k;W(Dx1X4ZI#)-U$0W2=!r}yx{&yan`DK(04 zW{kVM6M6RSljes2q)?Y z#Q-RXO|7E0ZA9AEf2}M^F?WhVK?qdG?R0Fqrx)gGX1l+`YKoBZ8o9EHBQJ#l(2|c^ zcE+VGp3Y40NcYkdpI?ZR@esP@8P=2;#(KF55|+))3(W71J>+8?Q_LrK-A%SK3Pq=e??;3fEvM(lqx4`p|F`o zHT=kxb5xO#;U@mY^M4|`Fv22>HyK$+6li#3jcJaaO|k4Ba+DP9^h4WQGxoXZ?^P*= zCH8{M_q>VG#|R?^G(?NiIrm&LLBpxXKr|H*clFn$v8ZOEX7E~(2vd^+?HaxE61=*K z>t_2lM6XihfBD#T5ZD8+ACW~-W#MJFFa#Dzk?cB7LehtjOA@vNIiH;3#Hy@ASBd zn1Q4yEXLSMw=bQ~%6%6V2eksrohK{grs+I#FDvX|Q#@81EAob~J@lI|X2Crz$0w&u zs?7e0W9WI#DYK|@kR~awjD})DA#UU$JfVdex5lTe9>fz$yHOD_9q#Yxn94}GL|ud= zQ8#WfC3q0RI`ALFe4jZG`f@3yijAh8l8uO~&t;8U0D#@G$a^{uUw;rLisjNrL+=lq2zo4C# z2&a$eZcZ%buZKN-RCdMOmRP8d8t%q`sE?{%HQy2@^)W*&qZsCDMyPn9n0?_Cu!K@= ze~^8aXrwyjBgWPqS@n~OsE1p%(pu+-W$6_IG{s$Q$R{bAvtn3{pKaQV7ZdZs`)7vo zEN04;fW<)-1_kyDClR{4Jnb^V;hf2|5<8Pk&Py z=Ih{mbrp%q)jiSJJtwVb7~p85`oGJBRAf+!QIKtY5(Hn_B5EmKl52|F_%T1K`FaI zr@#A|qI_3oJPPuY-BS$re#L)vVf&824o`zK^4JzWhgSo4CY=e6(3#*2O7LeVe@Cam z)qI`?8Q<-b-2f5_y`HrrSgUgu(?=Q^7_zL5Aq41VjQ8AR5D!v6JV{S|={hWs6rjfj zlat}8f2qzn;PuSI?NOY}qW)xcb-I8F%sk;p+5yF}{oxA_-;`i; zB}UE8(Q#`(9Yy63uSJS6US}f4e=F^Cj)MXeVm9N)jpwxOI;L9OkHN9n^s^ZovI!fr zk>}x_dFXKud87p|vOjXNfRDzUHTtr7b7hL7ro%@VnVh%yWXL|Ag35i)+ohZJ(F3OX zD43~MLsUDd_Spt$pa$l{}Qh8Q(JW;D2d%5Z4?kkP>UIm4m!f3rD^x?7!t z(7S_~&(S#ocN!!oyq^#lL$zl*n4)T>rzwo+h!ZqHpHul)1ahNlfcfjIEMxcje!ZO1O9j@w<+uCG+M_#b+^$~Lh1ayNjWUS9`6_P~= zxg(J~(z!#g0SH5#0f&8ge-q6~wxmsE+SEk9C`Ki`6vKQ-?95k^hldq;!20k|+@Qrp zbQ5KF7V|D6KOLCIW4U`EN5j6w@K0C}(XbDILC79dzBCPqYBvOGxMQZKrQ8&>vS=J- z=*&m=lYca9Q%qa!mLj)ofNT`K(SZ-KB2#1}qagBx)5Q%|Vn3d9e;HF;+Rt!`b7HZT zF8U=+7pLi%79ZlJ;!WI@!!CvpGk~}}{z?0cqd>XW8;HtHaMm70KiCdFfZvt%;1f>M)Kj-N!zqM|o?GSorb)Jq{S zQ)!Ne;v%YMa?C$?Oreu4qtifCP*a_5n$CI2Ej~+2t@(6ve-Mqa5YR5FYCmFHoY6$5 zD4{!ZEZa*q+)9gydvTuz;Sl&kMmWj4359{(MBeV%R z4{B+HwXA&|<#~7!m2OBjq%T^`k*oQ6TE=2!P6cx5*47%B8QRgv_1d#6R%f>l4_0e` ze$#?e_}K;Ce<1!iK^M2;tO)?VXM&>_yIQWGQNwvUx-(|~QTZ#7b{K`D$UuuLuFNEE zan^plZ{RA1`CT%6EAjtK(e$xgGz?u7UK`(!%ijrQ@Fn5+7>V}EiKg+?yfr#dvI{Rb z?IbC9PPYguuE2)RJbBt7E7}WbbbB==>5i79acuOR>M3o7xrX;4l91FZp36gvFtM42Y#kM zSIOWne?p;?Lk*{R5m7=T5F?2f0oUnGglB_M62bmu6c(YwQqS^90RQCHZy26wX8VGt zF8O7{M{@ndZ{*pUl&Z%^vr2ka)!5_B3uB7B z(^CW{MbcPvF{t9_HH{(6YK-4x;~9b3R>s{!e}^vHTmgYEz-#87rPax&w%P#KDP9>y z4-E~*hK3_se;1!xvni6^8tNfkRPJ&MZqycWC4kgdhHEznRB@qsWWIgn7J6lHJ)YuY zH3b*&AP#CtkIkg-AgLkiWtu%R%?^@LIxqN;TqWsk(n6xS86|2sz#P*w$s^e-?bdrx ze?(v(PpHLVi<2ZaGCZk_p-{XIs{RFhM<9SJW&PYmBXEug%x#xXG`#TS#RUjm%ELsw zN=FeQhR_2~f>ELh27C5mzhSX{fuUTA_x8)R?#B8Sd_4@7g!nO5`yWaWDzN5o*YfJQ zU%yyeLzf@Z#l2A4fAOXiLV+G77m=YOfBI7^ELwta_`IvkrTM0+cnND>w;z9&^w-v6 zuLw|)$}SKnDmsaEI4z<1eOnt1^TLwnL{oZ#Z$$0qWwU`s#nZF+)?>n619du30Tm`+ zob#Ry(C(kV!nlpE(rFy!a&KsZ#3O<#>YYY9psQqbz=kg9p{;wh_hUlnji2_+fBuhU z|Cjs_4EM%?TX8?At-CSbmL1BNPqFevi-c)?0e|tRS6Z(pMz4s#2MIHreQ_}2Qp>)RmyU@SaIYfifB8)j_sB82T2VL}VS~`YqYS7}m=2Y&{m#f5AeI3^r>RcfA}OkWqAyqVol^OKx)kNB65lC@@^|e? zcpgo-M|rE`t0QUo+`KH~bIIj)j!E&9 zu6QvPnRAYw4qaxh*F*e~5&PYeTuW0%OU3Scv-Ff}n&tcZl zs&OW-=*|N7AUGLG3CV7usp8>wO_u7hQ}lU&PB=iQyWQYSl}lZP|G@yeSAlyJ6koe% zs<>SbWPh0v@S1`g4X@fo`YyeVvRBY2ph>DCab*(GN1pwVe-nmfMLkq5#4U>T))Ezv zuIRU_`SH|8C|U>tf-B?4K+*^l;*ryFP@sF@M+F%}%kHtKK>r>dlz2Yo=c}fG$5*s3 z%jJ_HdyfYoqK{!k-@}{7(iaf3du8yQgck73>JVb6?@= zQJNmosuw11SIp9N69oh7NgL`(A0AR!4?{TEc?ppOezo{wh}zjYW5qxc3UF|V$La?OMm5>WuX{*oH? z{f>1TfA?OU**RD6aTrXcc2dHRR%Kd(VDq-{pX-^6}W<%6&;63DJbIz#<$TU1@r`mQhe{UE=6Ff^C*JR zey9#(pC*9sGi%*YFaKER}uOd_V?rOq*Yk3Z6R0gs~$Ini|r^R)0}CA^4Q?j*j7 z$TKkapFO+1y=^Dc>mPFR2^49b4YM@QpK`)QNl6i7^dwZ91r3a7;=!>ncvg|}2w7i3 z(eL8qs%6F*YQ3T0&mqnRAKIJv#~;wFgw&UVqUux56EB9^%==JvYxN*5y@yFL)07%XMK%Zsk+%Y^&|IEe^E5f5*t# zF)oO2zo$+-4kg13GS^-JwTAUee&xJd6~|Xx1e!8C?RH!8zcC6m1_MGWfBpovelGa{ zu(DMX^}3BkvE@Vt!}?+7I~b)4-X^>!GT9Rrp9C8bG{i}bnk=x-ttG=R`$;)F>$O_l z_}h7bs9vK6DH|wyXJ#{ap7--wIM?nGi$t2?H?b(0=}=)eE-2$jwa+K%@Cr>zif;Se zcOGdQQOWp@)?8(k=#k zWz}V)#NupNqKG@e6eLBJBVo?vISQWDmRM&p)t|AZvFf$6jJ&ex9w`7#>%E&G&hntL z0w(1Z(AWH(7+me~f3eCe8iHqc5n4w0C+RAvC<%Rq`jx9InhMo@P3}d%`Y5XmKYyrg zj!VnvtgPcjZNAPDa``FDg`4|hNF0Iz_THKx>Y+to&597oVM?Vg!uh!3FMlWP0Cue{gi{%;zi#8yttwmPYe=QVW-Uatf2E*U##P zT_SQ!f7!50g(AC0=RJ-&ZgZ%y-sP6=Yh$wD6^|ktN~bVFIILKts)~vezk?*#4=ilg2UhrYra|?>0m;xe(QIa%_T&h* z$`QHPB)Bibfu&4?ip|u;YS^wZ8X7ViH3Znwf40edD=e1vr`-Qb>*OQo`P%B=N#LIqj9g8MX!FXAMe zP`An$%2e%K`cpl!b(z^5 zXMo|5jOnI)zVg;}ICW}K`0lj_lpoILe?Wi%b0^Rejj1M0`;016UZpYN9X(S^mFBie zbE?$D(@XgZG(!t5kQN{(jHT>%j@PH}0>);zx;kWIvG$xDdl(1Xfd(M* z1riB=i9{V@VAsS`tP=w-tEA|>e-%QUQ?IX3+=@RF6yBKkZA1f(FQRFodzWCrOgF`JM32ZJ0$#l#7Wt z3~C>s--mqXa0x$dfB*zm9ply4iNo)k1>Xn&-KQKnDHwa~azQFl5+uCje+)it(k);$ zPPOuKF8Vwrbwl-M+T5S1wLhnGb{&v57lKa85JiGr1lLlbVmI&`*LCcz8!Z4=S63#y zukMt)xVgR=NzpI~X1UN#?k)G!+~Niy5b8IxsLv+?fBMeqU5?gScjP(_-E7obwih%B zg5{ZR?eg?ZTs2CSj*_hQ{GCTd>PV_lR8u zpANiT!s|8E!a4hhrlbfgEZ4rKqK;%yRjA)1U7W{`19Gt+A;1AT){9bc@MkdO;<*IG zOxv3)-d4Vu2po_outh$8c=+huUntF2{X$^$7*mUx*EcgCO`~DZZFg90kK~gnDdeZp zC|$49>vf*@HtLCC&dA2K`Bp60qaX{|6VS=BYdH5D1fU(o?8aj|!9km^lI$J))ay6) z_&XJPWdgtZaj?Vje^^jExS&aiD#8tO-U~}v!+2BYe5He%RF+p|;f1n%`Jzhl9>~6^ zT8eQ~NG)FPr8yR8gd#2v$YRqV&tepx$rFjpW)xpJ$B0)~pZ$~KobNg7Jd4`};%Ua- zp!kaX9R1=iXxqjfX;fD--bDY6@SH-Kh)ANzWh2tApA*%Jxj?AU zIB(xZ;T0~df70#jI=qTrhea3^^GTXs&8BX<-L7DC@(^4MV?*YZ zmK)Sm%J&B9B|?JB-?Ykq13&h{DNHPU&Fhwl_!A|=BrM_GZA4*6&?q*K2%MPGC1-Al zfHEVHZKbhOAj}5g|D&R?^N^AvV4|<$W(Sb~xuVYsK0;WkuXIHJz^+%9e`IwJ8*u-w zEAN+jf5`a}-@jZn_-Kl0K_0!>I|wR~-wm+K(L4V~zX@BjU(l z7^7LlH$n=k^KC30h} zQv_S`0|st##N&^}`jRPep^s&7 z)#D__)z%_NyaFV$*!H3F3gR|7Ht|A{f_r{6np~k0!^QJ;HV~55$0g5LOZF-b`&HH3 z40ee)`v_V|2VD$-ETxMG<3ZL|x@;KSqt0!!QM03bnD;g=P7?EMc2b7^ADG$Jf7WuL zxmxfz(3hw`42FCIkdQM~)ve^|)0~kqLYh*BB724?nX*wBK%fLXf^HC)DY7c*;p%Fr zi<+0v_H`q1z;&WIKcVEXBBPCusJQ&0lg>QT3Qdl$LjBSQ+N%oCl=f`9$F(CyTh5#uU z6dXi>9?<;=cefaYh~rn$V` zaHPVnlVH~IH!`XCqlvl^f8j{>$hc(4uiiu3Ouxe9uzKYTWMb5zO#Y~Z!cpi7h|er< zRzA}Zj&4j>h8}FV3Qr>QlByqbNZYu?H3#o^4|k5=9_+&k|K{!fTl}e1MFneU;u+E^ z9NSs1UPk7Y^3JW1Q}}I&^D3Q9Ms(;k#z@lY1P}2FWp6s%?+~xoe>>>mVF{o1wA)WP zxJ2rezJvC*wC$^_Yik@D7B(Ho=XAAE=CN}8BE;{S)S}vN}^}Nn--Q!sgErj(|{0x&8X6>g6%A`2$reQ}U-fH(af1B=K17Oha|J zq1UGcMRC0`snr2?f7O*`LAj4;a=%)4WqvKBU_l?KNc0n$%r(UdmtMvl*eZEq%?7|x z6A}-V>@ej9ksp5EAtv~R@*UTj)zwOz+;VKnft!{4K=9`Xdr@_zfWzy|O;3jVY8z=( zXmeEOzByOL$2v3~zvQud8)RXU$5;ulSpM+fR$pmoUh_Ibf6lEDu8q~fw%ImJq49<1 zRSLHztx`bDBVw*2xX-yHsef6H=4oBmmdTXC)JW#pd Date: Mon, 4 Dec 2023 11:27:30 -0800 Subject: [PATCH 006/234] Revert "make streams lookup modular (#11368)" (#11482) This reverts commit fa0f16ad0acf417db1186728560278a049357914. --- core/scripts/chaincli/handler/debug.go | 150 ++--- .../handler/mercury_lookup_handler.go | 534 ++++++++++++++++++ core/scripts/go.mod | 3 +- core/scripts/go.sum | 2 + .../v21/mercury/streams/streams.go | 143 +++-- .../v21/mercury/streams/streams_test.go | 20 +- 6 files changed, 666 insertions(+), 186 deletions(-) create mode 100644 core/scripts/chaincli/handler/mercury_lookup_handler.go diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 0075862d95d..fec8c6cd414 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -22,17 +22,12 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" - "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" @@ -41,7 +36,12 @@ import ( const ( ConditionTrigger uint8 = iota LogTrigger + + blockNumber = "blockNumber" expectedTypeAndVersion = "KeeperRegistry 2.1.0" + feedIdHex = "feedIdHex" + feedIDs = "feedIDs" + timestamp = "timestamp" ) var packer = encoding.NewAbiPacker() @@ -125,8 +125,6 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var checkResult iregistry21.CheckUpkeep var blockNum uint64 var performData []byte - var workID [32]byte - var trigger ocr2keepers.Trigger upkeepNeeded := false // check upkeep if triggerType == ConditionTrigger { @@ -179,8 +177,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } // check that tx for this upkeep / tx was not already performed message(fmt.Sprintf("LogTrigger{blockNum: %d, blockHash: %s, txHash: %s, logIndex: %d}", blockNum, receipt.BlockHash.Hex(), txHash, logIndex)) - trigger = mustAutomationTrigger(txHash, logIndex, blockNum, receipt.BlockHash) - workID = mustUpkeepWorkID(upkeepID, trigger) + workID := mustUpkeepWorkID(upkeepID, blockNum, receipt.BlockHash, txHash, logIndex) message(fmt.Sprintf("workID computed: %s", hex.EncodeToString(workID[:]))) hasKey, err := keeperRegistry21.HasDedupKey(latestCallOpts, workID) if err != nil { @@ -232,82 +229,73 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if checkResult.UpkeepFailureReason != 0 { message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) } - if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { - mc := &models.MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey} - mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) - lggr, _ := logger.NewLogger() - blockSub := &blockSubscriber{k.client} - streams := streams.NewStreamsLookup(packer, mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) + // TODO use the new streams lookup lib + //mc := &models.MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey} + //mercuryConfig := evm.NewMercuryConfig(mc, core.StreamsCompatibleABI) + //lggr, _ := logger.NewLogger() + //blockSub := &blockSubscriber{k.client} + //_ = streams.NewStreamsLookup(packer, mercuryConfig, blockSub, keeperRegistry21, k.rpcClient, lggr) streamsLookupErr, err := packer.DecodeStreamsLookupRequest(checkResult.PerformData) if err == nil { message("upkeep reverted with StreamsLookup") message(fmt.Sprintf("StreamsLookup data: {FeedParamKey: %s, Feeds: %v, TimeParamKey: %s, Time: %d, ExtraData: %s}", streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time.Uint64(), hexutil.Encode(streamsLookupErr.ExtraData))) - - streamsLookup := &mercury.StreamsLookup{ - StreamsLookupError: &mercury.StreamsLookupError{ - FeedParamKey: streamsLookupErr.FeedParamKey, - Feeds: streamsLookupErr.Feeds, - TimeParamKey: streamsLookupErr.TimeParamKey, - Time: streamsLookupErr.Time, - ExtraData: streamsLookupErr.ExtraData, - }, - UpkeepId: upkeepID, - Block: blockNum, - } - - if streamsLookup.IsMercuryV02() { + if streamsLookupErr.FeedParamKey == feedIdHex && streamsLookupErr.TimeParamKey == blockNumber { message("using mercury lookup v0.2") - // check if upkeep is allowed to use mercury v0.2 - _, _, _, allowed, err := streams.AllowedToUseMercury(latestCallOpts, upkeepID) + // handle v0.2 + cfg, err := keeperRegistry21.GetUpkeepPrivilegeConfig(triggerCallOpts, upkeepID) if err != nil { - failUnknown("failed to check if upkeep is allowed to use mercury", err) + failUnknown("failed to get upkeep privilege config ", err) + } + allowed := false + if len(cfg) > 0 { + var privilegeConfig streams.UpkeepPrivilegeConfig + if err := json.Unmarshal(cfg, &privilegeConfig); err != nil { + failUnknown("failed to unmarshal privilege config ", err) + } + allowed = privilegeConfig.MercuryEnabled } if !allowed { resolveIneligible("upkeep reverted with StreamsLookup but is not allowed to access streams") } - } else if streamsLookup.IsMercuryV03() { + } else if streamsLookupErr.FeedParamKey != feedIDs || streamsLookupErr.TimeParamKey != timestamp { // handle v0.3 - message("using mercury lookup v0.3") - } else { resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") + } else { + message("using mercury lookup v0.3") } + streamsLookup := &StreamsLookup{streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time, streamsLookupErr.ExtraData, upkeepID, blockNum} if k.cfg.MercuryLegacyURL == "" || k.cfg.MercuryURL == "" || k.cfg.MercuryID == "" || k.cfg.MercuryKey == "" { failCheckConfig("Mercury configs not set properly, check your MERCURY_LEGACY_URL, MERCURY_URL, MERCURY_ID and MERCURY_KEY", nil) } - - // do mercury request - automationCheckResult := mustAutomationCheckResult(upkeepID, checkResult, trigger) - values, err := streams.DoMercuryRequest(ctx, streamsLookup, &automationCheckResult) - - if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { + handler := NewMercuryLookupHandler(&MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey}, k.rpcClient) + state, failureReason, values, _, err := handler.doMercuryRequest(ctx, streamsLookup) + if failureReason == UpkeepFailureReasonInvalidRevertDataInput { resolveIneligible("upkeep used invalid revert data") } - if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { + if state == InvalidMercuryRequest { resolveIneligible("the mercury request data is invalid") } if err != nil { - resolveIneligible("failed to DoMercuryRequest") + failCheckConfig("failed to do mercury request ", err) } - - // do checkCallback - err = streams.CheckCallback(ctx, values, streamsLookup, &automationCheckResult) + callbackResult, err := keeperRegistry21.CheckCallback(triggerCallOpts, upkeepID, values, streamsLookup.extraData) if err != nil { failUnknown("failed to execute mercury callback ", err) } - if automationCheckResult.IneligibilityReason != 0 { - message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", automationCheckResult.IneligibilityReason)) + if callbackResult.UpkeepFailureReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) } - upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData - // do tenderly simulations for checkCallback - rawCall, err := core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) + upkeepNeeded, performData = callbackResult.UpkeepNeeded, callbackResult.PerformData + // do tenderly simulations + rawCall, err := core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.extraData) if err != nil { failUnknown("failed to pack raw checkCallback call", err) } addLink("checkCallback simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) - rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.ExtraData) + rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.extraData) if err != nil { failUnknown("failed to pack raw checkCallback (direct) call", err) } @@ -329,23 +317,6 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } -func mustAutomationCheckResult(upkeepID *big.Int, checkResult iregistry21.CheckUpkeep, trigger ocr2keepers.Trigger) ocr2keepers.CheckResult { - upkeepIdentifier := mustUpkeepIdentifier(upkeepID) - checkResult2 := ocr2keepers.CheckResult{ - Eligible: checkResult.UpkeepNeeded, - IneligibilityReason: checkResult.UpkeepFailureReason, - UpkeepID: upkeepIdentifier, - Trigger: trigger, - WorkID: core.UpkeepWorkID(upkeepIdentifier, trigger), - GasAllocated: 0, - PerformData: checkResult.PerformData, - FastGasWei: checkResult.FastGasWei, - LinkNative: checkResult.LinkNative, - } - - return checkResult2 -} - type blockSubscriber struct { ethClient *ethclient.Client } @@ -399,27 +370,9 @@ func packTriggerData(log *types.Log, blockTime uint64) ([]byte, error) { return b, nil } -func mustUpkeepWorkID(upkeepID *big.Int, trigger ocr2keepers.Trigger) [32]byte { - upkeepIdentifier := mustUpkeepIdentifier(upkeepID) - - workID := core.UpkeepWorkID(upkeepIdentifier, trigger) - workIDBytes, err := hex.DecodeString(workID) - if err != nil { - failUnknown("failed to decode workID", err) - } - - var result [32]byte - copy(result[:], workIDBytes[:]) - return result -} - -func mustUpkeepIdentifier(upkeepID *big.Int) ocr2keepers.UpkeepIdentifier { - upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} - upkeepIdentifier.FromBigInt(upkeepID) - return *upkeepIdentifier -} - -func mustAutomationTrigger(txHash [32]byte, logIndex int64, blockNum uint64, blockHash [32]byte) ocr2keepers.Trigger { +func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, txHash [32]byte, logIndex int64) [32]byte { + // TODO - this is a copy of the code in core.UpkeepWorkID + // We should refactor that code to be more easily exported ex not rely on Trigger structs trigger := ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ TxHash: txHash, @@ -428,7 +381,16 @@ func mustAutomationTrigger(txHash [32]byte, logIndex int64, blockNum uint64, blo BlockHash: blockHash, }, } - return trigger + upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} + upkeepIdentifier.FromBigInt(upkeepID) + workID := core.UpkeepWorkID(*upkeepIdentifier, trigger) + workIDBytes, err := hex.DecodeString(workID) + if err != nil { + failUnknown("failed to decode workID", err) + } + var result [32]byte + copy(result[:], workIDBytes[:]) + return result } func message(msg string) { @@ -440,11 +402,11 @@ func warning(msg string) { } func resolveIneligible(msg string) { - exit(fmt.Sprintf("✅ %s: this upkeep is not currently eligible", msg), nil, 0) + exit(fmt.Sprintf("✅ %s: this upkeep is not currently elligible", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently eligible", nil, 0) + exit("❌ this upkeep is currently elligible", nil, 0) } func rerun(msg string, err error) { @@ -545,3 +507,5 @@ func tenderlySimLink(cfg *config.Config, chainID int64, blockNumber uint64, inpu } return common.TenderlySimLink(responseJSON.Simulation.Id) } + +// TODO - link to performUpkeep tx if exists diff --git a/core/scripts/chaincli/handler/mercury_lookup_handler.go b/core/scripts/chaincli/handler/mercury_lookup_handler.go new file mode 100644 index 00000000000..1bd4b2e183c --- /dev/null +++ b/core/scripts/chaincli/handler/mercury_lookup_handler.go @@ -0,0 +1,534 @@ +package handler + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/avast/retry-go" + ethabi "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" +) + +// MercuryLookupHandler is responsible for initiating the calls to the Mercury server +// to determine whether the upkeeps are eligible +type MercuryLookupHandler struct { + credentials *MercuryCredentials + httpClient HttpClient + rpcClient *rpc.Client +} + +func NewMercuryLookupHandler( + credentials *MercuryCredentials, + rpcClient *rpc.Client, +) *MercuryLookupHandler { + return &MercuryLookupHandler{ + credentials: credentials, + httpClient: http.DefaultClient, + rpcClient: rpcClient, + } +} + +type MercuryVersion string + +type StreamsLookup struct { + feedParamKey string + feeds []string + timeParamKey string + time *big.Int + extraData []byte + upkeepId *big.Int + block uint64 +} + +//go:generate mockery --quiet --name HttpClient --output ./mocks/ --case=underscore +type HttpClient interface { + Do(req *http.Request) (*http.Response, error) +} + +type MercuryCredentials struct { + LegacyURL string + URL string + ClientID string + ClientKey string +} + +func (mc *MercuryCredentials) Validate() bool { + return mc.URL != "" && mc.ClientID != "" && mc.ClientKey != "" +} + +type MercuryData struct { + Index int + Error error + Retryable bool + Bytes [][]byte + State PipelineExecutionState +} + +// MercuryV02Response represents a JSON structure used by Mercury v0.2 +type MercuryV02Response struct { + ChainlinkBlob string `json:"chainlinkBlob"` +} + +// MercuryV03Response represents a JSON structure used by Mercury v0.3 +type MercuryV03Response struct { + Reports []MercuryV03Report `json:"reports"` +} + +type MercuryV03Report struct { + FeedID string `json:"feedID"` // feed id in hex encoded + ValidFromTimestamp uint32 `json:"validFromTimestamp"` + ObservationsTimestamp uint32 `json:"observationsTimestamp"` + FullReport string `json:"fullReport"` // the actual hex encoded mercury report of this feed, can be sent to verifier +} + +const ( + // DefaultAllowListExpiration decides how long an upkeep's allow list info will be valid for. + DefaultAllowListExpiration = 20 * time.Minute + // CleanupInterval decides when the expired items in cache will be deleted. + CleanupInterval = 25 * time.Minute +) + +const ( + ApplicationJson = "application/json" + BlockNumber = "blockNumber" // valid for v0.2 + FeedIDs = "feedIDs" // valid for v0.3 + FeedIdHex = "feedIdHex" // valid for v0.2 + HeaderAuthorization = "Authorization" + HeaderContentType = "Content-Type" + HeaderTimestamp = "X-Authorization-Timestamp" + HeaderSignature = "X-Authorization-Signature-SHA256" + HeaderUpkeepId = "X-Authorization-Upkeep-Id" + MercuryPathV2 = "/client?" // only used to access mercury v0.2 server + MercuryBatchPathV3 = "/api/v1/reports/bulk?" // only used to access mercury v0.3 server + RetryDelay = 500 * time.Millisecond + Timestamp = "timestamp" // valid for v0.3 + TotalAttempt = 3 + UserId = "userId" +) + +type UpkeepFailureReason uint8 +type PipelineExecutionState uint8 + +const ( + // upkeep failure onchain reasons + UpkeepFailureReasonNone UpkeepFailureReason = 0 + UpkeepFailureReasonUpkeepCancelled UpkeepFailureReason = 1 + UpkeepFailureReasonUpkeepPaused UpkeepFailureReason = 2 + UpkeepFailureReasonTargetCheckReverted UpkeepFailureReason = 3 + UpkeepFailureReasonUpkeepNotNeeded UpkeepFailureReason = 4 + UpkeepFailureReasonPerformDataExceedsLimit UpkeepFailureReason = 5 + UpkeepFailureReasonInsufficientBalance UpkeepFailureReason = 6 + UpkeepFailureReasonMercuryCallbackReverted UpkeepFailureReason = 7 + UpkeepFailureReasonRevertDataExceedsLimit UpkeepFailureReason = 8 + UpkeepFailureReasonRegistryPaused UpkeepFailureReason = 9 + // leaving a gap here for more onchain failure reasons in the future + // upkeep failure offchain reasons + UpkeepFailureReasonMercuryAccessNotAllowed UpkeepFailureReason = 32 + UpkeepFailureReasonTxHashNoLongerExists UpkeepFailureReason = 33 + UpkeepFailureReasonInvalidRevertDataInput UpkeepFailureReason = 34 + UpkeepFailureReasonSimulationFailed UpkeepFailureReason = 35 + UpkeepFailureReasonTxHashReorged UpkeepFailureReason = 36 + + // pipeline execution error + NoPipelineError PipelineExecutionState = 0 + CheckBlockTooOld PipelineExecutionState = 1 + CheckBlockInvalid PipelineExecutionState = 2 + RpcFlakyFailure PipelineExecutionState = 3 + MercuryFlakyFailure PipelineExecutionState = 4 + PackUnpackDecodeFailed PipelineExecutionState = 5 + MercuryUnmarshalError PipelineExecutionState = 6 + InvalidMercuryRequest PipelineExecutionState = 7 + InvalidMercuryResponse PipelineExecutionState = 8 // this will only happen if Mercury server sends bad responses + UpkeepNotAuthorized PipelineExecutionState = 9 +) + +// UpkeepPrivilegeConfig represents the administrative offchain config for each upkeep. It can be set by s_upkeepPrivilegeManager +// role on the registry. Upkeeps allowed to use Mercury server will have this set to true. +type UpkeepPrivilegeConfig struct { + MercuryEnabled bool `json:"mercuryEnabled"` +} + +// generateHMAC calculates a user HMAC for Mercury server authentication. +func (mlh *MercuryLookupHandler) generateHMAC(method string, path string, body []byte, clientId string, secret string, ts int64) string { + bodyHash := sha256.New() + bodyHash.Write(body) + hashString := fmt.Sprintf("%s %s %s %s %d", + method, + path, + hex.EncodeToString(bodyHash.Sum(nil)), + clientId, + ts) + signedMessage := hmac.New(sha256.New, []byte(secret)) + signedMessage.Write([]byte(hashString)) + userHmac := hex.EncodeToString(signedMessage.Sum(nil)) + return userHmac +} + +// singleFeedRequest sends a v0.2 Mercury request for a single feed report. +func (mlh *MercuryLookupHandler) singleFeedRequest(ctx context.Context, ch chan<- MercuryData, index int, ml *StreamsLookup) { + q := url.Values{ + ml.feedParamKey: {ml.feeds[index]}, + ml.timeParamKey: {ml.time.String()}, + } + mercuryURL := mlh.credentials.LegacyURL + reqUrl := fmt.Sprintf("%s%s%s", mercuryURL, MercuryPathV2, q.Encode()) + // mlh.logger.Debugf("request URL for upkeep %s feed %s: %s", ml.upkeepId.String(), ml.feeds[index], reqUrl) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) + if err != nil { + ch <- MercuryData{Index: index, Error: err, Retryable: false, State: InvalidMercuryRequest} + return + } + + ts := time.Now().UTC().UnixMilli() + signature := mlh.generateHMAC(http.MethodGet, MercuryPathV2+q.Encode(), []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) + req.Header.Set(HeaderContentType, ApplicationJson) + req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) + req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) + req.Header.Set(HeaderSignature, signature) + + // in the case of multiple retries here, use the last attempt's data + state := NoPipelineError + retryable := false + sent := false + retryErr := retry.Do( + func() error { + retryable = false + resp, err1 := mlh.httpClient.Do(req) + if err1 != nil { + // mlh.logger.Errorw("StreamsLookup GET request failed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) + retryable = true + state = MercuryFlakyFailure + return err1 + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + // mlh.logger.Errorf("Encountered error when closing the body of the response in single feed: %s", err) + } + }(resp.Body) + + body, err1 := io.ReadAll(resp.Body) + if err1 != nil { + retryable = false + state = InvalidMercuryResponse + return err1 + } + + if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError { + // mlh.logger.Errorw("StreamsLookup received retryable status code", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "statusCode", resp.StatusCode, "feed", ml.feeds[index]) + retryable = true + state = MercuryFlakyFailure + return errors.New(strconv.FormatInt(int64(resp.StatusCode), 10)) + } else if resp.StatusCode != http.StatusOK { + retryable = false + state = InvalidMercuryRequest + return fmt.Errorf("StreamsLookup upkeep %s block %s received status code %d for feed %s", ml.upkeepId.String(), ml.time.String(), resp.StatusCode, ml.feeds[index]) + } + + // mlh.logger.Debugf("at block %s upkeep %s received status code %d from mercury v0.2 with BODY=%s", ml.time.String(), ml.upkeepId.String(), resp.StatusCode, hexutil.Encode(body)) + + var m MercuryV02Response + err1 = json.Unmarshal(body, &m) + if err1 != nil { + // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) + retryable = false + state = MercuryUnmarshalError + return err1 + } + blobBytes, err1 := hexutil.Decode(m.ChainlinkBlob) + if err1 != nil { + // mlh.logger.Errorw("StreamsLookup failed to decode chainlinkBlob for feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "blob", m.ChainlinkBlob, "feed", ml.feeds[index], "error", err1) + retryable = false + state = InvalidMercuryResponse + return err1 + } + ch <- MercuryData{ + Index: index, + Bytes: [][]byte{blobBytes}, + Retryable: false, + State: NoPipelineError, + } + sent = true + return nil + }, + // only retry when the error is 404 Not Found or 500 Internal Server Error + retry.RetryIf(func(err error) bool { + return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) + }), + retry.Context(ctx), + retry.Delay(RetryDelay), + retry.Attempts(TotalAttempt)) + + if !sent { + md := MercuryData{ + Index: index, + Bytes: [][]byte{}, + Retryable: retryable, + Error: fmt.Errorf("failed to request feed for %s: %w", ml.feeds[index], retryErr), + State: state, + } + ch <- md + } +} + +// multiFeedsRequest sends a Mercury v0.3 request for a multi-feed report +func (mlh *MercuryLookupHandler) multiFeedsRequest(ctx context.Context, ch chan<- MercuryData, ml *StreamsLookup) { + params := fmt.Sprintf("%s=%s&%s=%s", FeedIDs, strings.Join(ml.feeds, ","), Timestamp, ml.time.String()) + reqUrl := fmt.Sprintf("%s%s%s", mlh.credentials.URL, MercuryBatchPathV3, params) + // mlh.logger.Debugf("request URL for upkeep %s userId %s: %s", ml.upkeepId.String(), mlh.credentials.ClientID, reqUrl) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) + if err != nil { + ch <- MercuryData{Index: 0, Error: err, Retryable: false, State: InvalidMercuryRequest} + return + } + + ts := time.Now().UTC().UnixMilli() + signature := mlh.generateHMAC(http.MethodGet, MercuryBatchPathV3+params, []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) + req.Header.Set(HeaderContentType, ApplicationJson) + // username here is often referred to as user id + req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) + req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) + req.Header.Set(HeaderSignature, signature) + // mercury will inspect authorization headers above to make sure this user (in automation's context, this node) is eligible to access mercury + // and if it has an automation role. it will then look at this upkeep id to check if it has access to all the requested feeds. + req.Header.Set(HeaderUpkeepId, ml.upkeepId.String()) + + // in the case of multiple retries here, use the last attempt's data + state := NoPipelineError + retryable := false + sent := false + retryErr := retry.Do( + func() error { + retryable = false + resp, err1 := mlh.httpClient.Do(req) + if err1 != nil { + // mlh.logger.Errorw("StreamsLookup GET request fails for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) + retryable = true + state = MercuryFlakyFailure + return err1 + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + // mlh.logger.Errorf("Encountered error when closing the body of the response in the multi feed: %s", err) + } + }(resp.Body) + body, err1 := io.ReadAll(resp.Body) + if err1 != nil { + retryable = false + state = InvalidMercuryResponse + return err1 + } + + // mlh.logger.Infof("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) + if resp.StatusCode == http.StatusUnauthorized { + retryable = false + state = UpkeepNotAuthorized + return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by unauthorized upkeep", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) + } else if resp.StatusCode == http.StatusBadRequest { + retryable = false + state = InvalidMercuryRequest + return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by invalid format of timestamp", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) + } else if resp.StatusCode == http.StatusInternalServerError { + retryable = true + state = MercuryFlakyFailure + return fmt.Errorf("%d", http.StatusInternalServerError) + } else if resp.StatusCode == 420 { + // in 0.3, this will happen when missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds + retryable = false + state = InvalidMercuryRequest + return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) + } else if resp.StatusCode != http.StatusOK { + retryable = false + state = InvalidMercuryRequest + return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) + } + + var response MercuryV03Response + err1 = json.Unmarshal(body, &response) + if err1 != nil { + // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) + retryable = false + state = MercuryUnmarshalError + return err1 + } + // in v0.3, if some feeds are not available, the server will only return available feeds, but we need to make sure ALL feeds are retrieved before calling user contract + // hence, retry in this case. retry will help when we send a very new timestamp and reports are not yet generated + if len(response.Reports) != len(ml.feeds) { + // TODO: AUTO-5044: calculate what reports are missing and log a warning + retryable = true + state = MercuryFlakyFailure + return fmt.Errorf("%d", http.StatusNotFound) + } + var reportBytes [][]byte + for _, rsp := range response.Reports { + b, err := hexutil.Decode(rsp.FullReport) + if err != nil { + retryable = false + state = InvalidMercuryResponse + return err + } + reportBytes = append(reportBytes, b) + } + ch <- MercuryData{ + Index: 0, + Bytes: reportBytes, + Retryable: false, + State: NoPipelineError, + } + sent = true + return nil + }, + // only retry when the error is 404 Not Found or 500 Internal Server Error + retry.RetryIf(func(err error) bool { + return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) + }), + retry.Context(ctx), + retry.Delay(RetryDelay), + retry.Attempts(TotalAttempt)) + + if !sent { + md := MercuryData{ + Index: 0, + Bytes: [][]byte{}, + Retryable: retryable, + Error: retryErr, + State: state, + } + ch <- md + } +} + +// doMercuryRequest sends requests to Mercury API to retrieve ChainlinkBlob. +func (mlh *MercuryLookupHandler) doMercuryRequest(ctx context.Context, ml *StreamsLookup) (PipelineExecutionState, UpkeepFailureReason, [][]byte, bool, error) { + var isMercuryV03 bool + resultLen := len(ml.feeds) + ch := make(chan MercuryData, resultLen) + if len(ml.feeds) == 0 { + return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) + } + if ml.feedParamKey == FeedIdHex && ml.timeParamKey == BlockNumber { + // only v0.2 + for i := range ml.feeds { + go mlh.singleFeedRequest(ctx, ch, i, ml) + } + } else if ml.feedParamKey == FeedIDs && ml.timeParamKey == Timestamp { + // only v0.3 + resultLen = 1 + isMercuryV03 = true + ch = make(chan MercuryData, resultLen) + go mlh.multiFeedsRequest(ctx, ch, ml) + } else { + return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) + } + + var reqErr error + results := make([][]byte, len(ml.feeds)) + retryable := true + allSuccess := true + // in v0.2, use the last execution error as the state, if no execution errors, state will be no error + state := NoPipelineError + for i := 0; i < resultLen; i++ { + m := <-ch + if m.Error != nil { + if reqErr == nil { + reqErr = errors.New(m.Error.Error()) + } else { + reqErr = errors.New(reqErr.Error() + m.Error.Error()) + } + retryable = retryable && m.Retryable + allSuccess = false + if m.State != NoPipelineError { + state = m.State + } + continue + } + if isMercuryV03 { + results = m.Bytes + } else { + results[m.Index] = m.Bytes[0] + } + } + // only retry when not all successful AND none are not retryable + return state, UpkeepFailureReasonNone, results, retryable && !allSuccess, reqErr +} + +// decodeStreamsLookup decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string timeParamKey, uint256 time, byte[] extraData) +// func (mlh *MercuryLookupHandler) decodeStreamsLookup(data []byte) (*StreamsLookup, error) { +// e := mlh.mercuryConfig.Abi.Errors["StreamsLookup"] +// unpack, err := e.Unpack(data) +// if err != nil { +// return nil, fmt.Errorf("unpack error: %w", err) +// } +// errorParameters := unpack.([]interface{}) + +// return &StreamsLookup{ +// feedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), +// feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), +// timeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), +// time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), +// extraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), +// }, nil +// } + +// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if +// this upkeep is allowed to use Mercury service. +// func (mlh *MercuryLookupHandler) allowedToUseMercury(upkeep models.Upkeep) (bool, error) { +// allowed, ok := mlh.mercuryConfig.AllowListCache.Get(upkeep.Admin.Hex()) +// if ok { +// return allowed.(bool), nil +// } + +// if upkeep.UpkeepPrivilegeConfig == nil { +// return false, fmt.Errorf("the upkeep privilege config was not retrieved for upkeep with ID %s", upkeep.UpkeepID) +// } + +// if len(upkeep.UpkeepPrivilegeConfig) == 0 { +// return false, fmt.Errorf("the upkeep privilege config is empty") +// } + +// var a UpkeepPrivilegeConfig +// err := json.Unmarshal(upkeep.UpkeepPrivilegeConfig, &a) +// if err != nil { +// return false, fmt.Errorf("failed to unmarshal privilege config for upkeep ID %s: %v", upkeep.UpkeepID, err) +// } + +// mlh.mercuryConfig.AllowListCache.Set(upkeep.Admin.Hex(), a.MercuryEnabled, cache.DefaultExpiration) +// return a.MercuryEnabled, nil +// } + +func (mlh *MercuryLookupHandler) CheckCallback(ctx context.Context, values [][]byte, lookup *StreamsLookup, registryABI ethabi.ABI, registryAddress common.Address) (hexutil.Bytes, error) { + payload, err := registryABI.Pack("checkCallback", lookup.upkeepId, values, lookup.extraData) + if err != nil { + return nil, err + } + + var theBytes hexutil.Bytes + args := map[string]interface{}{ + "to": registryAddress.Hex(), + "data": hexutil.Bytes(payload), + } + + // call checkCallback function at the block which OCR3 has agreed upon + err = mlh.rpcClient.CallContext(ctx, &theBytes, "eth_call", args, hexutil.EncodeUint64(lookup.block)) + if err != nil { + return nil, err + } + return theBytes, nil +} diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 89ddc54ca7e..91e26caef69 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -7,6 +7,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( github.com/ava-labs/coreth v0.12.1 + github.com/avast/retry-go v3.0.0+incompatible github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 github.com/ethereum/go-ethereum v1.12.0 @@ -18,6 +19,7 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.0 + github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 @@ -234,7 +236,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pressly/goose/v3 v3.16.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5f0e22fba03..1622f1d24df 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -150,6 +150,8 @@ github.com/ava-labs/avalanchego v1.10.1 h1:lBeamJ1iNq+p2oKg2nAs+A65m8vhSDjkiTDbw github.com/ava-labs/avalanchego v1.10.1/go.mod h1:ZvSXWlbkUKlbk3BsWx29a+8eVHe/WBsOxh55BSGoeRk= github.com/ava-labs/coreth v0.12.1 h1:EWSkFGHGVUxmu1pnSK/2pdcxaAVHbGspHqO3Ag+i7sA= github.com/ava-labs/coreth v0.12.1/go.mod h1:/5x54QlIKjlPebkdzTA5ic9wXdejbWOnQosztkv9jxo= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index cb9e2dd6752..aec23431921 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -16,6 +16,7 @@ import ( "github.com/patrickmn/go-cache" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" @@ -91,9 +92,8 @@ func NewStreamsLookup( // Lookup looks through check upkeep results looking for any that need off chain lookup func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult { lookups := map[int]*mercury.StreamsLookup{} - for _, checkResult := range checkResults { - copyCheckResult := checkResult - s.buildResult(ctx, ©CheckResult, lookups) + for i, checkResult := range checkResults { + s.buildResult(ctx, i, checkResult, checkResults, lookups) } var wg sync.WaitGroup @@ -101,7 +101,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe wg.Add(1) func(i int, lookup *mercury.StreamsLookup) { s.threadCtrl.Go(func(ctx context.Context) { - s.doLookup(ctx, &wg, lookup, &checkResults[i]) + s.doLookup(ctx, &wg, lookup, i, checkResults) }) }(i, lookup) } @@ -112,7 +112,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe } // buildResult checks if the upkeep is allowed by Mercury and builds a streams lookup request from the check result -func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { +func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keepers.CheckResult, checkResults []ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { lookupLggr := s.lggr.With("where", "StreamsLookup") if checkResult.IneligibilityReason != uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted) { // Streams Lookup only works when upkeep target check reverts @@ -129,7 +129,7 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec // Try to decode the revert error into streams lookup format. User upkeeps can revert with any reason, see if they // tried to call mercury - lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResult.PerformData)) + lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) streamsLookupErr, err := s.packer.DecodeStreamsLookupRequest(checkResult.PerformData) if err != nil { lookupLggr.Debugf("at block %d upkeep %s DecodeStreamsLookupRequest failed: %v", block, upkeepId, err) @@ -139,7 +139,7 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec streamsLookupResponse := &mercury.StreamsLookup{StreamsLookupError: streamsLookupErr} if len(streamsLookupResponse.Feeds) == 0 { - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) lookupLggr.Debugf("at block %s upkeep %s has empty feeds array", block, upkeepId) return } @@ -148,21 +148,21 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec if streamsLookupResponse.IsMercuryV02() { // check permission on the registry for mercury v0.2 opts := s.buildCallOpts(ctx, block) - if state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId.BigInt()); err != nil { + if state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId.BigInt()); err != nil { lookupLggr.Warnf("at block %s upkeep %s failed to query mercury allow list: %s", block, upkeepId, err) - checkResult.PipelineExecutionState = uint8(state) - checkResult.IneligibilityReason = uint8(reason) - checkResult.Retryable = retryable + checkResults[i].PipelineExecutionState = uint8(state) + checkResults[i].IneligibilityReason = uint8(reason) + checkResults[i].Retryable = retryable return } else if !allowed { lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) return } } else if streamsLookupResponse.IsMercuryVersionUnkown() { // if mercury version cannot be determined, set failure reason lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) return } @@ -171,103 +171,71 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec // in the revert for mercury v0.2, which is denoted by time in the struct bc starting from v0.3, only timestamp will be supported streamsLookupResponse.Block = uint64(block.Int64()) lookupLggr.Infof("at block %d upkeep %s DecodeStreamsLookupRequest feedKey=%s timeKey=%s feeds=%v time=%s extraData=%s", block, upkeepId, streamsLookupResponse.FeedParamKey, streamsLookupResponse.TimeParamKey, streamsLookupResponse.Feeds, streamsLookupResponse.Time, hexutil.Encode(streamsLookupResponse.ExtraData)) - lookups[len(lookups)] = streamsLookupResponse + lookups[i] = streamsLookupResponse } -func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) { +func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, i int, checkResults []ocr2keepers.CheckResult) { defer wg.Done() - values, err := s.DoMercuryRequest(ctx, lookup, checkResult) - if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s DoMercuryRequest err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) - } + state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) + pluginRetryKey := generatePluginRetryKey(checkResults[i].WorkID, lookup.Block) - if err := s.CheckCallback(ctx, values, lookup, checkResult); err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s CheckCallback err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) + if lookup.IsMercuryV02() { + state, reason, values, retryable, retryInterval, err = s.v02Client.DoRequest(ctx, lookup, pluginRetryKey) + } else if lookup.IsMercuryV03() { + state, reason, values, retryable, retryInterval, err = s.v03Client.DoRequest(ctx, lookup, pluginRetryKey) } -} -func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) error { - payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) if err != nil { - s.lggr.Errorf("at block %d upkeep %s checkCallback packing err: %s", lookup.Block, lookup.UpkeepId, err.Error()) - checkResult.Retryable = false - checkResult.PipelineExecutionState = uint8(mercury.PackUnpackDecodeFailed) - return err + s.lggr.Errorf("at block %d upkeep %s requested time %s retryable %v retryInterval %s doMercuryRequest: %s", lookup.Block, lookup.UpkeepId, lookup.Time, retryable, retryInterval, err.Error()) + checkResults[i].Retryable = retryable + checkResults[i].RetryInterval = retryInterval + checkResults[i].PipelineExecutionState = uint8(state) + checkResults[i].IneligibilityReason = uint8(reason) + return } - var mercuryBytes hexutil.Bytes - args := map[string]interface{}{ - "to": s.registry.Address().Hex(), - "data": hexutil.Bytes(payload), + for j, v := range values { + s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) } - // call checkCallback function at the block which OCR3 has agreed upon - if err = s.client.CallContext(ctx, &mercuryBytes, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { + state, retryable, mercuryBytes, err := s.checkCallback(ctx, values, lookup) + if err != nil { s.lggr.Errorf("at block %d upkeep %s checkCallback err: %s", lookup.Block, lookup.UpkeepId, err.Error()) - checkResult.Retryable = true - checkResult.PipelineExecutionState = uint8(mercury.RpcFlakyFailure) - return err + checkResults[i].Retryable = retryable + checkResults[i].PipelineExecutionState = uint8(state) + return } - s.lggr.Infof("at block %d upkeep %s requested time %s checkCallback mercuryBytes: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(mercuryBytes)) unpackCallBackState, needed, performData, failureReason, _, err := s.packer.UnpackCheckCallbackResult(mercuryBytes) if err != nil { s.lggr.Errorf("at block %d upkeep %s requested time %s UnpackCheckCallbackResult err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) - checkResult.PipelineExecutionState = unpackCallBackState - return err + checkResults[i].PipelineExecutionState = unpackCallBackState + return } if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) s.lggr.Debugf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) - return fmt.Errorf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) - + return } if !needed { - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) s.lggr.Debugf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) - return fmt.Errorf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) + return } - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) - checkResult.Eligible = true - checkResult.PerformData = performData + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) + checkResults[i].Eligible = true + checkResults[i].PerformData = performData s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) - - return nil -} - -func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) ([][]byte, error) { - state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) - pluginRetryKey := generatePluginRetryKey(checkResult.WorkID, lookup.Block) - - if lookup.IsMercuryV02() { - state, reason, values, retryable, retryInterval, err = s.v02Client.DoRequest(ctx, lookup, pluginRetryKey) - } else if lookup.IsMercuryV03() { - state, reason, values, retryable, retryInterval, err = s.v03Client.DoRequest(ctx, lookup, pluginRetryKey) - } - - if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s retryable %v retryInterval %s doMercuryRequest: %s", lookup.Block, lookup.UpkeepId, lookup.Time, retryable, retryInterval, err.Error()) - checkResult.Retryable = retryable - checkResult.RetryInterval = retryInterval - checkResult.PipelineExecutionState = uint8(state) - checkResult.IneligibilityReason = uint8(reason) - return nil, err - } - - for j, v := range values { - s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) - } - return values, nil } -// AllowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if +// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if // this upkeep is allowed to use Mercury service. -func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { +func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { allowed, ok := s.mercuryConfig.IsUpkeepAllowed(upkeepId.String()) if ok { return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, allowed.(bool), nil @@ -287,6 +255,7 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s "data": hexutil.Bytes(payload), } + // call checkCallback function at the block which OCR3 has agreed upon if err = s.client.CallContext(opts.Context, &resultBytes, "eth_call", args, hexutil.EncodeBig(opts.BlockNumber)); err != nil { return mercury.RpcFlakyFailure, mercury.MercuryUpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } @@ -312,6 +281,26 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil } +func (s *streams) checkCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup) (mercury.MercuryUpkeepState, bool, hexutil.Bytes, error) { + payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) + if err != nil { + return mercury.PackUnpackDecodeFailed, false, nil, err + } + + var b hexutil.Bytes + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), + } + + // call checkCallback function at the block which OCR3 has agreed upon + if err := s.client.CallContext(ctx, &b, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { + return mercury.RpcFlakyFailure, true, nil, err + } + + return mercury.NoPipelineError, false, b, nil +} + func (s *streams) buildCallOpts(ctx context.Context, block *big.Int) *bind.CallOpts { opts := bind.CallOpts{ Context: ctx, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 2475244b4d0..abcc37dca18 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -126,7 +126,6 @@ func TestStreams_CheckCallback(t *testing.T) { tests := []struct { name string lookup *mercury.StreamsLookup - input []ocr2keepers.CheckResult values [][]byte statusCode int @@ -154,9 +153,6 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, - input: []ocr2keepers.CheckResult{ - {}, - }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -189,9 +185,6 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, - input: []ocr2keepers.CheckResult{ - {}, - }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -223,9 +216,6 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, - input: []ocr2keepers.CheckResult{ - {}, - }, values: values, statusCode: http.StatusOK, callbackResp: []byte{}, @@ -265,10 +255,10 @@ func TestStreams_CheckCallback(t *testing.T) { }).Once() s.client = client - err = s.CheckCallback(testutils.Context(t), tt.values, tt.lookup, &tt.input[0]) - tt.wantErr(t, err, fmt.Sprintf("Error assertion failed: %v", tt.name)) - assert.Equal(t, uint8(tt.state), tt.input[0].PipelineExecutionState) - assert.Equal(t, tt.retryable, tt.input[0].Retryable) + state, retryable, _, err := s.checkCallback(testutils.Context(t), tt.values, tt.lookup) + tt.wantErr(t, err, fmt.Sprintf("Error asserion failed: %v", tt.name)) + assert.Equal(t, tt.state, state) + assert.Equal(t, tt.retryable, retryable) }) } } @@ -444,7 +434,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { BlockNumber: big.NewInt(10), } - state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId) + state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId) assert.Equal(t, tt.err, err) assert.Equal(t, tt.allowed, allowed) assert.Equal(t, tt.state, state) From 7d9274044baa79752940f6fed70c99d18746fbc4 Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:51:16 +0000 Subject: [PATCH 007/234] Update Loki auth in Github test workflow (#11488) --- .github/workflows/on-demand-vrfv2-performance-test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml index 100fdf73e61..56a5c8eee84 100644 --- a/.github/workflows/on-demand-vrfv2-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -71,7 +71,8 @@ jobs: contents: read env: LOKI_URL: ${{ secrets.LOKI_URL }} - LOKI_TOKEN: ${{ secrets.LOKI_TOKEN }} + LOKI_TENANT_ID: ${{ secrets.LOKI_TENANT_ID }} + LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} SELECTED_NETWORKS: ${{ inputs.network }} TEST_TYPE: ${{ inputs.performanceTestType }} VRFV2_TEST_DURATION: ${{ inputs.testDuration }} From 1032ba3ae823442732d9007ca8e479ca9debd350 Mon Sep 17 00:00:00 2001 From: Jim W Date: Mon, 4 Dec 2023 20:05:11 -0500 Subject: [PATCH 008/234] remove pkgerrors dependency from common pkg (#11479) * remove pkgerrors dependency from common pkg * fix naming inconsistencies * clean up error return * clean up error return and remove use of Unwrap --- common/client/multi_node.go | 7 +-- common/client/multi_node_test.go | 2 +- common/client/node.go | 10 ++-- common/client/node_lifecycle.go | 2 +- common/client/node_lifecycle_test.go | 2 +- common/client/send_only_node_test.go | 2 +- common/fee/models.go | 12 ++-- common/fee/utils.go | 4 +- common/headtracker/head_listener.go | 5 +- common/headtracker/head_tracker.go | 12 ++-- common/txmgr/broadcaster.go | 50 ++++++++--------- common/txmgr/confirmer.go | 83 ++++++++++++++-------------- common/txmgr/strategies.go | 4 +- common/txmgr/types/tx.go | 14 ++++- 14 files changed, 110 insertions(+), 99 deletions(-) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index db5380e91f5..dfd6585b642 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -7,7 +7,6 @@ import ( "sync" "time" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -166,12 +165,12 @@ func NewMultiNode[ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Dial(ctx context.Context) error { return c.StartOnce("MultiNode", func() (merr error) { if len(c.nodes) == 0 { - return errors.Errorf("no available nodes for chain %s", c.chainID.String()) + return fmt.Errorf("no available nodes for chain %s", c.chainID.String()) } var ms services.MultiStart for _, n := range c.nodes { if n.ConfiguredChainID().String() != c.chainID.String() { - return ms.CloseBecause(errors.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())) + 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())) } rawNode, ok := n.(*node[CHAIN_ID, HEAD, RPC_CLIENT]) if ok { @@ -188,7 +187,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } for _, s := range c.sendonlys { if s.ConfiguredChainID().String() != c.chainID.String() { - return ms.CloseBecause(errors.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())) + 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())) } if err := ms.Start(ctx, s); err != nil { return err diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 229f1320a14..82af7411080 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -1,13 +1,13 @@ package client import ( + "errors" "fmt" big "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" diff --git a/common/client/node.go b/common/client/node.go index 4fad18b42cf..ce144bbca86 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -2,13 +2,13 @@ package client import ( "context" + "errors" "fmt" "math/big" "net/url" "sync" "time" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -256,15 +256,15 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verify(callerCtx context.Context) (err error var chainID CHAIN_ID if chainID, err = n.rpc.ChainID(callerCtx); err != nil { promFailed() - return errors.Wrapf(err, "failed to verify chain ID for node %s", n.name) + return fmt.Errorf("failed to verify chain ID for node %s: %w", n.name, err) } else if chainID.String() != n.chainID.String() { promFailed() - return errors.Wrapf( - errInvalidChainID, - "rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s", + return fmt.Errorf( + "rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s: %w", chainID.String(), n.chainID.String(), n.name, + errInvalidChainID, ) } diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 59a59691c83..5ba0bff3238 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -2,12 +2,12 @@ package client import ( "context" + "errors" "fmt" "math" "math/big" "time" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 224b79d8378..bf94e6bd063 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -1,13 +1,13 @@ package client import ( + "errors" "fmt" big "math/big" "sync/atomic" "testing" "github.com/cometbft/cometbft/libs/rand" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/zap" diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go index 459f923cba8..79f4bfd60e3 100644 --- a/common/client/send_only_node_test.go +++ b/common/client/send_only_node_test.go @@ -1,11 +1,11 @@ package client import ( + "errors" "fmt" "net/url" "testing" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" diff --git a/common/fee/models.go b/common/fee/models.go index b843cc3f055..1fe4d2b053b 100644 --- a/common/fee/models.go +++ b/common/fee/models.go @@ -1,10 +1,10 @@ package fee import ( + "errors" + "fmt" "math/big" - "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-common/pkg/logger" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink/v2/common/chains/label" @@ -47,15 +47,15 @@ func CalculateBumpedFee( bumpedFeePrice = maxFee(lggr, currentfeePrice, bumpedFeePrice, maxFeePrice, "fee price", toChainUnit) if bumpedFeePrice.Cmp(maxFeePrice) > 0 { - return maxFeePrice, errors.Wrapf(ErrBumpFeeExceedsLimit, "bumped fee price of %s would exceed configured max fee price of %s (original price was %s). %s", - toChainUnit(bumpedFeePrice), toChainUnit(maxFeePrice), toChainUnit(originalfeePrice), label.NodeConnectivityProblemWarning) + return maxFeePrice, fmt.Errorf("bumped fee price of %s would exceed configured max fee price of %s (original price was %s). %s: %w", + toChainUnit(bumpedFeePrice), toChainUnit(maxFeePrice), toChainUnit(originalfeePrice), label.NodeConnectivityProblemWarning, ErrBumpFeeExceedsLimit) } else if bumpedFeePrice.Cmp(originalfeePrice) == 0 { // NOTE: This really shouldn't happen since we enforce minimums for // FeeEstimator.BumpPercent and FeeEstimator.BumpMin in the config validation, // but it's here anyway for a "belts and braces" approach - return bumpedFeePrice, errors.Wrapf(ErrBump, "bumped fee price of %s is equal to original fee price of %s."+ + return bumpedFeePrice, fmt.Errorf("bumped fee price of %s is equal to original fee price of %s."+ " ACTION REQUIRED: This is a configuration error, you must increase either "+ - "FeeEstimator.BumpPercent or FeeEstimator.BumpMin", toChainUnit(bumpedFeePrice), toChainUnit(bumpedFeePrice)) + "FeeEstimator.BumpPercent or FeeEstimator.BumpMin: %w", toChainUnit(bumpedFeePrice), toChainUnit(bumpedFeePrice), ErrBump) } return bumpedFeePrice, nil } diff --git a/common/fee/utils.go b/common/fee/utils.go index 71ababddbe3..eeb2c966719 100644 --- a/common/fee/utils.go +++ b/common/fee/utils.go @@ -1,10 +1,10 @@ package fee import ( + "fmt" "math" "math/big" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -12,7 +12,7 @@ func ApplyMultiplier(feeLimit uint32, multiplier float32) (uint32, error) { result := decimal.NewFromBigInt(big.NewInt(0).SetUint64(uint64(feeLimit)), 0).Mul(decimal.NewFromFloat32(multiplier)).IntPart() if result > math.MaxUint32 { - return 0, errors.Errorf("integer overflow when applying multiplier of %f to fee limit of %d", multiplier, feeLimit) + return 0, fmt.Errorf("integer overflow when applying multiplier of %f to fee limit of %d", multiplier, feeLimit) } return uint32(result), nil } diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index 2013895d0b8..0aebf606634 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -2,10 +2,11 @@ package headtracker import ( "context" + "errors" + "fmt" "sync/atomic" "time" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -202,7 +203,7 @@ func (hl *HeadListener[HTH, S, ID, BLOCK_HASH]) subscribeToHead(ctx context.Cont hl.headSubscription, err = hl.client.SubscribeNewHead(ctx, hl.chHeaders) if err != nil { close(hl.chHeaders) - return errors.Wrap(err, "Client#SubscribeNewHead") + return fmt.Errorf("Client#SubscribeNewHead: %w", err) } hl.connected.Store(true) diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 6e379776c0f..c977eb023cc 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -2,11 +2,11 @@ package headtracker import ( "context" + "errors" "fmt" "sync" "time" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -123,7 +123,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) Start(ctx context.Context) error ht.log.Errorw("Error getting initial head", "err", err) } else if initialHead.IsValid() { if err := ht.handleNewHead(ctx, initialHead); err != nil { - return errors.Wrap(err, "error handling initial head") + return fmt.Errorf("error handling initial head: %w", err) } } else { ht.log.Debug("Got nil initial head") @@ -179,7 +179,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) LatestChain() HTH { func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) getInitialHead(ctx context.Context) (HTH, error) { head, err := ht.client.HeadByNumber(ctx, nil) if err != nil { - return ht.getNilHead(), errors.Wrap(err, "failed to fetch initial head") + return ht.getNilHead(), fmt.Errorf("failed to fetch initial head: %w", err) } loggerFields := []interface{}{"head", head} if head.IsValid() { @@ -204,7 +204,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context if ctx.Err() != nil { return nil } else if err != nil { - return errors.Wrapf(err, "failed to save head: %#v", head) + return fmt.Errorf("failed to save head: %#v: %w", head, err) } if !prevHead.IsValid() || head.BlockNumber() > prevHead.BlockNumber() { @@ -212,7 +212,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context headWithChain := ht.headSaver.Chain(head.BlockHash()) if !headWithChain.IsValid() { - return errors.Errorf("HeadTracker#handleNewHighestHead headWithChain was unexpectedly nil") + return fmt.Errorf("HeadTracker#handleNewHighestHead headWithChain was unexpectedly nil") } ht.backfillMB.Deliver(headWithChain) ht.broadcastMB.Deliver(headWithChain) @@ -339,7 +339,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) backfill(ctx context.Context, hea ht.log.Debugw("context canceled, aborting backfill", "err", err, "ctx.Err", ctx.Err()) break } else if err != nil { - return errors.Wrap(err, "fetchAndSaveHead failed") + return fmt.Errorf("fetchAndSaveHead failed: %w", err) } } return diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 54ae653f662..f10ecafc670 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -3,13 +3,13 @@ package txmgr import ( "context" "database/sql" + "errors" "fmt" "slices" "sync" "time" "github.com/jpillora/backoff" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" @@ -210,7 +210,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) star var err error eb.enabledAddresses, err = eb.ks.EnabledAddressesForChain(eb.chainID) if err != nil { - return errors.Wrap(err, "Broadcaster: failed to load EnabledAddressesForChain") + return fmt.Errorf("Broadcaster: failed to load EnabledAddressesForChain: %w", err) } if len(eb.enabledAddresses) > 0 { @@ -246,7 +246,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) clos eb.initSync.Lock() defer eb.initSync.Unlock() if !eb.isStarted { - return errors.Wrap(services.ErrAlreadyStopped, "Broadcaster is not started") + return fmt.Errorf("Broadcaster is not started: %w", services.ErrAlreadyStopped) } close(eb.chStop) eb.wg.Wait() @@ -454,19 +454,19 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc err, retryable = eb.handleAnyInProgressTx(ctx, fromAddress) if err != nil { - return retryable, errors.Wrap(err, "processUnstartedTxs failed on handleAnyInProgressTx") + return retryable, fmt.Errorf("processUnstartedTxs failed on handleAnyInProgressTx: %w", err) } for { maxInFlightTransactions := eb.txConfig.MaxInFlight() if maxInFlightTransactions > 0 { nUnconfirmed, err := eb.txStore.CountUnconfirmedTransactions(ctx, fromAddress, eb.chainID) if err != nil { - return true, errors.Wrap(err, "CountUnconfirmedTransactions failed") + return true, fmt.Errorf("CountUnconfirmedTransactions failed: %w", err) } if nUnconfirmed >= maxInFlightTransactions { nUnstarted, err := eb.txStore.CountUnstartedTransactions(ctx, fromAddress, eb.chainID) if err != nil { - return true, errors.Wrap(err, "CountUnstartedTransactions failed") + return true, fmt.Errorf("CountUnstartedTransactions failed: %w", err) } eb.lggr.Warnw(fmt.Sprintf(`Transaction throttling; %d transactions in-flight and %d unstarted transactions pending (maximum number of in-flight transactions is %d per key). %s`, nUnconfirmed, nUnstarted, maxInFlightTransactions, label.MaxInFlightTransactionsWarning), "maxInFlightTransactions", maxInFlightTransactions, "nUnconfirmed", nUnconfirmed, "nUnstarted", nUnstarted) select { @@ -479,7 +479,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc } etx, err := eb.nextUnstartedTransactionWithSequence(fromAddress) if err != nil { - return true, errors.Wrap(err, "processUnstartedTxs failed on nextUnstartedTransactionWithSequence") + return true, fmt.Errorf("processUnstartedTxs failed on nextUnstartedTransactionWithSequence: %w", err) } if etx == nil { return false, nil @@ -489,18 +489,18 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc var retryable bool a, _, _, retryable, err = eb.NewTxAttempt(ctx, *etx, eb.lggr) if err != nil { - return retryable, errors.Wrap(err, "processUnstartedTxs failed on NewAttempt") + return retryable, fmt.Errorf("processUnstartedTxs failed on NewAttempt: %w", err) } if err := eb.txStore.UpdateTxUnstartedToInProgress(ctx, etx, &a); errors.Is(err, ErrTxRemoved) { eb.lggr.Debugw("tx removed", "txID", etx.ID, "subject", etx.Subject) continue } else if err != nil { - return true, errors.Wrap(err, "processUnstartedTxs failed on UpdateTxUnstartedToInProgress") + return true, fmt.Errorf("processUnstartedTxs failed on UpdateTxUnstartedToInProgress: %w", err) } if err, retryable := eb.handleInProgressTx(ctx, *etx, a, time.Now()); err != nil { - return retryable, errors.Wrap(err, "processUnstartedTxs failed on handleAnyInProgressTx") + return retryable, fmt.Errorf("processUnstartedTxs failed on handleInProgressTx: %w", err) } } } @@ -510,11 +510,11 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) handleAnyInProgressTx(ctx context.Context, fromAddress ADDR) (err error, retryable bool) { etx, err := eb.txStore.GetTxInProgress(ctx, fromAddress) if err != nil { - return errors.Wrap(err, "handleAnyInProgressTx failed"), true + return fmt.Errorf("handleAnyInProgressTx failed: %w", err), true } if etx != nil { if err, retryable := eb.handleInProgressTx(ctx, *etx, etx.TxAttempts[0], etx.CreatedAt); err != nil { - return errors.Wrap(err, "handleAnyInProgressTx failed"), retryable + return fmt.Errorf("handleAnyInProgressTx failed: %w", err), retryable } } return nil, false @@ -524,17 +524,17 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // Here we complete the job that we didn't finish last time. func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) handleInProgressTx(ctx context.Context, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time) (error, bool) { if etx.State != TxInProgress { - return errors.Errorf("invariant violation: expected transaction %v to be in_progress, it was %s", etx.ID, etx.State), false + return fmt.Errorf("invariant violation: expected transaction %v to be in_progress, it was %s", etx.ID, etx.State), false } checkerSpec, err := etx.GetChecker() if err != nil { - return errors.Wrap(err, "parsing transmit checker"), false + return fmt.Errorf("parsing transmit checker: %w", err), false } checker, err := eb.checkerFactory.BuildChecker(checkerSpec) if err != nil { - return errors.Wrap(err, "building transmit checker"), false + return fmt.Errorf("building transmit checker: %w", err), false } lgr := etx.GetLogger(logger.With(eb.lggr, "fee", attempt.TxFee)) @@ -659,7 +659,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand nextSequence, e := eb.client.PendingSequenceAt(ctx, etx.FromAddress) if e != nil { err = multierr.Combine(e, err) - return errors.Wrapf(err, "failed to fetch latest pending sequence after encountering unknown RPC error while sending transaction"), true + return fmt.Errorf("failed to fetch latest pending sequence after encountering unknown RPC error while sending transaction: %w", err), true } if nextSequence.Int64() > (*etx.Sequence).Int64() { // Despite the error, the RPC node considers the previously sent @@ -686,7 +686,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // // In all cases, the best thing we can do is go into a retry loop and keep // trying to send the transaction over again. - return errors.Wrapf(err, "retryable error while sending transaction %s (tx ID %d)", attempt.Hash.String(), etx.ID), true + return fmt.Errorf("retryable error while sending transaction %s (tx ID %d): %w", attempt.Hash.String(), etx.ID, err), true } } @@ -702,7 +702,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) next // Finish. No more transactions left to process. Hoorah! return nil, nil } - return nil, errors.Wrap(err, "findNextUnstartedTransactionFromAddress failed") + return nil, fmt.Errorf("findNextUnstartedTransactionFromAddress failed: %w", err) } sequence, err := eb.GetNextSequence(ctx, etx.FromAddress) @@ -726,7 +726,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryA replacementAttempt, bumpedFee, bumpedFeeLimit, retryable, err := eb.NewBumpTxAttempt(ctx, etx, attempt, nil, lgr) if err != nil { - return errors.Wrap(err, "tryAgainBumpFee failed"), retryable + return fmt.Errorf("tryAgainBumpFee failed: %w", err), retryable } return eb.saveTryAgainAttempt(ctx, lgr, etx, attempt, replacementAttempt, initialBroadcastAt, bumpedFee, bumpedFeeLimit) @@ -734,14 +734,14 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryA func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryAgainWithNewEstimation(ctx context.Context, lgr logger.Logger, txError error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time) (err error, retryable bool) { if attempt.TxType == 0x2 { - err = errors.Errorf("re-estimation is not supported for EIP-1559 transactions. Node returned error: %v. This is a bug", txError.Error()) + err = fmt.Errorf("re-estimation is not supported for EIP-1559 transactions. Node returned error: %v. This is a bug", txError.Error()) logger.Sugared(eb.lggr).AssumptionViolation(err.Error()) return err, false } replacementAttempt, fee, feeLimit, retryable, err := eb.NewTxAttemptWithType(ctx, etx, lgr, attempt.TxType, feetypes.OptForceRefetch) if err != nil { - return errors.Wrap(err, "tryAgainWithNewEstimation failed to build new attempt"), retryable + return fmt.Errorf("tryAgainWithNewEstimation failed to build new attempt: %w", err), retryable } lgr.Warnw("L2 rejected transaction due to incorrect fee, re-estimated and will try again", "etxID", etx.ID, "err", err, "newGasPrice", fee, "newGasLimit", feeLimit) @@ -751,7 +751,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryA func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) saveTryAgainAttempt(ctx context.Context, lgr logger.Logger, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], replacementAttempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time, newFee FEE, newFeeLimit uint32) (err error, retyrable bool) { if err = eb.txStore.SaveReplacementInProgressAttempt(ctx, attempt, &replacementAttempt); err != nil { - return errors.Wrap(err, "tryAgainWithNewFee failed"), true + return fmt.Errorf("tryAgainWithNewFee failed: %w", err), true } lgr.Debugw("Bumped fee on initial send", "oldFee", attempt.TxFee.String(), "newFee", newFee.String(), "newFeeLimit", newFeeLimit) return eb.handleInProgressTx(ctx, etx, replacementAttempt, initialBroadcastAt) @@ -761,7 +761,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) save ctx, cancel := eb.chStop.NewCtx() defer cancel() if etx.State != TxInProgress { - return errors.Errorf("can only transition to fatal_error from in_progress, transaction is currently %s", etx.State) + return fmt.Errorf("can only transition to fatal_error from in_progress, transaction is currently %s", etx.State) } if !etx.Error.Valid { return errors.New("expected error field to be set") @@ -779,11 +779,11 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) save // is relatively benign and probably nobody will ever run into it in // practice, but something to be aware of. if etx.PipelineTaskRunID.Valid && eb.resumeCallback != nil && etx.SignalCallback { - err := eb.resumeCallback(etx.PipelineTaskRunID.UUID, nil, errors.Errorf("fatal error while sending transaction: %s", etx.Error.String)) + err := eb.resumeCallback(etx.PipelineTaskRunID.UUID, nil, fmt.Errorf("fatal error while sending transaction: %s", etx.Error.String)) if errors.Is(err, sql.ErrNoRows) { lgr.Debugw("callback missing or already resumed", "etxID", etx.ID) } else if err != nil { - return errors.Wrap(err, "failed to resume pipeline") + return fmt.Errorf("failed to resume pipeline: %w", err) } else { // Mark tx as having completed callback if err := eb.txStore.UpdateTxCallbackCompleted(ctx, etx.PipelineTaskRunID.UUID, eb.chainID); err != nil { diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index a56768ce206..95be9ad23e6 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -3,13 +3,13 @@ package txmgr import ( "context" "encoding/hex" + "errors" "fmt" "sort" "strconv" "sync" "time" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" @@ -201,7 +201,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) sta var err error ec.enabledAddresses, err = ec.ks.EnabledAddressesForChain(ec.chainID) if err != nil { - return errors.Wrap(err, "Confirmer: failed to load EnabledAddressesForChain") + return fmt.Errorf("Confirmer: failed to load EnabledAddressesForChain: %w", err) } ec.ctx, ec.ctxCancel = context.WithCancel(context.Background()) @@ -223,7 +223,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) clo ec.initSync.Lock() defer ec.initSync.Unlock() if !ec.isStarted { - return errors.Wrap(utils.ErrAlreadyStopped, "Confirmer is not started") + return fmt.Errorf("Confirmer is not started: %w", utils.ErrAlreadyStopped) } ec.ctxCancel() ec.wg.Wait() @@ -281,28 +281,28 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) pro ec.lggr.Debugw("processHead start", "headNum", head.BlockNumber(), "id", "confirmer") if err := ec.txStore.SetBroadcastBeforeBlockNum(ctx, head.BlockNumber(), ec.chainID); err != nil { - return errors.Wrap(err, "SetBroadcastBeforeBlockNum failed") + return fmt.Errorf("SetBroadcastBeforeBlockNum failed: %w", err) } if err := ec.CheckConfirmedMissingReceipt(ctx); err != nil { - return errors.Wrap(err, "CheckConfirmedMissingReceipt failed") + return fmt.Errorf("CheckConfirmedMissingReceipt failed: %w", err) } if err := ec.CheckForReceipts(ctx, head.BlockNumber()); err != nil { - return errors.Wrap(err, "CheckForReceipts failed") + return fmt.Errorf("CheckForReceipts failed: %w", err) } ec.lggr.Debugw("Finished CheckForReceipts", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") mark = time.Now() if err := ec.RebroadcastWhereNecessary(ctx, head.BlockNumber()); err != nil { - return errors.Wrap(err, "RebroadcastWhereNecessary failed") + return fmt.Errorf("RebroadcastWhereNecessary failed: %w", err) } ec.lggr.Debugw("Finished RebroadcastWhereNecessary", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") mark = time.Now() if err := ec.EnsureConfirmedTransactionsInLongestChain(ctx, head); err != nil { - return errors.Wrap(err, "EnsureConfirmedTransactionsInLongestChain failed") + return fmt.Errorf("EnsureConfirmedTransactionsInLongestChain failed: %w", err) } ec.lggr.Debugw("Finished EnsureConfirmedTransactionsInLongestChain", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") @@ -310,7 +310,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) pro if ec.resumeCallback != nil { mark = time.Now() if err := ec.ResumePendingTaskRuns(ctx, head); err != nil { - return errors.Wrap(err, "ResumePendingTaskRuns failed") + return fmt.Errorf("ResumePendingTaskRuns failed: %w", err) } ec.lggr.Debugw("Finished ResumePendingTaskRuns", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") @@ -382,7 +382,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CheckForReceipts(ctx context.Context, blockNum int64) error { attempts, err := ec.txStore.FindTxAttemptsRequiringReceiptFetch(ctx, ec.chainID) if err != nil { - return errors.Wrap(err, "FindTxAttemptsRequiringReceiptFetch failed") + return fmt.Errorf("FindTxAttemptsRequiringReceiptFetch failed: %w", err) } if len(attempts) == 0 { return nil @@ -398,7 +398,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che for from, attempts := range attemptsByAddress { minedSequence, err := ec.getMinedSequenceForAddress(ctx, from) if err != nil { - return errors.Wrapf(err, "unable to fetch pending sequence for address: %v", from) + return fmt.Errorf("unable to fetch pending sequence for address: %v: %w", from, err) } // separateLikelyConfirmedAttempts is used as an optimisation: there is @@ -415,7 +415,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che start := time.Now() err = ec.fetchAndSaveReceipts(ctx, likelyConfirmed, blockNum) if err != nil { - return errors.Wrapf(err, "unable to fetch and save receipts for likely confirmed txs, for address: %v", from) + return fmt.Errorf("unable to fetch and save receipts for likely confirmed txs, for address: %v: %w", from, err) } ec.lggr.Debugw(fmt.Sprintf("Fetching and saving %v likely confirmed receipts done", likelyConfirmedCount), "time", time.Since(start)) @@ -423,11 +423,11 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che } if err := ec.txStore.MarkAllConfirmedMissingReceipt(ctx, ec.chainID); err != nil { - return errors.Wrap(err, "unable to mark txes as 'confirmed_missing_receipt'") + return fmt.Errorf("unable to mark txes as 'confirmed_missing_receipt': %w", err) } if err := ec.txStore.MarkOldTxesMissingReceiptAsErrored(ctx, blockNum, ec.chainConfig.FinalityDepth(), ec.chainID); err != nil { - return errors.Wrap(err, "unable to confirm buried unconfirmed txes") + return fmt.Errorf("unable to confirm buried unconfirmed txes': %w", err) } return nil } @@ -488,10 +488,10 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) fet receipts, err := ec.batchFetchReceipts(ctx, batch, blockNum) if err != nil { - return errors.Wrap(err, "batchFetchReceipts failed") + return fmt.Errorf("batchFetchReceipts failed: %w", err) } if err := ec.txStore.SaveFetchedReceipts(ctx, receipts, ec.chainID); err != nil { - return errors.Wrap(err, "saveFetchedReceipts failed") + return fmt.Errorf("saveFetchedReceipts failed: %w", err) } promNumConfirmedTxs.WithLabelValues(ec.chainID.String()).Add(float64(len(receipts))) @@ -514,7 +514,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat if ec.txConfig.ForwardersEnabled() { err = ec.txStore.PreloadTxes(ctx, attempts) if err != nil { - return nil, errors.Wrap(err, "Confirmer#batchFetchReceipts error loading txs for attempts") + return nil, fmt.Errorf("Confirmer#batchFetchReceipts error loading txs for attempts: %w", err) } } @@ -629,7 +629,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Reb func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) rebroadcastWhereNecessary(ctx context.Context, address ADDR, blockHeight int64) error { if err := ec.handleAnyInProgressAttempts(ctx, address, blockHeight); err != nil { - return errors.Wrap(err, "handleAnyInProgressAttempts failed") + return fmt.Errorf("handleAnyInProgressAttempts failed: %w", err) } threshold := int64(ec.feeConfig.BumpThreshold()) @@ -637,24 +637,24 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) reb maxInFlightTransactions := ec.txConfig.MaxInFlight() etxs, err := ec.FindTxsRequiringRebroadcast(ctx, ec.lggr, address, blockHeight, threshold, bumpDepth, maxInFlightTransactions, ec.chainID) if err != nil { - return errors.Wrap(err, "FindTxsRequiringRebroadcast failed") + return fmt.Errorf("FindTxsRequiringRebroadcast failed: %w", err) } for _, etx := range etxs { lggr := etx.GetLogger(ec.lggr) attempt, err := ec.attemptForRebroadcast(ctx, lggr, *etx) if err != nil { - return errors.Wrap(err, "attemptForRebroadcast failed") + return fmt.Errorf("attemptForRebroadcast failed: %w", err) } lggr.Debugw("Rebroadcasting transaction", "nPreviousAttempts", len(etx.TxAttempts), "fee", attempt.TxFee) if err := ec.txStore.SaveInProgressAttempt(ctx, &attempt); err != nil { - return errors.Wrap(err, "saveInProgressAttempt failed") + return fmt.Errorf("saveInProgressAttempt failed: %w", err) } if err := ec.handleInProgressAttempt(ctx, lggr, *etx, attempt, blockHeight); err != nil { - return errors.Wrap(err, "handleInProgressAttempt failed") + return fmt.Errorf("handleInProgressAttempt failed: %w", err) } } return nil @@ -670,14 +670,14 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han if ctx.Err() != nil { return nil } else if err != nil { - return errors.Wrap(err, "GetInProgressTxAttempts failed") + return fmt.Errorf("GetInProgressTxAttempts failed: %w", err) } for _, a := range attempts { err := ec.handleInProgressAttempt(ctx, a.Tx.GetLogger(ec.lggr), a.Tx, a, blockHeight) if ctx.Err() != nil { break } else if err != nil { - return errors.Wrap(err, "handleInProgressAttempt failed") + return fmt.Errorf("handleInProgressAttempt failed: %w", err) } } return nil @@ -769,7 +769,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) att } return attempt, err } - return attempt, errors.Errorf("invariant violation: Tx %v was unconfirmed but didn't have any attempts. "+ + return attempt, fmt.Errorf("invariant violation: Tx %v was unconfirmed but didn't have any attempts. "+ "Falling back to default gas price instead."+ "This is a bug! Please report to https://github.com/smartcontractkit/chainlink/issues", etx.ID) } @@ -802,17 +802,17 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bum return bumpedAttempt, err } - if errors.Is(errors.Cause(err), commonfee.ErrBumpFeeExceedsLimit) { + if errors.Is(err, commonfee.ErrBumpFeeExceedsLimit) { promGasBumpExceedsLimit.WithLabelValues(ec.chainID.String()).Inc() } - return bumpedAttempt, errors.Wrap(err, "error bumping gas") + return bumpedAttempt, fmt.Errorf("error bumping gas: %w", err) } func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleInProgressAttempt(ctx context.Context, lggr logger.Logger, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], blockHeight int64) error { if attempt.State != txmgrtypes.TxAttemptInProgress { - return errors.Errorf("invariant violation: expected tx_attempt %v to be in_progress, it was %s", attempt.ID, attempt.State) + return fmt.Errorf("invariant violation: expected tx_attempt %v to be in_progress, it was %s", attempt.ID, attempt.State) } now := time.Now() @@ -827,7 +827,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han // "Lazily" load attempts here since the overwhelmingly common case is // that we don't need them unless we enter this path if err := ec.txStore.LoadTxAttempts(ctx, &etx); err != nil { - return errors.Wrap(err, "failed to load TxAttempts while bumping on terminally underpriced error") + return fmt.Errorf("failed to load TxAttempts while bumping on terminally underpriced error: %w", err) } if len(etx.TxAttempts) == 0 { err := errors.New("expected to find at least 1 attempt") @@ -841,7 +841,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han } replacementAttempt, err := ec.bumpGas(ctx, etx, etx.TxAttempts) if err != nil { - return errors.Wrap(err, "could not bump gas for terminally underpriced transaction") + return fmt.Errorf("could not bump gas for terminally underpriced transaction: %w", err) } promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() logger.With(lggr, @@ -852,7 +852,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han ).Errorf("gas price was rejected by the node for being too low. Node returned: '%s'", sendError.Error()) if err := ec.txStore.SaveReplacementInProgressAttempt(ctx, attempt, &replacementAttempt); err != nil { - return errors.Wrap(err, "saveReplacementInProgressAttempt failed") + return fmt.Errorf("saveReplacementInProgressAttempt failed: %w", err) } return ec.handleInProgressAttempt(ctx, lggr, etx, replacementAttempt, blockHeight) case client.ExceedsMaxFee: @@ -896,7 +896,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han // node operator. The node may have it in the mempool so we must keep the // attempt (leave it in_progress). Safest thing to do is bail out and wait // for the next head. - return errors.Wrapf(sendError, "unexpected error sending tx %v with hash %s", etx.ID, attempt.Hash.String()) + return fmt.Errorf("unexpected error sending tx %v with hash %s: %w", etx.ID, attempt.Hash.String(), sendError) } } @@ -924,13 +924,13 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Ens } etxs, err := ec.txStore.FindTransactionsConfirmedInBlockRange(ctx, head.BlockNumber(), head.EarliestHeadInChain().BlockNumber(), ec.chainID) if err != nil { - return errors.Wrap(err, "findTransactionsConfirmedInBlockRange failed") + return fmt.Errorf("findTransactionsConfirmedInBlockRange failed: %w", err) } for _, etx := range etxs { if !hasReceiptInLongestChain(*etx, head) { if err := ec.markForRebroadcast(*etx, head); err != nil { - return errors.Wrapf(err, "markForRebroadcast failed for etx %v", etx.ID) + return fmt.Errorf("markForRebroadcast failed for etx %v: %w", etx.ID, err) } } } @@ -983,7 +983,7 @@ func hasReceiptInLongestChain[ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) markForRebroadcast(etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], head types.Head[BLOCK_HASH]) error { if len(etx.TxAttempts) == 0 { - return errors.Errorf("invariant violation: expected tx %v to have at least one attempt", etx.ID) + return fmt.Errorf("invariant violation: expected tx %v to have at least one attempt", etx.ID) } // Rebroadcast the one with the highest gas price @@ -1016,8 +1016,11 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) mar ec.lggr.Infow(fmt.Sprintf("Re-org detected. Rebroadcasting transaction %s which may have been re-org'd out of the main chain", attempt.Hash.String()), logValues...) // Put it back in progress and delete all receipts (they do not apply to the new chain) - err := ec.txStore.UpdateTxForRebroadcast(ec.ctx, etx, attempt) - return errors.Wrap(err, "markForRebroadcast failed") + if err := ec.txStore.UpdateTxForRebroadcast(ec.ctx, etx, attempt); err != nil { + return fmt.Errorf("markForRebroadcast failed: %w", err) + } + + return nil } // ForceRebroadcast sends a transaction for every sequence in the given sequence range at the given gas price. @@ -1037,7 +1040,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) For etx, err := ec.txStore.FindTxWithSequence(ctx, address, seq) if err != nil { - return errors.Wrap(err, "ForceRebroadcast failed") + return fmt.Errorf("ForceRebroadcast failed: %w", err) } if etx == nil { ec.lggr.Debugf("ForceRebroadcast: no tx found with sequence %s, will rebroadcast empty transaction", seq) @@ -1076,7 +1079,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) sen } txhash, err := ec.client.SendEmptyTransaction(ctx, ec.TxAttemptBuilder.NewEmptyTxAttempt, seq, gasLimit, fee, fromAddress) if err != nil { - return "", errors.Wrap(err, "(Confirmer).sendEmptyTransaction failed") + return "", fmt.Errorf("(Confirmer).sendEmptyTransaction failed: %w", err) } return txhash, nil } @@ -1099,7 +1102,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Res var taskErr error var output interface{} if data.FailOnRevert && data.Receipt.GetStatus() == 0 { - taskErr = errors.Errorf("transaction %s reverted on-chain", data.Receipt.GetTxHash()) + taskErr = fmt.Errorf("transaction %s reverted on-chain", data.Receipt.GetTxHash()) } else { output = data.Receipt } diff --git a/common/txmgr/strategies.go b/common/txmgr/strategies.go index b986d0d9b80..faba2ba97bc 100644 --- a/common/txmgr/strategies.go +++ b/common/txmgr/strategies.go @@ -2,10 +2,10 @@ package txmgr import ( "context" + "fmt" "time" "github.com/google/uuid" - "github.com/pkg/errors" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" ) @@ -63,7 +63,7 @@ func (s DropOldestStrategy) PruneQueue(ctx context.Context, pruneService txmgrty n, err = pruneService.PruneUnstartedTxQueue(ctx, s.queueSize, s.subject) if err != nil { - return 0, errors.Wrap(err, "DropOldestStrategy#PruneQueue failed") + return 0, fmt.Errorf("DropOldestStrategy#PruneQueue failed: %w", err) } return } diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index b8a16561d88..3af43b19617 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -3,6 +3,7 @@ package types import ( "context" "encoding/json" + "errors" "fmt" "math/big" "slices" @@ -10,7 +11,6 @@ import ( "time" "github.com/google/uuid" - "github.com/pkg/errors" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -245,7 +245,11 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetMeta() (*TxMeta[A return nil, nil } var m TxMeta[ADDR, TX_HASH] - return &m, errors.Wrap(json.Unmarshal(*e.Meta, &m), "unmarshalling meta") + if err := json.Unmarshal(*e.Meta, &m); err != nil { + return nil, fmt.Errorf("unmarshalling meta: %w", err) + } + + return &m, nil } // GetLogger returns a new logger with metadata fields. @@ -320,5 +324,9 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetChecker() (Transm return TransmitCheckerSpec[ADDR]{}, nil } var t TransmitCheckerSpec[ADDR] - return t, errors.Wrap(json.Unmarshal(*e.TransmitChecker, &t), "unmarshalling transmit checker") + if err := json.Unmarshal(*e.TransmitChecker, &t); err != nil { + return t, fmt.Errorf("unmarshalling transmit checker: %w", err) + } + + return t, nil } From 006e3c7dd5bf249ca85891ee9b9a26ed926cc1d1 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Tue, 5 Dec 2023 14:15:09 +0200 Subject: [PATCH 009/234] Extract mathutil (#11446) * Extract mathutil * Fix dependencies --- .../chains/evm/gas/block_history_estimator.go | 2 +- core/chains/evm/logpoller/log_poller.go | 2 +- core/services/blockhashstore/feeder_test.go | 3 +- .../ocr2vrf/coordinator/coordinator.go | 3 +- .../ocr2vrf/coordinator/coordinator_test.go | 2 +- core/services/vrf/v1/listener_v1.go | 3 +- .../vrf/v2/listener_v2_log_listener.go | 2 +- core/sessions/ldapauth/ldap.go | 2 +- core/sessions/localauth/orm.go | 2 +- core/utils/mathutil/mathutil.go | 23 ------------- core/utils/mathutil/mathutil_test.go | 33 ------------------- 11 files changed, 12 insertions(+), 65 deletions(-) delete mode 100644 core/utils/mathutil/mathutil.go delete mode 100644 core/utils/mathutil/mathutil_test.go diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 64dc331f657..0ec4721b797 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" @@ -25,7 +26,6 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) // MaxStartTime is the maximum amount of time we are allowed to spend diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index de1999da260..fb380f84b2c 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -22,12 +22,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) //go:generate mockery --quiet --name LogPoller --output ./mocks/ --case=underscore --structname LogPoller --filename log_poller.go diff --git a/core/services/blockhashstore/feeder_test.go b/core/services/blockhashstore/feeder_test.go index 8d9ed48c4bf..ad039798408 100644 --- a/core/services/blockhashstore/feeder_test.go +++ b/core/services/blockhashstore/feeder_test.go @@ -14,9 +14,10 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" + mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" bhsmocks "github.com/smartcontractkit/chainlink/v2/core/services/blockhashstore/mocks" diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go index 63538941532..a25fcca9fec 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go @@ -23,6 +23,8 @@ import ( "github.com/smartcontractkit/libocr/commontypes" ocr2Types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" + "github.com/smartcontractkit/chainlink-vrf/dkg" ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" @@ -38,7 +40,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" ocr2vrfconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/config" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) var _ ocr2vrftypes.CoordinatorInterface = &coordinator{} diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go index 418e4356c68..9c3eb087985 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go @@ -27,6 +27,7 @@ import ( ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -38,7 +39,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) func TestCoordinator_BeaconPeriod(t *testing.T) { diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 494847797fa..66a8ddcd58c 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -17,6 +17,8 @@ import ( "github.com/theodesp/go-heaps/pairing" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -28,7 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) var ( diff --git a/core/services/vrf/v2/listener_v2_log_listener.go b/core/services/vrf/v2/listener_v2_log_listener.go index b35593bd1ca..07b4c2c3800 100644 --- a/core/services/vrf/v2/listener_v2_log_listener.go +++ b/core/services/vrf/v2/listener_v2_log_listener.go @@ -10,12 +10,12 @@ import ( "github.com/ethereum/go-ethereum/common" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) func (lsn *listenerV2) runLogListener( diff --git a/core/sessions/ldapauth/ldap.go b/core/sessions/ldapauth/ldap.go index 04f6fbfbbb6..147f8bd2aed 100644 --- a/core/sessions/ldapauth/ldap.go +++ b/core/sessions/ldapauth/ldap.go @@ -34,6 +34,7 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/config" @@ -42,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) const ( diff --git a/core/sessions/localauth/orm.go b/core/sessions/localauth/orm.go index 090dc468a62..013f719ad34 100644 --- a/core/sessions/localauth/orm.go +++ b/core/sessions/localauth/orm.go @@ -9,6 +9,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -16,7 +17,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) type orm struct { diff --git a/core/utils/mathutil/mathutil.go b/core/utils/mathutil/mathutil.go deleted file mode 100644 index e9659a1f151..00000000000 --- a/core/utils/mathutil/mathutil.go +++ /dev/null @@ -1,23 +0,0 @@ -package mathutil - -import "golang.org/x/exp/constraints" - -func Max[V constraints.Ordered](first V, vals ...V) V { - max := first - for _, v := range vals { - if v > max { - max = v - } - } - return max -} - -func Min[V constraints.Ordered](first V, vals ...V) V { - min := first - for _, v := range vals { - if v < min { - min = v - } - } - return min -} diff --git a/core/utils/mathutil/mathutil_test.go b/core/utils/mathutil/mathutil_test.go deleted file mode 100644 index 24a464bb398..00000000000 --- a/core/utils/mathutil/mathutil_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package mathutil - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMax(t *testing.T) { - // Happy path - assert.Equal(t, 3, Max(3, 2, 1)) - // Single element - assert.Equal(t, 3, Max(3)) - // Signed - assert.Equal(t, -1, Max(-2, -1)) - // Uint64 - assert.Equal(t, uint64(2), Max(uint64(0), uint64(2))) - // String - assert.Equal(t, "c", Max("a", []string{"b", "c"}...)) -} - -func TestMin(t *testing.T) { - // Happy path - assert.Equal(t, 1, Min(3, 2, 1)) - // Single element - assert.Equal(t, 3, Min(3)) - // Signed - assert.Equal(t, -2, Min(-2, -1)) - // Uint64 - assert.Equal(t, uint64(0), Min(uint64(0), uint64(2))) - // String - assert.Equal(t, "a", Min("a", []string{"b", "c"}...)) -} From 6cc81208ad45a89914cd5d36048ce646c1ac6272 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Tue, 5 Dec 2023 14:17:19 +0200 Subject: [PATCH 010/234] VRF-784: add contract loader client for BSC to run CTF tests there (#11494) --- integration-tests/contracts/contract_loader.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index 6136e78b367..e66c95138b9 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -71,6 +71,8 @@ func NewContractLoader(bcClient blockchain.EVMClient, logger zerolog.Logger) (Co return &PolygonZkEvmContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil case *blockchain.WeMixClient: return &WeMixContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.BSCClient: + return &BSCContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil } return nil, errors.New("unknown blockchain client implementation for contract Loader, register blockchain client in NewContractLoader") } @@ -119,6 +121,11 @@ type WeMixContractLoader struct { *EthereumContractLoader } +// BSCContractLoader wraps ethereum contract deployments for BSC +type BSCContractLoader struct { + *EthereumContractLoader +} + // NewEthereumContractLoader returns an instantiated instance of the ETH contract Loader func NewEthereumContractLoader(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractLoader { return &EthereumContractLoader{ From 9d49a2002d8fb811ecbd754ac5171fb6c3807397 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:09:22 -0500 Subject: [PATCH 011/234] Check if ECR image exists before trying to publish (#11495) * Check if ECR image exists before trying to publish * Fix role to use correct secret --- .github/workflows/build-publish-pr.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml index a7186ee5a11..cdc9cf3f11c 100644 --- a/.github/workflows/build-publish-pr.yml +++ b/.github/workflows/build-publish-pr.yml @@ -16,11 +16,31 @@ jobs: permissions: id-token: write contents: read + env: + ECR_IMAGE_NAME: crib-chainlink-untrusted steps: + - name: Git Short SHA + shell: bash + env: + GIT_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + echo "GIT_SHORT_SHA=${GIT_PR_HEAD_SHA:0:7}" | tee -a "$GITHUB_ENV" + + - name: Check if image exists + id: check-image + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + with: + repository: ${{ env.ECR_IMAGE_NAME}} + tag: sha-${{ env.GIT_SHORT_SHA }} + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ secrets.AWS_OIDC_IAM_ROLE_PUBLISH_PR_ARN }} + - name: Checkout repository + if: steps.check-image.outputs.exists == 'false' uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Build and publish chainlink image + if: steps.check-image.outputs.exists == 'false' uses: ./.github/actions/build-sign-publish-chainlink with: publish: true @@ -29,7 +49,7 @@ jobs: aws-region: ${{ secrets.AWS_REGION }} sign-images: false ecr-hostname: ${{ secrets.AWS_SDLC_ECR_HOSTNAME }} - ecr-image-name: crib-chainlink-untrusted + ecr-image-name: ${{ env.ECR_IMAGE_NAME }} dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} From cf9ab4ec36292468172ef178e774d06faca005c5 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Tue, 5 Dec 2023 18:34:10 +0200 Subject: [PATCH 012/234] =?UTF-8?q?VRF-782:=20Fix=20setup-env=20script=20i?= =?UTF-8?q?n=20order=20to=20include=20flag=20for=20deploying=20=E2=80=A6?= =?UTF-8?q?=20(#11445)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-782: Fix setup-env script in order to include flag for deploying VRFOwner contract * Update main.go * VRF-782: Fix setup-env script to create ETH key --------- Co-authored-by: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> --- core/scripts/common/vrf/setup-envs/README.md | 6 ++++-- core/scripts/common/vrf/setup-envs/main.go | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/scripts/common/vrf/setup-envs/README.md b/core/scripts/common/vrf/setup-envs/README.md index 9aa76ffbbb7..5aa6aa4754f 100644 --- a/core/scripts/common/vrf/setup-envs/README.md +++ b/core/scripts/common/vrf/setup-envs/README.md @@ -38,7 +38,8 @@ go run . \ --batch-fulfillment-enabled="true" \ --min-confs=3 \ --register-vrf-key-against-address= +in order to call oracleWithdraw from this address> \ +--deploy-vrfv2-owner="true" ``` Optional parameters - will not be deployed if specified @@ -71,7 +72,7 @@ go run . \ --sending-key-funding-amount="1e17" \ --deploy-contracts-and-create-jobs="false" ``` -Then update corresponding deployment scripts with the new ETH addresses, specifying max gas price for each key +Then update corresponding deployment scripts in infra-k8s repo with the new ETH addresses, specifying max gas price for each key e.g.: ``` @@ -98,6 +99,7 @@ go run . \ --batch-fulfillment-enabled="true" \ --min-confs=3 \ --register-vrf-key-against-address="" \ +--deploy-vrfv2-owner="true" \ --link-address "" \ --link-eth-feed "" ``` diff --git a/core/scripts/common/vrf/setup-envs/main.go b/core/scripts/common/vrf/setup-envs/main.go index 94662aa1831..e9ea252e4c4 100644 --- a/core/scripts/common/vrf/setup-envs/main.go +++ b/core/scripts/common/vrf/setup-envs/main.go @@ -12,6 +12,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/shopspring/decimal" + "github.com/urfave/cli" + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/constants" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/model" @@ -21,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" - "github.com/urfave/cli" ) func newApp(remoteNodeURL string, writer io.Writer) (*clcmd.Shell, *cli.App) { @@ -87,6 +88,7 @@ func main() { batchCoordinatorAddressString := flag.String("batch-coordinator-address", "", "address Batch VRF Coordinator contract") registerVRFKeyAgainstAddress := flag.String("register-vrf-key-against-address", "", "VRF Key registration against address - "+ "from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments") + deployVRFOwner := flag.Bool("deploy-vrfv2-owner", true, "whether to deploy VRF owner contracts") e := helpers.SetupEnv(false) flag.Parse() @@ -211,6 +213,7 @@ func main() { coordinatorConfigV2, *batchFulfillmentEnabled, nodesMap, + *deployVRFOwner, ) case "v2plus": feeConfigV2Plus := vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig{ @@ -503,6 +506,7 @@ func createETHKeysIfNeeded(client *clcmd.Shell, app *cli.App, output *bytes.Buff var newKey presenters.ETHKeyResource flagSet := flag.NewFlagSet("blah", flag.ExitOnError) + flagSet.String("evm-chain-id", os.Getenv("ETH_CHAIN_ID"), "chain id") if *maxGasPriceGwei > 0 { helpers.PanicErr(flagSet.Set("max-gas-price-gwei", fmt.Sprintf("%d", *maxGasPriceGwei))) } From 14d22dfd39c5227ca2139108182a0b8801104f02 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:26:19 +0100 Subject: [PATCH 013/234] BCF-2823 Minor changes to distributeFunds (#11476) * Improve OperatorFactory deployNewOperatorAndForwarder natspec * Change to fwds distributeFunds to use call --- contracts/src/v0.8/operatorforwarder/dev/Operator.sol | 3 ++- contracts/src/v0.8/operatorforwarder/dev/OperatorFactory.sol | 2 +- .../generated/operator_factory/operator_factory.go | 2 +- .../generated/operator_wrapper/operator_wrapper.go | 2 +- .../generated-wrapper-dependency-versions-do-not-edit.txt | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/contracts/src/v0.8/operatorforwarder/dev/Operator.sol b/contracts/src/v0.8/operatorforwarder/dev/Operator.sol index c8451bf03ce..26295b27d16 100644 --- a/contracts/src/v0.8/operatorforwarder/dev/Operator.sol +++ b/contracts/src/v0.8/operatorforwarder/dev/Operator.sol @@ -318,7 +318,8 @@ contract Operator is AuthorizedReceiver, ConfirmedOwner, LinkTokenReceiver, Oper for (uint256 i = 0; i < receivers.length; ++i) { uint256 sendAmount = amounts[i]; valueRemaining = valueRemaining - sendAmount; - receivers[i].transfer(sendAmount); + (bool success, ) = receivers[i].call{value: sendAmount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); } require(valueRemaining == 0, "Too much ETH sent"); } diff --git a/contracts/src/v0.8/operatorforwarder/dev/OperatorFactory.sol b/contracts/src/v0.8/operatorforwarder/dev/OperatorFactory.sol index 62ace2451c5..0ff4bb6562e 100644 --- a/contracts/src/v0.8/operatorforwarder/dev/OperatorFactory.sol +++ b/contracts/src/v0.8/operatorforwarder/dev/OperatorFactory.sol @@ -34,7 +34,7 @@ contract OperatorFactory { } // @notice creates a new Operator contract with the msg.sender as owner and a - // new Operator Forwarder with the Operator as the owner + // new Operator Forwarder with the OperatorFactory as the owner function deployNewOperatorAndForwarder() external returns (address, address) { Operator operator = new Operator(linkToken, msg.sender); s_created[address(operator)] = true; diff --git a/core/gethwrappers/generated/operator_factory/operator_factory.go b/core/gethwrappers/generated/operator_factory/operator_factory.go index c9a6c57ce21..c14ef439368 100644 --- a/core/gethwrappers/generated/operator_factory/operator_factory.go +++ b/core/gethwrappers/generated/operator_factory/operator_factory.go @@ -32,7 +32,7 @@ var ( var OperatorFactoryMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"linkAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AuthorizedForwarderCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"OperatorCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"created\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deployNewForwarder\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"deployNewForwarderAndTransferOwnership\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deployNewOperator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deployNewOperatorAndForwarder\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"linkToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60a060405234801561001057600080fd5b50604051615caf380380615caf83398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b608051615c016100ae6000396000818161014f015281816101e6015281816102e3015281816103da015281816104be01526105a50152615c016000f3fe60806040523480156200001157600080fd5b5060043610620000875760003560e01c806357970e93116200006257806357970e931462000149578063d42efd831462000171578063d689d09514620001be578063f4adb6e114620001d557600080fd5b8063181f5a77146200008c57806332f01eae14620000e15780633babafdb1462000119575b600080fd5b620000c96040518060400160405280601581526020017f4f70657261746f72466163746f727920312e302e30000000000000000000000081525081565b604051620000d8919062000717565b60405180910390f35b620000eb620001df565b6040805173ffffffffffffffffffffffffffffffffffffffff938416815292909116602083015201620000d8565b62000123620003c6565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001620000d8565b620001237f000000000000000000000000000000000000000000000000000000000000000081565b620001ad620001823660046200075d565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b6040519015158152602001620000d8565b62000123620001cf3660046200077b565b620004b9565b62000123620005a0565b60008060007f000000000000000000000000000000000000000000000000000000000000000033604051620002149062000695565b73ffffffffffffffffffffffffffffffffffffffff928316815291166020820152604001604051809103906000f08015801562000255573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917fd3bb727b2e716a1f142bc9c63c66fe0ae4c5fbc89234f8aa77d0c864a7b63bab91a4604080516000808252602082019092527f000000000000000000000000000000000000000000000000000000000000000090309084906040516200031590620006a3565b62000324949392919062000805565b604051809103906000f08015801562000341573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033923092917f1c9576ab03e40fdf23673f82d904a0f029c8a6629272a4edad4be877e83af64b91a490939092509050565b6040805160008082526020820190925281907f000000000000000000000000000000000000000000000000000000000000000090339083906040516200040c90620006a3565b6200041b949392919062000805565b604051809103906000f08015801562000438573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917f1c9576ab03e40fdf23673f82d904a0f029c8a6629272a4edad4be877e83af64b91a4919050565b6000807f000000000000000000000000000000000000000000000000000000000000000033868686604051620004ef90620006a3565b620004ff95949392919062000852565b604051809103906000f0801580156200051c573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917f1c9576ab03e40fdf23673f82d904a0f029c8a6629272a4edad4be877e83af64b91a4949350505050565b6000807f000000000000000000000000000000000000000000000000000000000000000033604051620005d39062000695565b73ffffffffffffffffffffffffffffffffffffffff928316815291166020820152604001604051809103906000f08015801562000614573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917fd3bb727b2e716a1f142bc9c63c66fe0ae4c5fbc89234f8aa77d0c864a7b63bab91a4919050565b613c8880620008d483390190565b611699806200455c83390190565b6000815180845260005b81811015620006d957602081850181015186830182015201620006bb565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006200072c6020830184620006b1565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146200075857600080fd5b919050565b6000602082840312156200077057600080fd5b6200072c8262000733565b6000806000604084860312156200079157600080fd5b6200079c8462000733565b9250602084013567ffffffffffffffff80821115620007ba57600080fd5b818601915086601f830112620007cf57600080fd5b813581811115620007df57600080fd5b876020828501011115620007f257600080fd5b6020830194508093505050509250925092565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015280851660408401525060806060830152620008486080830184620006b1565b9695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015280861660408401525060806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050969550505050505056fe60a060405260016006553480156200001657600080fd5b5060405162003c8838038062003c888339810160408190526200003991620001ab565b808060006001600160a01b038216620000995760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0384811691909117909155811615620000cc57620000cc81620000e2565b505050506001600160a01b0316608052620001e3565b336001600160a01b038216036200013c5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000090565b600380546001600160a01b0319166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b80516001600160a01b0381168114620001a657600080fd5b919050565b60008060408385031215620001bf57600080fd5b620001ca836200018e565b9150620001da602084016200018e565b90509250929050565b608051613a4362000245600039600081816101ec0152818161075e015281816109f301528181610c4f015281816117d201528181611a3c01528181611adc01528181611e7701528181612310015281816125c30152612b4b0152613a436000f3fe6080604052600436106101965760003560e01c80636ae0bc76116100e1578063a4c0ed361161008a578063f2fde38b11610064578063f2fde38b146104aa578063f3fef3a3146104ca578063fa00763a146104ea578063fc4a03ed1461053057600080fd5b8063a4c0ed361461044a578063eb007d991461046a578063ee56997b1461048a57600080fd5b806379ba5097116100bb57806379ba5097146103ea5780638da5cb5b146103ff578063902fc3701461042a57600080fd5b80636ae0bc76146103975780636bd59ec0146103b75780636ee4d553146103ca57600080fd5b80633ec5bc1411610143578063501883011161011d578063501883011461033e57806352043783146103615780635ffa62881461037757600080fd5b80633ec5bc14146102ce57806340429946146102ee5780634ab0d1901461030e57600080fd5b8063181f5a7711610174578063181f5a77146102365780632408afaa1461028c5780633c6d41b9146102ae57600080fd5b806301994b991461019b578063033f49f7146101bd578063165d35e1146101dd575b600080fd5b3480156101a757600080fd5b506101bb6101b6366004612fbe565b610550565b005b3480156101c957600080fd5b506101bb6101d8366004613064565b610753565b3480156101e957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561024257600080fd5b5061027f6040518060400160405280600e81526020017f4f70657261746f7220312e302e3000000000000000000000000000000000000081525081565b60405161022d91906130dd565b34801561029857600080fd5b506102a161096c565b60405161022d919061312e565b3480156102ba57600080fd5b506101bb6102c93660046131bd565b6109db565b3480156102da57600080fd5b506101bb6102e936600461324a565b610ae3565b3480156102fa57600080fd5b506101bb6103093660046132a1565b610c37565b34801561031a57600080fd5b5061032e610329366004613344565b610d40565b604051901515815260200161022d565b34801561034a57600080fd5b50610353611036565b60405190815260200161022d565b34801561036d57600080fd5b5061035361012c81565b34801561038357600080fd5b506101bb61039236600461339e565b611045565b3480156103a357600080fd5b5061032e6103b236600461340a565b6110c9565b6101bb6103c536600461339e565b611445565b3480156103d657600080fd5b506101bb6103e536600461348e565b6115d8565b3480156103f657600080fd5b506101bb61185c565b34801561040b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff1661020c565b34801561043657600080fd5b5061032e6104453660046134cb565b61195d565b34801561045657600080fd5b506101bb61046536600461354a565b611ac4565b34801561047657600080fd5b506101bb61048536600461348e565b611c52565b34801561049657600080fd5b506101bb6104a5366004612fbe565b611f02565b3480156104b657600080fd5b506101bb6104c5366004613635565b612210565b3480156104d657600080fd5b506101bb6104e5366004613659565b612224565b3480156104f657600080fd5b5061032e610505366004613635565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b34801561053c57600080fd5b506101bb61054b36600461339e565b612389565b6105586124e5565b6105c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064015b60405180910390fd5b60005b8181101561074e576001600560008585858181106105e6576105e6613685565b90506020020160208101906105fb9190613635565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905582828281811061066057610660613685565b90506020020160208101906106759190613635565b73ffffffffffffffffffffffffffffffffffffffff167f615a0c1cb00a60d4acd77ec67acf2f17f223ef0932d591052fabc33643fe7e8260405160405180910390a28282828181106106c9576106c9613685565b90506020020160208101906106de9190613635565b73ffffffffffffffffffffffffffffffffffffffff166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561072557600080fd5b505af1158015610739573d6000803e3d6000fd5b5050505080610747906136e3565b90506105c6565b505050565b61075b61253a565b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610811576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff84163b61088f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d75737420666f727761726420746f206120636f6e747261637400000000000060448201526064016105ba565b60008473ffffffffffffffffffffffffffffffffffffffff1684846040516108b892919061371b565b6000604051808303816000865af19150503d80600081146108f5576040519150601f19603f3d011682016040523d82523d6000602084013e6108fa565b606091505b5050905080610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f466f727761726465642063616c6c206661696c6564000000000000000000000060448201526064016105ba565b5050505050565b606060018054806020026020016040519081016040528092919081815260200182805480156109d157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116109a6575b5050505050905090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610a8b8a8a8c8a8a8a6125bd565b91509150877fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658b848c8e8c878c8c8c604051610acf99989796959493929190613774565b60405180910390a250505050505050505050565b610aeb61253a565b60005b82811015610c3157600060056000868685818110610b0e57610b0e613685565b9050602002016020810190610b239190613635565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055838382818110610b8857610b88613685565b9050602002016020810190610b9d9190613635565b6040517ff2fde38b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152919091169063f2fde38b90602401600060405180830381600087803b158015610c0857600080fd5b505af1158015610c1c573d6000803e3d6000fd5b5050505080610c2a906136e3565b9050610aee565b50505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610cd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610ce78b8b8a8a8a8a6125bd565b91509150887fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658c848d8f8c878c8c8c604051610d2b99989796959493929190613774565b60405180910390a25050505050505050505050565b6000610d4a61289b565b600087815260046020526040812054889160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260056020526040902054869060ff1615610e7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b610e8c89898989896001612914565b60405189907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015610f24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008773ffffffffffffffffffffffffffffffffffffffff16878b87604051602401610f5a929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610fe391906137ff565b6000604051808303816000865af19150503d8060008114611020576040519150601f19603f3d011682016040523d82523d6000602084013e611025565b606091505b50909b9a5050505050505050505050565b6000611040612b0c565b905090565b61104d6124e5565b6110b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b6110bd8484610550565b610c3184848484612389565b60006110d361289b565b600088815260046020526040812054899160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003611174576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040902054879060ff1615611206576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b8985856020811015611274576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526573706f6e7365206d757374206265203e203332206279746573000000000060448201526064016105ba565b81358381146112df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f466972737420776f7264206d757374206265207265717565737449640000000060448201526064016105ba565b6112ee8e8e8e8e8e6002612914565b6040518e907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015611386576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008c73ffffffffffffffffffffffffffffffffffffffff168c8b8b6040516020016113b49392919061381b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526113ec916137ff565b6000604051808303816000865af19150503d8060008114611429576040519150601f19603f3d011682016040523d82523d6000602084013e61142e565b606091505b509098505050505050505050979650505050505050565b821580159061145357508281145b6114b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c6964206172726179206c656e67746828732900000000000000000060448201526064016105ba565b3460005b8481101561156f5760008484838181106114d9576114d9613685565b90506020020135905080836114ee9190613857565b925086868381811061150257611502613685565b90506020020160208101906115179190613635565b73ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561155c573d6000803e3d6000fd5b505080611568906136e3565b90506114bd565b508015610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f546f6f206d756368204554482073656e7400000000000000000000000000000060448201526064016105ba565b6040805160208082018690527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b16828401527fffffffff00000000000000000000000000000000000000000000000000000000851660548301526058808301859052835180840390910181526078909201909252805191012060009060008681526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116908216146116fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b42821115611765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000858152600460205260408082208290555186917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af1158015611830573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118549190613870565b505050505050565b60035473ffffffffffffffffffffffffffffffffffffffff1633146118dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016105ba565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560038054909116905560405173ffffffffffffffffffffffffffffffffffffffff909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b600061196761253a565b8380611971612b0c565b10156119ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517f4000aea000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634000aea090611a77908990899089908990600401613892565b6020604051808303816000875af1158015611a96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aba9190613870565b9695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611b63576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b60208101518190611b748183612bd5565b84602484015283604484015260003073ffffffffffffffffffffffffffffffffffffffff1684604051611ba791906137ff565b600060405180830381855af49150503d8060008114611be2576040519150601f19603f3d011682016040523d82523d6000602084013e611be7565b606091505b5050905080611854576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f556e61626c6520746f206372656174652072657175657374000000000000000060448201526064016105ba565b604080513360601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208083018290526034808401899052845180850390910181526054840185528051908201206074840188905260948401929092527fffffffff00000000000000000000000000000000000000000000000000000000861660a884015260ac8084018690528451808503909101815260cc9093019093528151919092012060009060008381526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614611da0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b42831115611e0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000828152600460205260408082208290555183917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018690527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af1158015611ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef99190613870565b50505050505050565b611f0a6124e5565b611f70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b80611fd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d7573742068617665206174206c6561737420312073656e646572000000000060448201526064016105ba565b60015460005b8181101561206c57600080600060018481548110611ffd57611ffd613685565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055612065816136e3565b9050611fdd565b5060005b828110156121c25760008085858481811061208d5761208d613685565b90506020020160208101906120a29190613635565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205460ff1615612133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4d757374206e6f742068617665206475706c69636174652073656e646572730060448201526064016105ba565b600160008086868581811061214a5761214a613685565b905060200201602081019061215f9190613635565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556121bb816136e3565b9050612070565b506121cf60018484612ede565b507ff263cfb3e4298332e776194610cf9fdc09ccb3ada8b9aa39764d882e11fbf0a08383336040516122039392919061391e565b60405180910390a1505050565b61221861253a565b61222181612d51565b50565b61222c61253a565b8080612236612b0c565b10156122c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612359573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237d9190613870565b61074e5761074e613958565b6123916124e5565b6123f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b7f1bb185903e2cb2f1b303523128b60e314dea81df4f8d9b7351cadd344f6e7727848484843360405161242e959493929190613987565b60405180910390a160005b838110156109655784848281811061245357612453613685565b90506020020160208101906124689190613635565b73ffffffffffffffffffffffffffffffffffffffff1663ee56997b84846040518363ffffffff1660e01b81526004016124a29291906139d7565b600060405180830381600087803b1580156124bc57600080fd5b505af11580156124d0573d6000803e3d6000fd5b50505050806124de906136e3565b9050612439565b3360009081526020819052604081205460ff168061104057503361251e60025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60025473ffffffffffffffffffffffffffffffffffffffff1633146125bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016105ba565b565b600080857f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612676576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b6040517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608b901b16602082015260348101869052605401604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301206000818152600490935291205490935060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001615612781576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4d75737420757365206120756e6971756520494400000000000000000000000060448201526064016105ba565b61278d61012c426139f3565b6040805160208082018c90527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b16828401527fffffffff000000000000000000000000000000000000000000000000000000008a1660548301526058808301859052835180840390910181526078909201909252805191012090925060405180604001604052808260ff1916815260200161282c87612e47565b60ff9081169091526000868152600460209081526040909120835193909101519091167f01000000000000000000000000000000000000000000000000000000000000000260089290921c91909117905560065461288b908a906139f3565b6006555050965096945050505050565b3360009081526020819052604090205460ff166125bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f7420617574686f72697a65642073656e646572000000000000000000000060448201526064016105ba565b6040805160208082018890527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b16828401527fffffffff00000000000000000000000000000000000000000000000000000000861660548301526058808301869052835180840390910181526078909201909252805191012060009060008881526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614612a38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b612a4182612e47565b60008881526004602052604090205460ff9182167f01000000000000000000000000000000000000000000000000000000000000009091049091161115612ae4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f446174612076657273696f6e73206d757374206d61746368000000000000000060448201526064016105ba565b85600654612af29190613857565b600655505050600093845250506004602052506040812055565b60006001600654612b1d9190613857565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612ba7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bcb9190613a06565b6110409190613857565b612be160026020613a1f565b612bec9060046139f3565b81511015612c56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c69642072657175657374206c656e6774680000000000000000000060448201526064016105ba565b7fffffffff0000000000000000000000000000000000000000000000000000000082167f3c6d41b9000000000000000000000000000000000000000000000000000000001480612ce757507fffffffff0000000000000000000000000000000000000000000000000000000082167f4042994600000000000000000000000000000000000000000000000000000000145b612d4d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d757374207573652077686974656c69737465642066756e6374696f6e73000060448201526064016105ba565b5050565b3373ffffffffffffffffffffffffffffffffffffffff821603612dd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016105ba565b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b600060ff821115612eda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f206269747300000000000000000000000000000000000000000000000000000060648201526084016105ba565b5090565b828054828255906000526020600020908101928215612f56579160200282015b82811115612f565781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190612efe565b50612eda9291505b80821115612eda5760008155600101612f5e565b60008083601f840112612f8457600080fd5b50813567ffffffffffffffff811115612f9c57600080fd5b6020830191508360208260051b8501011115612fb757600080fd5b9250929050565b60008060208385031215612fd157600080fd5b823567ffffffffffffffff811115612fe857600080fd5b612ff485828601612f72565b90969095509350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461222157600080fd5b60008083601f84011261303457600080fd5b50813567ffffffffffffffff81111561304c57600080fd5b602083019150836020828501011115612fb757600080fd5b60008060006040848603121561307957600080fd5b833561308481613000565b9250602084013567ffffffffffffffff8111156130a057600080fd5b6130ac86828701613022565b9497909650939450505050565b60005b838110156130d45781810151838201526020016130bc565b50506000910152565b60208152600082518060208401526130fc8160408501602087016130b9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020808252825182820181905260009190848201906040850190845b8181101561317c57835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161314a565b50909695505050505050565b80357fffffffff00000000000000000000000000000000000000000000000000000000811681146131b857600080fd5b919050565b60008060008060008060008060e0898b0312156131d957600080fd5b88356131e481613000565b9750602089013596506040890135955061320060608a01613188565b94506080890135935060a0890135925060c089013567ffffffffffffffff81111561322a57600080fd5b6132368b828c01613022565b999c989b5096995094979396929594505050565b60008060006040848603121561325f57600080fd5b833567ffffffffffffffff81111561327657600080fd5b61328286828701612f72565b909450925050602084013561329681613000565b809150509250925092565b60008060008060008060008060006101008a8c0312156132c057600080fd5b89356132cb81613000565b985060208a0135975060408a0135965060608a01356132e981613000565b95506132f760808b01613188565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff81111561332157600080fd5b61332d8c828d01613022565b915080935050809150509295985092959850929598565b60008060008060008060c0878903121561335d57600080fd5b8635955060208701359450604087013561337681613000565b935061338460608801613188565b92506080870135915060a087013590509295509295509295565b600080600080604085870312156133b457600080fd5b843567ffffffffffffffff808211156133cc57600080fd5b6133d888838901612f72565b909650945060208701359150808211156133f157600080fd5b506133fe87828801612f72565b95989497509550505050565b600080600080600080600060c0888a03121561342557600080fd5b8735965060208801359550604088013561343e81613000565b945061344c60608901613188565b93506080880135925060a088013567ffffffffffffffff81111561346f57600080fd5b61347b8a828b01613022565b989b979a50959850939692959293505050565b600080600080608085870312156134a457600080fd5b84359350602085013592506134bb60408601613188565b9396929550929360600135925050565b600080600080606085870312156134e157600080fd5b84356134ec81613000565b935060208501359250604085013567ffffffffffffffff81111561350f57600080fd5b6133fe87828801613022565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561355f57600080fd5b833561356a81613000565b925060208401359150604084013567ffffffffffffffff8082111561358e57600080fd5b818601915086601f8301126135a257600080fd5b8135818111156135b4576135b461351b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156135fa576135fa61351b565b8160405282815289602084870101111561361357600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561364757600080fd5b813561365281613000565b9392505050565b6000806040838503121561366c57600080fd5b823561367781613000565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613714576137146136b4565b5060010190565b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010073ffffffffffffffffffffffffffffffffffffffff808d1684528b60208501528a6040850152808a166060850152507fffffffff00000000000000000000000000000000000000000000000000000000881660808401528660a08401528560c08401528060e08401526137ef818401858761372b565b9c9b505050505050505050505050565b600082516138118184602087016130b9565b9190910192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000084168152818360048301376000910160040190815292915050565b8181038181111561386a5761386a6136b4565b92915050565b60006020828403121561388257600080fd5b8151801515811461365257600080fd5b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000611aba60608301848661372b565b8183526000602080850194508260005b858110156139135781356138eb81613000565b73ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016138d8565b509495945050505050565b6040815260006139326040830185876138c8565b905073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60608152600061399b6060830187896138c8565b82810360208401526139ae8186886138c8565b91505073ffffffffffffffffffffffffffffffffffffffff831660408301529695505050505050565b6020815260006139eb6020830184866138c8565b949350505050565b8082018082111561386a5761386a6136b4565b600060208284031215613a1857600080fd5b5051919050565b808202811582820484141761386a5761386a6136b456fea164736f6c6343000813000a60a06040523480156200001157600080fd5b50604051620016993803806200169983398101604081905262000034916200029d565b82826001600160a01b038216620000925760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c557620000c58162000199565b50506001600160a01b0384166200012b5760405162461bcd60e51b815260206004820152602360248201527f4c696e6b20746f6b656e2063616e6e6f742062652061207a65726f206164647260448201526265737360e81b606482015260840162000089565b6001600160a01b038085166080528216156200018f57816001600160a01b0316836001600160a01b03167f4e1e878dc28d5f040db5969163ff1acd75c44c3f655da2dde9c70bbd8e56dc7e836040516200018691906200038e565b60405180910390a35b50505050620003c3565b336001600160a01b03821603620001f35760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000089565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b03811681146200025c57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620002945781810151838201526020016200027a565b50506000910152565b60008060008060808587031215620002b457600080fd5b620002bf8562000244565b9350620002cf6020860162000244565b9250620002df6040860162000244565b60608601519092506001600160401b0380821115620002fd57600080fd5b818701915087601f8301126200031257600080fd5b81518181111562000327576200032762000261565b604051601f8201601f19908116603f0116810190838211818310171562000352576200035262000261565b816040528281528a60208487010111156200036c57600080fd5b6200037f83602083016020880162000277565b979a9699509497505050505050565b6020815260008251806020840152620003af81604085016020870162000277565b601f01601f19169190910160400192915050565b6080516112ac620003ed6000396000818161016d0152818161037501526105d301526112ac6000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c806379ba509711610081578063ee56997b1161005b578063ee56997b14610200578063f2fde38b14610213578063fa00763a1461022657600080fd5b806379ba5097146101c75780638da5cb5b146101cf578063b64fa9e6146101ed57600080fd5b80634d3e2323116100b25780634d3e23231461015557806357970e93146101685780636fadcf72146101b457600080fd5b8063033f49f7146100d9578063181f5a77146100ee5780632408afaa14610140575b600080fd5b6100ec6100e7366004610e72565b61026f565b005b61012a6040518060400160405280601981526020017f417574686f72697a6564466f7277617264657220312e312e300000000000000081525081565b6040516101379190610ef5565b60405180910390f35b610148610287565b6040516101379190610f61565b6100ec610163366004610e72565b6102f6565b61018f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610137565b6100ec6101c2366004610e72565b61036b565b6100ec61042d565b60005473ffffffffffffffffffffffffffffffffffffffff1661018f565b6100ec6101fb366004611007565b61052a565b6100ec61020e366004611073565b6106cb565b6100ec6102213660046110b5565b6109dc565b61025f6102343660046110b5565b73ffffffffffffffffffffffffffffffffffffffff1660009081526002602052604090205460ff1690565b6040519015158152602001610137565b6102776109f0565b610282838383610a73565b505050565b606060038054806020026020016040519081016040528092919081815260200182805480156102ec57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116102c1575b5050505050905090565b6102ff836109dc565b8273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f4e1e878dc28d5f040db5969163ff1acd75c44c3f655da2dde9c70bbd8e56dc7e848460405161035e9291906110d7565b60405180910390a3505050565b610373610c00565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610277576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f43616e6e6f7420666f727761726420746f204c696e6b20746f6b656e0000000060448201526064015b60405180910390fd5b60015473ffffffffffffffffffffffffffffffffffffffff1633146104ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610424565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610532610c00565b82811461059b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f417272617973206d7573742068617665207468652073616d65206c656e6774686044820152606401610424565b60005b838110156106c45760008585838181106105ba576105ba611124565b90506020020160208101906105cf91906110b5565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610686576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f43616e6e6f7420666f727761726420746f204c696e6b20746f6b656e000000006044820152606401610424565b6106b38185858581811061069c5761069c611124565b90506020028101906106ae9190611153565b610a73565b506106bd816111b8565b905061059e565b5050505050565b6106d3610c79565b610739576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e646572730000006044820152606401610424565b806107a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d7573742068617665206174206c6561737420312073656e64657200000000006044820152606401610424565b60035460005b8181101561083657600060026000600384815481106107c7576107c7611124565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561082f816111b8565b90506107a6565b5060005b8281101561098e576002600085858481811061085857610858611124565b905060200201602081019061086d91906110b5565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205460ff16156108fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4d757374206e6f742068617665206475706c69636174652073656e64657273006044820152606401610424565b60016002600086868581811061091657610916611124565b905060200201602081019061092b91906110b5565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055610987816111b8565b905061083a565b5061099b60038484610dac565b507ff263cfb3e4298332e776194610cf9fdc09ccb3ada8b9aa39764d882e11fbf0a08383336040516109cf93929190611217565b60405180910390a1505050565b6109e46109f0565b6109ed81610cb7565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610424565b565b73ffffffffffffffffffffffffffffffffffffffff83163b610af1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d75737420666f727761726420746f206120636f6e74726163740000000000006044820152606401610424565b6000808473ffffffffffffffffffffffffffffffffffffffff168484604051610b1b92919061128f565b6000604051808303816000865af19150503d8060008114610b58576040519150601f19603f3d011682016040523d82523d6000602084013e610b5d565b606091505b5091509150816106c4578051600003610bf8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f466f727761726465642063616c6c20726576657274656420776974686f75742060448201527f726561736f6e00000000000000000000000000000000000000000000000000006064820152608401610424565b805181602001fd5b3360009081526002602052604090205460ff16610a71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f7420617574686f72697a65642073656e64657200000000000000000000006044820152606401610424565b600033610c9b60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b3373ffffffffffffffffffffffffffffffffffffffff821603610d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610424565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b828054828255906000526020600020908101928215610e24579160200282015b82811115610e245781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190610dcc565b50610e30929150610e34565b5090565b5b80821115610e305760008155600101610e35565b803573ffffffffffffffffffffffffffffffffffffffff81168114610e6d57600080fd5b919050565b600080600060408486031215610e8757600080fd5b610e9084610e49565b9250602084013567ffffffffffffffff80821115610ead57600080fd5b818601915086601f830112610ec157600080fd5b813581811115610ed057600080fd5b876020828501011115610ee257600080fd5b6020830194508093505050509250925092565b600060208083528351808285015260005b81811015610f2257858101830151858201604001528201610f06565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b6020808252825182820181905260009190848201906040850190845b81811015610faf57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610f7d565b50909695505050505050565b60008083601f840112610fcd57600080fd5b50813567ffffffffffffffff811115610fe557600080fd5b6020830191508360208260051b850101111561100057600080fd5b9250929050565b6000806000806040858703121561101d57600080fd5b843567ffffffffffffffff8082111561103557600080fd5b61104188838901610fbb565b9096509450602087013591508082111561105a57600080fd5b5061106787828801610fbb565b95989497509550505050565b6000806020838503121561108657600080fd5b823567ffffffffffffffff81111561109d57600080fd5b6110a985828601610fbb565b90969095509350505050565b6000602082840312156110c757600080fd5b6110d082610e49565b9392505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261118857600080fd5b83018035915067ffffffffffffffff8211156111a357600080fd5b60200191503681900382131561100057600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611210577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b6040808252810183905260008460608301825b868110156112655773ffffffffffffffffffffffffffffffffffffffff61125084610e49565b1682526020928301929091019060010161122a565b50809250505073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b818382376000910190815291905056fea164736f6c6343000813000aa164736f6c6343000813000a", + Bin: "0x60a060405234801561001057600080fd5b50604051615d59380380615d5983398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b608051615cab6100ae6000396000818161014f015281816101e6015281816102e3015281816103da015281816104be01526105a50152615cab6000f3fe60806040523480156200001157600080fd5b5060043610620000875760003560e01c806357970e93116200006257806357970e931462000149578063d42efd831462000171578063d689d09514620001be578063f4adb6e114620001d557600080fd5b8063181f5a77146200008c57806332f01eae14620000e15780633babafdb1462000119575b600080fd5b620000c96040518060400160405280601581526020017f4f70657261746f72466163746f727920312e302e30000000000000000000000081525081565b604051620000d8919062000717565b60405180910390f35b620000eb620001df565b6040805173ffffffffffffffffffffffffffffffffffffffff938416815292909116602083015201620000d8565b62000123620003c6565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001620000d8565b620001237f000000000000000000000000000000000000000000000000000000000000000081565b620001ad620001823660046200075d565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b6040519015158152602001620000d8565b62000123620001cf3660046200077b565b620004b9565b62000123620005a0565b60008060007f000000000000000000000000000000000000000000000000000000000000000033604051620002149062000695565b73ffffffffffffffffffffffffffffffffffffffff928316815291166020820152604001604051809103906000f08015801562000255573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917fd3bb727b2e716a1f142bc9c63c66fe0ae4c5fbc89234f8aa77d0c864a7b63bab91a4604080516000808252602082019092527f000000000000000000000000000000000000000000000000000000000000000090309084906040516200031590620006a3565b62000324949392919062000805565b604051809103906000f08015801562000341573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033923092917f1c9576ab03e40fdf23673f82d904a0f029c8a6629272a4edad4be877e83af64b91a490939092509050565b6040805160008082526020820190925281907f000000000000000000000000000000000000000000000000000000000000000090339083906040516200040c90620006a3565b6200041b949392919062000805565b604051809103906000f08015801562000438573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917f1c9576ab03e40fdf23673f82d904a0f029c8a6629272a4edad4be877e83af64b91a4919050565b6000807f000000000000000000000000000000000000000000000000000000000000000033868686604051620004ef90620006a3565b620004ff95949392919062000852565b604051809103906000f0801580156200051c573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917f1c9576ab03e40fdf23673f82d904a0f029c8a6629272a4edad4be877e83af64b91a4949350505050565b6000807f000000000000000000000000000000000000000000000000000000000000000033604051620005d39062000695565b73ffffffffffffffffffffffffffffffffffffffff928316815291166020820152604001604051809103906000f08015801562000614573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff811660008181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555192935033928392917fd3bb727b2e716a1f142bc9c63c66fe0ae4c5fbc89234f8aa77d0c864a7b63bab91a4919050565b613d3280620008d483390190565b611699806200460683390190565b6000815180845260005b81811015620006d957602081850181015186830182015201620006bb565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006200072c6020830184620006b1565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146200075857600080fd5b919050565b6000602082840312156200077057600080fd5b6200072c8262000733565b6000806000604084860312156200079157600080fd5b6200079c8462000733565b9250602084013567ffffffffffffffff80821115620007ba57600080fd5b818601915086601f830112620007cf57600080fd5b813581811115620007df57600080fd5b876020828501011115620007f257600080fd5b6020830194508093505050509250925092565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015280851660408401525060806060830152620008486080830184620006b1565b9695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015280861660408401525060806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050969550505050505056fe60a060405260016006553480156200001657600080fd5b5060405162003d3238038062003d328339810160408190526200003991620001ab565b808060006001600160a01b038216620000995760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0384811691909117909155811615620000cc57620000cc81620000e2565b505050506001600160a01b0316608052620001e3565b336001600160a01b038216036200013c5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000090565b600380546001600160a01b0319166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b80516001600160a01b0381168114620001a657600080fd5b919050565b60008060408385031215620001bf57600080fd5b620001ca836200018e565b9150620001da602084016200018e565b90509250929050565b608051613aed62000245600039600081816101ec0152818161075e015281816109f301528181610c4f0152818161187c01528181611ae601528181611b8601528181611f21015281816123ba0152818161266d0152612bf50152613aed6000f3fe6080604052600436106101965760003560e01c80636ae0bc76116100e1578063a4c0ed361161008a578063f2fde38b11610064578063f2fde38b146104aa578063f3fef3a3146104ca578063fa00763a146104ea578063fc4a03ed1461053057600080fd5b8063a4c0ed361461044a578063eb007d991461046a578063ee56997b1461048a57600080fd5b806379ba5097116100bb57806379ba5097146103ea5780638da5cb5b146103ff578063902fc3701461042a57600080fd5b80636ae0bc76146103975780636bd59ec0146103b75780636ee4d553146103ca57600080fd5b80633ec5bc1411610143578063501883011161011d578063501883011461033e57806352043783146103615780635ffa62881461037757600080fd5b80633ec5bc14146102ce57806340429946146102ee5780634ab0d1901461030e57600080fd5b8063181f5a7711610174578063181f5a77146102365780632408afaa1461028c5780633c6d41b9146102ae57600080fd5b806301994b991461019b578063033f49f7146101bd578063165d35e1146101dd575b600080fd5b3480156101a757600080fd5b506101bb6101b6366004613068565b610550565b005b3480156101c957600080fd5b506101bb6101d836600461310e565b610753565b3480156101e957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561024257600080fd5b5061027f6040518060400160405280600e81526020017f4f70657261746f7220312e302e3000000000000000000000000000000000000081525081565b60405161022d9190613187565b34801561029857600080fd5b506102a161096c565b60405161022d91906131d8565b3480156102ba57600080fd5b506101bb6102c9366004613267565b6109db565b3480156102da57600080fd5b506101bb6102e93660046132f4565b610ae3565b3480156102fa57600080fd5b506101bb61030936600461334b565b610c37565b34801561031a57600080fd5b5061032e6103293660046133ee565b610d40565b604051901515815260200161022d565b34801561034a57600080fd5b50610353611036565b60405190815260200161022d565b34801561036d57600080fd5b5061035361012c81565b34801561038357600080fd5b506101bb610392366004613448565b611045565b3480156103a357600080fd5b5061032e6103b23660046134b4565b6110c9565b6101bb6103c5366004613448565b611445565b3480156103d657600080fd5b506101bb6103e5366004613538565b611682565b3480156103f657600080fd5b506101bb611906565b34801561040b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff1661020c565b34801561043657600080fd5b5061032e610445366004613575565b611a07565b34801561045657600080fd5b506101bb6104653660046135f4565b611b6e565b34801561047657600080fd5b506101bb610485366004613538565b611cfc565b34801561049657600080fd5b506101bb6104a5366004613068565b611fac565b3480156104b657600080fd5b506101bb6104c53660046136df565b6122ba565b3480156104d657600080fd5b506101bb6104e5366004613703565b6122ce565b3480156104f657600080fd5b5061032e6105053660046136df565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b34801561053c57600080fd5b506101bb61054b366004613448565b612433565b61055861258f565b6105c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064015b60405180910390fd5b60005b8181101561074e576001600560008585858181106105e6576105e661372f565b90506020020160208101906105fb91906136df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558282828181106106605761066061372f565b905060200201602081019061067591906136df565b73ffffffffffffffffffffffffffffffffffffffff167f615a0c1cb00a60d4acd77ec67acf2f17f223ef0932d591052fabc33643fe7e8260405160405180910390a28282828181106106c9576106c961372f565b90506020020160208101906106de91906136df565b73ffffffffffffffffffffffffffffffffffffffff166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561072557600080fd5b505af1158015610739573d6000803e3d6000fd5b50505050806107479061378d565b90506105c6565b505050565b61075b6125e4565b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610811576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff84163b61088f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d75737420666f727761726420746f206120636f6e747261637400000000000060448201526064016105ba565b60008473ffffffffffffffffffffffffffffffffffffffff1684846040516108b89291906137c5565b6000604051808303816000865af19150503d80600081146108f5576040519150601f19603f3d011682016040523d82523d6000602084013e6108fa565b606091505b5050905080610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f466f727761726465642063616c6c206661696c6564000000000000000000000060448201526064016105ba565b5050505050565b606060018054806020026020016040519081016040528092919081815260200182805480156109d157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116109a6575b5050505050905090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610a8b8a8a8c8a8a8a612667565b91509150877fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658b848c8e8c878c8c8c604051610acf9998979695949392919061381e565b60405180910390a250505050505050505050565b610aeb6125e4565b60005b82811015610c3157600060056000868685818110610b0e57610b0e61372f565b9050602002016020810190610b2391906136df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055838382818110610b8857610b8861372f565b9050602002016020810190610b9d91906136df565b6040517ff2fde38b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152919091169063f2fde38b90602401600060405180830381600087803b158015610c0857600080fd5b505af1158015610c1c573d6000803e3d6000fd5b5050505080610c2a9061378d565b9050610aee565b50505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610cd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610ce78b8b8a8a8a8a612667565b91509150887fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658c848d8f8c878c8c8c604051610d2b9998979695949392919061381e565b60405180910390a25050505050505050505050565b6000610d4a612945565b600087815260046020526040812054889160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260056020526040902054869060ff1615610e7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b610e8c898989898960016129be565b60405189907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015610f24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008773ffffffffffffffffffffffffffffffffffffffff16878b87604051602401610f5a929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610fe391906138a9565b6000604051808303816000865af19150503d8060008114611020576040519150601f19603f3d011682016040523d82523d6000602084013e611025565b606091505b50909b9a5050505050505050505050565b6000611040612bb6565b905090565b61104d61258f565b6110b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b6110bd8484610550565b610c3184848484612433565b60006110d3612945565b600088815260046020526040812054899160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003611174576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040902054879060ff1615611206576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b8985856020811015611274576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526573706f6e7365206d757374206265203e203332206279746573000000000060448201526064016105ba565b81358381146112df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f466972737420776f7264206d757374206265207265717565737449640000000060448201526064016105ba565b6112ee8e8e8e8e8e60026129be565b6040518e907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015611386576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008c73ffffffffffffffffffffffffffffffffffffffff168c8b8b6040516020016113b4939291906138c5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526113ec916138a9565b6000604051808303816000865af19150503d8060008114611429576040519150601f19603f3d011682016040523d82523d6000602084013e61142e565b606091505b509098505050505050505050979650505050505050565b821580159061145357508281145b6114b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c6964206172726179206c656e67746828732900000000000000000060448201526064016105ba565b3460005b848110156116195760008484838181106114d9576114d961372f565b90506020020135905080836114ee9190613901565b925060008787848181106115045761150461372f565b905060200201602081019061151991906136df565b73ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114611570576040519150601f19603f3d011682016040523d82523d6000602084013e611575565b606091505b5050905080611606576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016105ba565b5050806116129061378d565b90506114bd565b508015610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f546f6f206d756368204554482073656e7400000000000000000000000000000060448201526064016105ba565b6040805160208082018690527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b16828401527fffffffff00000000000000000000000000000000000000000000000000000000851660548301526058808301859052835180840390910181526078909201909252805191012060009060008681526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116908216146117a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b4282111561180f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000858152600460205260408082208290555186917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af11580156118da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fe919061391a565b505050505050565b60035473ffffffffffffffffffffffffffffffffffffffff163314611987576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016105ba565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560038054909116905560405173ffffffffffffffffffffffffffffffffffffffff909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6000611a116125e4565b8380611a1b612bb6565b1015611aa9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517f4000aea000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634000aea090611b2190899089908990899060040161393c565b6020604051808303816000875af1158015611b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b64919061391a565b9695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611c0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b60208101518190611c1e8183612c7f565b84602484015283604484015260003073ffffffffffffffffffffffffffffffffffffffff1684604051611c5191906138a9565b600060405180830381855af49150503d8060008114611c8c576040519150601f19603f3d011682016040523d82523d6000602084013e611c91565b606091505b50509050806118fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f556e61626c6520746f206372656174652072657175657374000000000000000060448201526064016105ba565b604080513360601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208083018290526034808401899052845180850390910181526054840185528051908201206074840188905260948401929092527fffffffff00000000000000000000000000000000000000000000000000000000861660a884015260ac8084018690528451808503909101815260cc9093019093528151919092012060009060008381526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614611e4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b42831115611eb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000828152600460205260408082208290555183917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018690527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af1158015611f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fa3919061391a565b50505050505050565b611fb461258f565b61201a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b80612081576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d7573742068617665206174206c6561737420312073656e646572000000000060448201526064016105ba565b60015460005b81811015612116576000806000600184815481106120a7576120a761372f565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561210f8161378d565b9050612087565b5060005b8281101561226c576000808585848181106121375761213761372f565b905060200201602081019061214c91906136df565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205460ff16156121dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4d757374206e6f742068617665206475706c69636174652073656e646572730060448201526064016105ba565b60016000808686858181106121f4576121f461372f565b905060200201602081019061220991906136df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556122658161378d565b905061211a565b5061227960018484612f88565b507ff263cfb3e4298332e776194610cf9fdc09ccb3ada8b9aa39764d882e11fbf0a08383336040516122ad939291906139c8565b60405180910390a1505050565b6122c26125e4565b6122cb81612dfb565b50565b6122d66125e4565b80806122e0612bb6565b101561236e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612403573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612427919061391a565b61074e5761074e613a02565b61243b61258f565b6124a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b7f1bb185903e2cb2f1b303523128b60e314dea81df4f8d9b7351cadd344f6e772784848484336040516124d8959493929190613a31565b60405180910390a160005b83811015610965578484828181106124fd576124fd61372f565b905060200201602081019061251291906136df565b73ffffffffffffffffffffffffffffffffffffffff1663ee56997b84846040518363ffffffff1660e01b815260040161254c929190613a81565b600060405180830381600087803b15801561256657600080fd5b505af115801561257a573d6000803e3d6000fd5b50505050806125889061378d565b90506124e3565b3360009081526020819052604081205460ff16806110405750336125c860025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60025473ffffffffffffffffffffffffffffffffffffffff163314612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016105ba565b565b600080857f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612720576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b6040517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608b901b16602082015260348101869052605401604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301206000818152600490935291205490935060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00161561282b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4d75737420757365206120756e6971756520494400000000000000000000000060448201526064016105ba565b61283761012c42613a9d565b6040805160208082018c90527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b16828401527fffffffff000000000000000000000000000000000000000000000000000000008a1660548301526058808301859052835180840390910181526078909201909252805191012090925060405180604001604052808260ff191681526020016128d687612ef1565b60ff9081169091526000868152600460209081526040909120835193909101519091167f01000000000000000000000000000000000000000000000000000000000000000260089290921c919091179055600654612935908a90613a9d565b6006555050965096945050505050565b3360009081526020819052604090205460ff16612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f7420617574686f72697a65642073656e646572000000000000000000000060448201526064016105ba565b6040805160208082018890527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b16828401527fffffffff00000000000000000000000000000000000000000000000000000000861660548301526058808301869052835180840390910181526078909201909252805191012060009060008881526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b612aeb82612ef1565b60008881526004602052604090205460ff9182167f01000000000000000000000000000000000000000000000000000000000000009091049091161115612b8e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f446174612076657273696f6e73206d757374206d61746368000000000000000060448201526064016105ba565b85600654612b9c9190613901565b600655505050600093845250506004602052506040812055565b60006001600654612bc79190613901565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c759190613ab0565b6110409190613901565b612c8b60026020613ac9565b612c96906004613a9d565b81511015612d00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c69642072657175657374206c656e6774680000000000000000000060448201526064016105ba565b7fffffffff0000000000000000000000000000000000000000000000000000000082167f3c6d41b9000000000000000000000000000000000000000000000000000000001480612d9157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4042994600000000000000000000000000000000000000000000000000000000145b612df7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d757374207573652077686974656c69737465642066756e6374696f6e73000060448201526064016105ba565b5050565b3373ffffffffffffffffffffffffffffffffffffffff821603612e7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016105ba565b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b600060ff821115612f84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f206269747300000000000000000000000000000000000000000000000000000060648201526084016105ba565b5090565b828054828255906000526020600020908101928215613000579160200282015b828111156130005781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190612fa8565b50612f849291505b80821115612f845760008155600101613008565b60008083601f84011261302e57600080fd5b50813567ffffffffffffffff81111561304657600080fd5b6020830191508360208260051b850101111561306157600080fd5b9250929050565b6000806020838503121561307b57600080fd5b823567ffffffffffffffff81111561309257600080fd5b61309e8582860161301c565b90969095509350505050565b73ffffffffffffffffffffffffffffffffffffffff811681146122cb57600080fd5b60008083601f8401126130de57600080fd5b50813567ffffffffffffffff8111156130f657600080fd5b60208301915083602082850101111561306157600080fd5b60008060006040848603121561312357600080fd5b833561312e816130aa565b9250602084013567ffffffffffffffff81111561314a57600080fd5b613156868287016130cc565b9497909650939450505050565b60005b8381101561317e578181015183820152602001613166565b50506000910152565b60208152600082518060208401526131a6816040850160208701613163565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020808252825182820181905260009190848201906040850190845b8181101561322657835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016131f4565b50909695505050505050565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461326257600080fd5b919050565b60008060008060008060008060e0898b03121561328357600080fd5b883561328e816130aa565b975060208901359650604089013595506132aa60608a01613232565b94506080890135935060a0890135925060c089013567ffffffffffffffff8111156132d457600080fd5b6132e08b828c016130cc565b999c989b5096995094979396929594505050565b60008060006040848603121561330957600080fd5b833567ffffffffffffffff81111561332057600080fd5b61332c8682870161301c565b9094509250506020840135613340816130aa565b809150509250925092565b60008060008060008060008060006101008a8c03121561336a57600080fd5b8935613375816130aa565b985060208a0135975060408a0135965060608a0135613393816130aa565b95506133a160808b01613232565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff8111156133cb57600080fd5b6133d78c828d016130cc565b915080935050809150509295985092959850929598565b60008060008060008060c0878903121561340757600080fd5b86359550602087013594506040870135613420816130aa565b935061342e60608801613232565b92506080870135915060a087013590509295509295509295565b6000806000806040858703121561345e57600080fd5b843567ffffffffffffffff8082111561347657600080fd5b6134828883890161301c565b9096509450602087013591508082111561349b57600080fd5b506134a88782880161301c565b95989497509550505050565b600080600080600080600060c0888a0312156134cf57600080fd5b873596506020880135955060408801356134e8816130aa565b94506134f660608901613232565b93506080880135925060a088013567ffffffffffffffff81111561351957600080fd5b6135258a828b016130cc565b989b979a50959850939692959293505050565b6000806000806080858703121561354e57600080fd5b843593506020850135925061356560408601613232565b9396929550929360600135925050565b6000806000806060858703121561358b57600080fd5b8435613596816130aa565b935060208501359250604085013567ffffffffffffffff8111156135b957600080fd5b6134a8878288016130cc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561360957600080fd5b8335613614816130aa565b925060208401359150604084013567ffffffffffffffff8082111561363857600080fd5b818601915086601f83011261364c57600080fd5b81358181111561365e5761365e6135c5565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156136a4576136a46135c5565b816040528281528960208487010111156136bd57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156136f157600080fd5b81356136fc816130aa565b9392505050565b6000806040838503121561371657600080fd5b8235613721816130aa565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036137be576137be61375e565b5060010190565b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010073ffffffffffffffffffffffffffffffffffffffff808d1684528b60208501528a6040850152808a166060850152507fffffffff00000000000000000000000000000000000000000000000000000000881660808401528660a08401528560c08401528060e084015261389981840185876137d5565b9c9b505050505050505050505050565b600082516138bb818460208701613163565b9190910192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000084168152818360048301376000910160040190815292915050565b818103818111156139145761391461375e565b92915050565b60006020828403121561392c57600080fd5b815180151581146136fc57600080fd5b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000611b646060830184866137d5565b8183526000602080850194508260005b858110156139bd578135613995816130aa565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613982565b509495945050505050565b6040815260006139dc604083018587613972565b905073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b606081526000613a45606083018789613972565b8281036020840152613a58818688613972565b91505073ffffffffffffffffffffffffffffffffffffffff831660408301529695505050505050565b602081526000613a95602083018486613972565b949350505050565b808201808211156139145761391461375e565b600060208284031215613ac257600080fd5b5051919050565b80820281158282048414176139145761391461375e56fea164736f6c6343000813000a60a06040523480156200001157600080fd5b50604051620016993803806200169983398101604081905262000034916200029d565b82826001600160a01b038216620000925760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c557620000c58162000199565b50506001600160a01b0384166200012b5760405162461bcd60e51b815260206004820152602360248201527f4c696e6b20746f6b656e2063616e6e6f742062652061207a65726f206164647260448201526265737360e81b606482015260840162000089565b6001600160a01b038085166080528216156200018f57816001600160a01b0316836001600160a01b03167f4e1e878dc28d5f040db5969163ff1acd75c44c3f655da2dde9c70bbd8e56dc7e836040516200018691906200038e565b60405180910390a35b50505050620003c3565b336001600160a01b03821603620001f35760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000089565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b03811681146200025c57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620002945781810151838201526020016200027a565b50506000910152565b60008060008060808587031215620002b457600080fd5b620002bf8562000244565b9350620002cf6020860162000244565b9250620002df6040860162000244565b60608601519092506001600160401b0380821115620002fd57600080fd5b818701915087601f8301126200031257600080fd5b81518181111562000327576200032762000261565b604051601f8201601f19908116603f0116810190838211818310171562000352576200035262000261565b816040528281528a60208487010111156200036c57600080fd5b6200037f83602083016020880162000277565b979a9699509497505050505050565b6020815260008251806020840152620003af81604085016020870162000277565b601f01601f19169190910160400192915050565b6080516112ac620003ed6000396000818161016d0152818161037501526105d301526112ac6000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c806379ba509711610081578063ee56997b1161005b578063ee56997b14610200578063f2fde38b14610213578063fa00763a1461022657600080fd5b806379ba5097146101c75780638da5cb5b146101cf578063b64fa9e6146101ed57600080fd5b80634d3e2323116100b25780634d3e23231461015557806357970e93146101685780636fadcf72146101b457600080fd5b8063033f49f7146100d9578063181f5a77146100ee5780632408afaa14610140575b600080fd5b6100ec6100e7366004610e72565b61026f565b005b61012a6040518060400160405280601981526020017f417574686f72697a6564466f7277617264657220312e312e300000000000000081525081565b6040516101379190610ef5565b60405180910390f35b610148610287565b6040516101379190610f61565b6100ec610163366004610e72565b6102f6565b61018f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610137565b6100ec6101c2366004610e72565b61036b565b6100ec61042d565b60005473ffffffffffffffffffffffffffffffffffffffff1661018f565b6100ec6101fb366004611007565b61052a565b6100ec61020e366004611073565b6106cb565b6100ec6102213660046110b5565b6109dc565b61025f6102343660046110b5565b73ffffffffffffffffffffffffffffffffffffffff1660009081526002602052604090205460ff1690565b6040519015158152602001610137565b6102776109f0565b610282838383610a73565b505050565b606060038054806020026020016040519081016040528092919081815260200182805480156102ec57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116102c1575b5050505050905090565b6102ff836109dc565b8273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f4e1e878dc28d5f040db5969163ff1acd75c44c3f655da2dde9c70bbd8e56dc7e848460405161035e9291906110d7565b60405180910390a3505050565b610373610c00565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610277576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f43616e6e6f7420666f727761726420746f204c696e6b20746f6b656e0000000060448201526064015b60405180910390fd5b60015473ffffffffffffffffffffffffffffffffffffffff1633146104ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610424565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610532610c00565b82811461059b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f417272617973206d7573742068617665207468652073616d65206c656e6774686044820152606401610424565b60005b838110156106c45760008585838181106105ba576105ba611124565b90506020020160208101906105cf91906110b5565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610686576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f43616e6e6f7420666f727761726420746f204c696e6b20746f6b656e000000006044820152606401610424565b6106b38185858581811061069c5761069c611124565b90506020028101906106ae9190611153565b610a73565b506106bd816111b8565b905061059e565b5050505050565b6106d3610c79565b610739576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e646572730000006044820152606401610424565b806107a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d7573742068617665206174206c6561737420312073656e64657200000000006044820152606401610424565b60035460005b8181101561083657600060026000600384815481106107c7576107c7611124565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561082f816111b8565b90506107a6565b5060005b8281101561098e576002600085858481811061085857610858611124565b905060200201602081019061086d91906110b5565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205460ff16156108fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4d757374206e6f742068617665206475706c69636174652073656e64657273006044820152606401610424565b60016002600086868581811061091657610916611124565b905060200201602081019061092b91906110b5565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055610987816111b8565b905061083a565b5061099b60038484610dac565b507ff263cfb3e4298332e776194610cf9fdc09ccb3ada8b9aa39764d882e11fbf0a08383336040516109cf93929190611217565b60405180910390a1505050565b6109e46109f0565b6109ed81610cb7565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610424565b565b73ffffffffffffffffffffffffffffffffffffffff83163b610af1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d75737420666f727761726420746f206120636f6e74726163740000000000006044820152606401610424565b6000808473ffffffffffffffffffffffffffffffffffffffff168484604051610b1b92919061128f565b6000604051808303816000865af19150503d8060008114610b58576040519150601f19603f3d011682016040523d82523d6000602084013e610b5d565b606091505b5091509150816106c4578051600003610bf8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f466f727761726465642063616c6c20726576657274656420776974686f75742060448201527f726561736f6e00000000000000000000000000000000000000000000000000006064820152608401610424565b805181602001fd5b3360009081526002602052604090205460ff16610a71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f7420617574686f72697a65642073656e64657200000000000000000000006044820152606401610424565b600033610c9b60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b3373ffffffffffffffffffffffffffffffffffffffff821603610d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610424565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b828054828255906000526020600020908101928215610e24579160200282015b82811115610e245781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190610dcc565b50610e30929150610e34565b5090565b5b80821115610e305760008155600101610e35565b803573ffffffffffffffffffffffffffffffffffffffff81168114610e6d57600080fd5b919050565b600080600060408486031215610e8757600080fd5b610e9084610e49565b9250602084013567ffffffffffffffff80821115610ead57600080fd5b818601915086601f830112610ec157600080fd5b813581811115610ed057600080fd5b876020828501011115610ee257600080fd5b6020830194508093505050509250925092565b600060208083528351808285015260005b81811015610f2257858101830151858201604001528201610f06565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b6020808252825182820181905260009190848201906040850190845b81811015610faf57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610f7d565b50909695505050505050565b60008083601f840112610fcd57600080fd5b50813567ffffffffffffffff811115610fe557600080fd5b6020830191508360208260051b850101111561100057600080fd5b9250929050565b6000806000806040858703121561101d57600080fd5b843567ffffffffffffffff8082111561103557600080fd5b61104188838901610fbb565b9096509450602087013591508082111561105a57600080fd5b5061106787828801610fbb565b95989497509550505050565b6000806020838503121561108657600080fd5b823567ffffffffffffffff81111561109d57600080fd5b6110a985828601610fbb565b90969095509350505050565b6000602082840312156110c757600080fd5b6110d082610e49565b9392505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261118857600080fd5b83018035915067ffffffffffffffff8211156111a357600080fd5b60200191503681900382131561100057600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611210577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b6040808252810183905260008460608301825b868110156112655773ffffffffffffffffffffffffffffffffffffffff61125084610e49565b1682526020928301929091019060010161122a565b50809250505073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b818382376000910190815291905056fea164736f6c6343000813000aa164736f6c6343000813000a", } var OperatorFactoryABI = OperatorFactoryMetaData.ABI diff --git a/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go b/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go index 4b7f6347639..db0ca418b2c 100644 --- a/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go +++ b/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go @@ -32,7 +32,7 @@ var ( var OperatorMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"senders\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"changedBy\",\"type\":\"address\"}],\"name\":\"AuthorizedSendersChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CancelOracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"specId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requester\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"callbackAddr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes4\",\"name\":\"callbackFunctionId\",\"type\":\"bytes4\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"cancelExpiration\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"dataVersion\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"acceptedContract\",\"type\":\"address\"}],\"name\":\"OwnableContractAccepted\",\"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\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"senders\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"changedBy\",\"type\":\"address\"}],\"name\":\"TargetsUpdatedAuthorizedSenders\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"EXPIRYTIME\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"senders\",\"type\":\"address[]\"}],\"name\":\"acceptAuthorizedReceivers\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"ownable\",\"type\":\"address[]\"}],\"name\":\"acceptOwnableContracts\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"bytes4\",\"name\":\"callbackFunc\",\"type\":\"bytes4\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"cancelOracleRequest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"bytes4\",\"name\":\"callbackFunc\",\"type\":\"bytes4\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"cancelOracleRequestByRequester\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable[]\",\"name\":\"receivers\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"distributeFunds\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"callbackAddress\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"callbackFunctionId\",\"type\":\"bytes4\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"data\",\"type\":\"bytes32\"}],\"name\":\"fulfillOracleRequest\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"callbackAddress\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"callbackFunctionId\",\"type\":\"bytes4\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"fulfillOracleRequest2\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAuthorizedSenders\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChainlinkToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"isAuthorizedSender\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"specId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes4\",\"name\":\"callbackFunctionId\",\"type\":\"bytes4\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"dataVersion\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"operatorRequest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"specId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callbackAddress\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"callbackFunctionId\",\"type\":\"bytes4\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"dataVersion\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"oracleRequest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"ownerForward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"ownerTransferAndCall\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"senders\",\"type\":\"address[]\"}],\"name\":\"setAuthorizedSenders\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"senders\",\"type\":\"address[]\"}],\"name\":\"setAuthorizedSendersOn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"ownable\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnableContracts\",\"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\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawable\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60a060405260016006553480156200001657600080fd5b5060405162003c8838038062003c888339810160408190526200003991620001ab565b808060006001600160a01b038216620000995760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0384811691909117909155811615620000cc57620000cc81620000e2565b505050506001600160a01b0316608052620001e3565b336001600160a01b038216036200013c5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000090565b600380546001600160a01b0319166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b80516001600160a01b0381168114620001a657600080fd5b919050565b60008060408385031215620001bf57600080fd5b620001ca836200018e565b9150620001da602084016200018e565b90509250929050565b608051613a4362000245600039600081816101ec0152818161075e015281816109f301528181610c4f015281816117d201528181611a3c01528181611adc01528181611e7701528181612310015281816125c30152612b4b0152613a436000f3fe6080604052600436106101965760003560e01c80636ae0bc76116100e1578063a4c0ed361161008a578063f2fde38b11610064578063f2fde38b146104aa578063f3fef3a3146104ca578063fa00763a146104ea578063fc4a03ed1461053057600080fd5b8063a4c0ed361461044a578063eb007d991461046a578063ee56997b1461048a57600080fd5b806379ba5097116100bb57806379ba5097146103ea5780638da5cb5b146103ff578063902fc3701461042a57600080fd5b80636ae0bc76146103975780636bd59ec0146103b75780636ee4d553146103ca57600080fd5b80633ec5bc1411610143578063501883011161011d578063501883011461033e57806352043783146103615780635ffa62881461037757600080fd5b80633ec5bc14146102ce57806340429946146102ee5780634ab0d1901461030e57600080fd5b8063181f5a7711610174578063181f5a77146102365780632408afaa1461028c5780633c6d41b9146102ae57600080fd5b806301994b991461019b578063033f49f7146101bd578063165d35e1146101dd575b600080fd5b3480156101a757600080fd5b506101bb6101b6366004612fbe565b610550565b005b3480156101c957600080fd5b506101bb6101d8366004613064565b610753565b3480156101e957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561024257600080fd5b5061027f6040518060400160405280600e81526020017f4f70657261746f7220312e302e3000000000000000000000000000000000000081525081565b60405161022d91906130dd565b34801561029857600080fd5b506102a161096c565b60405161022d919061312e565b3480156102ba57600080fd5b506101bb6102c93660046131bd565b6109db565b3480156102da57600080fd5b506101bb6102e936600461324a565b610ae3565b3480156102fa57600080fd5b506101bb6103093660046132a1565b610c37565b34801561031a57600080fd5b5061032e610329366004613344565b610d40565b604051901515815260200161022d565b34801561034a57600080fd5b50610353611036565b60405190815260200161022d565b34801561036d57600080fd5b5061035361012c81565b34801561038357600080fd5b506101bb61039236600461339e565b611045565b3480156103a357600080fd5b5061032e6103b236600461340a565b6110c9565b6101bb6103c536600461339e565b611445565b3480156103d657600080fd5b506101bb6103e536600461348e565b6115d8565b3480156103f657600080fd5b506101bb61185c565b34801561040b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff1661020c565b34801561043657600080fd5b5061032e6104453660046134cb565b61195d565b34801561045657600080fd5b506101bb61046536600461354a565b611ac4565b34801561047657600080fd5b506101bb61048536600461348e565b611c52565b34801561049657600080fd5b506101bb6104a5366004612fbe565b611f02565b3480156104b657600080fd5b506101bb6104c5366004613635565b612210565b3480156104d657600080fd5b506101bb6104e5366004613659565b612224565b3480156104f657600080fd5b5061032e610505366004613635565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b34801561053c57600080fd5b506101bb61054b36600461339e565b612389565b6105586124e5565b6105c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064015b60405180910390fd5b60005b8181101561074e576001600560008585858181106105e6576105e6613685565b90506020020160208101906105fb9190613635565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905582828281811061066057610660613685565b90506020020160208101906106759190613635565b73ffffffffffffffffffffffffffffffffffffffff167f615a0c1cb00a60d4acd77ec67acf2f17f223ef0932d591052fabc33643fe7e8260405160405180910390a28282828181106106c9576106c9613685565b90506020020160208101906106de9190613635565b73ffffffffffffffffffffffffffffffffffffffff166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561072557600080fd5b505af1158015610739573d6000803e3d6000fd5b5050505080610747906136e3565b90506105c6565b505050565b61075b61253a565b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610811576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff84163b61088f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d75737420666f727761726420746f206120636f6e747261637400000000000060448201526064016105ba565b60008473ffffffffffffffffffffffffffffffffffffffff1684846040516108b892919061371b565b6000604051808303816000865af19150503d80600081146108f5576040519150601f19603f3d011682016040523d82523d6000602084013e6108fa565b606091505b5050905080610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f466f727761726465642063616c6c206661696c6564000000000000000000000060448201526064016105ba565b5050505050565b606060018054806020026020016040519081016040528092919081815260200182805480156109d157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116109a6575b5050505050905090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610a8b8a8a8c8a8a8a6125bd565b91509150877fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658b848c8e8c878c8c8c604051610acf99989796959493929190613774565b60405180910390a250505050505050505050565b610aeb61253a565b60005b82811015610c3157600060056000868685818110610b0e57610b0e613685565b9050602002016020810190610b239190613635565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055838382818110610b8857610b88613685565b9050602002016020810190610b9d9190613635565b6040517ff2fde38b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152919091169063f2fde38b90602401600060405180830381600087803b158015610c0857600080fd5b505af1158015610c1c573d6000803e3d6000fd5b5050505080610c2a906136e3565b9050610aee565b50505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610cd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610ce78b8b8a8a8a8a6125bd565b91509150887fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658c848d8f8c878c8c8c604051610d2b99989796959493929190613774565b60405180910390a25050505050505050505050565b6000610d4a61289b565b600087815260046020526040812054889160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260056020526040902054869060ff1615610e7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b610e8c89898989896001612914565b60405189907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015610f24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008773ffffffffffffffffffffffffffffffffffffffff16878b87604051602401610f5a929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610fe391906137ff565b6000604051808303816000865af19150503d8060008114611020576040519150601f19603f3d011682016040523d82523d6000602084013e611025565b606091505b50909b9a5050505050505050505050565b6000611040612b0c565b905090565b61104d6124e5565b6110b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b6110bd8484610550565b610c3184848484612389565b60006110d361289b565b600088815260046020526040812054899160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003611174576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040902054879060ff1615611206576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b8985856020811015611274576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526573706f6e7365206d757374206265203e203332206279746573000000000060448201526064016105ba565b81358381146112df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f466972737420776f7264206d757374206265207265717565737449640000000060448201526064016105ba565b6112ee8e8e8e8e8e6002612914565b6040518e907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015611386576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008c73ffffffffffffffffffffffffffffffffffffffff168c8b8b6040516020016113b49392919061381b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526113ec916137ff565b6000604051808303816000865af19150503d8060008114611429576040519150601f19603f3d011682016040523d82523d6000602084013e61142e565b606091505b509098505050505050505050979650505050505050565b821580159061145357508281145b6114b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c6964206172726179206c656e67746828732900000000000000000060448201526064016105ba565b3460005b8481101561156f5760008484838181106114d9576114d9613685565b90506020020135905080836114ee9190613857565b925086868381811061150257611502613685565b90506020020160208101906115179190613635565b73ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561155c573d6000803e3d6000fd5b505080611568906136e3565b90506114bd565b508015610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f546f6f206d756368204554482073656e7400000000000000000000000000000060448201526064016105ba565b6040805160208082018690527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b16828401527fffffffff00000000000000000000000000000000000000000000000000000000851660548301526058808301859052835180840390910181526078909201909252805191012060009060008681526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116908216146116fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b42821115611765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000858152600460205260408082208290555186917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af1158015611830573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118549190613870565b505050505050565b60035473ffffffffffffffffffffffffffffffffffffffff1633146118dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016105ba565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560038054909116905560405173ffffffffffffffffffffffffffffffffffffffff909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b600061196761253a565b8380611971612b0c565b10156119ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517f4000aea000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634000aea090611a77908990899089908990600401613892565b6020604051808303816000875af1158015611a96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aba9190613870565b9695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611b63576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b60208101518190611b748183612bd5565b84602484015283604484015260003073ffffffffffffffffffffffffffffffffffffffff1684604051611ba791906137ff565b600060405180830381855af49150503d8060008114611be2576040519150601f19603f3d011682016040523d82523d6000602084013e611be7565b606091505b5050905080611854576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f556e61626c6520746f206372656174652072657175657374000000000000000060448201526064016105ba565b604080513360601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208083018290526034808401899052845180850390910181526054840185528051908201206074840188905260948401929092527fffffffff00000000000000000000000000000000000000000000000000000000861660a884015260ac8084018690528451808503909101815260cc9093019093528151919092012060009060008381526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614611da0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b42831115611e0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000828152600460205260408082208290555183917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018690527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af1158015611ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef99190613870565b50505050505050565b611f0a6124e5565b611f70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b80611fd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d7573742068617665206174206c6561737420312073656e646572000000000060448201526064016105ba565b60015460005b8181101561206c57600080600060018481548110611ffd57611ffd613685565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055612065816136e3565b9050611fdd565b5060005b828110156121c25760008085858481811061208d5761208d613685565b90506020020160208101906120a29190613635565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205460ff1615612133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4d757374206e6f742068617665206475706c69636174652073656e646572730060448201526064016105ba565b600160008086868581811061214a5761214a613685565b905060200201602081019061215f9190613635565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556121bb816136e3565b9050612070565b506121cf60018484612ede565b507ff263cfb3e4298332e776194610cf9fdc09ccb3ada8b9aa39764d882e11fbf0a08383336040516122039392919061391e565b60405180910390a1505050565b61221861253a565b61222181612d51565b50565b61222c61253a565b8080612236612b0c565b10156122c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612359573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237d9190613870565b61074e5761074e613958565b6123916124e5565b6123f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b7f1bb185903e2cb2f1b303523128b60e314dea81df4f8d9b7351cadd344f6e7727848484843360405161242e959493929190613987565b60405180910390a160005b838110156109655784848281811061245357612453613685565b90506020020160208101906124689190613635565b73ffffffffffffffffffffffffffffffffffffffff1663ee56997b84846040518363ffffffff1660e01b81526004016124a29291906139d7565b600060405180830381600087803b1580156124bc57600080fd5b505af11580156124d0573d6000803e3d6000fd5b50505050806124de906136e3565b9050612439565b3360009081526020819052604081205460ff168061104057503361251e60025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60025473ffffffffffffffffffffffffffffffffffffffff1633146125bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016105ba565b565b600080857f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612676576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b6040517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608b901b16602082015260348101869052605401604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301206000818152600490935291205490935060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001615612781576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4d75737420757365206120756e6971756520494400000000000000000000000060448201526064016105ba565b61278d61012c426139f3565b6040805160208082018c90527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b16828401527fffffffff000000000000000000000000000000000000000000000000000000008a1660548301526058808301859052835180840390910181526078909201909252805191012090925060405180604001604052808260ff1916815260200161282c87612e47565b60ff9081169091526000868152600460209081526040909120835193909101519091167f01000000000000000000000000000000000000000000000000000000000000000260089290921c91909117905560065461288b908a906139f3565b6006555050965096945050505050565b3360009081526020819052604090205460ff166125bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f7420617574686f72697a65642073656e646572000000000000000000000060448201526064016105ba565b6040805160208082018890527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b16828401527fffffffff00000000000000000000000000000000000000000000000000000000861660548301526058808301869052835180840390910181526078909201909252805191012060009060008881526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614612a38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b612a4182612e47565b60008881526004602052604090205460ff9182167f01000000000000000000000000000000000000000000000000000000000000009091049091161115612ae4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f446174612076657273696f6e73206d757374206d61746368000000000000000060448201526064016105ba565b85600654612af29190613857565b600655505050600093845250506004602052506040812055565b60006001600654612b1d9190613857565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612ba7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bcb9190613a06565b6110409190613857565b612be160026020613a1f565b612bec9060046139f3565b81511015612c56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c69642072657175657374206c656e6774680000000000000000000060448201526064016105ba565b7fffffffff0000000000000000000000000000000000000000000000000000000082167f3c6d41b9000000000000000000000000000000000000000000000000000000001480612ce757507fffffffff0000000000000000000000000000000000000000000000000000000082167f4042994600000000000000000000000000000000000000000000000000000000145b612d4d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d757374207573652077686974656c69737465642066756e6374696f6e73000060448201526064016105ba565b5050565b3373ffffffffffffffffffffffffffffffffffffffff821603612dd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016105ba565b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b600060ff821115612eda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f206269747300000000000000000000000000000000000000000000000000000060648201526084016105ba565b5090565b828054828255906000526020600020908101928215612f56579160200282015b82811115612f565781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190612efe565b50612eda9291505b80821115612eda5760008155600101612f5e565b60008083601f840112612f8457600080fd5b50813567ffffffffffffffff811115612f9c57600080fd5b6020830191508360208260051b8501011115612fb757600080fd5b9250929050565b60008060208385031215612fd157600080fd5b823567ffffffffffffffff811115612fe857600080fd5b612ff485828601612f72565b90969095509350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461222157600080fd5b60008083601f84011261303457600080fd5b50813567ffffffffffffffff81111561304c57600080fd5b602083019150836020828501011115612fb757600080fd5b60008060006040848603121561307957600080fd5b833561308481613000565b9250602084013567ffffffffffffffff8111156130a057600080fd5b6130ac86828701613022565b9497909650939450505050565b60005b838110156130d45781810151838201526020016130bc565b50506000910152565b60208152600082518060208401526130fc8160408501602087016130b9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020808252825182820181905260009190848201906040850190845b8181101561317c57835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161314a565b50909695505050505050565b80357fffffffff00000000000000000000000000000000000000000000000000000000811681146131b857600080fd5b919050565b60008060008060008060008060e0898b0312156131d957600080fd5b88356131e481613000565b9750602089013596506040890135955061320060608a01613188565b94506080890135935060a0890135925060c089013567ffffffffffffffff81111561322a57600080fd5b6132368b828c01613022565b999c989b5096995094979396929594505050565b60008060006040848603121561325f57600080fd5b833567ffffffffffffffff81111561327657600080fd5b61328286828701612f72565b909450925050602084013561329681613000565b809150509250925092565b60008060008060008060008060006101008a8c0312156132c057600080fd5b89356132cb81613000565b985060208a0135975060408a0135965060608a01356132e981613000565b95506132f760808b01613188565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff81111561332157600080fd5b61332d8c828d01613022565b915080935050809150509295985092959850929598565b60008060008060008060c0878903121561335d57600080fd5b8635955060208701359450604087013561337681613000565b935061338460608801613188565b92506080870135915060a087013590509295509295509295565b600080600080604085870312156133b457600080fd5b843567ffffffffffffffff808211156133cc57600080fd5b6133d888838901612f72565b909650945060208701359150808211156133f157600080fd5b506133fe87828801612f72565b95989497509550505050565b600080600080600080600060c0888a03121561342557600080fd5b8735965060208801359550604088013561343e81613000565b945061344c60608901613188565b93506080880135925060a088013567ffffffffffffffff81111561346f57600080fd5b61347b8a828b01613022565b989b979a50959850939692959293505050565b600080600080608085870312156134a457600080fd5b84359350602085013592506134bb60408601613188565b9396929550929360600135925050565b600080600080606085870312156134e157600080fd5b84356134ec81613000565b935060208501359250604085013567ffffffffffffffff81111561350f57600080fd5b6133fe87828801613022565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561355f57600080fd5b833561356a81613000565b925060208401359150604084013567ffffffffffffffff8082111561358e57600080fd5b818601915086601f8301126135a257600080fd5b8135818111156135b4576135b461351b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156135fa576135fa61351b565b8160405282815289602084870101111561361357600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561364757600080fd5b813561365281613000565b9392505050565b6000806040838503121561366c57600080fd5b823561367781613000565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613714576137146136b4565b5060010190565b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010073ffffffffffffffffffffffffffffffffffffffff808d1684528b60208501528a6040850152808a166060850152507fffffffff00000000000000000000000000000000000000000000000000000000881660808401528660a08401528560c08401528060e08401526137ef818401858761372b565b9c9b505050505050505050505050565b600082516138118184602087016130b9565b9190910192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000084168152818360048301376000910160040190815292915050565b8181038181111561386a5761386a6136b4565b92915050565b60006020828403121561388257600080fd5b8151801515811461365257600080fd5b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000611aba60608301848661372b565b8183526000602080850194508260005b858110156139135781356138eb81613000565b73ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016138d8565b509495945050505050565b6040815260006139326040830185876138c8565b905073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60608152600061399b6060830187896138c8565b82810360208401526139ae8186886138c8565b91505073ffffffffffffffffffffffffffffffffffffffff831660408301529695505050505050565b6020815260006139eb6020830184866138c8565b949350505050565b8082018082111561386a5761386a6136b4565b600060208284031215613a1857600080fd5b5051919050565b808202811582820484141761386a5761386a6136b456fea164736f6c6343000813000a", + Bin: "0x60a060405260016006553480156200001657600080fd5b5060405162003d3238038062003d328339810160408190526200003991620001ab565b808060006001600160a01b038216620000995760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0384811691909117909155811615620000cc57620000cc81620000e2565b505050506001600160a01b0316608052620001e3565b336001600160a01b038216036200013c5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000090565b600380546001600160a01b0319166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b80516001600160a01b0381168114620001a657600080fd5b919050565b60008060408385031215620001bf57600080fd5b620001ca836200018e565b9150620001da602084016200018e565b90509250929050565b608051613aed62000245600039600081816101ec0152818161075e015281816109f301528181610c4f0152818161187c01528181611ae601528181611b8601528181611f21015281816123ba0152818161266d0152612bf50152613aed6000f3fe6080604052600436106101965760003560e01c80636ae0bc76116100e1578063a4c0ed361161008a578063f2fde38b11610064578063f2fde38b146104aa578063f3fef3a3146104ca578063fa00763a146104ea578063fc4a03ed1461053057600080fd5b8063a4c0ed361461044a578063eb007d991461046a578063ee56997b1461048a57600080fd5b806379ba5097116100bb57806379ba5097146103ea5780638da5cb5b146103ff578063902fc3701461042a57600080fd5b80636ae0bc76146103975780636bd59ec0146103b75780636ee4d553146103ca57600080fd5b80633ec5bc1411610143578063501883011161011d578063501883011461033e57806352043783146103615780635ffa62881461037757600080fd5b80633ec5bc14146102ce57806340429946146102ee5780634ab0d1901461030e57600080fd5b8063181f5a7711610174578063181f5a77146102365780632408afaa1461028c5780633c6d41b9146102ae57600080fd5b806301994b991461019b578063033f49f7146101bd578063165d35e1146101dd575b600080fd5b3480156101a757600080fd5b506101bb6101b6366004613068565b610550565b005b3480156101c957600080fd5b506101bb6101d836600461310e565b610753565b3480156101e957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561024257600080fd5b5061027f6040518060400160405280600e81526020017f4f70657261746f7220312e302e3000000000000000000000000000000000000081525081565b60405161022d9190613187565b34801561029857600080fd5b506102a161096c565b60405161022d91906131d8565b3480156102ba57600080fd5b506101bb6102c9366004613267565b6109db565b3480156102da57600080fd5b506101bb6102e93660046132f4565b610ae3565b3480156102fa57600080fd5b506101bb61030936600461334b565b610c37565b34801561031a57600080fd5b5061032e6103293660046133ee565b610d40565b604051901515815260200161022d565b34801561034a57600080fd5b50610353611036565b60405190815260200161022d565b34801561036d57600080fd5b5061035361012c81565b34801561038357600080fd5b506101bb610392366004613448565b611045565b3480156103a357600080fd5b5061032e6103b23660046134b4565b6110c9565b6101bb6103c5366004613448565b611445565b3480156103d657600080fd5b506101bb6103e5366004613538565b611682565b3480156103f657600080fd5b506101bb611906565b34801561040b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff1661020c565b34801561043657600080fd5b5061032e610445366004613575565b611a07565b34801561045657600080fd5b506101bb6104653660046135f4565b611b6e565b34801561047657600080fd5b506101bb610485366004613538565b611cfc565b34801561049657600080fd5b506101bb6104a5366004613068565b611fac565b3480156104b657600080fd5b506101bb6104c53660046136df565b6122ba565b3480156104d657600080fd5b506101bb6104e5366004613703565b6122ce565b3480156104f657600080fd5b5061032e6105053660046136df565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b34801561053c57600080fd5b506101bb61054b366004613448565b612433565b61055861258f565b6105c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064015b60405180910390fd5b60005b8181101561074e576001600560008585858181106105e6576105e661372f565b90506020020160208101906105fb91906136df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558282828181106106605761066061372f565b905060200201602081019061067591906136df565b73ffffffffffffffffffffffffffffffffffffffff167f615a0c1cb00a60d4acd77ec67acf2f17f223ef0932d591052fabc33643fe7e8260405160405180910390a28282828181106106c9576106c961372f565b90506020020160208101906106de91906136df565b73ffffffffffffffffffffffffffffffffffffffff166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561072557600080fd5b505af1158015610739573d6000803e3d6000fd5b50505050806107479061378d565b90506105c6565b505050565b61075b6125e4565b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610811576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff84163b61088f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d75737420666f727761726420746f206120636f6e747261637400000000000060448201526064016105ba565b60008473ffffffffffffffffffffffffffffffffffffffff1684846040516108b89291906137c5565b6000604051808303816000865af19150503d80600081146108f5576040519150601f19603f3d011682016040523d82523d6000602084013e6108fa565b606091505b5050905080610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f466f727761726465642063616c6c206661696c6564000000000000000000000060448201526064016105ba565b5050505050565b606060018054806020026020016040519081016040528092919081815260200182805480156109d157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116109a6575b5050505050905090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610a8b8a8a8c8a8a8a612667565b91509150877fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658b848c8e8c878c8c8c604051610acf9998979695949392919061381e565b60405180910390a250505050505050505050565b610aeb6125e4565b60005b82811015610c3157600060056000868685818110610b0e57610b0e61372f565b9050602002016020810190610b2391906136df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055838382818110610b8857610b8861372f565b9050602002016020810190610b9d91906136df565b6040517ff2fde38b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152919091169063f2fde38b90602401600060405180830381600087803b158015610c0857600080fd5b505af1158015610c1c573d6000803e3d6000fd5b5050505080610c2a9061378d565b9050610aee565b50505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610cd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b600080610ce78b8b8a8a8a8a612667565b91509150887fd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c658c848d8f8c878c8c8c604051610d2b9998979695949392919061381e565b60405180910390a25050505050505050505050565b6000610d4a612945565b600087815260046020526040812054889160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260056020526040902054869060ff1615610e7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b610e8c898989898960016129be565b60405189907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015610f24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008773ffffffffffffffffffffffffffffffffffffffff16878b87604051602401610f5a929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610fe391906138a9565b6000604051808303816000865af19150503d8060008114611020576040519150601f19603f3d011682016040523d82523d6000602084013e611025565b606091505b50909b9a5050505050505050505050565b6000611040612bb6565b905090565b61104d61258f565b6110b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b6110bd8484610550565b610c3184848484612433565b60006110d3612945565b600088815260046020526040812054899160089190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169003611174576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d757374206861766520612076616c696420726571756573744964000000000060448201526064016105ba565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040902054879060ff1615611206576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742063616c6c206f776e656420636f6e747261637400000000000060448201526064016105ba565b8985856020811015611274576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526573706f6e7365206d757374206265203e203332206279746573000000000060448201526064016105ba565b81358381146112df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f466972737420776f7264206d757374206265207265717565737449640000000060448201526064016105ba565b6112ee8e8e8e8e8e60026129be565b6040518e907f9e9bc7616d42c2835d05ae617e508454e63b30b934be8aa932ebc125e0e58a6490600090a262061a805a1015611386576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4d7573742070726f7669646520636f6e73756d657220656e6f7567682067617360448201526064016105ba565b60008c73ffffffffffffffffffffffffffffffffffffffff168c8b8b6040516020016113b4939291906138c5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526113ec916138a9565b6000604051808303816000865af19150503d8060008114611429576040519150601f19603f3d011682016040523d82523d6000602084013e61142e565b606091505b509098505050505050505050979650505050505050565b821580159061145357508281145b6114b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c6964206172726179206c656e67746828732900000000000000000060448201526064016105ba565b3460005b848110156116195760008484838181106114d9576114d961372f565b90506020020135905080836114ee9190613901565b925060008787848181106115045761150461372f565b905060200201602081019061151991906136df565b73ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114611570576040519150601f19603f3d011682016040523d82523d6000602084013e611575565b606091505b5050905080611606576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016105ba565b5050806116129061378d565b90506114bd565b508015610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f546f6f206d756368204554482073656e7400000000000000000000000000000060448201526064016105ba565b6040805160208082018690527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b16828401527fffffffff00000000000000000000000000000000000000000000000000000000851660548301526058808301859052835180840390910181526078909201909252805191012060009060008681526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116908216146117a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b4282111561180f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000858152600460205260408082208290555186917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af11580156118da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fe919061391a565b505050505050565b60035473ffffffffffffffffffffffffffffffffffffffff163314611987576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016105ba565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560038054909116905560405173ffffffffffffffffffffffffffffffffffffffff909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6000611a116125e4565b8380611a1b612bb6565b1015611aa9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517f4000aea000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634000aea090611b2190899089908990899060040161393c565b6020604051808303816000875af1158015611b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b64919061391a565b9695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611c0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4d75737420757365204c494e4b20746f6b656e0000000000000000000000000060448201526064016105ba565b60208101518190611c1e8183612c7f565b84602484015283604484015260003073ffffffffffffffffffffffffffffffffffffffff1684604051611c5191906138a9565b600060405180830381855af49150503d8060008114611c8c576040519150601f19603f3d011682016040523d82523d6000602084013e611c91565b606091505b50509050806118fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f556e61626c6520746f206372656174652072657175657374000000000000000060448201526064016105ba565b604080513360601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208083018290526034808401899052845180850390910181526054840185528051908201206074840188905260948401929092527fffffffff00000000000000000000000000000000000000000000000000000000861660a884015260ac8084018690528451808503909101815260cc9093019093528151919092012060009060008381526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614611e4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b42831115611eb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f52657175657374206973206e6f7420657870697265640000000000000000000060448201526064016105ba565b6000828152600460205260408082208290555183917fa7842b9ec549398102c0d91b1b9919b2f20558aefdadf57528a95c6cd3292e9391a26040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018690527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af1158015611f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fa3919061391a565b50505050505050565b611fb461258f565b61201a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b80612081576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d7573742068617665206174206c6561737420312073656e646572000000000060448201526064016105ba565b60015460005b81811015612116576000806000600184815481106120a7576120a761372f565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561210f8161378d565b9050612087565b5060005b8281101561226c576000808585848181106121375761213761372f565b905060200201602081019061214c91906136df565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205460ff16156121dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4d757374206e6f742068617665206475706c69636174652073656e646572730060448201526064016105ba565b60016000808686858181106121f4576121f461372f565b905060200201602081019061220991906136df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556122658161378d565b905061211a565b5061227960018484612f88565b507ff263cfb3e4298332e776194610cf9fdc09ccb3ada8b9aa39764d882e11fbf0a08383336040516122ad939291906139c8565b60405180910390a1505050565b6122c26125e4565b6122cb81612dfb565b50565b6122d66125e4565b80806122e0612bb6565b101561236e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f416d6f756e74207265717565737465642069732067726561746572207468616e60448201527f20776974686472617761626c652062616c616e6365000000000000000000000060648201526084016105ba565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612403573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612427919061391a565b61074e5761074e613a02565b61243b61258f565b6124a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073657420617574686f72697a65642073656e6465727300000060448201526064016105ba565b7f1bb185903e2cb2f1b303523128b60e314dea81df4f8d9b7351cadd344f6e772784848484336040516124d8959493929190613a31565b60405180910390a160005b83811015610965578484828181106124fd576124fd61372f565b905060200201602081019061251291906136df565b73ffffffffffffffffffffffffffffffffffffffff1663ee56997b84846040518363ffffffff1660e01b815260040161254c929190613a81565b600060405180830381600087803b15801561256657600080fd5b505af115801561257a573d6000803e3d6000fd5b50505050806125889061378d565b90506124e3565b3360009081526020819052604081205460ff16806110405750336125c860025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60025473ffffffffffffffffffffffffffffffffffffffff163314612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016105ba565b565b600080857f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612720576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f43616e6e6f742063616c6c20746f204c494e4b0000000000000000000000000060448201526064016105ba565b6040517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608b901b16602082015260348101869052605401604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301206000818152600490935291205490935060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00161561282b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4d75737420757365206120756e6971756520494400000000000000000000000060448201526064016105ba565b61283761012c42613a9d565b6040805160208082018c90527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b16828401527fffffffff000000000000000000000000000000000000000000000000000000008a1660548301526058808301859052835180840390910181526078909201909252805191012090925060405180604001604052808260ff191681526020016128d687612ef1565b60ff9081169091526000868152600460209081526040909120835193909101519091167f01000000000000000000000000000000000000000000000000000000000000000260089290921c919091179055600654612935908a90613a9d565b6006555050965096945050505050565b3360009081526020819052604090205460ff16612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f7420617574686f72697a65642073656e646572000000000000000000000060448201526064016105ba565b6040805160208082018890527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b16828401527fffffffff00000000000000000000000000000000000000000000000000000000861660548301526058808301869052835180840390910181526078909201909252805191012060009060008881526004602052604090205490915060081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690821614612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f506172616d7320646f206e6f74206d617463682072657175657374204944000060448201526064016105ba565b612aeb82612ef1565b60008881526004602052604090205460ff9182167f01000000000000000000000000000000000000000000000000000000000000009091049091161115612b8e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f446174612076657273696f6e73206d757374206d61746368000000000000000060448201526064016105ba565b85600654612b9c9190613901565b600655505050600093845250506004602052506040812055565b60006001600654612bc79190613901565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c759190613ab0565b6110409190613901565b612c8b60026020613ac9565b612c96906004613a9d565b81511015612d00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c69642072657175657374206c656e6774680000000000000000000060448201526064016105ba565b7fffffffff0000000000000000000000000000000000000000000000000000000082167f3c6d41b9000000000000000000000000000000000000000000000000000000001480612d9157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4042994600000000000000000000000000000000000000000000000000000000145b612df7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d757374207573652077686974656c69737465642066756e6374696f6e73000060448201526064016105ba565b5050565b3373ffffffffffffffffffffffffffffffffffffffff821603612e7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016105ba565b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b600060ff821115612f84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f206269747300000000000000000000000000000000000000000000000000000060648201526084016105ba565b5090565b828054828255906000526020600020908101928215613000579160200282015b828111156130005781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190612fa8565b50612f849291505b80821115612f845760008155600101613008565b60008083601f84011261302e57600080fd5b50813567ffffffffffffffff81111561304657600080fd5b6020830191508360208260051b850101111561306157600080fd5b9250929050565b6000806020838503121561307b57600080fd5b823567ffffffffffffffff81111561309257600080fd5b61309e8582860161301c565b90969095509350505050565b73ffffffffffffffffffffffffffffffffffffffff811681146122cb57600080fd5b60008083601f8401126130de57600080fd5b50813567ffffffffffffffff8111156130f657600080fd5b60208301915083602082850101111561306157600080fd5b60008060006040848603121561312357600080fd5b833561312e816130aa565b9250602084013567ffffffffffffffff81111561314a57600080fd5b613156868287016130cc565b9497909650939450505050565b60005b8381101561317e578181015183820152602001613166565b50506000910152565b60208152600082518060208401526131a6816040850160208701613163565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020808252825182820181905260009190848201906040850190845b8181101561322657835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016131f4565b50909695505050505050565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461326257600080fd5b919050565b60008060008060008060008060e0898b03121561328357600080fd5b883561328e816130aa565b975060208901359650604089013595506132aa60608a01613232565b94506080890135935060a0890135925060c089013567ffffffffffffffff8111156132d457600080fd5b6132e08b828c016130cc565b999c989b5096995094979396929594505050565b60008060006040848603121561330957600080fd5b833567ffffffffffffffff81111561332057600080fd5b61332c8682870161301c565b9094509250506020840135613340816130aa565b809150509250925092565b60008060008060008060008060006101008a8c03121561336a57600080fd5b8935613375816130aa565b985060208a0135975060408a0135965060608a0135613393816130aa565b95506133a160808b01613232565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff8111156133cb57600080fd5b6133d78c828d016130cc565b915080935050809150509295985092959850929598565b60008060008060008060c0878903121561340757600080fd5b86359550602087013594506040870135613420816130aa565b935061342e60608801613232565b92506080870135915060a087013590509295509295509295565b6000806000806040858703121561345e57600080fd5b843567ffffffffffffffff8082111561347657600080fd5b6134828883890161301c565b9096509450602087013591508082111561349b57600080fd5b506134a88782880161301c565b95989497509550505050565b600080600080600080600060c0888a0312156134cf57600080fd5b873596506020880135955060408801356134e8816130aa565b94506134f660608901613232565b93506080880135925060a088013567ffffffffffffffff81111561351957600080fd5b6135258a828b016130cc565b989b979a50959850939692959293505050565b6000806000806080858703121561354e57600080fd5b843593506020850135925061356560408601613232565b9396929550929360600135925050565b6000806000806060858703121561358b57600080fd5b8435613596816130aa565b935060208501359250604085013567ffffffffffffffff8111156135b957600080fd5b6134a8878288016130cc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561360957600080fd5b8335613614816130aa565b925060208401359150604084013567ffffffffffffffff8082111561363857600080fd5b818601915086601f83011261364c57600080fd5b81358181111561365e5761365e6135c5565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156136a4576136a46135c5565b816040528281528960208487010111156136bd57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156136f157600080fd5b81356136fc816130aa565b9392505050565b6000806040838503121561371657600080fd5b8235613721816130aa565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036137be576137be61375e565b5060010190565b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010073ffffffffffffffffffffffffffffffffffffffff808d1684528b60208501528a6040850152808a166060850152507fffffffff00000000000000000000000000000000000000000000000000000000881660808401528660a08401528560c08401528060e084015261389981840185876137d5565b9c9b505050505050505050505050565b600082516138bb818460208701613163565b9190910192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000084168152818360048301376000910160040190815292915050565b818103818111156139145761391461375e565b92915050565b60006020828403121561392c57600080fd5b815180151581146136fc57600080fd5b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000611b646060830184866137d5565b8183526000602080850194508260005b858110156139bd578135613995816130aa565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613982565b509495945050505050565b6040815260006139dc604083018587613972565b905073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b606081526000613a45606083018789613972565b8281036020840152613a58818688613972565b91505073ffffffffffffffffffffffffffffffffffffffff831660408301529695505050505050565b602081526000613a95602083018486613972565b949350505050565b808201808211156139145761391461375e565b600060208284031215613ac257600080fd5b5051919050565b80820281158282048414176139145761391461375e56fea164736f6c6343000813000a", } var OperatorABI = OperatorMetaData.ABI diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 6efc75fce98..1db18d8366d 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -46,8 +46,8 @@ mock_ethlink_aggregator_wrapper: ../../contracts/solc/v0.6/MockETHLINKAggregator mock_gas_aggregator_wrapper: ../../contracts/solc/v0.6/MockGASAggregator/MockGASAggregator.abi ../../contracts/solc/v0.6/MockGASAggregator/MockGASAggregator.bin bacbb1ea4dc6beac0db8a13ca5c75e2fd61b903d70feea9b3b1c8b10fe8df4f3 multiwordconsumer_wrapper: ../../contracts/solc/v0.7/MultiWordConsumer/MultiWordConsumer.abi ../../contracts/solc/v0.7/MultiWordConsumer/MultiWordConsumer.bin 6e68abdf614e3ed0f5066c1b5f9d7c1199f1e7c5c5251fe8a471344a59afc6ba offchain_aggregator_wrapper: OffchainAggregator/OffchainAggregator.abi - 5c8d6562e94166d4790f1ee6e4321d359d9f7262e6c5452a712b1f1c896f45cf -operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.abi ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.bin 0fdfacf8879537b854875608dfca41c6221c342174417112acaa67dfcadafddc -operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin d7abd0e67f30a3a4c9c04c896124391306fa364fcf579fa6df04dbf912b48568 +operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.abi ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.bin 357203fabe3df436eb015e2d5094374c6967a9fc922ac8edc265b27aac4d67cf +operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin c5e1db81070d940a82ef100b0bce38e055593cbeebbc73abf9d45c30d6020cd2 oracle_wrapper: ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin 0a7a0cc4da7dc2a3d0a0c36c746b1adc044af5cad1838367356a0604f3255a01 From 79da81ffd0b459395d82cc66db4d5142336b190c Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Wed, 6 Dec 2023 10:55:57 -0500 Subject: [PATCH 014/234] Create auto update workflow (#11483) * Create auto-update.yml * Ignore merge conflicts --- .github/workflows/auto-update.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/auto-update.yml diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml new file mode 100644 index 00000000000..963145c4049 --- /dev/null +++ b/.github/workflows/auto-update.yml @@ -0,0 +1,17 @@ +name: Auto Update +on: + push: + branches: + - develop +jobs: + autoupdate: + name: Auto Update + runs-on: ubuntu-latest + steps: + - uses: docker://chinthakagodawita/autoupdate-action:v1 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + PR_FILTER: "labelled" + PR_LABELS: "auto-update" + MERGE_MSG: "Branch was auto-updated." + MERGE_CONFLICT_ACTION: "ignore" From d65ca4fd20568b8cd623b517fd3e33a34709f7d0 Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 6 Dec 2023 09:20:53 -0700 Subject: [PATCH 015/234] [TT-744] Bump core results output filter (#11486) * [TT-755] Bump core results output filter * bump to merged action version --- .github/workflows/ci-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index fd8dfed88ab..bac6f763892 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -84,7 +84,7 @@ jobs: run: ./tools/bin/${{ matrix.cmd }} ./... - name: Print Filtered Test Results if: ${{ failure() && matrix.cmd == 'go_core_tests' }} - uses: smartcontractkit/chainlink-github-actions/go/go-test-results-parsing@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/go/go-test-results-parsing@a052942591aaa12716eb9835b490d812a77d0831 # v2.3.1 with: results-file: ./output.txt output-file: ./output-short.txt From c17067b874bebcda72cae8a335d9c6b5fc681f3a Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:20:41 -0800 Subject: [PATCH 016/234] Interfaces and skeleton methods for ChainReader EVM POC (#10990) * Implement skeleton interfaces, structs, & methods for ChainReader EVM POC - Read ChainReader config in from RelayConfig - Add some initialization and validation relay skeletons - Use medianProviderWrapper instead of passing medianContract separately This avoids us having to modify the signature of NewMedianFactory, which would require further modifications to all non-evm repos and chainlink-relay - Add chain_reader_test.go with some basic relay tests Co-authored-by: Jordan Krage - Add chain reader config validation - Add chain reader config validation tests - Add config for chain reader median contract to cr validation testcases - Add unimplemented Encode(), Decode(), GetMaxEncodingSize(), GetMaxDecodingSize() - Add ChainReader() method to mock provider for plugin test - Rename relaymercury.ChainReader to MercuryChainReader, resolve name collisions - Add tests for errors during ChainReader construction - Propagate InvalidConfig & any other errors back to client We should ignore Unimplemented until node ops have been given ample time to migrate to the new job spec (including a section for ChainReader config) so that we can remove the old product-specific MedianContract component from MedianProvider. All other errors we can immediately start passing back to the client, letting the core node decide how to handle them (eg. displaying an "invalid job spec" message to the UI if the RelayConfig was invalid or the ContractID missing) * Update relay versions * Simplify chain reader config validation * Update commit hashes one final time now that all relays are merged. --------- Co-authored-by: ilija --- core/scripts/go.mod | 9 +- core/scripts/go.sum | 18 +- core/services/chainlink/relayer_factory.go | 3 +- core/services/functions/listener.go | 1 + .../services/ocr2/plugins/functions/plugin.go | 1 + core/services/ocr2/plugins/median/plugin.go | 68 +++++ core/services/ocr2/plugins/median/services.go | 83 +++++- core/services/ocr2/plugins/mercury/plugin.go | 2 +- .../plugins/ocr2keeper/integration_21_test.go | 1 - core/services/ocr2/plugins/ocr2keeper/util.go | 1 - core/services/relay/evm/chain_reader.go | 154 ++++++++++ core/services/relay/evm/chain_reader_test.go | 272 ++++++++++++++++++ core/services/relay/evm/config_poller.go | 1 + core/services/relay/evm/evm.go | 34 ++- core/services/relay/evm/functions.go | 5 + .../relay/evm/functions/logpoller_wrapper.go | 1 + .../evm/functions/offchain_config_digester.go | 8 +- core/services/relay/evm/median_test.go | 47 +++ .../relay/evm/mercury/v1/data_source.go | 9 +- .../relay/evm/mercury/v1/data_source_test.go | 26 +- core/services/relay/evm/mercury_provider.go | 54 ++-- core/services/relay/evm/ocr2keeper.go | 4 + core/services/relay/evm/ocr2vrf.go | 8 + core/services/relay/evm/types/types.go | 35 ++- .../relay/grpc_provider_server_test.go | 3 +- core/services/relay/relay_test.go | 1 + go.mod | 9 +- go.sum | 18 +- integration-tests/docker/test_env/test_env.go | 18 +- integration-tests/go.mod | 9 +- integration-tests/go.sum | 18 +- plugins/medianpoc/plugin_test.go | 7 +- 32 files changed, 828 insertions(+), 100 deletions(-) create mode 100644 core/services/ocr2/plugins/median/plugin.go create mode 100644 core/services/relay/evm/chain_reader.go create mode 100644 core/services/relay/evm/chain_reader_test.go create mode 100644 core/services/relay/evm/median_test.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 91e26caef69..b0b5f801ed5 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -119,6 +119,7 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 // indirect @@ -257,12 +258,12 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1622f1d24df..dd69053b405 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -443,6 +443,8 @@ github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -1212,18 +1214,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 8b8749013fc..4ed73d8e53b 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -5,9 +5,8 @@ import ( "errors" "fmt" - "github.com/pelletier/go-toml/v2" - "github.com/jmoiron/sqlx" + "github.com/pelletier/go-toml/v2" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index 65c364adb7c..f9d74f1bae9 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/cbor" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 2ebd7e30805..61fa7f5d825 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -14,6 +14,7 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go new file mode 100644 index 00000000000..cad2099832d --- /dev/null +++ b/core/services/ocr2/plugins/median/plugin.go @@ -0,0 +1,68 @@ +package median + +import ( + "context" + + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type Plugin struct { + loop.Plugin + stop services.StopChan +} + +func NewPlugin(lggr logger.Logger) *Plugin { + return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(services.StopChan)} +} + +func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProvider, dataSource, juelsPerFeeCoin median.DataSource, errorLog loop.ErrorLog) (loop.ReportingPluginFactory, error) { + var ctxVals loop.ContextValues + ctxVals.SetValues(ctx) + lggr := logger.With(p.Logger, ctxVals.Args()...) + + factory := median.NumericalMedianFactory{ + ContractTransmitter: provider.MedianContract(), + DataSource: dataSource, + JuelsPerFeeCoinDataSource: juelsPerFeeCoin, + Logger: logger.NewOCRWrapper(lggr, true, func(msg string) { + ctx, cancelFn := p.stop.NewCtx() + defer cancelFn() + if err := errorLog.SaveError(ctx, msg); err != nil { + lggr.Errorw("Unable to save error", "err", msg) + } + }), + OnchainConfigCodec: provider.OnchainConfigCodec(), + ReportCodec: provider.ReportCodec(), + } + s := &reportingPluginFactoryService{lggr: logger.Named(lggr, "ReportingPluginFactory"), ReportingPluginFactory: factory} + + p.SubService(s) + + return s, nil +} + +type reportingPluginFactoryService struct { + services.StateMachine + lggr logger.Logger + ocrtypes.ReportingPluginFactory +} + +func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() } + +func (r *reportingPluginFactoryService) Start(ctx context.Context) error { + return r.StartOnce("ReportingPluginFactory", func() error { return nil }) +} + +func (r *reportingPluginFactoryService) Close() error { + return r.StopOnce("ReportingPluginFactory", func() error { return nil }) +} + +func (r *reportingPluginFactoryService) HealthReport() map[string]error { + return map[string]error{r.Name(): r.Healthy()} +} diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 2d1ef720545..4adfc306d64 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -5,9 +5,14 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "time" + "github.com/ethereum/go-ethereum/common" libocr "github.com/smartcontractkit/libocr/offchainreporting2plus" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + mediantypes "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -52,6 +57,21 @@ func (m *medianConfig) JobPipelineResultWriteQueueDepth() uint64 { return m.jobPipelineResultWriteQueueDepth } +// This wrapper avoids the need to modify the signature of NewMedianFactory in all of the non-evm +// relay repos as well as its primary definition in chainlink-common. Once ChainReader is implemented +// and working on all 4 blockchain families, we can remove the original MedianContract() method from +// MedianProvider and pass medianContract as a separate param to NewMedianFactory +type medianProviderWrapper struct { + types.MedianProvider + contract mediantypes.MedianContract +} + +// Override relay's implementation of MedianContract with product plugin's implementation of +// MedianContract, making use of product-agnostic ChainReader to read the contract instead of relay MedianContract +func (m medianProviderWrapper) MedianContract() mediantypes.MedianContract { + return m.contract +} + func NewMedianServices(ctx context.Context, jb job.Job, isNewlyCreatedJob bool, @@ -125,11 +145,24 @@ func NewMedianServices(ctx context.Context, CreatedAt: time.Now(), }, lggr) - if cmdName := env.MedianPluginCmd.Get(); cmdName != "" { + medianPluginCmd := env.MedianPluginCmd.Get() + medianLoopEnabled := medianPluginCmd != "" + + // TODO BCF-2821 handle this properly as this blocks Solana chain reader dev + if !medianLoopEnabled && medianProvider.ChainReader() != nil { + lggr.Info("Chain Reader enabled") + medianProvider = medianProviderWrapper{ + medianProvider, // attach newer MedianContract which uses ChainReader + newMedianContract(provider.ChainReader(), common.HexToAddress(spec.ContractID)), + } + } else { + lggr.Info("Chain Reader disabled") + } + if medianLoopEnabled { // use unique logger names so we can use it to register a loop medianLggr := lggr.Named("Median").Named(spec.ContractID).Named(spec.GetID()) - cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), cmdName) + cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), medianPluginCmd) if err2 != nil { err = fmt.Errorf("failed to register loop: %w", err2) abort() @@ -159,3 +192,49 @@ func NewMedianServices(ctx context.Context, } return } + +type medianContract struct { + chainReader types.ChainReader + contract types.BoundContract +} + +type latestTransmissionDetailsResponse struct { + configDigest ocr2types.ConfigDigest + epoch uint32 + round uint8 + latestAnswer *big.Int + latestTimestamp time.Time +} + +type latestRoundRequested struct { + configDigest ocr2types.ConfigDigest + epoch uint32 + round uint8 +} + +func (m *medianContract) LatestTransmissionDetails(ctx context.Context) (configDigest ocr2types.ConfigDigest, epoch uint32, round uint8, latestAnswer *big.Int, latestTimestamp time.Time, err error) { + var resp latestTransmissionDetailsResponse + + err = m.chainReader.GetLatestValue(ctx, m.contract, "LatestTransmissionDetails", nil, &resp) + if err != nil { + return + } + + return resp.configDigest, resp.epoch, resp.round, resp.latestAnswer, resp.latestTimestamp, err +} + +func (m *medianContract) LatestRoundRequested(ctx context.Context, lookback time.Duration) (configDigest ocr2types.ConfigDigest, epoch uint32, round uint8, err error) { + var resp latestRoundRequested + + err = m.chainReader.GetLatestValue(ctx, m.contract, "LatestRoundReported", map[string]string{}, &resp) + if err != nil { + return + } + + return resp.configDigest, resp.epoch, resp.round, err +} + +func newMedianContract(chainReader types.ChainReader, address common.Address) *medianContract { + contract := types.BoundContract{Address: address.String(), Name: "median", Pending: true} + return &medianContract{chainReader, contract} +} diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index f5c11dc7313..b2767d6bcf5 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -67,7 +67,7 @@ func NewServices( lggr, saver, chEnhancedTelem, - ocr2Provider.ChainReader(), + ocr2Provider.MercuryChainReader(), ocr2Provider.MercuryServerFetcher(), pluginConfig.InitialBlockNumber.Ptr(), feedID, diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index 7ccd785a668..c2b6612f664 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -32,7 +32,6 @@ import ( ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 02a0d033bc1..76e5bb6e00e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -12,7 +12,6 @@ import ( ocr2keepers20polling "github.com/smartcontractkit/chainlink-automation/pkg/v2/observer/polling" ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go new file mode 100644 index 00000000000..e4da4cc1a49 --- /dev/null +++ b/core/services/relay/evm/chain_reader.go @@ -0,0 +1,154 @@ +package evm + +import ( + "context" + "fmt" + "slices" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type ChainReaderService interface { + services.ServiceCtx + commontypes.ChainReader +} + +type chainReader struct { + lggr logger.Logger + contractID common.Address + lp logpoller.LogPoller +} + +// NewChainReaderService constructor for ChainReader +func NewChainReaderService(lggr logger.Logger, lp logpoller.LogPoller, contractID common.Address, config types.ChainReaderConfig) (*chainReader, error) { + if err := validateChainReaderConfig(config); err != nil { + return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidConfig, err) + } + + // TODO BCF-2814 implement initialisation of chain reading definitions and pass them into chainReader + return &chainReader{lggr.Named("ChainReader"), contractID, lp}, nil +} + +func (cr *chainReader) Name() string { return cr.lggr.Name() } + +func (cr *chainReader) initialize() error { + // Initialize chain reader, start cache polling loop, etc. + return nil +} + +func (cr *chainReader) Start(ctx context.Context) error { + if err := cr.initialize(); err != nil { + return fmt.Errorf("Failed to initialize ChainReader: %w", err) + } + return nil +} + +func (cr *chainReader) Close() error { return nil } + +func (cr *chainReader) Ready() error { return nil } + +func (cr *chainReader) HealthReport() map[string]error { + return map[string]error{cr.Name(): nil} +} + +func (cr *chainReader) GetLatestValue(ctx context.Context, bc commontypes.BoundContract, method string, params any, returnVal any) error { + return commontypes.UnimplementedError("Unimplemented method GetLatestValue called") +} + +func validateChainReaderConfig(cfg types.ChainReaderConfig) error { + if len(cfg.ChainContractReaders) == 0 { + return fmt.Errorf("%w: no contract readers defined", commontypes.ErrInvalidConfig) + } + + for contractName, chainContractReader := range cfg.ChainContractReaders { + abi, err := abi.JSON(strings.NewReader(chainContractReader.ContractABI)) + if err != nil { + return fmt.Errorf("invalid abi: %w", err) + } + + for chainReadingDefinitionName, chainReaderDefinition := range chainContractReader.ChainReaderDefinitions { + switch chainReaderDefinition.ReadType { + case types.Method: + err = validateMethods(abi, chainReaderDefinition) + case types.Event: + err = validateEvents(abi, chainReaderDefinition) + default: + return fmt.Errorf("%w: invalid chainreading definition read type: %d for contract: %q", commontypes.ErrInvalidConfig, chainReaderDefinition.ReadType, contractName) + } + if err != nil { + return fmt.Errorf("%w: invalid chainreading definition: %q for contract: %q, err: %w", commontypes.ErrInvalidConfig, chainReadingDefinitionName, contractName, err) + } + } + } + + return nil +} + +func validateEvents(contractABI abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { + event, methodExists := contractABI.Events[chainReaderDefinition.ChainSpecificName] + if !methodExists { + return fmt.Errorf("event: %s doesn't exist", chainReaderDefinition.ChainSpecificName) + } + + var abiEventIndexedInputs []abi.Argument + for _, eventInput := range event.Inputs { + if eventInput.Indexed { + abiEventIndexedInputs = append(abiEventIndexedInputs, eventInput) + } + } + + var chainReaderEventParams []string + for chainReaderEventParam := range chainReaderDefinition.Params { + chainReaderEventParams = append(chainReaderEventParams, chainReaderEventParam) + } + + if !areChainReaderArgumentsValid(abiEventIndexedInputs, chainReaderEventParams) { + var abiEventIndexedInputsNames []string + for _, abiEventIndexedInput := range abiEventIndexedInputs { + abiEventIndexedInputsNames = append(abiEventIndexedInputsNames, abiEventIndexedInput.Name) + } + return fmt.Errorf("params: [%s] don't match abi event indexed inputs: [%s]", strings.Join(chainReaderEventParams, ","), strings.Join(abiEventIndexedInputsNames, ",")) + } + return nil +} + +func validateMethods(abi abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { + method, methodExists := abi.Methods[chainReaderDefinition.ChainSpecificName] + if !methodExists { + return fmt.Errorf("method: %q doesn't exist", chainReaderDefinition.ChainSpecificName) + } + + var methodNames []string + for methodName := range chainReaderDefinition.Params { + methodNames = append(methodNames, methodName) + } + + if !areChainReaderArgumentsValid(method.Inputs, methodNames) { + var abiMethodInputs []string + for _, input := range method.Inputs { + abiMethodInputs = append(abiMethodInputs, input.Name) + } + return fmt.Errorf("params: [%s] don't match abi method inputs: [%s]", strings.Join(methodNames, ","), strings.Join(abiMethodInputs, ",")) + } + + return nil +} + +func areChainReaderArgumentsValid(contractArgs []abi.Argument, chainReaderArgs []string) bool { + for _, contractArg := range contractArgs { + if !slices.Contains(chainReaderArgs, contractArg.Name) { + return false + } + } + + return true +} diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go new file mode 100644 index 00000000000..ece2234ab55 --- /dev/null +++ b/core/services/relay/evm/chain_reader_test.go @@ -0,0 +1,272 @@ +package evm + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + mocklogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type chainReaderTestHelper struct { +} + +func (crTestHelper chainReaderTestHelper) makeChainReaderConfig(abi string, params map[string]any) evmtypes.ChainReaderConfig { + return evmtypes.ChainReaderConfig{ + ChainContractReaders: map[string]evmtypes.ChainContractReader{ + "MyContract": { + ContractABI: abi, + ChainReaderDefinitions: map[string]evmtypes.ChainReaderDefinition{ + "MyGenericMethod": { + ChainSpecificName: "name", + Params: params, + CacheEnabled: false, + ReadType: evmtypes.Method, + }, + }, + }, + }, + } +} + +func (crTestHelper chainReaderTestHelper) makeChainReaderConfigFromStrings(abi string, chainReadingDefinitions string) (evmtypes.ChainReaderConfig, error) { + chainReaderConfigTemplate := `{ + "chainContractReaders": { + "testContract": { + "contractName": "testContract", + "contractABI": "[%s]", + "chainReaderDefinitions": { + %s + } + } + } + }` + + abi = strings.Replace(abi, `"`, `\"`, -1) + formattedCfgJsonString := fmt.Sprintf(chainReaderConfigTemplate, abi, chainReadingDefinitions) + var chainReaderConfig evmtypes.ChainReaderConfig + err := json.Unmarshal([]byte(formattedCfgJsonString), &chainReaderConfig) + return chainReaderConfig, err +} + +func TestNewChainReader(t *testing.T) { + lggr := logger.TestLogger(t) + lp := mocklogpoller.NewLogPoller(t) + chain := mocks.NewChain(t) + contractID := testutils.NewAddress() + contractABI := `[{"inputs":[{"internalType":"string","name":"param","type":"string"}],"name":"name","stateMutability":"view","type":"function"}]` + + t.Run("happy path", func(t *testing.T) { + params := make(map[string]any) + params["param"] = "" + chainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, params) + chain.On("LogPoller").Return(lp) + _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, chainReaderConfig) + assert.NoError(t, err) + }) + + t.Run("invalid config", func(t *testing.T) { + invalidChainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, map[string]any{}) // missing param + _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, invalidChainReaderConfig) + assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) + }) + + t.Run("ChainReader config is empty", func(t *testing.T) { + emptyChainReaderConfig := evmtypes.ChainReaderConfig{} + _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, emptyChainReaderConfig) + assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) + assert.ErrorContains(t, err, "no contract readers defined") + }) +} + +func TestChainReaderStartClose(t *testing.T) { + lggr := logger.TestLogger(t) + lp := mocklogpoller.NewLogPoller(t) + cr := chainReader{ + lggr: lggr, + lp: lp, + } + err := cr.Start(testutils.Context(t)) + assert.NoError(t, err) + err = cr.Close() + assert.NoError(t, err) +} + +// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 +func TestValidateChainReaderConfig_HappyPath(t *testing.T) { + type testCase struct { + name string + abiInput string + chainReadingDefinitions string + } + + var testCases []testCase + testCases = append(testCases, + testCase{ + name: "eventWithMultipleIndexedTopics", + abiInput: `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"}`, + chainReadingDefinitions: `"Swap":{ + "chainSpecificName": "Swap", + "params":{ + "sender": "0x0", + "to": "0x0" + }, + "readType": 1 + }`, + }) + + testCases = append(testCases, + testCase{ + name: "methodWithOneParamAndMultipleResponses", + abiInput: `{"constant":true,"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserAccountData","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"getUserAccountData":{ + "chainSpecificName": "getUserAccountData", + "params":{ + "_user": "0x0" + }, + "readType": 0 + }`, + }) + + testCases = append(testCases, + testCase{ + name: "methodWithMultipleParamsAndOneResult", + abiInput: `{"inputs":[{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"},{"internalType":"uint256","name":"_inputQuantity","type":"uint256"}],"name":"getSwapOutput","stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"getSwapOutput":{ + "chainSpecificName": "getSwapOutput", + "params":{ + "_input":"0x0", + "_output":"0x0", + "_inputQuantity":"0x0" + }, + "readType": 0 + }`, + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) + assert.NoError(t, err) + assert.NoError(t, validateChainReaderConfig(cfg)) + }) + } + + t.Run("large config with all test cases", func(t *testing.T) { + var largeABI string + var manyChainReadingDefinitions string + for _, tc := range testCases { + largeABI += tc.abiInput + "," + manyChainReadingDefinitions += tc.chainReadingDefinitions + "," + } + + largeABI = largeABI[:len(largeABI)-1] + manyChainReadingDefinitions = manyChainReadingDefinitions[:len(manyChainReadingDefinitions)-1] + cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(largeABI, manyChainReadingDefinitions) + assert.NoError(t, err) + assert.NoError(t, validateChainReaderConfig(cfg)) + }) +} + +// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 +func TestValidateChainReaderConfig_BadPath(t *testing.T) { + type testCase struct { + name string + abiInput string + chainReadingDefinitions string + expected error + } + + var testCases []testCase + mismatchedEventArgumentsTestABI := `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Swap","type":"event"}` + testCases = append(testCases, + testCase{ + name: "mismatched abi and event chain reading param values", + abiInput: mismatchedEventArgumentsTestABI, + chainReadingDefinitions: `"Swap":{ + "chainSpecificName": "Swap", + "params":{ + "malformedParam": "0x0" + }, + "readType": 1 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi event indexed inputs: [sender]"), + }) + + mismatchedFunctionArgumentsTestABI := `{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"Swap","payable":false,"stateMutability":"view","type":"function"}` + testCases = append(testCases, + testCase{ + name: "mismatched abi and method chain reading param values", + abiInput: mismatchedFunctionArgumentsTestABI, + chainReadingDefinitions: `"Swap":{ + "chainSpecificName": "Swap", + "params":{ + "malformedParam": "0x0" + }, + "readType": 0 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi method inputs: [from]"), + }, + ) + + testCases = append(testCases, + testCase{ + name: "event doesn't exist", + abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"TestMethod":{ + "chainSpecificName": "Swap", + "readType": 1 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"TestMethod\" for contract: \"testContract\", err: event: Swap doesn't exist"), + }, + ) + + testCases = append(testCases, + testCase{ + name: "method doesn't exist", + abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"TestMethod":{ + "chainSpecificName": "Swap", + "readType": 0 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"TestMethod\" for contract: \"testContract\", err: method: \"Swap\" doesn't exist"), + }, + ) + + testCases = append(testCases, testCase{ + name: "invalid abi", + abiInput: `broken abi`, + chainReadingDefinitions: `"TestMethod":{ + "chainSpecificName": "Swap", + "readType": 0 + }`, + expected: fmt.Errorf("invalid abi"), + }) + + testCases = append(testCases, testCase{ + name: "invalid read type", + abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"TestMethod":{"readType": 59}`, + expected: fmt.Errorf("invalid chainreading definition read type: 59"), + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) + assert.NoError(t, err) + if tc.expected == nil { + assert.NoError(t, validateChainReaderConfig(cfg)) + } else { + assert.ErrorContains(t, validateChainReaderConfig(cfg), tc.expected.Error()) + } + }) + } +} diff --git a/core/services/relay/evm/config_poller.go b/core/services/relay/evm/config_poller.go index fe39ed0e343..dc75fe037fe 100644 --- a/core/services/relay/evm/config_poller.go +++ b/core/services/relay/evm/config_poller.go @@ -17,6 +17,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 088a69a2582..aea704adacf 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -54,6 +54,7 @@ type Relayer struct { mercuryPool wsrpc.Pool eventBroadcaster pg.EventBroadcaster pgCfg pg.QConfig + chainReader commontypes.ChainReader } type CSAETHKeystore interface { @@ -189,8 +190,7 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty } transmitter := mercury.NewTransmitter(lggr, cw.ContractConfigTracker(), client, privKey.PublicKey, rargs.JobID, *relayConfig.FeedID, r.db, r.pgCfg, transmitterCodec) - chainReader := NewChainReader(r.chain.HeadTracker()) - return NewMercuryProvider(cw, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, chainReader, lggr), nil + return NewMercuryProvider(cw, r.chainReader, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil } func (r *Relayer) NewFunctionsProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { @@ -502,6 +502,10 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp if expectedChainID != r.chain.ID().String() { return nil, fmt.Errorf("internal error: chain id in spec does not match this relayer's chain: have %s expected %s", relayConfig.ChainID.String(), r.chain.ID().String()) } + if !common.IsHexAddress(relayOpts.ContractID) { + return nil, fmt.Errorf("invalid contractID %s, expected hex address", relayOpts.ContractID) + } + contractID := common.HexToAddress(relayOpts.ContractID) configWatcher, err := newConfigProvider(lggr, r.chain, relayOpts, r.eventBroadcaster) if err != nil { @@ -518,12 +522,26 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp if err != nil { return nil, err } - return &medianProvider{ + + medianProvider := medianProvider{ configWatcher: configWatcher, reportCodec: reportCodec, contractTransmitter: contractTransmitter, medianContract: medianContract, - }, nil + } + + // allow fallback until chain reader is default and median contract is removed, but still log just in case + var chainReaderService commontypes.ChainReader + if relayConfig.ChainReader != nil { + if chainReaderService, err = NewChainReaderService(lggr, r.chain.LogPoller(), contractID, *relayConfig.ChainReader); err != nil { + return nil, err + } + } else { + lggr.Info("ChainReader missing from RelayConfig; falling back to internal MedianContract") + } + medianProvider.chainReader = chainReaderService + + return &medianProvider, nil } var _ commontypes.MedianProvider = (*medianProvider)(nil) @@ -533,8 +551,8 @@ type medianProvider struct { contractTransmitter ContractTransmitter reportCodec median.ReportCodec medianContract *medianContract - - ms services.MultiStart + chainReader commontypes.ChainReader + ms services.MultiStart } func (p *medianProvider) Name() string { @@ -582,3 +600,7 @@ func (p *medianProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigeste func (p *medianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return p.configWatcher.ContractConfigTracker() } + +func (p *medianProvider) ChainReader() commontypes.ChainReader { + return p.chainReader +} diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index 10e5d543b1a..f1d652fd6f8 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -85,6 +86,10 @@ func (p *functionsProvider) Name() string { return p.configWatcher.Name() } +func (p *functionsProvider) ChainReader() commontypes.ChainReader { + return nil +} + func NewFunctionsProvider(chain legacyevm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { relayOpts := evmRelayTypes.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index 95f45022ab3..e7f3a1a96af 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" diff --git a/core/services/relay/evm/functions/offchain_config_digester.go b/core/services/relay/evm/functions/offchain_config_digester.go index b4467543d98..29547e794ce 100644 --- a/core/services/relay/evm/functions/offchain_config_digester.go +++ b/core/services/relay/evm/functions/offchain_config_digester.go @@ -10,13 +10,13 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) var ( - _ types.OffchainConfigDigester = &functionsOffchainConfigDigester{} - _ relaytypes.RouteUpdateSubscriber = &functionsOffchainConfigDigester{} - FunctionsDigestPrefix = types.ConfigDigestPrefixEVM + _ types.OffchainConfigDigester = &functionsOffchainConfigDigester{} + _ evmRelayTypes.RouteUpdateSubscriber = &functionsOffchainConfigDigester{} + FunctionsDigestPrefix = types.ConfigDigestPrefixEVM // In order to support multiple OCR plugins with a single jobspec & OCR2Base contract, each plugin must have a unique config digest. // This is accomplished by overriding the single config digest from the contract with a unique prefix for each plugin via this custom offchain digester & config poller. ThresholdDigestPrefix = types.ConfigDigestPrefix(7) diff --git a/core/services/relay/evm/median_test.go b/core/services/relay/evm/median_test.go new file mode 100644 index 00000000000..4286290d289 --- /dev/null +++ b/core/services/relay/evm/median_test.go @@ -0,0 +1,47 @@ +package evm + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +func TestNewMedianProvider(t *testing.T) { + lggr := logger.TestLogger(t) + + chain := mocks.NewChain(t) + chainID := testutils.NewRandomEVMChainID() + chain.On("ID").Return(chainID) + contractID := testutils.NewAddress() + relayer := Relayer{lggr: lggr, chain: chain} + + pargs := commontypes.PluginArgs{} + + t.Run("wrong chainID", func(t *testing.T) { + relayConfigBadChainID := evmtypes.RelayConfig{} + rc, err2 := json.Marshal(&relayConfigBadChainID) + rargs2 := commontypes.RelayArgs{ContractID: contractID.String(), RelayConfig: rc} + require.NoError(t, err2) + _, err2 = relayer.NewMedianProvider(rargs2, pargs) + assert.ErrorContains(t, err2, "chain id in spec does not match") + }) + + t.Run("invalid contractID", func(t *testing.T) { + relayConfig := evmtypes.RelayConfig{ChainID: utils.NewBig(chainID)} + rc, err2 := json.Marshal(&relayConfig) + require.NoError(t, err2) + rargsBadContractID := commontypes.RelayArgs{ContractID: "NotAContractID", RelayConfig: rc} + _, err2 = relayer.NewMedianProvider(rargsBadContractID, pargs) + assert.ErrorContains(t, err2, "invalid contractID") + }) +} diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 429780ef2eb..ce48ec6cf94 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -67,7 +67,7 @@ type datasource struct { mu sync.RWMutex chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData - chainReader mercury.ChainReader + mercuryChainReader mercury.ChainReader fetcher Fetcher initialBlockNumber *int64 @@ -77,8 +77,8 @@ type datasource struct { var _ v1.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader mercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { - return &datasource{pr, jb, spec, lggr, s, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, mercuryChainReader mercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, lggr, s, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, mercuryChainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} } type ErrEmptyLatestReport struct { @@ -292,7 +292,8 @@ func (ds *datasource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.T } func (ds *datasource) setLatestBlocks(ctx context.Context, obs *v1types.Observation) error { - latestBlocks, err := ds.chainReader.LatestHeads(ctx, nBlocksObservation) + latestBlocks, err := ds.mercuryChainReader.LatestHeads(ctx, nBlocksObservation) + if err != nil { ds.lggr.Errorw("failed to read latest blocks", "error", err) return err diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 72dc4327f19..ce0d71acc6f 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -15,7 +15,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" @@ -33,7 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -var _ mercury.ServerFetcher = &mockFetcher{} +var _ mercurytypes.ServerFetcher = &mockFetcher{} type mockFetcher struct { num *int64 @@ -71,10 +71,10 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg type mockChainReader struct { err error - obs []mercury.Head + obs []mercurytypes.Head } -func (m *mockChainReader) LatestHeads(context.Context, int) ([]mercury.Head, error) { +func (m *mockChainReader) LatestHeads(context.Context, int) ([]mercurytypes.Head, error) { return m.obs, m.err } @@ -118,7 +118,7 @@ func TestMercury_Observe(t *testing.T) { ds.spec = spec h := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - ds.chainReader = evm.NewChainReader(h) + ds.mercuryChainReader = evm.NewMercuryChainReader(h) head := &evmtypes.Head{ Number: int64(rand.Int31()), @@ -210,7 +210,7 @@ func TestMercury_Observe(t *testing.T) { t.Run("if no current block available", func(t *testing.T) { h2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) h2.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainReader = evm.NewChainReader(h2) + ds.mercuryChainReader = evm.NewMercuryChainReader(h2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -221,7 +221,7 @@ func TestMercury_Observe(t *testing.T) { }) }) - ds.chainReader = evm.NewChainReader(h) + ds.mercuryChainReader = evm.NewMercuryChainReader(h) t.Run("when fetchMaxFinalizedBlockNum=false", func(t *testing.T) { t.Run("when run execution fails, returns error", func(t *testing.T) { @@ -321,7 +321,7 @@ func TestMercury_Observe(t *testing.T) { t.Run("when chain length is zero", func(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainReader = evm.NewChainReader(ht2) + ds.mercuryChainReader = evm.NewMercuryChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -346,7 +346,7 @@ func TestMercury_Observe(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return(h6) - ds.chainReader = evm.NewChainReader(ht2) + ds.mercuryChainReader = evm.NewMercuryChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -369,7 +369,7 @@ func TestMercury_Observe(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return(heads[len(heads)-1]) - ds.chainReader = evm.NewChainReader(ht2) + ds.mercuryChainReader = evm.NewMercuryChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -384,7 +384,7 @@ func TestMercury_Observe(t *testing.T) { }) t.Run("when chain reader returns an error", func(t *testing.T) { - ds.chainReader = &mockChainReader{ + ds.mercuryChainReader = &mockChainReader{ err: io.EOF, obs: nil, } @@ -414,7 +414,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { t.Run("returns head from headtracker if present", func(t *testing.T) { headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) headTracker.On("LatestChain").Return(&h, nil) - ds.chainReader = evm.NewChainReader(headTracker) + ds.mercuryChainReader = evm.NewMercuryChainReader(headTracker) obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) @@ -433,7 +433,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { // This can happen in some cases e.g. RPC node is offline headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainReader = evm.NewChainReader(headTracker) + ds.mercuryChainReader = evm.NewChainReader(headTracker) obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index b6a2232da3f..2ff882efa6b 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -7,48 +7,50 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" - relaymercury "github.com/smartcontractkit/chainlink-data-streams/mercury" + "github.com/smartcontractkit/chainlink-data-streams/mercury" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" + evmmercury "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" ) -var _ types.MercuryProvider = (*mercuryProvider)(nil) +var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { - configWatcher *configWatcher - transmitter mercury.Transmitter - reportCodecV1 v1.ReportCodec - reportCodecV2 v2.ReportCodec - reportCodecV3 v3.ReportCodec - chainReader mercurytypes.ChainReader - logger logger.Logger - - ms services.MultiStart + configWatcher *configWatcher + chainReader commontypes.ChainReader + transmitter evmmercury.Transmitter + reportCodecV1 v1.ReportCodec + reportCodecV2 v2.ReportCodec + reportCodecV3 v3.ReportCodec + mercuryChainReader mercurytypes.ChainReader + logger logger.Logger + ms services.MultiStart } func NewMercuryProvider( configWatcher *configWatcher, - transmitter mercury.Transmitter, + chainReader commontypes.ChainReader, + mercuryChainReader mercurytypes.ChainReader, + transmitter evmmercury.Transmitter, reportCodecV1 v1.ReportCodec, reportCodecV2 v2.ReportCodec, reportCodecV3 v3.ReportCodec, - chainReader mercurytypes.ChainReader, lggr logger.Logger, ) *mercuryProvider { return &mercuryProvider{ configWatcher, + chainReader, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, - chainReader, + mercuryChainReader, lggr, services.MultiStart{}, } @@ -77,6 +79,10 @@ func (p *mercuryProvider) HealthReport() map[string]error { return report } +func (p *mercuryProvider) MercuryChainReader() mercurytypes.ChainReader { + return p.mercuryChainReader +} + func (p *mercuryProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return p.configWatcher.ContractConfigTracker() } @@ -86,7 +92,7 @@ func (p *mercuryProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigest } func (p *mercuryProvider) OnchainConfigCodec() mercurytypes.OnchainConfigCodec { - return relaymercury.StandardOnchainConfigCodec{} + return mercury.StandardOnchainConfigCodec{} } func (p *mercuryProvider) ReportCodecV1() v1.ReportCodec { @@ -109,23 +115,27 @@ func (p *mercuryProvider) MercuryServerFetcher() mercurytypes.ServerFetcher { return p.transmitter } -func (p *mercuryProvider) ChainReader() mercurytypes.ChainReader { +func (p *mercuryProvider) ChainReader() commontypes.ChainReader { return p.chainReader } -var _ mercurytypes.ChainReader = (*chainReader)(nil) +var _ mercurytypes.ChainReader = (*mercuryChainReader)(nil) -type chainReader struct { +type mercuryChainReader struct { tracker httypes.HeadTracker } func NewChainReader(h httypes.HeadTracker) mercurytypes.ChainReader { - return &chainReader{ + return &mercuryChainReader{h} +} + +func NewMercuryChainReader(h httypes.HeadTracker) mercurytypes.ChainReader { + return &mercuryChainReader{ tracker: h, } } -func (r *chainReader) LatestHeads(ctx context.Context, k int) ([]mercurytypes.Head, error) { +func (r *mercuryChainReader) LatestHeads(ctx context.Context, k int) ([]mercurytypes.Head, error) { evmBlocks := r.tracker.LatestChain().AsSlice(k) if len(evmBlocks) == 0 { return nil, nil diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 3b3bfeb652d..abc03c7abb1 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -130,6 +130,10 @@ func (c *ocr2keeperProvider) ContractTransmitter() ocrtypes.ContractTransmitter return c.contractTransmitter } +func (c *ocr2keeperProvider) ChainReader() commontypes.ChainReader { + return nil +} + func newOCR2KeeperConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index b7a2220588b..39d0503b8b1 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -110,6 +110,10 @@ func (c *dkgProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } +func (c *dkgProvider) ChainReader() commontypes.ChainReader { + return nil +} + type ocr2vrfProvider struct { *configWatcher contractTransmitter ContractTransmitter @@ -119,6 +123,10 @@ func (c *ocr2vrfProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } +func (c *ocr2vrfProvider) ChainReader() commontypes.ChainReader { + return nil +} + func newOCR2VRFConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index d2edef8b118..24afb65c55a 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -20,11 +20,38 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +type ChainReaderConfig struct { + // ChainContractReaders key is contract name + ChainContractReaders map[string]ChainContractReader `json:"chainContractReaders"` +} + +type ChainContractReader struct { + ContractABI string `json:"contractABI"` + // ChainReaderDefinitions key is chainAgnostic read name. + ChainReaderDefinitions map[string]ChainReaderDefinition `json:"chainReaderDefinitions"` +} + +type ChainReaderDefinition struct { + ChainSpecificName string `json:"chainSpecificName"` // chain specific contract method name or event type. + Params map[string]any `json:"params"` + ReturnValues []string `json:"returnValues"` + CacheEnabled bool `json:"cacheEnabled"` + ReadType ReadType `json:"readType"` +} + +type ReadType int64 + +const ( + Method ReadType = 0 + Event ReadType = 1 +) + type RelayConfig struct { - ChainID *utils.Big `json:"chainID"` - FromBlock uint64 `json:"fromBlock"` - EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` - ConfigContractAddress *common.Address `json:"configContractAddress"` + ChainID *utils.Big `json:"chainID"` + FromBlock uint64 `json:"fromBlock"` + EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` + ConfigContractAddress *common.Address `json:"configContractAddress"` + ChainReader *ChainReaderConfig `json:"chainReader"` // Contract-specific SendingKeys pq.StringArray `json:"sendingKeys"` diff --git a/core/services/relay/grpc_provider_server_test.go b/core/services/relay/grpc_provider_server_test.go index 6aff32f5e32..72bbbca0f44 100644 --- a/core/services/relay/grpc_provider_server_test.go +++ b/core/services/relay/grpc_provider_server_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -17,7 +18,7 @@ func TestProviderServer(t *testing.T) { lggr := logger.TestLogger(t) _, err := NewProviderServer(mp, "unsupported-type", lggr) - require.Error(t, err) + require.ErrorContains(t, err, "unsupported-type") ps, err := NewProviderServer(staticMedianProvider{}, types.Median, lggr) require.NoError(t, err) diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index 18a7b1b44ea..d23895699df 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) diff --git a/go.mod b/go.mod index 0cbd2f16d1c..2ac067959ef 100644 --- a/go.mod +++ b/go.mod @@ -66,12 +66,12 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 @@ -171,6 +171,7 @@ require ( github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 diff --git a/go.sum b/go.sum index 96e5c069454..b7305210e05 100644 --- a/go.sum +++ b/go.sum @@ -440,6 +440,8 @@ github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -1215,18 +1217,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 030a3ae9622..b1b179badc5 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -206,6 +206,15 @@ func (te *CLClusterTestEnv) Cleanup() error { te.logWhetherAllContainersAreRunning() + // Getting the absolute path + wd, err := os.Getwd() + if err != nil { + return err + } + + wd = filepath.Join(wd, "logs") + te.l.Info().Str("Working dir", wd).Msg("Would write test logs here") + // TODO: This is an imperfect and temporary solution, see TT-590 for a more sustainable solution // Collect logs if the test fails, or if we just want them if te.t.Failed() || os.Getenv("TEST_LOG_COLLECT") == "true" { @@ -285,7 +294,14 @@ func (te *CLClusterTestEnv) collectTestLogs() error { return err } - te.l.Info().Str("Logs Location", folder).Msg("Wrote test logs") + // Getting the absolute path + wd, err := os.Getwd() + if err != nil { + return err + } + absolutePath := filepath.Join(wd, folder) + + te.l.Info().Str("Logs absolute Location", absolutePath).Msg("Wrote test logs") return nil } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index a161e67941d..01d7c4dbef9 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 github.com/smartcontractkit/chainlink-testing-framework v1.20.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -160,6 +160,7 @@ require ( github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-errors/errors v1.4.2 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 // indirect @@ -351,11 +352,11 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0392afe287a..f3d707d18ec 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -520,6 +520,8 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -1505,18 +1507,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= github.com/smartcontractkit/chainlink-testing-framework v1.20.0 h1:gQPQRKJuMh6QTAIMkqZ7v5WkjEmbcoMIX/V6WPVrvuI= github.com/smartcontractkit/chainlink-testing-framework v1.20.0/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index 0d6c0360e43..ba06d5ea462 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -7,9 +7,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" @@ -72,6 +71,10 @@ func (p provider) OnchainConfigCodec() median.OnchainConfigCodec { return mockOnchainConfigCodec{} } +func (p provider) ChainReader() types.ChainReader { + return nil +} + func TestNewPlugin(t *testing.T) { lggr := logger.TestLogger(t) p := NewPlugin(lggr) From 47d0690d3d130e13b1975fd9edb0b982e584fda6 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 6 Dec 2023 13:12:28 -0500 Subject: [PATCH 017/234] Improve logging for cache (#11453) * Include ServerURL in cache logging * Add additional cache tracing * TEMP - DO NOT MERGE THIS - promote Trace=>Debug cache logging * Log if call was cached or not * Revert "TEMP - DO NOT MERGE THIS - promote Trace=>Debug cache logging" This reverts commit f0f3f07a59c8d310ac74849519d46ea9b53495c1. --- core/services/relay/evm/mercury/wsrpc/cache/cache.go | 7 ++++++- core/services/relay/evm/mercury/wsrpc/client.go | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache.go b/core/services/relay/evm/mercury/wsrpc/cache/cache.go index cab5654081b..181900bc15c 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache.go @@ -213,12 +213,14 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest if time.Now().Before(v.expiresAt) { // CACHE HIT promCacheHitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() + m.lggr.Tracew("LatestReport CACHE HIT (hot path)", "feedID", feedIDHex) defer v.RUnlock() return v.val, nil } else if v.fetching { // CACHE WAIT promCacheWaitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() + m.lggr.Tracew("LatestReport CACHE WAIT (hot path)", "feedID", feedIDHex) // if someone else is fetching then wait for the fetch to complete ch := v.fetchCh v.RUnlock() @@ -234,11 +236,13 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest if time.Now().Before(v.expiresAt) { // CACHE HIT promCacheHitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() + m.lggr.Tracew("LatestReport CACHE HIT (cold path)", "feedID", feedIDHex) defer v.RUnlock() return v.val, nil } else if v.fetching { // CACHE WAIT promCacheWaitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() + m.lggr.Tracew("LatestReport CACHE WAIT (cold path)", "feedID", feedIDHex) // if someone else is fetching then wait for the fetch to complete ch := v.fetchCh v.Unlock() @@ -246,6 +250,7 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest } // CACHE MISS promCacheMissCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() + m.lggr.Tracew("LatestReport CACHE MISS (cold path)", "feedID", feedIDHex) // initiate the fetch and wait for result ch := v.initiateFetch() v.Unlock() @@ -328,7 +333,7 @@ func (m *memCache) fetch(req *pb.LatestReportRequest, v *cacheVal) { func (m *memCache) Start(context.Context) error { return m.StartOnce(m.Name(), func() error { - m.lggr.Debugw("MemCache starting", "config", m.cfg) + m.lggr.Debugw("MemCache starting", "config", m.cfg, "serverURL", m.client.ServerURL()) m.wg.Add(1) go m.runloop() return nil diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index 5cdf1f44e96..5b6bfa1a9b0 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -303,17 +303,21 @@ func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) if err = w.waitForReady(ctx); err != nil { return nil, errors.Wrap(err, "LatestReport failed") } + var cached bool if w.cache == nil { resp, err = w.rawClient.LatestReport(ctx, req) } else { + cached = true resp, err = w.cache.LatestReport(ctx, req) } if err != nil { - lggr.Errorw("LatestReport failed", "err", err, "resp", resp) + lggr.Errorw("LatestReport failed", "err", err, "resp", resp, "cached", cached) } else if resp.Error != "" { - lggr.Errorw("LatestReport failed; mercury server returned error", "err", resp.Error, "resp", resp) + lggr.Errorw("LatestReport failed; mercury server returned error", "err", resp.Error, "resp", resp, "cached", cached) + } else if !cached { + lggr.Debugw("LatestReport succeeded", "resp", resp, "cached", cached) } else { - lggr.Debugw("LatestReport succeeded", "resp", resp) + lggr.Tracew("LatestReport succeeded", "resp", resp, "cached", cached) } return } From 346448ee5248ef4347214edb7dd7c70f696da5ca Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 6 Dec 2023 11:46:07 -0700 Subject: [PATCH 018/234] Only run keepers tests in the merge_group for PRs to develop (#11506) --- .github/workflows/integration-tests.yml | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9e2cce02122..5e3cf02a7c0 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -144,7 +144,7 @@ jobs: - name: Build Chainlink Image if: needs.changes.outputs.src == 'true' uses: ./.github/actions/build-chainlink-image - with: + with: tag_suffix: ${{ matrix.image.tag-suffix }} dockerfile: ${{ matrix.image.dockerfile }} git_commit_sha: ${{ github.sha }} @@ -210,17 +210,33 @@ jobs: MATRIX_JSON_AUTOMATION=$(./scripts/buildTestMatrixList.sh ./smoke/automation_test.go automation ubuntu20.04-8cores-32GB 1) MATRIX_JSON_KEEPER=$(./scripts/buildTestMatrixList.sh ./smoke/keeper_test.go keeper ubuntu20.04-8cores-32GB 1) COMBINED_ARRAY=$(jq -c -n "$MATRIX_JSON_AUTOMATION + $MATRIX_JSON_KEEPER") - echo "MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV + + # if we running a PR against the develop branch we should only run the automation tests unless we are in the merge group event + if [[ "$GITHUB_EVENT_NAME" == "merge_group" ]]; then + echo "We are in a merge_group event, run both automation and keepers tests" + echo "MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV + else + echo "we are not in a merge_group event, if this is a PR to develop run only automation tests, otherwise run everything because we could be running against a release branch" + target_branch=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.base.ref) + if [[ "$target_branch" == "develop" ]]; then + echo "only run automation tests" + echo "MATRIX_JSON=${MATRIX_JSON_AUTOMATION}" >> $GITHUB_ENV + else + echo "run both automation and keepers tests" + echo "MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV + fi + fi eth-smoke-tests-matrix-automation: - if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || (github.event_name == 'workflow_dispatch' && !inputs.simulatedNetwork)) }} + if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} environment: integration permissions: checks: write pull-requests: write id-token: write contents: read - needs: [build-chainlink, changes, compare-tests, build-lint-integration-tests] + needs: + [build-chainlink, changes, compare-tests, build-lint-integration-tests] env: SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 CHAINLINK_COMMIT_SHA: ${{ github.sha }} @@ -281,7 +297,7 @@ jobs: continue-on-error: true eth-smoke-tests-matrix: - if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || (github.event_name == 'workflow_dispatch' && !inputs.simulatedNetwork)) }} + if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} environment: integration permissions: checks: write @@ -410,7 +426,7 @@ jobs: - name: Show Otel-Collector Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | - docker logs otel-collector + docker logs otel-collector ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' @@ -450,7 +466,7 @@ jobs: - name: Show Otel-Collector Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | - docker logs otel-collector + docker logs otel-collector - name: Collect Metrics if: always() id: collect-gha-metrics @@ -464,7 +480,7 @@ jobs: - name: Permissions on traces if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | - ls -l ./integration-tests/smoke/traces + ls -l ./integration-tests/smoke/traces - name: Upload Trace Data if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' uses: actions/upload-artifact@v3 @@ -472,7 +488,6 @@ jobs: name: trace-data path: ./integration-tests/smoke/traces/trace-data.json - ### Used to check the required checks box when the matrix completes eth-smoke-tests: if: always() From 4d8e0934396bfe02dcce9f8fc31c6377b846250b Mon Sep 17 00:00:00 2001 From: Gabriel Paradiso Date: Wed, 6 Dec 2023 20:43:17 +0100 Subject: [PATCH 019/234] [FUN-1094] Minor fixes (#11434) * fix: do not emit AddedAccess if the recipient already had access & minor doc improvements * chore: save gas by avoiding declaring a new var --- .../gas-snapshots/functions.gas-snapshot | 40 +++++++++---------- .../functions/dev/v1_X/FunctionsBilling.sol | 34 +++++----------- .../dev/v1_X/FunctionsCoordinator.sol | 6 +-- .../functions/dev/v1_X/FunctionsRouter.sol | 10 ++--- .../accessControl/TermsOfServiceAllowList.sol | 29 +++++--------- .../interfaces/ITermsOfServiceAllowList.sol | 8 ++++ .../dev/v1_X/interfaces/IFunctionsBilling.sol | 17 ++++++++ .../tests/v1_X/FunctionsBilling.t.sol | 12 +++--- .../tests/v1_X/FunctionsCoordinator.t.sol | 2 +- .../tests/v1_X/FunctionsRouter.t.sol | 2 +- .../FunctionsTermsOfServiceAllowList.t.sol | 27 +++++++------ .../src/v0.8/functions/tests/v1_X/Setup.t.sol | 10 +++-- .../FunctionsCoordinatorHarness.sol | 3 +- .../FunctionsCoordinatorTestHelper.sol | 3 +- .../v0.8/functions/v1/FunctionsRouter.test.ts | 2 +- .../functions_allow_list.go | 4 +- .../functions_coordinator.go | 4 +- .../functions_router/functions_router.go | 18 ++++----- ...rapper-dependency-versions-do-not-edit.txt | 6 +-- 19 files changed, 124 insertions(+), 113 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index 4e6cf83cd1a..8275d9afafe 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14578318) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14578296) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14578312) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14589732) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14589709) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14589681) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14589632) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14589621) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14589665) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14579333) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14579311) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14579327) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14590747) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14590724) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14590696) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14590647) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14590636) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14590680) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -118,12 +118,12 @@ FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumers() ( FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumersAfterConfigUpdate() (gas: 164837) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNoSubscription() (gas: 12946) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotAllowedSender() (gas: 57809) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotSubscriptionOwner() (gas: 87162) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotSubscriptionOwner() (gas: 87177) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfPaused() (gas: 18094) FunctionsSubscriptions_AddConsumer:test_AddConsumer_Success() (gas: 95480) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNoSubscription() (gas: 15041) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotAllowedSender() (gas: 57885) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89272) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89287) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPaused() (gas: 20148) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 194325) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitAllBalanceAsDeposit() (gas: 114506) @@ -142,10 +142,10 @@ FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Reve FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13459) FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59592) FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15010) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43508, ~: 45548) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46020, ~: 48060) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43774, ~: 45548) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46286, ~: 48060) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51089, ~: 53040) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51443, ~: 53040) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) @@ -181,7 +181,7 @@ FunctionsSubscriptions_RecoverFunds:test_RecoverFunds_Success(uint64) (runs: 256 FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfInvalidConsumer() (gas: 30260) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNoSubscription() (gas: 15019) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 57800) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87208) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87223) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPaused() (gas: 18049) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191858) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_Success() (gas: 41979) @@ -195,14 +195,14 @@ FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_Success() (gas: 5775 FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfNotAllowedSender() (gas: 26390) FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfPaused() (gas: 15759) FunctionsSubscriptions_createSubscription:test_CreateSubscription_Success() (gas: 152576) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:testAcceptTermsOfService_InvalidSigner_vuln() (gas: 94815) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:testAcceptTermsOfService_InvalidSigner_vuln() (gas: 94830) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfAcceptorIsNotSender() (gas: 25837) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfBlockedSender() (gas: 44348) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfInvalidSigner() (gas: 23597) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientContractIsNotSender() (gas: 1866530) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientIsNotSender() (gas: 26003) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForContract() (gas: 1946547) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForSelf() (gas: 104851) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForContract() (gas: 1946562) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForSelf() (gas: 103411) FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_RevertIfNotOwner() (gas: 15469) FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_Success() (gas: 51794) FunctionsTermsOfServiceAllowList_Constructor:test_Constructor_Success() (gas: 12187) @@ -214,10 +214,10 @@ FunctionsTermsOfServiceAllowList_HasAccess:test_HasAccess_TrueWhenDisabled() (ga FunctionsTermsOfServiceAllowList_IsBlockedSender:test_IsBlockedSender_SuccessFalse() (gas: 15354) FunctionsTermsOfServiceAllowList_IsBlockedSender:test_IsBlockedSender_SuccessTrue() (gas: 41957) FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_RevertIfNotOwner() (gas: 13525) -FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_Success() (gas: 95184) +FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_Success() (gas: 95199) FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 13727) FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_Success() (gas: 22073) -Gas_AcceptTermsOfService:test_AcceptTermsOfService_Gas() (gas: 84675) +Gas_AcceptTermsOfService:test_AcceptTermsOfService_Gas() (gas: 84690) Gas_AddConsumer:test_AddConsumer_Gas() (gas: 79087) Gas_CreateSubscription:test_CreateSubscription_Gas() (gas: 73375) Gas_FundSubscription:test_FundSubscription_Gas() (gas: 38546) diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index cb4f2f45677..c3f351e6c76 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol"; import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; -import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; +import {IFunctionsBilling, FunctionsBillingConfig} from "./interfaces/IFunctionsBilling.sol"; import {Routable} from "./Routable.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; @@ -37,25 +37,9 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { event CommitmentDeleted(bytes32 requestId); - // ================================================================ - // | Configuration state | - // ================================================================ - - struct Config { - uint32 fulfillmentGasPriceOverEstimationBP; // ══╗ Percentage of gas price overestimation to account for changes in gas price between request and response. Held as basis points (one hundredth of 1 percentage point) - uint32 feedStalenessSeconds; // ║ How long before we consider the feed price to be stale and fallback to fallbackNativePerUnitLink. - uint32 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback. This amount is always billed for every request. - uint32 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback. This amount is always billed for every request. - uint72 donFee; // ║ Additional flat fee (in Juels of LINK) that will be split between Node Operators. Max value is 2^80 - 1 == 1.2m LINK. - uint40 minimumEstimateGasPriceWei; // ║ The lowest amount of wei that will be used as the tx.gasprice when estimating the cost to fulfill the request - uint16 maxSupportedRequestDataVersion; // ═══════╝ The highest support request data version supported by the node. All lower versions should also be supported. - uint224 fallbackNativePerUnitLink; // ═══════════╗ Fallback NATIVE CURRENCY / LINK conversion rate if the data feed is stale - uint32 requestTimeoutSeconds; // ════════════════╝ How many seconds it takes before we consider a request to be timed out - } - - Config private s_config; + FunctionsBillingConfig private s_config; - event ConfigUpdated(Config config); + event ConfigUpdated(FunctionsBillingConfig config); error UnsupportedRequestDataVersion(); error InsufficientBalance(); @@ -81,7 +65,7 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { // ================================================================ // | Initialization | // ================================================================ - constructor(address router, Config memory config, address linkToNativeFeed) Routable(router) { + constructor(address router, FunctionsBillingConfig memory config, address linkToNativeFeed) Routable(router) { s_linkToNativeFeed = AggregatorV3Interface(linkToNativeFeed); updateConfig(config); @@ -93,13 +77,13 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { /// @notice Gets the Chainlink Coordinator's billing configuration /// @return config - function getConfig() external view returns (Config memory) { + function getConfig() external view returns (FunctionsBillingConfig memory) { return s_config; } /// @notice Sets the Chainlink Coordinator's billing configuration - /// @param config - See the contents of the Config struct in IFunctionsBilling.Config for more information - function updateConfig(Config memory config) public { + /// @param config - See the contents of the FunctionsBillingConfig struct in IFunctionsBilling.sol for more information + function updateConfig(FunctionsBillingConfig memory config) public { _onlyOwner(); s_config = config; @@ -122,7 +106,7 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { /// @inheritdoc IFunctionsBilling function getWeiPerUnitLink() public view returns (uint256) { - Config memory config = s_config; + FunctionsBillingConfig memory config = s_config; (, int256 weiPerUnitLink, , uint256 timestamp, ) = s_linkToNativeFeed.latestRoundData(); // solhint-disable-next-line not-rely-on-time if (config.feedStalenessSeconds < block.timestamp - timestamp && config.feedStalenessSeconds > 0) { @@ -199,7 +183,7 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { function _startBilling( FunctionsResponse.RequestMeta memory request ) internal returns (FunctionsResponse.Commitment memory commitment) { - Config memory config = s_config; + FunctionsBillingConfig memory config = s_config; // Nodes should support all past versions of the structure if (request.dataVersion > config.maxSupportedRequestDataVersion) { diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol index 16e9029ce3f..33c45c218d4 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import {IFunctionsCoordinator} from "./interfaces/IFunctionsCoordinator.sol"; import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; -import {FunctionsBilling} from "./FunctionsBilling.sol"; +import {FunctionsBilling, FunctionsBillingConfig} from "./FunctionsBilling.sol"; import {OCR2Base} from "./ocr/OCR2Base.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; @@ -17,7 +17,7 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli /// @inheritdoc ITypeAndVersion // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - string public constant override typeAndVersion = "Functions Coordinator v1.1.0"; + string public constant override typeAndVersion = "Functions Coordinator v1.2.0"; event OracleRequest( bytes32 indexed requestId, @@ -42,7 +42,7 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli constructor( address router, - Config memory config, + FunctionsBillingConfig memory config, address linkToNativeFeed ) OCR2Base() FunctionsBilling(router, config, linkToNativeFeed) {} diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol index 2e12a75679a..d86d881151c 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol @@ -19,7 +19,7 @@ contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, using FunctionsResponse for FunctionsResponse.FulfillResult; // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - string public constant override typeAndVersion = "Functions Router v1.0.0"; + string public constant override typeAndVersion = "Functions Router v2.0.0"; // We limit return data to a selector plus 4 words. This is to avoid // malicious contracts from returning large amounts of data and causing @@ -90,7 +90,7 @@ contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, uint72 adminFee; // ║ Flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network bytes4 handleOracleFulfillmentSelector; // ║ The function selector that is used when calling back to the Client contract uint16 gasForCallExactCheck; // ════════════════╝ Used during calling back to the client. Ensures we have at least enough gas to be able to revert if gasAmount > 63//64*gas available. - uint32[] maxCallbackGasLimits; // ══════════════╸ List of max callback gas limits used by flag with GAS_FLAG_INDEX + uint32[] maxCallbackGasLimits; // ══════════════╸ List of max callback gas limits used by flag with MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX uint16 subscriptionDepositMinimumRequests; //═══╗ Amount of requests that must be completed before the full subscription balance will be released when closing a subscription account. uint72 subscriptionDepositJuels; // ════════════╝ Amount of subscription funds that are held as a deposit until Config.subscriptionDepositMinimumRequests are made using the subscription. } @@ -306,7 +306,7 @@ contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, bytes memory response, bytes memory err, uint96 juelsPerGas, - uint96 costWithoutCallback, + uint96 costWithoutFulfillment, address transmitter, FunctionsResponse.Commitment memory commitment ) external override returns (FunctionsResponse.FulfillResult resultCode, uint96) { @@ -341,7 +341,7 @@ contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, { uint96 callbackCost = juelsPerGas * SafeCast.toUint96(commitment.callbackGasLimit); - uint96 totalCostJuels = commitment.adminFee + costWithoutCallback + callbackCost; + uint96 totalCostJuels = commitment.adminFee + costWithoutFulfillment + callbackCost; // Check that the subscription can still afford to fulfill the request if (totalCostJuels > getSubscription(commitment.subscriptionId).balance) { @@ -379,7 +379,7 @@ contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, commitment.adminFee, juelsPerGas, SafeCast.toUint96(result.gasUsed), - costWithoutCallback + costWithoutFulfillment ); emit RequestProcessed({ diff --git a/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol index b36d063ad7d..3bb7c7b04cc 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import {ITermsOfServiceAllowList} from "./interfaces/ITermsOfServiceAllowList.sol"; +import {ITermsOfServiceAllowList, TermsOfServiceAllowListConfig} from "./interfaces/ITermsOfServiceAllowList.sol"; import {IAccessController} from "../../../../shared/interfaces/IAccessController.sol"; import {ITypeAndVersion} from "../../../../shared/interfaces/ITypeAndVersion.sol"; @@ -17,7 +17,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, /// @inheritdoc ITypeAndVersion // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - string public constant override typeAndVersion = "Functions Terms of Service Allow List v1.0.0"; + string public constant override typeAndVersion = "Functions Terms of Service Allow List v1.1.0"; EnumerableSet.AddressSet private s_allowedSenders; mapping(address => bool) private s_blockedSenders; @@ -30,23 +30,15 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, error InvalidUsage(); error RecipientIsBlocked(); - // ================================================================ - // | Configuration state | - // ================================================================ - struct Config { - bool enabled; // ═════════════╗ When enabled, access will be checked against s_allowedSenders. When disabled, all access will be allowed. - address signerPublicKey; // ══╝ The key pair that needs to sign the acceptance data - } + TermsOfServiceAllowListConfig private s_config; - Config private s_config; - - event ConfigUpdated(Config config); + event ConfigUpdated(TermsOfServiceAllowListConfig config); // ================================================================ // | Initialization | // ================================================================ - constructor(Config memory config) ConfirmedOwner(msg.sender) { + constructor(TermsOfServiceAllowListConfig memory config) ConfirmedOwner(msg.sender) { updateConfig(config); } @@ -56,13 +48,13 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, /// @notice Gets the contracts's configuration /// @return config - function getConfig() external view returns (Config memory) { + function getConfig() external view returns (TermsOfServiceAllowListConfig memory) { return s_config; } /// @notice Sets the contracts's configuration - /// @param config - See the contents of the TermsOfServiceAllowList.Config struct for more information - function updateConfig(Config memory config) public onlyOwner { + /// @param config - See the contents of the TermsOfServiceAllowListConfig struct in ITermsOfServiceAllowList.sol for more information + function updateConfig(TermsOfServiceAllowListConfig memory config) public onlyOwner { s_config = config; emit ConfigUpdated(config); } @@ -99,8 +91,9 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, } // Add recipient to the allow list - s_allowedSenders.add(recipient); - emit AddedAccess(recipient); + if (s_allowedSenders.add(recipient)) { + emit AddedAccess(recipient); + } } /// @inheritdoc ITermsOfServiceAllowList diff --git a/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol index af4daa18bc3..209d25c0ab3 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol @@ -38,3 +38,11 @@ interface ITermsOfServiceAllowList { /// @param sender - Address of the sender to unblock function unblockSender(address sender) external; } + +// ================================================================ +// | Configuration state | +// ================================================================ +struct TermsOfServiceAllowListConfig { + bool enabled; // ═════════════╗ When enabled, access will be checked against s_allowedSenders. When disabled, all access will be allowed. + address signerPublicKey; // ══╝ The key pair that needs to sign the acceptance data +} diff --git a/contracts/src/v0.8/functions/dev/v1_X/interfaces/IFunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/interfaces/IFunctionsBilling.sol index 6291d05e57c..0bd7817f779 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/interfaces/IFunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/interfaces/IFunctionsBilling.sol @@ -40,5 +40,22 @@ interface IFunctionsBilling { function oracleWithdraw(address recipient, uint96 amount) external; /// @notice Withdraw all LINK earned by Oracles through fulfilling requests + /// @dev transmitter addresses must support LINK tokens to avoid tokens from getting stuck as oracleWithdrawAll() calls will forward tokens directly to transmitters function oracleWithdrawAll() external; } + +// ================================================================ +// | Configuration state | +// ================================================================ + +struct FunctionsBillingConfig { + uint32 fulfillmentGasPriceOverEstimationBP; // ══╗ Percentage of gas price overestimation to account for changes in gas price between request and response. Held as basis points (one hundredth of 1 percentage point) + uint32 feedStalenessSeconds; // ║ How long before we consider the feed price to be stale and fallback to fallbackNativePerUnitLink. + uint32 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback. This amount is always billed for every request. + uint32 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback. This amount is always billed for every request. + uint72 donFee; // ║ Additional flat fee (in Juels of LINK) that will be split between Node Operators. Max value is 2^80 - 1 == 1.2m LINK. + uint40 minimumEstimateGasPriceWei; // ║ The lowest amount of wei that will be used as the tx.gasprice when estimating the cost to fulfill the request + uint16 maxSupportedRequestDataVersion; // ═══════╝ The highest support request data version supported by the node. All lower versions should also be supported. + uint224 fallbackNativePerUnitLink; // ═══════════╗ Fallback NATIVE CURRENCY / LINK conversion rate if the data feed is stale + uint32 requestTimeoutSeconds; // ════════════════╝ How many seconds it takes before we consider a request to be timed out +} diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol index 739521c5305..66640003427 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol @@ -10,6 +10,8 @@ import {Routable} from "../../dev/v1_X/Routable.sol"; import {FunctionsRouterSetup, FunctionsSubscriptionSetup, FunctionsClientRequestSetup, FunctionsFulfillmentSetup, FunctionsMultipleFulfillmentsSetup} from "./Setup.t.sol"; +import {FunctionsBillingConfig} from "../../dev/v1_X/interfaces/IFunctionsBilling.sol"; + /// @notice #constructor contract FunctionsBilling_Constructor is FunctionsSubscriptionSetup { function test_Constructor_Success() public { @@ -25,7 +27,7 @@ contract FunctionsBilling_GetConfig is FunctionsRouterSetup { vm.stopPrank(); vm.startPrank(STRANGER_ADDRESS); - FunctionsBilling.Config memory config = s_functionsCoordinator.getConfig(); + FunctionsBillingConfig memory config = s_functionsCoordinator.getConfig(); assertEq(config.feedStalenessSeconds, getCoordinatorConfig().feedStalenessSeconds); assertEq(config.gasOverheadBeforeCallback, getCoordinatorConfig().gasOverheadBeforeCallback); assertEq(config.gasOverheadAfterCallback, getCoordinatorConfig().gasOverheadAfterCallback); @@ -39,12 +41,12 @@ contract FunctionsBilling_GetConfig is FunctionsRouterSetup { /// @notice #updateConfig contract FunctionsBilling_UpdateConfig is FunctionsRouterSetup { - FunctionsBilling.Config internal configToSet; + FunctionsBillingConfig internal configToSet; function setUp() public virtual override { FunctionsRouterSetup.setUp(); - configToSet = FunctionsBilling.Config({ + configToSet = FunctionsBillingConfig({ feedStalenessSeconds: getCoordinatorConfig().feedStalenessSeconds * 2, gasOverheadAfterCallback: getCoordinatorConfig().gasOverheadAfterCallback * 2, gasOverheadBeforeCallback: getCoordinatorConfig().gasOverheadBeforeCallback * 2, @@ -66,7 +68,7 @@ contract FunctionsBilling_UpdateConfig is FunctionsRouterSetup { s_functionsCoordinator.updateConfig(configToSet); } - event ConfigUpdated(FunctionsBilling.Config config); + event ConfigUpdated(FunctionsBillingConfig config); function test_UpdateConfig_Success() public { // topic0 (function signature, always checked), NOT topic1 (false), NOT topic2 (false), NOT topic3 (false), and data (true). @@ -79,7 +81,7 @@ contract FunctionsBilling_UpdateConfig is FunctionsRouterSetup { s_functionsCoordinator.updateConfig(configToSet); - FunctionsBilling.Config memory config = s_functionsCoordinator.getConfig(); + FunctionsBillingConfig memory config = s_functionsCoordinator.getConfig(); assertEq(config.feedStalenessSeconds, configToSet.feedStalenessSeconds); assertEq(config.gasOverheadAfterCallback, configToSet.gasOverheadAfterCallback); assertEq(config.gasOverheadBeforeCallback, configToSet.gasOverheadBeforeCallback); diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol index f6d3d41e632..c21a2c090f7 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol @@ -14,7 +14,7 @@ import {FunctionsRouterSetup, FunctionsDONSetup, FunctionsSubscriptionSetup} fro /// @notice #constructor contract FunctionsCoordinator_Constructor is FunctionsRouterSetup { function test_Constructor_Success() public { - assertEq(s_functionsCoordinator.typeAndVersion(), "Functions Coordinator v1.1.0"); + assertEq(s_functionsCoordinator.typeAndVersion(), "Functions Coordinator v1.2.0"); assertEq(s_functionsCoordinator.owner(), OWNER_ADDRESS); } } diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol index 081fe2f6649..43540ba02b8 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol @@ -21,7 +21,7 @@ import "forge-std/Vm.sol"; /// @notice #constructor contract FunctionsRouter_Constructor is FunctionsRouterSetup { function test_Constructor_Success() public { - assertEq(s_functionsRouter.typeAndVersion(), "Functions Router v1.0.0"); + assertEq(s_functionsRouter.typeAndVersion(), "Functions Router v2.0.0"); assertEq(s_functionsRouter.owner(), OWNER_ADDRESS); } } diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol index 450ec48d504..08c981dd4f9 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol @@ -2,14 +2,16 @@ pragma solidity ^0.8.19; import {TermsOfServiceAllowList} from "../../dev/v1_X/accessControl/TermsOfServiceAllowList.sol"; +import {TermsOfServiceAllowListConfig} from "../../dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol"; import {FunctionsClientTestHelper} from "./testhelpers/FunctionsClientTestHelper.sol"; import {FunctionsRoutesSetup, FunctionsOwnerAcceptTermsOfServiceSetup} from "./Setup.t.sol"; +import "forge-std/Vm.sol"; /// @notice #constructor contract FunctionsTermsOfServiceAllowList_Constructor is FunctionsRoutesSetup { function test_Constructor_Success() public { - assertEq(s_termsOfServiceAllowList.typeAndVersion(), "Functions Terms of Service Allow List v1.0.0"); + assertEq(s_termsOfServiceAllowList.typeAndVersion(), "Functions Terms of Service Allow List v1.1.0"); assertEq(s_termsOfServiceAllowList.owner(), OWNER_ADDRESS); } } @@ -21,7 +23,7 @@ contract FunctionsTermsOfServiceAllowList_GetConfig is FunctionsRoutesSetup { vm.stopPrank(); vm.startPrank(STRANGER_ADDRESS); - TermsOfServiceAllowList.Config memory config = s_termsOfServiceAllowList.getConfig(); + TermsOfServiceAllowListConfig memory config = s_termsOfServiceAllowList.getConfig(); assertEq(config.enabled, getTermsOfServiceConfig().enabled); assertEq(config.signerPublicKey, getTermsOfServiceConfig().signerPublicKey); } @@ -36,14 +38,14 @@ contract FunctionsTermsOfServiceAllowList_UpdateConfig is FunctionsRoutesSetup { vm.expectRevert("Only callable by owner"); s_termsOfServiceAllowList.updateConfig( - TermsOfServiceAllowList.Config({enabled: true, signerPublicKey: STRANGER_ADDRESS}) + TermsOfServiceAllowListConfig({enabled: true, signerPublicKey: STRANGER_ADDRESS}) ); } - event ConfigUpdated(TermsOfServiceAllowList.Config config); + event ConfigUpdated(TermsOfServiceAllowListConfig config); function test_UpdateConfig_Success() public { - TermsOfServiceAllowList.Config memory configToSet = TermsOfServiceAllowList.Config({ + TermsOfServiceAllowListConfig memory configToSet = TermsOfServiceAllowListConfig({ enabled: false, signerPublicKey: TOS_SIGNER }); @@ -58,7 +60,7 @@ contract FunctionsTermsOfServiceAllowList_UpdateConfig is FunctionsRoutesSetup { s_termsOfServiceAllowList.updateConfig(configToSet); - TermsOfServiceAllowList.Config memory config = s_termsOfServiceAllowList.getConfig(); + TermsOfServiceAllowListConfig memory config = s_termsOfServiceAllowList.getConfig(); assertEq(config.enabled, configToSet.enabled); assertEq(config.signerPublicKey, configToSet.signerPublicKey); } @@ -156,7 +158,7 @@ contract FunctionsTermsOfServiceAllowList_AcceptTermsOfService is FunctionsRoute function testAcceptTermsOfService_InvalidSigner_vuln() public { // Set the signer as the zero address - TermsOfServiceAllowList.Config memory allowListConfig; + TermsOfServiceAllowListConfig memory allowListConfig; allowListConfig.enabled = true; allowListConfig.signerPublicKey = address(0); s_termsOfServiceAllowList.updateConfig(allowListConfig); @@ -197,11 +199,12 @@ contract FunctionsTermsOfServiceAllowList_AcceptTermsOfService is FunctionsRoute assertTrue(s_termsOfServiceAllowList.hasAccess(STRANGER_ADDRESS, new bytes(0))); - // Event emitted even though adding existing item into EnumerableSet set does nothing - // TODO: handle differently in contract - vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData); - emit AddedAccess(STRANGER_ADDRESS); + // Check the addedAccess is not emitted, given the recipient was already in the list + vm.recordLogs(); s_termsOfServiceAllowList.acceptTermsOfService(STRANGER_ADDRESS, STRANGER_ADDRESS, r, s, v); + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 0); + assertTrue(s_termsOfServiceAllowList.hasAccess(STRANGER_ADDRESS, new bytes(0))); } @@ -258,7 +261,7 @@ contract FunctionsTermsOfServiceAllowList_HasAccess is FunctionsRoutesSetup { function test_HasAccess_TrueWhenDisabled() public { // Disable allow list, which opens all access s_termsOfServiceAllowList.updateConfig( - TermsOfServiceAllowList.Config({enabled: false, signerPublicKey: TOS_SIGNER}) + TermsOfServiceAllowListConfig({enabled: false, signerPublicKey: TOS_SIGNER}) ); // Send as stranger diff --git a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol index 41ee663e8b0..0e131f9b89c 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol @@ -9,7 +9,9 @@ import {FunctionsBilling} from "../../dev/v1_X/FunctionsBilling.sol"; import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; import {TermsOfServiceAllowList} from "../../dev/v1_X/accessControl/TermsOfServiceAllowList.sol"; +import {TermsOfServiceAllowListConfig} from "../../dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol"; import {MockLinkToken} from "../../../mocks/MockLinkToken.sol"; +import {FunctionsBillingConfig} from "../../dev/v1_X/interfaces/IFunctionsBilling.sol"; import "forge-std/Vm.sol"; @@ -64,9 +66,9 @@ contract FunctionsRouterSetup is BaseTest { }); } - function getCoordinatorConfig() public view returns (FunctionsBilling.Config memory) { + function getCoordinatorConfig() public view returns (FunctionsBillingConfig memory) { return - FunctionsBilling.Config({ + FunctionsBillingConfig({ feedStalenessSeconds: 24 * 60 * 60, // 1 day gasOverheadAfterCallback: 93_942, gasOverheadBeforeCallback: 105_000, @@ -79,8 +81,8 @@ contract FunctionsRouterSetup is BaseTest { }); } - function getTermsOfServiceConfig() public view returns (TermsOfServiceAllowList.Config memory) { - return TermsOfServiceAllowList.Config({enabled: true, signerPublicKey: TOS_SIGNER}); + function getTermsOfServiceConfig() public view returns (TermsOfServiceAllowListConfig memory) { + return TermsOfServiceAllowListConfig({enabled: true, signerPublicKey: TOS_SIGNER}); } } diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol index c1b6d5d0b14..15d9790f617 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.19; import {FunctionsCoordinator} from "../../../dev/v1_X/FunctionsCoordinator.sol"; import {FunctionsBilling} from "../../../dev/v1_X/FunctionsBilling.sol"; import {FunctionsResponse} from "../../../dev/v1_X/libraries/FunctionsResponse.sol"; +import {FunctionsBillingConfig} from "../../../dev/v1_X/interfaces/IFunctionsBilling.sol"; /// @title Functions Coordinator Test Harness /// @notice Contract to expose internal functions for testing purposes @@ -13,7 +14,7 @@ contract FunctionsCoordinatorHarness is FunctionsCoordinator { constructor( address router, - FunctionsBilling.Config memory config, + FunctionsBillingConfig memory config, address linkToNativeFeed ) FunctionsCoordinator(router, config, linkToNativeFeed) { s_linkToNativeFeed_HARNESS = linkToNativeFeed; diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol index 5e57e62e599..1a7d721d63a 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol @@ -3,11 +3,12 @@ pragma solidity ^0.8.19; import {FunctionsCoordinator} from "../../../dev/v1_X/FunctionsCoordinator.sol"; import {FunctionsBilling} from "../../../dev/v1_X/FunctionsBilling.sol"; +import {FunctionsBillingConfig} from "../../../dev/v1_X/interfaces/IFunctionsBilling.sol"; contract FunctionsCoordinatorTestHelper is FunctionsCoordinator { constructor( address router, - FunctionsBilling.Config memory config, + FunctionsBillingConfig memory config, address linkToNativeFeed ) FunctionsCoordinator(router, config, linkToNativeFeed) {} diff --git a/contracts/test/v0.8/functions/v1/FunctionsRouter.test.ts b/contracts/test/v0.8/functions/v1/FunctionsRouter.test.ts index aecad5466bb..e484283e80b 100644 --- a/contracts/test/v0.8/functions/v1/FunctionsRouter.test.ts +++ b/contracts/test/v0.8/functions/v1/FunctionsRouter.test.ts @@ -20,7 +20,7 @@ describe('Functions Router - Request lifecycle', () => { describe('Config', () => { it('#typeAndVersion', async () => { expect(await contracts.router.typeAndVersion()).to.be.equal( - 'Functions Router v1.0.0', + 'Functions Router v2.0.0', ) }) it('non-owner is unable to update config', async () => { diff --git a/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go b/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go index 0ccb08cdaa9..b0c4894f14c 100644 --- a/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go +++ b/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go @@ -36,8 +36,8 @@ type TermsOfServiceAllowListConfig struct { } var TermsOfServiceAllowListMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowList.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidUsage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RecipientIsBlocked\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"AddedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"BlockedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structTermsOfServiceAllowList.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"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\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"UnblockedAccess\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"}],\"name\":\"acceptTermsOfService\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"blockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAllowedSenders\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowList.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"getMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"hasAccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"isBlockedSender\",\"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\":\"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\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"unblockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowList.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60806040523480156200001157600080fd5b50604051620012c4380380620012c4833981016040819052620000349162000269565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d9565b505050620000d2816200018460201b60201c565b50620002ea565b336001600160a01b03821603620001335760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200018e6200020b565b805160058054602080850180516001600160a81b0319909316941515610100600160a81b03198116959095176101006001600160a01b039485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910160405180910390a150565b6000546001600160a01b03163314620002675760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b6000604082840312156200027c57600080fd5b604080519081016001600160401b0381118282101715620002ad57634e487b7160e01b600052604160045260246000fd5b60405282518015158114620002c157600080fd5b815260208301516001600160a01b0381168114620002de57600080fd5b60208201529392505050565b610fca80620002fa6000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806382184c7b1161008c578063a39b06e311610066578063a39b06e3146101b8578063a5e1d61d146101d9578063c3f909d4146101ec578063f2fde38b1461024b57600080fd5b806382184c7b1461016a57806389f9a2c41461017d5780638da5cb5b1461019057600080fd5b80636b14daf8116100bd5780636b14daf81461012a57806379ba50971461014d578063817ef62e1461015557600080fd5b8063181f5a77146100e45780633908c4d41461010257806347663acb14610117575b600080fd5b6100ec61025e565b6040516100f99190610c4f565b60405180910390f35b610115610110366004610ce4565b61027a565b005b610115610125366004610d45565b6104f0565b61013d610138366004610d60565b61057b565b60405190151581526020016100f9565b6101156105a5565b61015d6106a7565b6040516100f99190610de3565b610115610178366004610d45565b6106b8565b61011561018b366004610e3d565b61074b565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6101cb6101c6366004610ec6565b610806565b6040519081526020016100f9565b61013d6101e7366004610d45565b610864565b60408051808201825260008082526020918201528151808301835260055460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff6101009092048216928401928352845190815291511691810191909152016100f9565b610115610259366004610d45565b6108a5565b6040518060600160405280602c8152602001610f92602c913981565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602052604090205460ff16156102da576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006102e68686610806565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206005546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156103c0573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614610417576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061045c57503373ffffffffffffffffffffffffffffffffffffffff87161480159061045c5750333b155b15610493576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61049e6002866108b9565b5060405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a1505050505050565b6104f86108db565b73ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b91015b60405180910390a150565b60055460009060ff166105905750600161059e565b61059b60028561095e565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461062b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606106b3600261098d565b905090565b6106c06108db565b6106cb60028261099a565b5073ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19101610570565b6107536108db565b805160058054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a9101610570565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b1660348201526000906048016040516020818303038152906040528051906020012090505b92915050565b60055460009060ff1661087957506000919050565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205460ff1690565b6108ad6108db565b6108b6816109bc565b50565b600061059e8373ffffffffffffffffffffffffffffffffffffffff8416610ab1565b60005473ffffffffffffffffffffffffffffffffffffffff16331461095c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610622565b565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561059e565b6060600061059e83610b00565b600061059e8373ffffffffffffffffffffffffffffffffffffffff8416610b5c565b3373ffffffffffffffffffffffffffffffffffffffff821603610a3b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610622565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054610af85750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561085e565b50600061085e565b606081600001805480602002602001604051908101604052809291908181526020018280548015610b5057602002820191906000526020600020905b815481526020019060010190808311610b3c575b50505050509050919050565b60008181526001830160205260408120548015610c45576000610b80600183610ef9565b8554909150600090610b9490600190610ef9565b9050818114610bf9576000866000018281548110610bb457610bb4610f33565b9060005260206000200154905080876000018481548110610bd757610bd7610f33565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610c0a57610c0a610f62565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061085e565b600091505061085e565b600060208083528351808285015260005b81811015610c7c57858101830151858201604001528201610c60565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610cdf57600080fd5b919050565b600080600080600060a08688031215610cfc57600080fd5b610d0586610cbb565b9450610d1360208701610cbb565b93506040860135925060608601359150608086013560ff81168114610d3757600080fd5b809150509295509295909350565b600060208284031215610d5757600080fd5b61059e82610cbb565b600080600060408486031215610d7557600080fd5b610d7e84610cbb565b9250602084013567ffffffffffffffff80821115610d9b57600080fd5b818601915086601f830112610daf57600080fd5b813581811115610dbe57600080fd5b876020828501011115610dd057600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b81811015610e3157835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610dff565b50909695505050505050565b600060408284031215610e4f57600080fd5b6040516040810181811067ffffffffffffffff82111715610e99577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358015158114610eac57600080fd5b8152610eba60208401610cbb565b60208201529392505050565b60008060408385031215610ed957600080fd5b610ee283610cbb565b9150610ef060208401610cbb565b90509250929050565b8181038181111561085e577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e302e30a164736f6c6343000813000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidUsage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RecipientIsBlocked\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"AddedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"BlockedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"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\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"UnblockedAccess\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"}],\"name\":\"acceptTermsOfService\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"blockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAllowedSenders\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"getMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"hasAccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"isBlockedSender\",\"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\":\"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\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"unblockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040523480156200001157600080fd5b50604051620012c9380380620012c9833981016040819052620000349162000269565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d9565b505050620000d2816200018460201b60201c565b50620002ea565b336001600160a01b03821603620001335760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200018e6200020b565b805160058054602080850180516001600160a81b0319909316941515610100600160a81b03198116959095176101006001600160a01b039485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910160405180910390a150565b6000546001600160a01b03163314620002675760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b6000604082840312156200027c57600080fd5b604080519081016001600160401b0381118282101715620002ad57634e487b7160e01b600052604160045260246000fd5b60405282518015158114620002c157600080fd5b815260208301516001600160a01b0381168114620002de57600080fd5b60208201529392505050565b610fcf80620002fa6000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806382184c7b1161008c578063a39b06e311610066578063a39b06e3146101b8578063a5e1d61d146101d9578063c3f909d4146101ec578063f2fde38b1461024b57600080fd5b806382184c7b1461016a57806389f9a2c41461017d5780638da5cb5b1461019057600080fd5b80636b14daf8116100bd5780636b14daf81461012a57806379ba50971461014d578063817ef62e1461015557600080fd5b8063181f5a77146100e45780633908c4d41461010257806347663acb14610117575b600080fd5b6100ec61025e565b6040516100f99190610c54565b60405180910390f35b610115610110366004610ce9565b61027a565b005b610115610125366004610d4a565b6104f5565b61013d610138366004610d65565b610580565b60405190151581526020016100f9565b6101156105aa565b61015d6106ac565b6040516100f99190610de8565b610115610178366004610d4a565b6106bd565b61011561018b366004610e42565b610750565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6101cb6101c6366004610ecb565b61080b565b6040519081526020016100f9565b61013d6101e7366004610d4a565b610869565b60408051808201825260008082526020918201528151808301835260055460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff6101009092048216928401928352845190815291511691810191909152016100f9565b610115610259366004610d4a565b6108aa565b6040518060600160405280602c8152602001610f97602c913981565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602052604090205460ff16156102da576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006102e6868661080b565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206005546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156103c0573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614610417576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061045c57503373ffffffffffffffffffffffffffffffffffffffff87161480159061045c5750333b155b15610493576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61049e6002866108be565b156104ed5760405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a15b505050505050565b6104fd6108e0565b73ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b91015b60405180910390a150565b60055460009060ff16610595575060016105a3565b6105a0600285610963565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610630576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606106b86002610992565b905090565b6106c56108e0565b6106d060028261099f565b5073ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19101610575565b6107586108e0565b805160058054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a9101610575565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b1660348201526000906048016040516020818303038152906040528051906020012090505b92915050565b60055460009060ff1661087e57506000919050565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205460ff1690565b6108b26108e0565b6108bb816109c1565b50565b60006105a38373ffffffffffffffffffffffffffffffffffffffff8416610ab6565b60005473ffffffffffffffffffffffffffffffffffffffff163314610961576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610627565b565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156105a3565b606060006105a383610b05565b60006105a38373ffffffffffffffffffffffffffffffffffffffff8416610b61565b3373ffffffffffffffffffffffffffffffffffffffff821603610a40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610627565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054610afd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610863565b506000610863565b606081600001805480602002602001604051908101604052809291908181526020018280548015610b5557602002820191906000526020600020905b815481526020019060010190808311610b41575b50505050509050919050565b60008181526001830160205260408120548015610c4a576000610b85600183610efe565b8554909150600090610b9990600190610efe565b9050818114610bfe576000866000018281548110610bb957610bb9610f38565b9060005260206000200154905080876000018481548110610bdc57610bdc610f38565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610c0f57610c0f610f67565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610863565b6000915050610863565b600060208083528351808285015260005b81811015610c8157858101830151858201604001528201610c65565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ce457600080fd5b919050565b600080600080600060a08688031215610d0157600080fd5b610d0a86610cc0565b9450610d1860208701610cc0565b93506040860135925060608601359150608086013560ff81168114610d3c57600080fd5b809150509295509295909350565b600060208284031215610d5c57600080fd5b6105a382610cc0565b600080600060408486031215610d7a57600080fd5b610d8384610cc0565b9250602084013567ffffffffffffffff80821115610da057600080fd5b818601915086601f830112610db457600080fd5b813581811115610dc357600080fd5b876020828501011115610dd557600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b81811015610e3657835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610e04565b50909695505050505050565b600060408284031215610e5457600080fd5b6040516040810181811067ffffffffffffffff82111715610e9e577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358015158114610eb157600080fd5b8152610ebf60208401610cc0565b60208201529392505050565b60008060408385031215610ede57600080fd5b610ee783610cc0565b9150610ef560208401610cc0565b90509250929050565b81810381811115610863577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e312e30a164736f6c6343000813000a", } var TermsOfServiceAllowListABI = TermsOfServiceAllowListMetaData.ABI diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index 5f0d2d45f2d..a280297782e 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -71,8 +71,8 @@ type FunctionsResponseRequestMeta struct { } var FunctionsCoordinatorMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"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\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"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\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"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\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"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\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a06040523480156200001157600080fd5b50604051620056d0380380620056d083398101604081905262000034916200046d565b8282828233806000816200008f5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c257620000c28162000139565b5050506001600160a01b038116620000ed57604051632530e88560e11b815260040160405180910390fd5b6001600160a01b03908116608052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200012d82620001e4565b5050505050506200062c565b336001600160a01b03821603620001935760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000086565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee62000342565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033790839062000576565b60405180910390a150565b6200034c6200034e565b565b6000546001600160a01b031633146200034c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000086565b80516001600160a01b0381168114620003c257600080fd5b919050565b60405161012081016001600160401b0381118282101715620003f957634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c257600080fd5b80516001600160481b0381168114620003c257600080fd5b805164ffffffffff81168114620003c257600080fd5b805161ffff81168114620003c257600080fd5b80516001600160e01b0381168114620003c257600080fd5b60008060008385036101608112156200048557600080fd5b6200049085620003aa565b935061012080601f1983011215620004a757600080fd5b620004b1620003c7565b9150620004c160208701620003ff565b8252620004d160408701620003ff565b6020830152620004e460608701620003ff565b6040830152620004f760808701620003ff565b60608301526200050a60a0870162000414565b60808301526200051d60c087016200042c565b60a08301526200053060e0870162000442565b60c08301526101006200054581880162000455565b60e084015262000557828801620003ff565b908301525091506200056d6101408501620003aa565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005c960808401826001600160481b03169052565b5060a0830151620005e360a084018264ffffffffff169052565b5060c0830151620005fa60c084018261ffff169052565b5060e08301516200061660e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805161505e6200067260003960008181610845015281816109d301528181610ca601528181610f3a01528181611045015281816117890152613490015261505e6000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046139d4565b61059c565b005b6101a56101b5366004613b7d565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613ca1565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613d42565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613dd1565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046139d4565b610d92565b6102a0610de2565b6040516102039190613e5b565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613e6e565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613e87565b610fd4565b6040516102039190613fdc565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004614030565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b60405161020391906140e7565b61053b6105363660046141d7565b611785565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f66118e5565b6101a561056e3660046142f0565b61193c565b61057b6124b8565b604051908152602001610203565b6101a56105973660046143bd565b612711565b6105a4612725565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec828483614473565b505050565b6105f96127a8565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906108369083906140e7565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d29190614599565b905090565b6108df6127b0565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff166145e5565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6127a8565b610ba26127b0565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561460a565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614639565b9050610bb1565b5050565b610d9a612725565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec828483614473565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e60906143da565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea8906143da565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed4906143da565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614671565b61295c565b90506110bf60608301604084016143bd565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a0880161475e565b61111f610160880161014089016143bd565b611129888061477b565b61113b6101208b016101008c016147e0565b60208b01356111516101008d0160e08e016147fb565b8b60405161116799989796959493929190614818565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16111d68a8a8a8a8a8a612dfa565b6003546000906002906111f49060ff808216916101009004166148c0565b6111fe9190614908565b6112099060016148c0565b60ff169050878114611277576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b878614611306576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f7265706f727420727320616e64207373206d757374206265206f66206571756160448201527f6c206c656e6774680000000000000000000000000000000000000000000000006064820152608401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113495761134961492a565b600281111561135a5761135a61492a565b90525090506002816020015160028111156113775761137761492a565b141580156113c057506006816000015160ff168154811061139a5761139a61460a565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff163314155b15611427576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b5050505061143361396c565b6000808a8a604051611446929190614959565b60405190819003812061145d918e90602001614969565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b898110156117675760006001848984602081106114c6576114c661460a565b6114d391901a601b6148c0565b8e8e868181106114e5576114e561460a565b905060200201358d8d878181106114fe576114fe61460a565b905060200201356040516000815260200160405260405161153b949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561155d573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff808216855292965092945084019161010090041660028111156115dd576115dd61492a565b60028111156115ee576115ee61492a565b905250925060018360200151600281111561160b5761160b61492a565b14611672576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061168c5761168c61460a565b602002015173ffffffffffffffffffffffffffffffffffffffff161461170e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117285761172861460a565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117536001866148c0565b9450508061176090614639565b90506114a7565b505050611778833383858e8e612eb1565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b15801561182557600080fd5b505afa158015611839573d6000803e3d6000fd5b5050505066038d7ea4c6800082111561187e576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611888610841565b905060006118cb87878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b90506118d9858583856130b0565b98975050505050505050565b6060600c80546118f4906143da565b905060000361192f576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea8906143da565b855185518560ff16601f8311156119af576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611a19576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611aa7576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611ab281600361497d565b8311611b1a576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611b22612725565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611b69908861321d565b60055415611d1e57600554600090611b8390600190614994565b9050600060058281548110611b9a57611b9a61460a565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611bd457611bd461460a565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611c5457611c546149a7565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611cbd57611cbd6149a7565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611b69915050565b60005b8151518110156122d557815180516000919083908110611d4357611d4361460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611dc8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f7369676e6572206d757374206e6f7420626520656d70747900000000000000006044820152606401610b0d565b600073ffffffffffffffffffffffffffffffffffffffff1682602001518281518110611df657611df661460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611e7b576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d7074790000006044820152606401610b0d565b60006004600084600001518481518110611e9757611e9761460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611ee157611ee161492a565b14611f48576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611f7957611f7961460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561201a5761201a61492a565b02179055506000915061202a9050565b60046000846020015184815181106120445761204461460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff16600281111561208e5761208e61492a565b146120f5576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff8216815260208101600281525060046000846020015184815181106121285761212861460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016176101008360028111156121c9576121c961492a565b0217905550508251805160059250839081106121e7576121e761460a565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106122635761226361460a565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055806122cd81614639565b915050611d21565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161238d918491740100000000000000000000000000000000000000009004166149d6565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123ec4630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a00151613236565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986124a3988b9891977401000000000000000000000000000000000000000090920463ffffffff169690959194919391926149f3565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a9190614aa3565b50935050925050804261267d9190614994565b836020015163ffffffff1610801561269f57506000836020015163ffffffff16115b156126cd57505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b6000821361270a576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b612719612725565b612722816132e1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146127a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6127a6612725565b600b546bffffffffffffffffffffffff166000036127ca57565b60006127d4610de2565b80519091506000819003612814576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b546000906128339083906bffffffffffffffffffffffff16614af3565b905060005b828110156128fe5781600a60008684815181106128575761285761460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128bf9190614b1e565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806128f790614639565b9050612838565b506129098282614b43565b600b80546000906129299084906bffffffffffffffffffffffff166145e5565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612b17576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612b548560e001513a8488608001516130b0565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612bb0576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612bc99190614b6b565b905060003087604001518860a001518960c001516001612be99190614b7e565b8a5180516020918201206101008d015160e08e0151604051612c9d98979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612dac9190613fdc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612e0782602061497d565b612e1285602061497d565b612e1e88610144614b6b565b612e289190614b6b565b612e329190614b6b565b612e3d906000614b6b565b9050368114612ea8576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b600080808080612ec386880188614c7a565b84519499509297509095509350915060ff16801580612ee3575084518114155b80612eef575083518114155b80612efb575082518114155b80612f07575081518114155b15612f6e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4669656c6473206d75737420626520657175616c206c656e67746800000000006044820152606401610b0d565b60005b818110156130a1576000613006888381518110612f9057612f9061460a565b6020026020010151888481518110612faa57612faa61460a565b6020026020010151888581518110612fc457612fc461460a565b6020026020010151888681518110612fde57612fde61460a565b6020026020010151888781518110612ff857612ff861460a565b6020026020010151886133d6565b9050600081600681111561301c5761301c61492a565b1480613039575060018160068111156130375761303761492a565b145b15613090578782815181106130505761305061460a565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b5061309a81614639565b9050612f71565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561310b57600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b600854600090612710906131259063ffffffff168761497d565b61312f9190614d4c565b6131399086614b6b565b60085490915060009087906131729063ffffffff6c010000000000000000000000008204811691680100000000000000009004166149d6565b61317c91906149d6565b63ffffffff16905060006131c66000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136ea92505050565b905060006131e7826131d8858761497d565b6131e29190614b6b565b61382c565b9050600061320368ffffffffffffffffff808916908a16614b1e565b905061320f8183614b1e565b9a9950505050505050505050565b6000613227610de2565b511115610d8e57610d8e6127b0565b6000808a8a8a8a8a8a8a8a8a60405160200161325a99989796959493929190614d60565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613360576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133ed9190614e2c565b905060003a8261012001518361010001516134089190614ef4565b64ffffffffff16613419919061497d565b905060008460ff166134616000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136ea92505050565b61346b9190614d4c565b9050600061347c6131e28385614b6b565b905060006134893a61382c565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff16896134e89190614b1e565b338d6040518763ffffffff1660e01b815260040161350b96959493929190614f12565b60408051808303816000875af1158015613529573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354d9190614f8e565b909250905060008260068111156135665761356661492a565b1480613583575060018260068111156135815761358161492a565b145b156136d95760008e8152600760205260408120556135a18185614b1e565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161360d91859116614b1e565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b61368c9190614b1e565b6136969190614b1e565b6136a09190614b1e565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b6000466136f681613860565b1561377257606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061376b9190614fc1565b9392505050565b61377b81613883565b156138235773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e8460405180608001604052806048815260200161500a604891396040516020016137db929190614fda565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016138069190613ca1565b602060405180830381865afa158015613747573d6000803e3d6000fd5b50600092915050565b600061385a6138396124b8565b61384b84670de0b6b3a764000061497d565b6138559190614d4c565b6138ca565b92915050565b600061a4b1821480613874575062066eed82145b8061385a57505062066eee1490565b6000600a82148061389557506101a482145b806138a2575062aa37dc82145b806138ae575061210582145b806138bb575062014a3382145b8061385a57505062014a341490565b60006bffffffffffffffffffffffff821115613968576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f84011261399d57600080fd5b50813567ffffffffffffffff8111156139b557600080fd5b6020830191508360208285010111156139cd57600080fd5b9250929050565b600080602083850312156139e757600080fd5b823567ffffffffffffffff8111156139fe57600080fd5b613a0a8582860161398b565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613a6957613a69613a16565b60405290565b604051610160810167ffffffffffffffff81118282101715613a6957613a69613a16565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613ada57613ada613a16565b604052919050565b63ffffffff8116811461272257600080fd5b803561117081613ae2565b68ffffffffffffffffff8116811461272257600080fd5b803561117081613aff565b64ffffffffff8116811461272257600080fd5b803561117081613b21565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613b9057600080fd5b613b98613a45565b613ba183613af4565b8152613baf60208401613af4565b6020820152613bc060408401613af4565b6040820152613bd160608401613af4565b6060820152613be260808401613b16565b6080820152613bf360a08401613b34565b60a0820152613c0460c08401613b3f565b60c0820152613c1560e08401613b51565b60e0820152610100613c28818501613af4565b908201529392505050565b60005b83811015613c4e578181015183820152602001613c36565b50506000910152565b60008151808452613c6f816020860160208601613c33565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061376b6020830184613c57565b600082601f830112613cc557600080fd5b813567ffffffffffffffff811115613cdf57613cdf613a16565b613d1060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613a93565b818152846020838601011115613d2557600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613d5457600080fd5b813567ffffffffffffffff811115613d6b57600080fd5b613d7784828501613cb4565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461272257600080fd5b803561117081613d7f565b6bffffffffffffffffffffffff8116811461272257600080fd5b803561117081613dac565b60008060408385031215613de457600080fd5b8235613def81613d7f565b91506020830135613dff81613dac565b809150509250929050565b600081518084526020808501945080840160005b83811015613e5057815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e1e565b509495945050505050565b60208152600061376b6020830184613e0a565b600060208284031215613e8057600080fd5b5035919050565b600060208284031215613e9957600080fd5b813567ffffffffffffffff811115613eb057600080fd5b8201610160818503121561376b57600080fd5b805182526020810151613eee602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613f0e60408401826bffffffffffffffffffffffff169052565b506060810151613f36606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613f52608084018267ffffffffffffffff169052565b5060a0810151613f6a60a084018263ffffffff169052565b5060c0810151613f8760c084018268ffffffffffffffffff169052565b5060e0810151613fa460e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b610160810161385a8284613ec3565b60008083601f840112613ffd57600080fd5b50813567ffffffffffffffff81111561401557600080fd5b6020830191508360208260051b85010111156139cd57600080fd5b60008060008060008060008060e0898b03121561404c57600080fd5b606089018a81111561405d57600080fd5b8998503567ffffffffffffffff8082111561407757600080fd5b6140838c838d0161398b565b909950975060808b013591508082111561409c57600080fd5b6140a88c838d01613feb565b909750955060a08b01359150808211156140c157600080fd5b506140ce8b828c01613feb565b999c989b50969995989497949560c00135949350505050565b815163ffffffff90811682526020808401518216908301526040808401518216908301526060808401519182169083015261012082019050608083015161413b608084018268ffffffffffffffffff169052565b5060a083015161415460a084018264ffffffffff169052565b5060c083015161416a60c084018261ffff169052565b5060e083015161419a60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461272257600080fd5b8035611170816141b6565b6000806000806000608086880312156141ef57600080fd5b85356141fa816141b6565b9450602086013567ffffffffffffffff81111561421657600080fd5b6142228882890161398b565b909550935050604086013561423681613ae2565b949793965091946060013592915050565b600067ffffffffffffffff82111561426157614261613a16565b5060051b60200190565b600082601f83011261427c57600080fd5b8135602061429161428c83614247565b613a93565b82815260059290921b840181019181810190868411156142b057600080fd5b8286015b848110156142d45780356142c781613d7f565b83529183019183016142b4565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561430957600080fd5b863567ffffffffffffffff8082111561432157600080fd5b61432d8a838b0161426b565b9750602089013591508082111561434357600080fd5b61434f8a838b0161426b565b965061435d60408a016142df565b9550606089013591508082111561437357600080fd5b61437f8a838b01613cb4565b945061438d60808a016141cc565b935060a08901359150808211156143a357600080fd5b506143b089828a01613cb4565b9150509295509295509295565b6000602082840312156143cf57600080fd5b813561376b81613d7f565b600181811c908216806143ee57607f821691505b602082108103614427577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156144545750805b601f850160051c820191505b81811015610a8857828155600101614460565b67ffffffffffffffff83111561448b5761448b613a16565b61449f8361449983546143da565b8361442d565b6000601f8411600181146144f157600085156144bb5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355614587565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156145405786850135825560209485019460019092019101614520565b508682101561457b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613aff565b6000602082840312156145ab57600080fd5b815161376b81613aff565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561270a5761270a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361466a5761466a6145b6565b5060010190565b6000610160823603121561468457600080fd5b61468c613a6f565b823567ffffffffffffffff8111156146a357600080fd5b6146af36828601613cb4565b825250602083013560208201526146c860408401613da1565b60408201526146d960608401613dc6565b60608201526146ea60808401613b16565b60808201526146fb60a084016141cc565b60a082015261470c60c084016141cc565b60c082015261471d60e08401613af4565b60e0820152610100614730818501613b3f565b908201526101206147428482016141cc565b90820152610140614754848201613da1565b9082015292915050565b60006020828403121561477057600080fd5b813561376b816141b6565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147b057600080fd5b83018035915067ffffffffffffffff8211156147cb57600080fd5b6020019150368190038213156139cd57600080fd5b6000602082840312156147f257600080fd5b61376b82613b3f565b60006020828403121561480d57600080fd5b813561376b81613ae2565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061320f60e0830184613ec3565b60ff818116838216019081111561385a5761385a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061491b5761491b6148d9565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b808202811582820484141761385a5761385a6145b6565b8181038181111561385a5761385a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561270a5761270a6145b6565b600061012063ffffffff808d1684528b6020850152808b16604085015250806060840152614a238184018a613e0a565b90508281036080840152614a378189613e0a565b905060ff871660a084015282810360c0840152614a548187613c57565b905067ffffffffffffffff851660e0840152828103610100840152614a798185613c57565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a08688031215614abb57600080fd5b614ac486614a89565b9450602086015193506040860151925060608601519150614ae760808701614a89565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614b1257614b126148d9565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561270a5761270a6145b6565b6bffffffffffffffffffffffff8181168382160280821691908281146141ae576141ae6145b6565b8082018082111561385a5761385a6145b6565b67ffffffffffffffff81811683821601908082111561270a5761270a6145b6565b600082601f830112614bb057600080fd5b81356020614bc061428c83614247565b82815260059290921b84018101918181019086841115614bdf57600080fd5b8286015b848110156142d45780358352918301918301614be3565b600082601f830112614c0b57600080fd5b81356020614c1b61428c83614247565b82815260059290921b84018101918181019086841115614c3a57600080fd5b8286015b848110156142d457803567ffffffffffffffff811115614c5e5760008081fd5b614c6c8986838b0101613cb4565b845250918301918301614c3e565b600080600080600060a08688031215614c9257600080fd5b853567ffffffffffffffff80821115614caa57600080fd5b614cb689838a01614b9f565b96506020880135915080821115614ccc57600080fd5b614cd889838a01614bfa565b95506040880135915080821115614cee57600080fd5b614cfa89838a01614bfa565b94506060880135915080821115614d1057600080fd5b614d1c89838a01614bfa565b93506080880135915080821115614d3257600080fd5b50614d3f88828901614bfa565b9150509295509295909350565b600082614d5b57614d5b6148d9565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614da78285018b613e0a565b91508382036080850152614dbb828a613e0a565b915060ff881660a085015283820360c0850152614dd88288613c57565b90861660e08501528381036101008501529050614a798185613c57565b805161117081613d7f565b805161117081613dac565b8051611170816141b6565b805161117081613ae2565b805161117081613b21565b60006101608284031215614e3f57600080fd5b614e47613a6f565b82518152614e5760208401614df5565b6020820152614e6860408401614e00565b6040820152614e7960608401614df5565b6060820152614e8a60808401614e0b565b6080820152614e9b60a08401614e16565b60a0820152614eac60c0840161458e565b60c0820152614ebd60e0840161458e565b60e0820152610100614ed0818501614e21565b90820152610120614ee2848201614e21565b90820152610140613c28848201614e16565b64ffffffffff81811683821601908082111561270a5761270a6145b6565b6000610200808352614f268184018a613c57565b90508281036020840152614f3a8189613c57565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614f83905060a0830184613ec3565b979650505050505050565b60008060408385031215614fa157600080fd5b825160078110614fb057600080fd5b6020840151909250613dff81613dac565b600060208284031215614fd357600080fd5b5051919050565b60008351614fec818460208801613c33565b835190830190615000818360208801613c33565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBillingConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBillingConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"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\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"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\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBillingConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"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\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"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\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBillingConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b50604051620056d0380380620056d083398101604081905262000034916200046d565b8282828233806000816200008f5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c257620000c28162000139565b5050506001600160a01b038116620000ed57604051632530e88560e11b815260040160405180910390fd5b6001600160a01b03908116608052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200012d82620001e4565b5050505050506200062c565b336001600160a01b03821603620001935760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000086565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee62000342565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033790839062000576565b60405180910390a150565b6200034c6200034e565b565b6000546001600160a01b031633146200034c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000086565b80516001600160a01b0381168114620003c257600080fd5b919050565b60405161012081016001600160401b0381118282101715620003f957634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c257600080fd5b80516001600160481b0381168114620003c257600080fd5b805164ffffffffff81168114620003c257600080fd5b805161ffff81168114620003c257600080fd5b80516001600160e01b0381168114620003c257600080fd5b60008060008385036101608112156200048557600080fd5b6200049085620003aa565b935061012080601f1983011215620004a757600080fd5b620004b1620003c7565b9150620004c160208701620003ff565b8252620004d160408701620003ff565b6020830152620004e460608701620003ff565b6040830152620004f760808701620003ff565b60608301526200050a60a0870162000414565b60808301526200051d60c087016200042c565b60a08301526200053060e0870162000442565b60c08301526101006200054581880162000455565b60e084015262000557828801620003ff565b908301525091506200056d6101408501620003aa565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005c960808401826001600160481b03169052565b5060a0830151620005e360a084018264ffffffffff169052565b5060c0830151620005fa60c084018261ffff169052565b5060e08301516200061660e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805161505e6200067260003960008181610845015281816109d301528181610ca601528181610f3a01528181611045015281816117890152613490015261505e6000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046139d4565b61059c565b005b6101a56101b5366004613b7d565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e322e300000000081525081565b6040516102039190613ca1565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613d42565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613dd1565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046139d4565b610d92565b6102a0610de2565b6040516102039190613e5b565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613e6e565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613e87565b610fd4565b6040516102039190613fdc565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004614030565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b60405161020391906140e7565b61053b6105363660046141d7565b611785565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f66118e5565b6101a561056e3660046142f0565b61193c565b61057b6124b8565b604051908152602001610203565b6101a56105973660046143bd565b612711565b6105a4612725565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec828483614473565b505050565b6105f96127a8565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906108369083906140e7565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d29190614599565b905090565b6108df6127b0565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff166145e5565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6127a8565b610ba26127b0565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561460a565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614639565b9050610bb1565b5050565b610d9a612725565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec828483614473565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e60906143da565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea8906143da565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed4906143da565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614671565b61295c565b90506110bf60608301604084016143bd565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a0880161475e565b61111f610160880161014089016143bd565b611129888061477b565b61113b6101208b016101008c016147e0565b60208b01356111516101008d0160e08e016147fb565b8b60405161116799989796959493929190614818565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16111d68a8a8a8a8a8a612dfa565b6003546000906002906111f49060ff808216916101009004166148c0565b6111fe9190614908565b6112099060016148c0565b60ff169050878114611277576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b878614611306576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f7265706f727420727320616e64207373206d757374206265206f66206571756160448201527f6c206c656e6774680000000000000000000000000000000000000000000000006064820152608401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113495761134961492a565b600281111561135a5761135a61492a565b90525090506002816020015160028111156113775761137761492a565b141580156113c057506006816000015160ff168154811061139a5761139a61460a565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff163314155b15611427576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b5050505061143361396c565b6000808a8a604051611446929190614959565b60405190819003812061145d918e90602001614969565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b898110156117675760006001848984602081106114c6576114c661460a565b6114d391901a601b6148c0565b8e8e868181106114e5576114e561460a565b905060200201358d8d878181106114fe576114fe61460a565b905060200201356040516000815260200160405260405161153b949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561155d573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff808216855292965092945084019161010090041660028111156115dd576115dd61492a565b60028111156115ee576115ee61492a565b905250925060018360200151600281111561160b5761160b61492a565b14611672576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061168c5761168c61460a565b602002015173ffffffffffffffffffffffffffffffffffffffff161461170e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117285761172861460a565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117536001866148c0565b9450508061176090614639565b90506114a7565b505050611778833383858e8e612eb1565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b15801561182557600080fd5b505afa158015611839573d6000803e3d6000fd5b5050505066038d7ea4c6800082111561187e576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611888610841565b905060006118cb87878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b90506118d9858583856130b0565b98975050505050505050565b6060600c80546118f4906143da565b905060000361192f576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea8906143da565b855185518560ff16601f8311156119af576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611a19576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611aa7576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611ab281600361497d565b8311611b1a576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611b22612725565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611b69908861321d565b60055415611d1e57600554600090611b8390600190614994565b9050600060058281548110611b9a57611b9a61460a565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611bd457611bd461460a565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611c5457611c546149a7565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611cbd57611cbd6149a7565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611b69915050565b60005b8151518110156122d557815180516000919083908110611d4357611d4361460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611dc8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f7369676e6572206d757374206e6f7420626520656d70747900000000000000006044820152606401610b0d565b600073ffffffffffffffffffffffffffffffffffffffff1682602001518281518110611df657611df661460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611e7b576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d7074790000006044820152606401610b0d565b60006004600084600001518481518110611e9757611e9761460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611ee157611ee161492a565b14611f48576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611f7957611f7961460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561201a5761201a61492a565b02179055506000915061202a9050565b60046000846020015184815181106120445761204461460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff16600281111561208e5761208e61492a565b146120f5576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff8216815260208101600281525060046000846020015184815181106121285761212861460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016176101008360028111156121c9576121c961492a565b0217905550508251805160059250839081106121e7576121e761460a565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106122635761226361460a565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055806122cd81614639565b915050611d21565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161238d918491740100000000000000000000000000000000000000009004166149d6565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123ec4630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a00151613236565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986124a3988b9891977401000000000000000000000000000000000000000090920463ffffffff169690959194919391926149f3565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a9190614aa3565b50935050925050804261267d9190614994565b836020015163ffffffff1610801561269f57506000836020015163ffffffff16115b156126cd57505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b6000821361270a576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b612719612725565b612722816132e1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146127a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6127a6612725565b600b546bffffffffffffffffffffffff166000036127ca57565b60006127d4610de2565b80519091506000819003612814576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b546000906128339083906bffffffffffffffffffffffff16614af3565b905060005b828110156128fe5781600a60008684815181106128575761285761460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128bf9190614b1e565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806128f790614639565b9050612838565b506129098282614b43565b600b80546000906129299084906bffffffffffffffffffffffff166145e5565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612b17576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612b548560e001513a8488608001516130b0565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612bb0576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612bc99190614b6b565b905060003087604001518860a001518960c001516001612be99190614b7e565b8a5180516020918201206101008d015160e08e0151604051612c9d98979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612dac9190613fdc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612e0782602061497d565b612e1285602061497d565b612e1e88610144614b6b565b612e289190614b6b565b612e329190614b6b565b612e3d906000614b6b565b9050368114612ea8576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b600080808080612ec386880188614c7a565b84519499509297509095509350915060ff16801580612ee3575084518114155b80612eef575083518114155b80612efb575082518114155b80612f07575081518114155b15612f6e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4669656c6473206d75737420626520657175616c206c656e67746800000000006044820152606401610b0d565b60005b818110156130a1576000613006888381518110612f9057612f9061460a565b6020026020010151888481518110612faa57612faa61460a565b6020026020010151888581518110612fc457612fc461460a565b6020026020010151888681518110612fde57612fde61460a565b6020026020010151888781518110612ff857612ff861460a565b6020026020010151886133d6565b9050600081600681111561301c5761301c61492a565b1480613039575060018160068111156130375761303761492a565b145b15613090578782815181106130505761305061460a565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b5061309a81614639565b9050612f71565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561310b57600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b600854600090612710906131259063ffffffff168761497d565b61312f9190614d4c565b6131399086614b6b565b60085490915060009087906131729063ffffffff6c010000000000000000000000008204811691680100000000000000009004166149d6565b61317c91906149d6565b63ffffffff16905060006131c66000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136ea92505050565b905060006131e7826131d8858761497d565b6131e29190614b6b565b61382c565b9050600061320368ffffffffffffffffff808916908a16614b1e565b905061320f8183614b1e565b9a9950505050505050505050565b6000613227610de2565b511115610d8e57610d8e6127b0565b6000808a8a8a8a8a8a8a8a8a60405160200161325a99989796959493929190614d60565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613360576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133ed9190614e2c565b905060003a8261012001518361010001516134089190614ef4565b64ffffffffff16613419919061497d565b905060008460ff166134616000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136ea92505050565b61346b9190614d4c565b9050600061347c6131e28385614b6b565b905060006134893a61382c565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff16896134e89190614b1e565b338d6040518763ffffffff1660e01b815260040161350b96959493929190614f12565b60408051808303816000875af1158015613529573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354d9190614f8e565b909250905060008260068111156135665761356661492a565b1480613583575060018260068111156135815761358161492a565b145b156136d95760008e8152600760205260408120556135a18185614b1e565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161360d91859116614b1e565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b61368c9190614b1e565b6136969190614b1e565b6136a09190614b1e565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b6000466136f681613860565b1561377257606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061376b9190614fc1565b9392505050565b61377b81613883565b156138235773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e8460405180608001604052806048815260200161500a604891396040516020016137db929190614fda565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016138069190613ca1565b602060405180830381865afa158015613747573d6000803e3d6000fd5b50600092915050565b600061385a6138396124b8565b61384b84670de0b6b3a764000061497d565b6138559190614d4c565b6138ca565b92915050565b600061a4b1821480613874575062066eed82145b8061385a57505062066eee1490565b6000600a82148061389557506101a482145b806138a2575062aa37dc82145b806138ae575061210582145b806138bb575062014a3382145b8061385a57505062014a341490565b60006bffffffffffffffffffffffff821115613968576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f84011261399d57600080fd5b50813567ffffffffffffffff8111156139b557600080fd5b6020830191508360208285010111156139cd57600080fd5b9250929050565b600080602083850312156139e757600080fd5b823567ffffffffffffffff8111156139fe57600080fd5b613a0a8582860161398b565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613a6957613a69613a16565b60405290565b604051610160810167ffffffffffffffff81118282101715613a6957613a69613a16565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613ada57613ada613a16565b604052919050565b63ffffffff8116811461272257600080fd5b803561117081613ae2565b68ffffffffffffffffff8116811461272257600080fd5b803561117081613aff565b64ffffffffff8116811461272257600080fd5b803561117081613b21565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613b9057600080fd5b613b98613a45565b613ba183613af4565b8152613baf60208401613af4565b6020820152613bc060408401613af4565b6040820152613bd160608401613af4565b6060820152613be260808401613b16565b6080820152613bf360a08401613b34565b60a0820152613c0460c08401613b3f565b60c0820152613c1560e08401613b51565b60e0820152610100613c28818501613af4565b908201529392505050565b60005b83811015613c4e578181015183820152602001613c36565b50506000910152565b60008151808452613c6f816020860160208601613c33565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061376b6020830184613c57565b600082601f830112613cc557600080fd5b813567ffffffffffffffff811115613cdf57613cdf613a16565b613d1060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613a93565b818152846020838601011115613d2557600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613d5457600080fd5b813567ffffffffffffffff811115613d6b57600080fd5b613d7784828501613cb4565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461272257600080fd5b803561117081613d7f565b6bffffffffffffffffffffffff8116811461272257600080fd5b803561117081613dac565b60008060408385031215613de457600080fd5b8235613def81613d7f565b91506020830135613dff81613dac565b809150509250929050565b600081518084526020808501945080840160005b83811015613e5057815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e1e565b509495945050505050565b60208152600061376b6020830184613e0a565b600060208284031215613e8057600080fd5b5035919050565b600060208284031215613e9957600080fd5b813567ffffffffffffffff811115613eb057600080fd5b8201610160818503121561376b57600080fd5b805182526020810151613eee602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613f0e60408401826bffffffffffffffffffffffff169052565b506060810151613f36606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613f52608084018267ffffffffffffffff169052565b5060a0810151613f6a60a084018263ffffffff169052565b5060c0810151613f8760c084018268ffffffffffffffffff169052565b5060e0810151613fa460e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b610160810161385a8284613ec3565b60008083601f840112613ffd57600080fd5b50813567ffffffffffffffff81111561401557600080fd5b6020830191508360208260051b85010111156139cd57600080fd5b60008060008060008060008060e0898b03121561404c57600080fd5b606089018a81111561405d57600080fd5b8998503567ffffffffffffffff8082111561407757600080fd5b6140838c838d0161398b565b909950975060808b013591508082111561409c57600080fd5b6140a88c838d01613feb565b909750955060a08b01359150808211156140c157600080fd5b506140ce8b828c01613feb565b999c989b50969995989497949560c00135949350505050565b815163ffffffff90811682526020808401518216908301526040808401518216908301526060808401519182169083015261012082019050608083015161413b608084018268ffffffffffffffffff169052565b5060a083015161415460a084018264ffffffffff169052565b5060c083015161416a60c084018261ffff169052565b5060e083015161419a60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461272257600080fd5b8035611170816141b6565b6000806000806000608086880312156141ef57600080fd5b85356141fa816141b6565b9450602086013567ffffffffffffffff81111561421657600080fd5b6142228882890161398b565b909550935050604086013561423681613ae2565b949793965091946060013592915050565b600067ffffffffffffffff82111561426157614261613a16565b5060051b60200190565b600082601f83011261427c57600080fd5b8135602061429161428c83614247565b613a93565b82815260059290921b840181019181810190868411156142b057600080fd5b8286015b848110156142d45780356142c781613d7f565b83529183019183016142b4565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561430957600080fd5b863567ffffffffffffffff8082111561432157600080fd5b61432d8a838b0161426b565b9750602089013591508082111561434357600080fd5b61434f8a838b0161426b565b965061435d60408a016142df565b9550606089013591508082111561437357600080fd5b61437f8a838b01613cb4565b945061438d60808a016141cc565b935060a08901359150808211156143a357600080fd5b506143b089828a01613cb4565b9150509295509295509295565b6000602082840312156143cf57600080fd5b813561376b81613d7f565b600181811c908216806143ee57607f821691505b602082108103614427577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156144545750805b601f850160051c820191505b81811015610a8857828155600101614460565b67ffffffffffffffff83111561448b5761448b613a16565b61449f8361449983546143da565b8361442d565b6000601f8411600181146144f157600085156144bb5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355614587565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156145405786850135825560209485019460019092019101614520565b508682101561457b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613aff565b6000602082840312156145ab57600080fd5b815161376b81613aff565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561270a5761270a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361466a5761466a6145b6565b5060010190565b6000610160823603121561468457600080fd5b61468c613a6f565b823567ffffffffffffffff8111156146a357600080fd5b6146af36828601613cb4565b825250602083013560208201526146c860408401613da1565b60408201526146d960608401613dc6565b60608201526146ea60808401613b16565b60808201526146fb60a084016141cc565b60a082015261470c60c084016141cc565b60c082015261471d60e08401613af4565b60e0820152610100614730818501613b3f565b908201526101206147428482016141cc565b90820152610140614754848201613da1565b9082015292915050565b60006020828403121561477057600080fd5b813561376b816141b6565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147b057600080fd5b83018035915067ffffffffffffffff8211156147cb57600080fd5b6020019150368190038213156139cd57600080fd5b6000602082840312156147f257600080fd5b61376b82613b3f565b60006020828403121561480d57600080fd5b813561376b81613ae2565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061320f60e0830184613ec3565b60ff818116838216019081111561385a5761385a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061491b5761491b6148d9565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b808202811582820484141761385a5761385a6145b6565b8181038181111561385a5761385a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561270a5761270a6145b6565b600061012063ffffffff808d1684528b6020850152808b16604085015250806060840152614a238184018a613e0a565b90508281036080840152614a378189613e0a565b905060ff871660a084015282810360c0840152614a548187613c57565b905067ffffffffffffffff851660e0840152828103610100840152614a798185613c57565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a08688031215614abb57600080fd5b614ac486614a89565b9450602086015193506040860151925060608601519150614ae760808701614a89565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614b1257614b126148d9565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561270a5761270a6145b6565b6bffffffffffffffffffffffff8181168382160280821691908281146141ae576141ae6145b6565b8082018082111561385a5761385a6145b6565b67ffffffffffffffff81811683821601908082111561270a5761270a6145b6565b600082601f830112614bb057600080fd5b81356020614bc061428c83614247565b82815260059290921b84018101918181019086841115614bdf57600080fd5b8286015b848110156142d45780358352918301918301614be3565b600082601f830112614c0b57600080fd5b81356020614c1b61428c83614247565b82815260059290921b84018101918181019086841115614c3a57600080fd5b8286015b848110156142d457803567ffffffffffffffff811115614c5e5760008081fd5b614c6c8986838b0101613cb4565b845250918301918301614c3e565b600080600080600060a08688031215614c9257600080fd5b853567ffffffffffffffff80821115614caa57600080fd5b614cb689838a01614b9f565b96506020880135915080821115614ccc57600080fd5b614cd889838a01614bfa565b95506040880135915080821115614cee57600080fd5b614cfa89838a01614bfa565b94506060880135915080821115614d1057600080fd5b614d1c89838a01614bfa565b93506080880135915080821115614d3257600080fd5b50614d3f88828901614bfa565b9150509295509295909350565b600082614d5b57614d5b6148d9565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614da78285018b613e0a565b91508382036080850152614dbb828a613e0a565b915060ff881660a085015283820360c0850152614dd88288613c57565b90861660e08501528381036101008501529050614a798185613c57565b805161117081613d7f565b805161117081613dac565b8051611170816141b6565b805161117081613ae2565b805161117081613b21565b60006101608284031215614e3f57600080fd5b614e47613a6f565b82518152614e5760208401614df5565b6020820152614e6860408401614e00565b6040820152614e7960608401614df5565b6060820152614e8a60808401614e0b565b6080820152614e9b60a08401614e16565b60a0820152614eac60c0840161458e565b60c0820152614ebd60e0840161458e565b60e0820152610100614ed0818501614e21565b90820152610120614ee2848201614e21565b90820152610140613c28848201614e16565b64ffffffffff81811683821601908082111561270a5761270a6145b6565b6000610200808352614f268184018a613c57565b90508281036020840152614f3a8189613c57565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614f83905060a0830184613ec3565b979650505050505050565b60008060408385031215614fa157600080fd5b825160078110614fb057600080fd5b6020840151909250613dff81613dac565b600060208284031215614fd357600080fd5b5051919050565b60008351614fec818460208801613c33565b835190830190615000818360208801613c33565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", } var FunctionsCoordinatorABI = FunctionsCoordinatorMetaData.ABI diff --git a/core/gethwrappers/functions/generated/functions_router/functions_router.go b/core/gethwrappers/functions/generated/functions_router/functions_router.go index 592f95b568f..368ef65560b 100644 --- a/core/gethwrappers/functions/generated/functions_router/functions_router.go +++ b/core/gethwrappers/functions/generated/functions_router/functions_router.go @@ -70,8 +70,8 @@ type IFunctionsSubscriptionsSubscription struct { } var FunctionsRouterMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotRemoveWithPendingRequests\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateRequestId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyRequestData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"limit\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"IdentifierIsReserved\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint96\",\"name\":\"currentBalanceJuels\",\"type\":\"uint96\"}],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"value\",\"type\":\"uint8\"}],\"name\":\"InvalidGasFlagValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProposal\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeSubscriptionOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromCoordinator\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"RouteNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderMustAcceptTermsOfService\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeoutNotExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"maximumConsumers\",\"type\":\"uint16\"}],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"indexed\":false,\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"proposedContractSetId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"proposedContractSetFromAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"proposedContractSetToAddress\",\"type\":\"address\"}],\"name\":\"ContractProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"ContractUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"enumFunctionsResponse.FulfillResult\",\"name\":\"resultCode\",\"type\":\"uint8\"}],\"name\":\"RequestNotProcessed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"enumFunctionsResponse.FulfillResult\",\"name\":\"resultCode\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"response\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"callbackReturnData\",\"type\":\"bytes\"}],\"name\":\"RequestProcessed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"donId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestStart\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"RequestTimedOut\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"fundsRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fundsAmount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MAX_CALLBACK_RETURN_BYTES\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"createSubscriptionWithConsumer\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"response\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"costWithoutCallback\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"fulfill\",\"outputs\":[{\"internalType\":\"enumFunctionsResponse.FulfillResult\",\"name\":\"resultCode\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"getConsumer\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"}],\"internalType\":\"structIFunctionsSubscriptions.Consumer\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"getContractById\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"getFlags\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"getProposedContractById\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getProposedContractSet\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"getSubscription\",\"outputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"blockedBalance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"}],\"internalType\":\"structIFunctionsSubscriptions.Subscription\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSubscriptionCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionIdStart\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionIdEnd\",\"type\":\"uint64\"}],\"name\":\"getSubscriptionsInRange\",\"outputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"blockedBalance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"}],\"internalType\":\"structIFunctionsSubscriptions.Subscription[]\",\"name\":\"subscriptions\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTotalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"}],\"name\":\"isValidCallbackGasLimit\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"ownerWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"proposedContractSetIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"address[]\",\"name\":\"proposedContractSetAddresses\",\"type\":\"address[]\"}],\"name\":\"proposeContractsUpdate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"proposeSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"donId\",\"type\":\"bytes32\"}],\"name\":\"sendRequest\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"donId\",\"type\":\"bytes32\"}],\"name\":\"sendRequestToProposed\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"allowListId\",\"type\":\"bytes32\"}],\"name\":\"setAllowListId\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"}],\"name\":\"setFlags\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment[]\",\"name\":\"requestsToTimeoutByCommitment\",\"type\":\"tuple[]\"}],\"name\":\"timeoutRequests\",\"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\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"updateContracts\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a06040523480156200001157600080fd5b506040516200673c3803806200673c833981016040819052620000349162000549565b6001600160a01b0382166080526006805460ff191690553380600081620000a25760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600680546001600160a01b0380851661010002610100600160a81b031990921691909117909155811615620000dc57620000dc81620000f8565b505050620000f081620001aa60201b60201c565b50506200071a565b336001600160a01b03821603620001525760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000099565b600780546001600160a01b0319166001600160a01b03838116918217909255600654604051919261010090910416907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b620001b4620002c0565b8051600a80546020808501516040860151606087015161ffff908116600160781b0261ffff60781b1960e09390931c6b010000000000000000000000029290921665ffffffffffff60581b196001600160481b0390941662010000026001600160581b031990961691909716179390931716939093171781556080830151805184936200024792600b9291019062000323565b5060a08201516002909101805460c0909301516001600160481b031662010000026001600160581b031990931661ffff909216919091179190911790556040517ea5832bf95f66c7814294cc4db681f20ee79608bfb8912a5321d66cfed5e98590620002b590839062000652565b60405180910390a150565b60065461010090046001600160a01b03163314620003215760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000099565b565b82805482825590600052602060002090600701600890048101928215620003c75791602002820160005b838211156200039357835183826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026200034d565b8015620003c55782816101000a81549063ffffffff021916905560040160208160030104928301926001030262000393565b505b50620003d5929150620003d9565b5090565b5b80821115620003d55760008155600101620003da565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b03811182821017156200042b576200042b620003f0565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200045c576200045c620003f0565b604052919050565b805161ffff811681146200047757600080fd5b919050565b80516001600160481b03811681146200047757600080fd5b80516001600160e01b0319811681146200047757600080fd5b600082601f830112620004bf57600080fd5b815160206001600160401b03821115620004dd57620004dd620003f0565b8160051b620004ee82820162000431565b92835284810182019282810190878511156200050957600080fd5b83870192505b848310156200053e57825163ffffffff811681146200052e5760008081fd5b825291830191908301906200050f565b979650505050505050565b600080604083850312156200055d57600080fd5b82516001600160a01b03811681146200057557600080fd5b60208401519092506001600160401b03808211156200059357600080fd5b9084019060e08287031215620005a857600080fd5b620005b262000406565b620005bd8362000464565b8152620005cd602084016200047c565b6020820152620005e06040840162000494565b6040820152620005f36060840162000464565b60608201526080830151828111156200060b57600080fd5b6200061988828601620004ad565b6080830152506200062d60a0840162000464565b60a08201526200064060c084016200047c565b60c08201528093505050509250929050565b6020808252825161ffff90811683830152838201516001600160481b03166040808501919091528401516001600160e01b0319166060808501919091528401511660808084019190915283015160e060a0840152805161010084018190526000929182019083906101208601905b80831015620006e857835163ffffffff168252928401926001929092019190840190620006c0565b5060a087015161ffff811660c0880152935060c08701516001600160481b03811660e088015293509695505050505050565b608051615fea62000752600039600081816111cd0152818161208c015281816129b801528181612a7c01526135d30152615fea6000f3fe608060405234801561001057600080fd5b50600436106102e95760003560e01c80637341c10c11610191578063b734c0f4116100e3578063e72f6e3011610097578063ea320e0b11610071578063ea320e0b146106dd578063ec2454e5146106f0578063f2fde38b1461071057600080fd5b8063e72f6e30146106a4578063e82622aa146106b7578063e82ad7d4146106ca57600080fd5b8063c3f909d4116100c8578063c3f909d414610669578063cc77470a1461067e578063d7ae1d301461069157600080fd5b8063b734c0f41461064b578063badc3eb61461065357600080fd5b80639f87fad711610145578063a4c0ed361161011f578063a4c0ed361461061d578063a9c9a91814610630578063aab396bd1461064357600080fd5b80639f87fad7146105e2578063a21a23e4146105f5578063a47c7696146105fd57600080fd5b8063823597401161017657806382359740146105a45780638456cb59146105b75780638da5cb5b146105bf57600080fd5b80637341c10c1461058957806379ba50971461059c57600080fd5b806341db4ca31161024a5780635ed6dfba116101fe57806366419970116101d857806366419970146104e1578063674603d0146105085780636a2215de1461055157600080fd5b80635ed6dfba146104a85780636162a323146104bb57806366316d8d146104ce57600080fd5b80634b8832d31161022f5780634b8832d31461045057806355fedefa146104635780635c975abb1461049157600080fd5b806341db4ca31461041c578063461d27621461043d57600080fd5b80631ded3b36116102a1578063330605291161028657806333060529146103e05780633e871e4d146104015780633f4ba83a1461041457600080fd5b80631ded3b361461039f5780632a905ccc146103b257600080fd5b806310fc49c1116102d257806310fc49c11461032357806312b5834914610336578063181f5a771461035657600080fd5b806302bcc5b6146102ee5780630c5d49cb14610303575b600080fd5b6103016102fc366004614ba6565b610723565b005b61030b608481565b60405161ffff90911681526020015b60405180910390f35b610301610331366004614be7565b610783565b6000546040516bffffffffffffffffffffffff909116815260200161031a565b6103926040518060400160405280601781526020017f46756e6374696f6e7320526f757465722076312e302e3000000000000000000081525081565b60405161031a9190614c8e565b6103016103ad366004614ca1565b61087f565b600a5462010000900468ffffffffffffffffff1660405168ffffffffffffffffff909116815260200161031a565b6103f36103ee366004614f8c565b6108b1565b60405161031a929190615074565b61030161040f366004615135565b610c7c565b610301610e91565b61042f61042a366004615249565b610ea3565b60405190815260200161031a565b61042f61044b366004615249565b610f03565b61030161045e3660046152cd565b610f0f565b61042f610471366004614ba6565b67ffffffffffffffff166000908152600360208190526040909120015490565b60065460ff165b604051901515815260200161031a565b6103016104b63660046152fb565b61105d565b6103016104c93660046153bd565b611216565b6103016104dc3660046152fb565b611396565b60025467ffffffffffffffff165b60405167ffffffffffffffff909116815260200161031a565b61051b610516366004615490565b61147f565b6040805182511515815260208084015167ffffffffffffffff90811691830191909152928201519092169082015260600161031a565b61056461055f3660046154be565b61150f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031a565b6103016105973660046152cd565b6115ce565b610301611781565b6103016105b2366004614ba6565b6118a8565b6103016119ef565b600654610100900473ffffffffffffffffffffffffffffffffffffffff16610564565b6103016105f03660046152cd565b6119ff565b6104ef611daa565b61061061060b366004614ba6565b611f37565b60405161031a91906155a7565b61030161062b3660046155ba565b61206c565b61056461063e3660046154be565b6122b8565b60095461042f565b610301612317565b61065b612463565b60405161031a929190615616565b610671612533565b60405161031a919061566d565b6104ef61068c366004615749565b61269a565b61030161069f3660046152cd565b61291a565b6103016106b2366004615749565b61297f565b6103016106c5366004615766565b612af8565b6104986106d8366004614ba6565b612db7565b6103016106eb3660046154be565b612f06565b6107036106fe3660046157dc565b612f13565b60405161031a91906157fa565b61030161071e366004615749565b6131a8565b61072b6131b9565b610734816131c1565b67ffffffffffffffff81166000908152600360205260408120546107809183916c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690613237565b50565b67ffffffffffffffff8216600090815260036020819052604082200154600b54911a9081106107e8576040517f45c108ce00000000000000000000000000000000000000000000000000000000815260ff821660048201526024015b60405180910390fd5b6000600a6001018260ff16815481106108035761080361587a565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1690508063ffffffff168363ffffffff161115610879576040517f1d70f87a00000000000000000000000000000000000000000000000000000000815263ffffffff821660048201526024016107df565b50505050565b6108876131b9565b610890826131c1565b67ffffffffffffffff90911660009081526003602081905260409091200155565b6000806108bc613689565b826020015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610925576040517f8bec23e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82516000908152600560205260409020548061098a5783516020850151604051600295507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b60405180910390a25060009050610c71565b808460405160200161099c91906158db565b60405160208183030381529060405280519060200120146109f45783516020850151604051600695507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b8361012001518460a0015163ffffffff16610a0f9190615a37565b64ffffffffff165a1015610a5a5783516020850151604051600495507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b506000610a708460a0015163ffffffff16613691565b610a7a9088615a55565b9050600081878660c0015168ffffffffffffffffff16610a9a9190615a7d565b610aa49190615a7d565b9050610ab38560800151611f37565b600001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610b2b5784516020860151604051600596507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee191610b17918a9089906158a9565b60405180910390a25060009150610c719050565b84604001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610b905784516020860151604051600396507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee191610b17918a9089906158a9565b505082516000908152600560205260408120819055835160a08501516060860151610bc092918c918c9190613733565b8051909150610bd0576001610bd3565b60005b92506000610c0d8560800151866040015187606001518860c0015168ffffffffffffffffff168c610c078860200151613691565b8d6138f1565b9050846080015167ffffffffffffffff1685600001517f64778f26c70b60a8d7e29e2451b3844302d959448401c0535b768ed88c6b505e836020015189888f8f8960400151604051610c6496959493929190615aa2565b60405180910390a3519150505b965096945050505050565b610c84613c17565b8151815181141580610c965750600881115b15610ccd576040517fee03280800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e47576000848281518110610cec57610cec61587a565b602002602001015190506000848381518110610d0a57610d0a61587a565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610d75575060008281526008602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610dac576040517fee03280800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260086020526040908190205490517f8b052f0f4bf82fede7daffea71592b29d5ef86af1f3c7daaa0345dbb2f52f48191610e2c91859173ffffffffffffffffffffffffffffffffffffffff1690859092835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60405180910390a1505080610e4090615b25565b9050610cd0565b506040805180820190915283815260208082018490528451600d91610e709183918801906149e6565b506020828101518051610e899260018501920190614a2d565b505050505050565b610e99613c17565b610ea1613c9d565b565b600080610eaf8361150f565b9050610ef783828a8a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150613d1a9050565b98975050505050505050565b600080610eaf836122b8565b610f17613689565b610f20826140ef565b610f286141b5565b73ffffffffffffffffffffffffffffffffffffffff81161580610f8f575067ffffffffffffffff821660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff8281166c0100000000000000000000000090920416145b15610fc6576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff821660008181526003602090815260409182902060010180546bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8716908102919091179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be910160405180910390a25050565b6110656131b9565b806bffffffffffffffffffffffff1660000361109b5750306000908152600160205260409020546bffffffffffffffffffffffff165b306000908152600160205260409020546bffffffffffffffffffffffff908116908216811015611107576040517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526024016107df565b30600090815260016020526040812080548492906111349084906bffffffffffffffffffffffff16615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550816000808282829054906101000a90046bffffffffffffffffffffffff1661118a9190615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061121183836bffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166142bf9092919063ffffffff16565b505050565b61121e613c17565b8051600a80546020808501516040860151606087015161ffff9081166f01000000000000000000000000000000027fffffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffff60e09390931c6b01000000000000000000000002929092167fffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffff68ffffffffffffffffff90941662010000027fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000909616919097161793909317169390931717815560808301518051849361130592600b92910190614aa7565b5060a08201516002909101805460c09093015168ffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffffffffff000000000000000000000090931661ffff909216919091179190911790556040517ea5832bf95f66c7814294cc4db681f20ee79608bfb8912a5321d66cfed5e9859061138b90839061566d565b60405180910390a150565b61139e613689565b806bffffffffffffffffffffffff166000036113e6576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600160205260409020546bffffffffffffffffffffffff908116908216811015611452576040517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526024016107df565b33600090815260016020526040812080548492906111349084906bffffffffffffffffffffffff16615b5d565b60408051606080820183526000808352602080840182905292840181905273ffffffffffffffffffffffffffffffffffffffff861681526004835283812067ffffffffffffffff868116835290845290849020845192830185525460ff81161515835261010081048216938301939093526901000000000000000000909204909116918101919091525b92915050565b6000805b600d5460ff8216101561159857600d805460ff83169081106115375761153761587a565b9060005260206000200154830361158857600e805460ff831690811061155f5761155f61587a565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff169392505050565b61159181615b82565b9050611513565b506040517f80833e33000000000000000000000000000000000000000000000000000000008152600481018390526024016107df565b6115d6613689565b6115df826140ef565b6115e76141b5565b60006115f6600a5461ffff1690565b67ffffffffffffffff841660009081526003602052604090206002015490915061ffff821611611658576040517fb72bc70300000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016107df565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260046020908152604080832067ffffffffffffffff8716845290915290205460ff16156116a057505050565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260046020908152604080832067ffffffffffffffff881680855290835281842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155600384528285206002018054918201815585529383902090930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016851790555192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e091015b60405180910390a2505050565b60075473ffffffffffffffffffffffffffffffffffffffff163314611802576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107df565b600680547fffffffffffffffffffffff0000000000000000000000000000000000000000ff81166101003381810292909217909355600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556040519290910473ffffffffffffffffffffffffffffffffffffffff169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6118b0613689565b6118b86141b5565b67ffffffffffffffff81166000908152600360205260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481169290910416338114611958576040517f4e1d9f1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107df565b67ffffffffffffffff831660008181526003602090815260409182902080546c01000000000000000000000000339081026bffffffffffffffffffffffff928316178355600190920180549091169055825173ffffffffffffffffffffffffffffffffffffffff87168152918201527f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f09101611774565b6119f7613c17565b610ea161434c565b611a07613689565b611a10826140ef565b611a186141b5565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020908152604080832067ffffffffffffffff8087168552908352928190208151606081018352905460ff8116151582526101008104851693820193909352690100000000000000000090920490921691810191909152611a9782846143a7565b806040015167ffffffffffffffff16816020015167ffffffffffffffff1614611aec576040517f06eb10c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8316600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611b6757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611b3c575b5050505050905060005b8151811015611d0f578373ffffffffffffffffffffffffffffffffffffffff16828281518110611ba357611ba361587a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611cff578160018351611bd59190615ba1565b81518110611be557611be561587a565b6020026020010151600360008767ffffffffffffffff1667ffffffffffffffff1681526020019081526020016000206002018281548110611c2857611c2861587a565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff87168152600390915260409020600201805480611ca257611ca2615bb4565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055611d0f565b611d0881615b25565b9050611b71565b5073ffffffffffffffffffffffffffffffffffffffff8316600081815260046020908152604080832067ffffffffffffffff89168085529083529281902080547fffffffffffffffffffffffffffffff00000000000000000000000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b910160405180910390a250505050565b6000611db4613689565b611dbc6141b5565b60028054600090611dd69067ffffffffffffffff16615be3565b825467ffffffffffffffff8083166101009490940a93840293021916919091179091556040805160c0810182526000808252336020830152918101829052606081018290529192506080820190604051908082528060200260200182016040528015611e4c578160200160208202803683370190505b5081526000602091820181905267ffffffffffffffff841681526003825260409081902083518484015173ffffffffffffffffffffffffffffffffffffffff9081166c010000000000000000000000009081026bffffffffffffffffffffffff9384161784559386015160608701519091169093029216919091176001820155608083015180519192611ee792600285019290910190614a2d565b5060a0919091015160039091015560405133815267ffffffffffffffff8216907f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a290565b6040805160c0810182526000808252602082018190529181018290526060808201839052608082015260a0810191909152611f71826131c1565b67ffffffffffffffff8216600090815260036020908152604091829020825160c08101845281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c0100000000000000000000000092839004811684870152600185015491821684880152919004166060820152600282018054855181860281018601909652808652919492936080860193929083018282801561205257602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612027575b505050505081526020016003820154815250509050919050565b612074613689565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146120e3576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020811461211d576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061212b82840184614ba6565b67ffffffffffffffff81166000908152600360205260409020549091506c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166121a4576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260036020526040812080546bffffffffffffffffffffffff16918691906121db8385615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550846000808282829054906101000a90046bffffffffffffffffffffffff166122319190615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508167ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88287846122989190615c0a565b6040805192835260208301919091520160405180910390a2505050505050565b60008181526008602052604081205473ffffffffffffffffffffffffffffffffffffffff1680611509576040517f80833e33000000000000000000000000000000000000000000000000000000008152600481018490526024016107df565b61231f613c17565b60005b600d54811015612442576000600d60000182815481106123445761234461587a565b906000526020600020015490506000600d60010183815481106123695761236961587a565b6000918252602080832091909101548483526008825260409283902054835186815273ffffffffffffffffffffffffffffffffffffffff91821693810193909352169181018290529091507ff8a6175bca1ba37d682089187edc5e20a859989727f10ca6bd9a5bc0de8caf949060600160405180910390a160009182526008602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905561243b81615b25565b9050612322565b50600d60006124518282614b51565b61245f600183016000614b51565b5050565b606080600d600001600d600101818054806020026020016040519081016040528092919081815260200182805480156124bb57602002820191906000526020600020905b8154815260200190600101908083116124a7575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561252457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116124f9575b50505050509050915091509091565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082015260a0810182905260c08101919091526040805160e08082018352600a805461ffff808216855262010000820468ffffffffffffffffff166020808701919091526b010000000000000000000000830490941b7fffffffff0000000000000000000000000000000000000000000000000000000016858701526f01000000000000000000000000000000909104166060840152600b805485518185028101850190965280865293949193608086019383018282801561266557602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116126285790505b50505091835250506002919091015461ffff8116602083015262010000900468ffffffffffffffffff16604090910152919050565b60006126a4613689565b6126ac6141b5565b600280546000906126c69067ffffffffffffffff16615be3565b825467ffffffffffffffff8083166101009490940a93840293021916919091179091556040805160c081018252600080825233602083015291810182905260608101829052919250608082019060405190808252806020026020018201604052801561273c578160200160208202803683370190505b5081526000602091820181905267ffffffffffffffff841681526003825260409081902083518484015173ffffffffffffffffffffffffffffffffffffffff9081166c010000000000000000000000009081026bffffffffffffffffffffffff93841617845593860151606087015190911690930292169190911760018201556080830151805191926127d792600285019290910190614a2d565b5060a0919091015160039182015567ffffffffffffffff82166000818152602092835260408082206002018054600180820183559184528584200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff891690811790915583526004855281832084845285529181902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555133815290917f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf910160405180910390a260405173ffffffffffffffffffffffffffffffffffffffff8316815267ffffffffffffffff8216907f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09060200160405180910390a2919050565b612922613689565b61292b826140ef565b6129336141b5565b61293c82612db7565b15612973576040517f06eb10c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61245f82826001613237565b6129876131b9565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612a14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a389190615c1d565b6000549091506bffffffffffffffffffffffff1681811015611211576000612a608284615ba1565b9050612aa373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001685836142bf565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a150505050565b612b00613689565b60005b81811015611211576000838383818110612b1f57612b1f61587a565b90506101600201803603810190612b369190615c36565b80516080820151600082815260056020908152604091829020549151949550929391929091612b67918691016158db565b6040516020818303038152906040528051906020012014612bb4576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82610140015163ffffffff16421015612bf9576040517fa2376fe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208301516040517f85b214cf0000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff909116906385b214cf90602401600060405180830381600087803b158015612c6757600080fd5b505af1158015612c7b573d6000803e3d6000fd5b50505060408085015167ffffffffffffffff84166000908152600360205291822060010180549193509190612cbf9084906bffffffffffffffffffffffff16615b5d565b82546bffffffffffffffffffffffff9182166101009390930a928302919092021990911617905550606083015173ffffffffffffffffffffffffffffffffffffffff16600090815260046020908152604080832067ffffffffffffffff808616855292529091208054600192600991612d479185916901000000000000000000900416615c53565b825467ffffffffffffffff9182166101009390930a9283029190920219909116179055506000828152600560205260408082208290555183917ff1ca1e9147be737b04a2b018a79405f687a97de8dd8a2559bbe62357343af41491a250505080612db090615b25565b9050612b03565b67ffffffffffffffff8116600090815260036020908152604080832060020180548251818502810185019093528083528493830182828015612e2f57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612e04575b5050505050905060005b8151811015612efc57600060046000848481518110612e5a57612e5a61587a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff808a168352908452908290208251606081018452905460ff8116151582526101008104831694820185905269010000000000000000009004909116918101829052925014612eeb57506001949350505050565b50612ef581615b25565b9050612e39565b5060009392505050565b612f0e613c17565b600955565b60608167ffffffffffffffff168367ffffffffffffffff161180612f46575060025467ffffffffffffffff908116908316115b80612f5b575060025467ffffffffffffffff16155b15612f92576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9c8383615c74565b612fa7906001615c53565b67ffffffffffffffff1667ffffffffffffffff811115612fc957612fc9614ccd565b60405190808252806020026020018201604052801561304657816020015b6040805160c081018252600080825260208083018290529282018190526060808301829052608083015260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181612fe75790505b50905060005b6130568484615c74565b67ffffffffffffffff1681116131a1576003600061307e8367ffffffffffffffff8816615c0a565b67ffffffffffffffff1681526020808201929092526040908101600020815160c08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481168488015260018501549182168487015291900416606082015260028201805484518187028101870190955280855291949293608086019390929083018282801561316057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311613135575b505050505081526020016003820154815250508282815181106131855761318561587a565b60200260200101819052508061319a90615b25565b905061304c565b5092915050565b6131b0613c17565b6107808161441b565b610ea1613c17565b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16610780576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff83166000908152600360209081526040808320815160c08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481168488015260018501549182168487015291900416606082015260028201805484518187028101870190955280855291949293608086019390929083018282801561331857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116132ed575b50505091835250506003919091015460209091015280519091506000805b83608001515181101561342e5760008460800151828151811061335b5761335b61587a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260048352604080822067ffffffffffffffff808e16845294529020549092506133bb9169010000000000000000009091041684615c53565b73ffffffffffffffffffffffffffffffffffffffff909116600090815260046020908152604080832067ffffffffffffffff8c168452909152902080547fffffffffffffffffffffffffffffff0000000000000000000000000000000000169055915061342781615b25565b9050613336565b5067ffffffffffffffff8616600090815260036020526040812081815560018101829055906134606002830182614b51565b50600060039190910155600c5461ffff81169062010000900468ffffffffffffffffff1685801561349e57508161ffff168367ffffffffffffffff16105b1561355a576000846bffffffffffffffffffffffff168268ffffffffffffffffff16116134d6578168ffffffffffffffffff166134d8565b845b90506bffffffffffffffffffffffff81161561355857306000908152600160205260408120805483929061351b9084906bffffffffffffffffffffffff16615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080856135559190615b5d565b94505b505b6bffffffffffffffffffffffff841615613617576000805485919081906135909084906bffffffffffffffffffffffff16615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061361787856bffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166142bf9092919063ffffffff16565b6040805173ffffffffffffffffffffffffffffffffffffffff891681526bffffffffffffffffffffffff8616602082015267ffffffffffffffff8a16917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050505050565b610ea1614517565b60006bffffffffffffffffffffffff82111561372f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f362062697473000000000000000000000000000000000000000000000000000060648201526084016107df565b5090565b60408051606080820183526000808352602083015291810191909152813b1580156137865750506040805160608101825260008082526020808301829052835191825281018352918101919091526138e8565b600a546040516000916b010000000000000000000000900460e01b906137b4908a908a908a90602401615c95565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009590951694909417909352600a548151608480825260c0820190935292945061ffff6f01000000000000000000000000000000909104169260009283928392820181803683370190505090505a8481101561388257600080fd5b8490036040810481038a1061389657600080fd5b505a60008087516020890160008d8ff193505a900391503d60848111156138bb575060845b808252806000602084013e5060408051606081018252931515845260208401929092529082015293505050505b95945050505050565b604080518082019091526000808252602082015260006139118486615a55565b90506000816139208886615a7d565b61392a9190615a7d565b67ffffffffffffffff8b166000908152600360205260409020549091506bffffffffffffffffffffffff80831691161080613991575067ffffffffffffffff8a166000908152600360205260409020600101546bffffffffffffffffffffffff808b169116105b156139f45767ffffffffffffffff8a16600090815260036020526040908190205490517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff90911660048201526024016107df565b67ffffffffffffffff8a1660009081526003602052604081208054839290613a2b9084906bffffffffffffffffffffffff16615b5d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915567ffffffffffffffff8c16600090815260036020526040812060010180548d94509092613a7f91859116615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508184613ab99190615a7d565b3360009081526001602052604081208054909190613ae69084906bffffffffffffffffffffffff16615a7d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915530600090815260016020526040812080548b94509092613b2d91859116615a7d565b82546bffffffffffffffffffffffff9182166101009390930a92830291909202199091161790555073ffffffffffffffffffffffffffffffffffffffff8816600090815260046020908152604080832067ffffffffffffffff808f16855292529091208054600192600991613bb19185916901000000000000000000900416615c53565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040518060400160405280836bffffffffffffffffffffffff168152602001826bffffffffffffffffffffffff1681525092505050979650505050505050565b600654610100900473ffffffffffffffffffffffffffffffffffffffff163314610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107df565b613ca5614584565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000613d24613689565b613d2d856131c1565b613d3733866143a7565b613d418583610783565b8351600003613d7b576040517ec1cfc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000613d8686611f37565b90506000613d94338861147f565b600a54604080516101608101825289815267ffffffffffffffff8b1660009081526003602081815293822001549495506201000090930468ffffffffffffffffff169373ffffffffffffffffffffffffffffffffffffffff8d169263a631571e929190820190815233602082015260408881015189519190920191613e1891615b5d565b6bffffffffffffffffffffffff1681526020018568ffffffffffffffffff1681526020018c67ffffffffffffffff168152602001866020015167ffffffffffffffff1681526020018963ffffffff1681526020018a61ffff168152602001866040015167ffffffffffffffff168152602001876020015173ffffffffffffffffffffffffffffffffffffffff168152506040518263ffffffff1660e01b8152600401613ec49190615cc0565b610160604051808303816000875af1158015613ee4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f089190615e25565b805160009081526005602052604090205490915015613f595780516040517f304f32e800000000000000000000000000000000000000000000000000000000815260048101919091526024016107df565b604051806101600160405280826000015181526020018b73ffffffffffffffffffffffffffffffffffffffff16815260200182604001516bffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018a67ffffffffffffffff1681526020018763ffffffff1681526020018368ffffffffffffffffff1681526020018260e0015168ffffffffffffffffff16815260200182610100015164ffffffffff16815260200182610120015164ffffffffff16815260200182610140015163ffffffff1681525060405160200161404491906158db565b60405160208183030381529060405280519060200120600560008360000151815260200190815260200160002081905550614084338a83604001516145f0565b8867ffffffffffffffff168b82600001517ff67aec45c9a7ede407974a3e0c3a743dffeab99ee3f2d4c9a8144c2ebf2c7ec9876020015133328e8e8e8a604001516040516140d89796959493929190615ef8565b60405180910390a4519a9950505050505050505050565b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1680614166576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161461245f576040517f5a68151d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095460009081526008602052604090205473ffffffffffffffffffffffffffffffffffffffff16806141e55750565b604080516000815260208101918290527f6b14daf80000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff821690636b14daf89061424690339060248101615f70565b602060405180830381865afa158015614263573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142879190615f9f565b610780576040517f229062630000000000000000000000000000000000000000000000000000000081523360048201526024016107df565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526112119084906146cb565b614354614517565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613cf03390565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260046020908152604080832067ffffffffffffffff8516845290915290205460ff1661245f576040517f71e8313700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82160361449a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107df565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600654604051919261010090910416907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60065460ff1615610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016107df565b60065460ff16610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016107df565b67ffffffffffffffff82166000908152600360205260408120600101805483929061462a9084906bffffffffffffffffffffffff16615a7d565b82546bffffffffffffffffffffffff91821661010093840a908102920219161790915573ffffffffffffffffffffffffffffffffffffffff8516600090815260046020908152604080832067ffffffffffffffff80891685529252909120805460019450909284926146a0928492900416615c53565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505050565b600061472d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166147d79092919063ffffffff16565b805190915015611211578080602001905181019061474b9190615f9f565b611211576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107df565b60606147e684846000856147ee565b949350505050565b606082471015614880576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107df565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516148a99190615fc1565b60006040518083038185875af1925050503d80600081146148e6576040519150601f19603f3d011682016040523d82523d6000602084013e6148eb565b606091505b50915091506148fc87838387614907565b979650505050505050565b6060831561499d5782516000036149965773ffffffffffffffffffffffffffffffffffffffff85163b614996576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107df565b50816147e6565b6147e683838151156149b25781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df9190614c8e565b828054828255906000526020600020908101928215614a21579160200282015b82811115614a21578251825591602001919060010190614a06565b5061372f929150614b6b565b828054828255906000526020600020908101928215614a21579160200282015b82811115614a2157825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614a4d565b82805482825590600052602060002090600701600890048101928215614a215791602002820160005b83821115614b1457835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614ad0565b8015614b445782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614b14565b505061372f929150614b6b565b508054600082559060005260206000209081019061078091905b5b8082111561372f5760008155600101614b6c565b67ffffffffffffffff8116811461078057600080fd5b8035614ba181614b80565b919050565b600060208284031215614bb857600080fd5b8135614bc381614b80565b9392505050565b63ffffffff8116811461078057600080fd5b8035614ba181614bca565b60008060408385031215614bfa57600080fd5b8235614c0581614b80565b91506020830135614c1581614bca565b809150509250929050565b60005b83811015614c3b578181015183820152602001614c23565b50506000910152565b60008151808452614c5c816020860160208601614c20565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000614bc36020830184614c44565b60008060408385031215614cb457600080fd5b8235614cbf81614b80565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715614d2057614d20614ccd565b60405290565b60405160e0810167ffffffffffffffff81118282101715614d2057614d20614ccd565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614d9057614d90614ccd565b604052919050565b600082601f830112614da957600080fd5b813567ffffffffffffffff811115614dc357614dc3614ccd565b614df460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614d49565b818152846020838601011115614e0957600080fd5b816020850160208301376000918101602001919091529392505050565b6bffffffffffffffffffffffff8116811461078057600080fd5b8035614ba181614e26565b73ffffffffffffffffffffffffffffffffffffffff8116811461078057600080fd5b8035614ba181614e4b565b68ffffffffffffffffff8116811461078057600080fd5b8035614ba181614e78565b64ffffffffff8116811461078057600080fd5b8035614ba181614e9a565b60006101608284031215614ecb57600080fd5b614ed3614cfc565b905081358152614ee560208301614e6d565b6020820152614ef660408301614e40565b6040820152614f0760608301614e6d565b6060820152614f1860808301614b96565b6080820152614f2960a08301614bdc565b60a0820152614f3a60c08301614e8f565b60c0820152614f4b60e08301614e8f565b60e0820152610100614f5e818401614ead565b90820152610120614f70838201614ead565b90820152610140614f82838201614bdc565b9082015292915050565b6000806000806000806102008789031215614fa657600080fd5b863567ffffffffffffffff80821115614fbe57600080fd5b614fca8a838b01614d98565b97506020890135915080821115614fe057600080fd5b50614fed89828a01614d98565b9550506040870135614ffe81614e26565b9350606087013561500e81614e26565b9250608087013561501e81614e4b565b915061502d8860a08901614eb8565b90509295509295509295565b60078110615070577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b604081016150828285615039565b6bffffffffffffffffffffffff831660208301529392505050565b600067ffffffffffffffff8211156150b7576150b7614ccd565b5060051b60200190565b600082601f8301126150d257600080fd5b813560206150e76150e28361509d565b614d49565b82815260059290921b8401810191818101908684111561510657600080fd5b8286015b8481101561512a57803561511d81614e4b565b835291830191830161510a565b509695505050505050565b6000806040838503121561514857600080fd5b823567ffffffffffffffff8082111561516057600080fd5b818501915085601f83011261517457600080fd5b813560206151846150e28361509d565b82815260059290921b840181019181810190898411156151a357600080fd5b948201945b838610156151c1578535825294820194908201906151a8565b965050860135925050808211156151d757600080fd5b506151e4858286016150c1565b9150509250929050565b60008083601f84011261520057600080fd5b50813567ffffffffffffffff81111561521857600080fd5b60208301915083602082850101111561523057600080fd5b9250929050565b803561ffff81168114614ba157600080fd5b60008060008060008060a0878903121561526257600080fd5b863561526d81614b80565b9550602087013567ffffffffffffffff81111561528957600080fd5b61529589828a016151ee565b90965094506152a8905060408801615237565b925060608701356152b881614bca565b80925050608087013590509295509295509295565b600080604083850312156152e057600080fd5b82356152eb81614b80565b91506020830135614c1581614e4b565b6000806040838503121561530e57600080fd5b823561531981614e4b565b91506020830135614c1581614e26565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614ba157600080fd5b600082601f83011261536a57600080fd5b8135602061537a6150e28361509d565b82815260059290921b8401810191818101908684111561539957600080fd5b8286015b8481101561512a5780356153b081614bca565b835291830191830161539d565b6000602082840312156153cf57600080fd5b813567ffffffffffffffff808211156153e757600080fd5b9083019060e082860312156153fb57600080fd5b615403614d26565b61540c83615237565b815261541a60208401614e8f565b602082015261542b60408401615329565b604082015261543c60608401615237565b606082015260808301358281111561545357600080fd5b61545f87828601615359565b60808301525061547160a08401615237565b60a082015261548260c08401614e8f565b60c082015295945050505050565b600080604083850312156154a357600080fd5b82356154ae81614e4b565b91506020830135614c1581614b80565b6000602082840312156154d057600080fd5b5035919050565b600081518084526020808501945080840160005b8381101561551d57815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016154eb565b509495945050505050565b60006bffffffffffffffffffffffff808351168452602083015173ffffffffffffffffffffffffffffffffffffffff8082166020870152826040860151166040870152806060860151166060870152505050608082015160c0608085015261559360c08501826154d7565b60a093840151949093019390935250919050565b602081526000614bc36020830184615528565b600080600080606085870312156155d057600080fd5b84356155db81614e4b565b935060208501359250604085013567ffffffffffffffff8111156155fe57600080fd5b61560a878288016151ee565b95989497509550505050565b604080825283519082018190526000906020906060840190828701845b8281101561564f57815184529284019290840190600101615633565b5050508381038285015261566381866154d7565b9695505050505050565b60006020808352610100830161ffff808651168386015268ffffffffffffffffff838701511660408601527fffffffff00000000000000000000000000000000000000000000000000000000604087015116606086015280606087015116608086015250608085015160e060a0860152818151808452610120870191508483019350600092505b8083101561571a57835163ffffffff1682529284019260019290920191908401906156f4565b5060a087015161ffff811660c0880152935060c087015168ffffffffffffffffff811660e08801529350615663565b60006020828403121561575b57600080fd5b8135614bc381614e4b565b6000806020838503121561577957600080fd5b823567ffffffffffffffff8082111561579157600080fd5b818501915085601f8301126157a557600080fd5b8135818111156157b457600080fd5b866020610160830285010111156157ca57600080fd5b60209290920196919550909350505050565b600080604083850312156157ef57600080fd5b82356154ae81614b80565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561586d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261585b858351615528565b94509285019290850190600101615821565b5092979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff848116825283166020820152606081016147e66040830184615039565b8151815260208083015161016083019161590c9084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015161592c60408401826bffffffffffffffffffffffff169052565b506060830151615954606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615970608084018267ffffffffffffffff169052565b5060a083015161598860a084018263ffffffff169052565b5060c08301516159a560c084018268ffffffffffffffffff169052565b5060e08301516159c260e084018268ffffffffffffffffff169052565b506101008381015164ffffffffff81168483015250506101208381015164ffffffffff81168483015250506101408381015163ffffffff8116848301525b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b64ffffffffff8181168382160190808211156131a1576131a1615a08565b6bffffffffffffffffffffffff818116838216028082169190828114615a0057615a00615a08565b6bffffffffffffffffffffffff8181168382160190808211156131a1576131a1615a08565b6bffffffffffffffffffffffff8716815273ffffffffffffffffffffffffffffffffffffffff86166020820152615adc6040820186615039565b60c060608201526000615af260c0830186614c44565b8281036080840152615b048186614c44565b905082810360a0840152615b188185614c44565b9998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615b5657615b56615a08565b5060010190565b6bffffffffffffffffffffffff8281168282160390808211156131a1576131a1615a08565b600060ff821660ff8103615b9857615b98615a08565b60010192915050565b8181038181111561150957611509615a08565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600067ffffffffffffffff808316818103615c0057615c00615a08565b6001019392505050565b8082018082111561150957611509615a08565b600060208284031215615c2f57600080fd5b5051919050565b60006101608284031215615c4957600080fd5b614bc38383614eb8565b67ffffffffffffffff8181168382160190808211156131a1576131a1615a08565b67ffffffffffffffff8281168282160390808211156131a1576131a1615a08565b838152606060208201526000615cae6060830185614c44565b82810360408401526156638185614c44565b6020815260008251610160806020850152615cdf610180850183614c44565b9150602085015160408501526040850151615d12606086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060608501516bffffffffffffffffffffffff8116608086015250608085015168ffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015167ffffffffffffffff811660e08601525060e0850151610100615d898187018363ffffffff169052565b8601519050610120615da08682018361ffff169052565b8601519050610140615dbd8682018367ffffffffffffffff169052565b9095015173ffffffffffffffffffffffffffffffffffffffff1693019290925250919050565b8051614ba181614e4b565b8051614ba181614e26565b8051614ba181614b80565b8051614ba181614bca565b8051614ba181614e78565b8051614ba181614e9a565b60006101608284031215615e3857600080fd5b615e40614cfc565b82518152615e5060208401615de3565b6020820152615e6160408401615dee565b6040820152615e7260608401615de3565b6060820152615e8360808401615df9565b6080820152615e9460a08401615e04565b60a0820152615ea560c08401615e0f565b60c0820152615eb660e08401615e0f565b60e0820152610100615ec9818501615e1a565b90820152610120615edb848201615e1a565b90820152610140615eed848201615e04565b908201529392505050565b600073ffffffffffffffffffffffffffffffffffffffff808a168352808916602084015280881660408401525060e06060830152615f3960e0830187614c44565b61ffff9590951660808301525063ffffffff9290921660a08301526bffffffffffffffffffffffff1660c090910152949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006147e66040830184614c44565b600060208284031215615fb157600080fd5b81518015158114614bc357600080fd5b60008251615fd3818460208701614c20565b919091019291505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotRemoveWithPendingRequests\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateRequestId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyRequestData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"limit\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"IdentifierIsReserved\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint96\",\"name\":\"currentBalanceJuels\",\"type\":\"uint96\"}],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"value\",\"type\":\"uint8\"}],\"name\":\"InvalidGasFlagValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProposal\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeSubscriptionOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromCoordinator\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"RouteNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderMustAcceptTermsOfService\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeoutNotExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"maximumConsumers\",\"type\":\"uint16\"}],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"indexed\":false,\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"proposedContractSetId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"proposedContractSetFromAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"proposedContractSetToAddress\",\"type\":\"address\"}],\"name\":\"ContractProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"ContractUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"enumFunctionsResponse.FulfillResult\",\"name\":\"resultCode\",\"type\":\"uint8\"}],\"name\":\"RequestNotProcessed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"enumFunctionsResponse.FulfillResult\",\"name\":\"resultCode\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"response\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"callbackReturnData\",\"type\":\"bytes\"}],\"name\":\"RequestProcessed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"donId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestStart\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"RequestTimedOut\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"fundsRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fundsAmount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MAX_CALLBACK_RETURN_BYTES\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"createSubscriptionWithConsumer\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"response\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"costWithoutFulfillment\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"fulfill\",\"outputs\":[{\"internalType\":\"enumFunctionsResponse.FulfillResult\",\"name\":\"resultCode\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"getConsumer\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"}],\"internalType\":\"structIFunctionsSubscriptions.Consumer\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"getContractById\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"getFlags\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"getProposedContractById\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getProposedContractSet\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"getSubscription\",\"outputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"blockedBalance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"}],\"internalType\":\"structIFunctionsSubscriptions.Subscription\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSubscriptionCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionIdStart\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionIdEnd\",\"type\":\"uint64\"}],\"name\":\"getSubscriptionsInRange\",\"outputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"blockedBalance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"}],\"internalType\":\"structIFunctionsSubscriptions.Subscription[]\",\"name\":\"subscriptions\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTotalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"}],\"name\":\"isValidCallbackGasLimit\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"ownerWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"proposedContractSetIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"address[]\",\"name\":\"proposedContractSetAddresses\",\"type\":\"address[]\"}],\"name\":\"proposeContractsUpdate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"proposeSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"donId\",\"type\":\"bytes32\"}],\"name\":\"sendRequest\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"donId\",\"type\":\"bytes32\"}],\"name\":\"sendRequestToProposed\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"allowListId\",\"type\":\"bytes32\"}],\"name\":\"setAllowListId\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"}],\"name\":\"setFlags\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment[]\",\"name\":\"requestsToTimeoutByCommitment\",\"type\":\"tuple[]\"}],\"name\":\"timeoutRequests\",\"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\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"maxConsumersPerSubscription\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"bytes4\",\"name\":\"handleOracleFulfillmentSelector\",\"type\":\"bytes4\"},{\"internalType\":\"uint16\",\"name\":\"gasForCallExactCheck\",\"type\":\"uint16\"},{\"internalType\":\"uint32[]\",\"name\":\"maxCallbackGasLimits\",\"type\":\"uint32[]\"},{\"internalType\":\"uint16\",\"name\":\"subscriptionDepositMinimumRequests\",\"type\":\"uint16\"},{\"internalType\":\"uint72\",\"name\":\"subscriptionDepositJuels\",\"type\":\"uint72\"}],\"internalType\":\"structFunctionsRouter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"updateContracts\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b506040516200673c3803806200673c833981016040819052620000349162000549565b6001600160a01b0382166080526006805460ff191690553380600081620000a25760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600680546001600160a01b0380851661010002610100600160a81b031990921691909117909155811615620000dc57620000dc81620000f8565b505050620000f081620001aa60201b60201c565b50506200071a565b336001600160a01b03821603620001525760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000099565b600780546001600160a01b0319166001600160a01b03838116918217909255600654604051919261010090910416907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b620001b4620002c0565b8051600a80546020808501516040860151606087015161ffff908116600160781b0261ffff60781b1960e09390931c6b010000000000000000000000029290921665ffffffffffff60581b196001600160481b0390941662010000026001600160581b031990961691909716179390931716939093171781556080830151805184936200024792600b9291019062000323565b5060a08201516002909101805460c0909301516001600160481b031662010000026001600160581b031990931661ffff909216919091179190911790556040517ea5832bf95f66c7814294cc4db681f20ee79608bfb8912a5321d66cfed5e98590620002b590839062000652565b60405180910390a150565b60065461010090046001600160a01b03163314620003215760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000099565b565b82805482825590600052602060002090600701600890048101928215620003c75791602002820160005b838211156200039357835183826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026200034d565b8015620003c55782816101000a81549063ffffffff021916905560040160208160030104928301926001030262000393565b505b50620003d5929150620003d9565b5090565b5b80821115620003d55760008155600101620003da565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b03811182821017156200042b576200042b620003f0565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200045c576200045c620003f0565b604052919050565b805161ffff811681146200047757600080fd5b919050565b80516001600160481b03811681146200047757600080fd5b80516001600160e01b0319811681146200047757600080fd5b600082601f830112620004bf57600080fd5b815160206001600160401b03821115620004dd57620004dd620003f0565b8160051b620004ee82820162000431565b92835284810182019282810190878511156200050957600080fd5b83870192505b848310156200053e57825163ffffffff811681146200052e5760008081fd5b825291830191908301906200050f565b979650505050505050565b600080604083850312156200055d57600080fd5b82516001600160a01b03811681146200057557600080fd5b60208401519092506001600160401b03808211156200059357600080fd5b9084019060e08287031215620005a857600080fd5b620005b262000406565b620005bd8362000464565b8152620005cd602084016200047c565b6020820152620005e06040840162000494565b6040820152620005f36060840162000464565b60608201526080830151828111156200060b57600080fd5b6200061988828601620004ad565b6080830152506200062d60a0840162000464565b60a08201526200064060c084016200047c565b60c08201528093505050509250929050565b6020808252825161ffff90811683830152838201516001600160481b03166040808501919091528401516001600160e01b0319166060808501919091528401511660808084019190915283015160e060a0840152805161010084018190526000929182019083906101208601905b80831015620006e857835163ffffffff168252928401926001929092019190840190620006c0565b5060a087015161ffff811660c0880152935060c08701516001600160481b03811660e088015293509695505050505050565b608051615fea62000752600039600081816111cd0152818161208c015281816129b801528181612a7c01526135d30152615fea6000f3fe608060405234801561001057600080fd5b50600436106102e95760003560e01c80637341c10c11610191578063b734c0f4116100e3578063e72f6e3011610097578063ea320e0b11610071578063ea320e0b146106dd578063ec2454e5146106f0578063f2fde38b1461071057600080fd5b8063e72f6e30146106a4578063e82622aa146106b7578063e82ad7d4146106ca57600080fd5b8063c3f909d4116100c8578063c3f909d414610669578063cc77470a1461067e578063d7ae1d301461069157600080fd5b8063b734c0f41461064b578063badc3eb61461065357600080fd5b80639f87fad711610145578063a4c0ed361161011f578063a4c0ed361461061d578063a9c9a91814610630578063aab396bd1461064357600080fd5b80639f87fad7146105e2578063a21a23e4146105f5578063a47c7696146105fd57600080fd5b8063823597401161017657806382359740146105a45780638456cb59146105b75780638da5cb5b146105bf57600080fd5b80637341c10c1461058957806379ba50971461059c57600080fd5b806341db4ca31161024a5780635ed6dfba116101fe57806366419970116101d857806366419970146104e1578063674603d0146105085780636a2215de1461055157600080fd5b80635ed6dfba146104a85780636162a323146104bb57806366316d8d146104ce57600080fd5b80634b8832d31161022f5780634b8832d31461045057806355fedefa146104635780635c975abb1461049157600080fd5b806341db4ca31461041c578063461d27621461043d57600080fd5b80631ded3b36116102a1578063330605291161028657806333060529146103e05780633e871e4d146104015780633f4ba83a1461041457600080fd5b80631ded3b361461039f5780632a905ccc146103b257600080fd5b806310fc49c1116102d257806310fc49c11461032357806312b5834914610336578063181f5a771461035657600080fd5b806302bcc5b6146102ee5780630c5d49cb14610303575b600080fd5b6103016102fc366004614ba6565b610723565b005b61030b608481565b60405161ffff90911681526020015b60405180910390f35b610301610331366004614be7565b610783565b6000546040516bffffffffffffffffffffffff909116815260200161031a565b6103926040518060400160405280601781526020017f46756e6374696f6e7320526f757465722076322e302e3000000000000000000081525081565b60405161031a9190614c8e565b6103016103ad366004614ca1565b61087f565b600a5462010000900468ffffffffffffffffff1660405168ffffffffffffffffff909116815260200161031a565b6103f36103ee366004614f8c565b6108b1565b60405161031a929190615074565b61030161040f366004615135565b610c7c565b610301610e91565b61042f61042a366004615249565b610ea3565b60405190815260200161031a565b61042f61044b366004615249565b610f03565b61030161045e3660046152cd565b610f0f565b61042f610471366004614ba6565b67ffffffffffffffff166000908152600360208190526040909120015490565b60065460ff165b604051901515815260200161031a565b6103016104b63660046152fb565b61105d565b6103016104c93660046153bd565b611216565b6103016104dc3660046152fb565b611396565b60025467ffffffffffffffff165b60405167ffffffffffffffff909116815260200161031a565b61051b610516366004615490565b61147f565b6040805182511515815260208084015167ffffffffffffffff90811691830191909152928201519092169082015260600161031a565b61056461055f3660046154be565b61150f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031a565b6103016105973660046152cd565b6115ce565b610301611781565b6103016105b2366004614ba6565b6118a8565b6103016119ef565b600654610100900473ffffffffffffffffffffffffffffffffffffffff16610564565b6103016105f03660046152cd565b6119ff565b6104ef611daa565b61061061060b366004614ba6565b611f37565b60405161031a91906155a7565b61030161062b3660046155ba565b61206c565b61056461063e3660046154be565b6122b8565b60095461042f565b610301612317565b61065b612463565b60405161031a929190615616565b610671612533565b60405161031a919061566d565b6104ef61068c366004615749565b61269a565b61030161069f3660046152cd565b61291a565b6103016106b2366004615749565b61297f565b6103016106c5366004615766565b612af8565b6104986106d8366004614ba6565b612db7565b6103016106eb3660046154be565b612f06565b6107036106fe3660046157dc565b612f13565b60405161031a91906157fa565b61030161071e366004615749565b6131a8565b61072b6131b9565b610734816131c1565b67ffffffffffffffff81166000908152600360205260408120546107809183916c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690613237565b50565b67ffffffffffffffff8216600090815260036020819052604082200154600b54911a9081106107e8576040517f45c108ce00000000000000000000000000000000000000000000000000000000815260ff821660048201526024015b60405180910390fd5b6000600a6001018260ff16815481106108035761080361587a565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1690508063ffffffff168363ffffffff161115610879576040517f1d70f87a00000000000000000000000000000000000000000000000000000000815263ffffffff821660048201526024016107df565b50505050565b6108876131b9565b610890826131c1565b67ffffffffffffffff90911660009081526003602081905260409091200155565b6000806108bc613689565b826020015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610925576040517f8bec23e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82516000908152600560205260409020548061098a5783516020850151604051600295507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b60405180910390a25060009050610c71565b808460405160200161099c91906158db565b60405160208183030381529060405280519060200120146109f45783516020850151604051600695507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b8361012001518460a0015163ffffffff16610a0f9190615a37565b64ffffffffff165a1015610a5a5783516020850151604051600495507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b506000610a708460a0015163ffffffff16613691565b610a7a9088615a55565b9050600081878660c0015168ffffffffffffffffff16610a9a9190615a7d565b610aa49190615a7d565b9050610ab38560800151611f37565b600001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610b2b5784516020860151604051600596507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee191610b17918a9089906158a9565b60405180910390a25060009150610c719050565b84604001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610b905784516020860151604051600396507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee191610b17918a9089906158a9565b505082516000908152600560205260408120819055835160a08501516060860151610bc092918c918c9190613733565b8051909150610bd0576001610bd3565b60005b92506000610c0d8560800151866040015187606001518860c0015168ffffffffffffffffff168c610c078860200151613691565b8d6138f1565b9050846080015167ffffffffffffffff1685600001517f64778f26c70b60a8d7e29e2451b3844302d959448401c0535b768ed88c6b505e836020015189888f8f8960400151604051610c6496959493929190615aa2565b60405180910390a3519150505b965096945050505050565b610c84613c17565b8151815181141580610c965750600881115b15610ccd576040517fee03280800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e47576000848281518110610cec57610cec61587a565b602002602001015190506000848381518110610d0a57610d0a61587a565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610d75575060008281526008602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610dac576040517fee03280800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260086020526040908190205490517f8b052f0f4bf82fede7daffea71592b29d5ef86af1f3c7daaa0345dbb2f52f48191610e2c91859173ffffffffffffffffffffffffffffffffffffffff1690859092835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60405180910390a1505080610e4090615b25565b9050610cd0565b506040805180820190915283815260208082018490528451600d91610e709183918801906149e6565b506020828101518051610e899260018501920190614a2d565b505050505050565b610e99613c17565b610ea1613c9d565b565b600080610eaf8361150f565b9050610ef783828a8a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150613d1a9050565b98975050505050505050565b600080610eaf836122b8565b610f17613689565b610f20826140ef565b610f286141b5565b73ffffffffffffffffffffffffffffffffffffffff81161580610f8f575067ffffffffffffffff821660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff8281166c0100000000000000000000000090920416145b15610fc6576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff821660008181526003602090815260409182902060010180546bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8716908102919091179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be910160405180910390a25050565b6110656131b9565b806bffffffffffffffffffffffff1660000361109b5750306000908152600160205260409020546bffffffffffffffffffffffff165b306000908152600160205260409020546bffffffffffffffffffffffff908116908216811015611107576040517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526024016107df565b30600090815260016020526040812080548492906111349084906bffffffffffffffffffffffff16615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550816000808282829054906101000a90046bffffffffffffffffffffffff1661118a9190615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061121183836bffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166142bf9092919063ffffffff16565b505050565b61121e613c17565b8051600a80546020808501516040860151606087015161ffff9081166f01000000000000000000000000000000027fffffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffff60e09390931c6b01000000000000000000000002929092167fffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffff68ffffffffffffffffff90941662010000027fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000909616919097161793909317169390931717815560808301518051849361130592600b92910190614aa7565b5060a08201516002909101805460c09093015168ffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffffffffff000000000000000000000090931661ffff909216919091179190911790556040517ea5832bf95f66c7814294cc4db681f20ee79608bfb8912a5321d66cfed5e9859061138b90839061566d565b60405180910390a150565b61139e613689565b806bffffffffffffffffffffffff166000036113e6576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600160205260409020546bffffffffffffffffffffffff908116908216811015611452576040517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526024016107df565b33600090815260016020526040812080548492906111349084906bffffffffffffffffffffffff16615b5d565b60408051606080820183526000808352602080840182905292840181905273ffffffffffffffffffffffffffffffffffffffff861681526004835283812067ffffffffffffffff868116835290845290849020845192830185525460ff81161515835261010081048216938301939093526901000000000000000000909204909116918101919091525b92915050565b6000805b600d5460ff8216101561159857600d805460ff83169081106115375761153761587a565b9060005260206000200154830361158857600e805460ff831690811061155f5761155f61587a565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff169392505050565b61159181615b82565b9050611513565b506040517f80833e33000000000000000000000000000000000000000000000000000000008152600481018390526024016107df565b6115d6613689565b6115df826140ef565b6115e76141b5565b60006115f6600a5461ffff1690565b67ffffffffffffffff841660009081526003602052604090206002015490915061ffff821611611658576040517fb72bc70300000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016107df565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260046020908152604080832067ffffffffffffffff8716845290915290205460ff16156116a057505050565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260046020908152604080832067ffffffffffffffff881680855290835281842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155600384528285206002018054918201815585529383902090930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016851790555192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e091015b60405180910390a2505050565b60075473ffffffffffffffffffffffffffffffffffffffff163314611802576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107df565b600680547fffffffffffffffffffffff0000000000000000000000000000000000000000ff81166101003381810292909217909355600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556040519290910473ffffffffffffffffffffffffffffffffffffffff169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6118b0613689565b6118b86141b5565b67ffffffffffffffff81166000908152600360205260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481169290910416338114611958576040517f4e1d9f1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107df565b67ffffffffffffffff831660008181526003602090815260409182902080546c01000000000000000000000000339081026bffffffffffffffffffffffff928316178355600190920180549091169055825173ffffffffffffffffffffffffffffffffffffffff87168152918201527f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f09101611774565b6119f7613c17565b610ea161434c565b611a07613689565b611a10826140ef565b611a186141b5565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020908152604080832067ffffffffffffffff8087168552908352928190208151606081018352905460ff8116151582526101008104851693820193909352690100000000000000000090920490921691810191909152611a9782846143a7565b806040015167ffffffffffffffff16816020015167ffffffffffffffff1614611aec576040517f06eb10c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8316600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611b6757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611b3c575b5050505050905060005b8151811015611d0f578373ffffffffffffffffffffffffffffffffffffffff16828281518110611ba357611ba361587a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611cff578160018351611bd59190615ba1565b81518110611be557611be561587a565b6020026020010151600360008767ffffffffffffffff1667ffffffffffffffff1681526020019081526020016000206002018281548110611c2857611c2861587a565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff87168152600390915260409020600201805480611ca257611ca2615bb4565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055611d0f565b611d0881615b25565b9050611b71565b5073ffffffffffffffffffffffffffffffffffffffff8316600081815260046020908152604080832067ffffffffffffffff89168085529083529281902080547fffffffffffffffffffffffffffffff00000000000000000000000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b910160405180910390a250505050565b6000611db4613689565b611dbc6141b5565b60028054600090611dd69067ffffffffffffffff16615be3565b825467ffffffffffffffff8083166101009490940a93840293021916919091179091556040805160c0810182526000808252336020830152918101829052606081018290529192506080820190604051908082528060200260200182016040528015611e4c578160200160208202803683370190505b5081526000602091820181905267ffffffffffffffff841681526003825260409081902083518484015173ffffffffffffffffffffffffffffffffffffffff9081166c010000000000000000000000009081026bffffffffffffffffffffffff9384161784559386015160608701519091169093029216919091176001820155608083015180519192611ee792600285019290910190614a2d565b5060a0919091015160039091015560405133815267ffffffffffffffff8216907f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a290565b6040805160c0810182526000808252602082018190529181018290526060808201839052608082015260a0810191909152611f71826131c1565b67ffffffffffffffff8216600090815260036020908152604091829020825160c08101845281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c0100000000000000000000000092839004811684870152600185015491821684880152919004166060820152600282018054855181860281018601909652808652919492936080860193929083018282801561205257602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612027575b505050505081526020016003820154815250509050919050565b612074613689565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146120e3576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020811461211d576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061212b82840184614ba6565b67ffffffffffffffff81166000908152600360205260409020549091506c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166121a4576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260036020526040812080546bffffffffffffffffffffffff16918691906121db8385615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550846000808282829054906101000a90046bffffffffffffffffffffffff166122319190615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508167ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88287846122989190615c0a565b6040805192835260208301919091520160405180910390a2505050505050565b60008181526008602052604081205473ffffffffffffffffffffffffffffffffffffffff1680611509576040517f80833e33000000000000000000000000000000000000000000000000000000008152600481018490526024016107df565b61231f613c17565b60005b600d54811015612442576000600d60000182815481106123445761234461587a565b906000526020600020015490506000600d60010183815481106123695761236961587a565b6000918252602080832091909101548483526008825260409283902054835186815273ffffffffffffffffffffffffffffffffffffffff91821693810193909352169181018290529091507ff8a6175bca1ba37d682089187edc5e20a859989727f10ca6bd9a5bc0de8caf949060600160405180910390a160009182526008602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905561243b81615b25565b9050612322565b50600d60006124518282614b51565b61245f600183016000614b51565b5050565b606080600d600001600d600101818054806020026020016040519081016040528092919081815260200182805480156124bb57602002820191906000526020600020905b8154815260200190600101908083116124a7575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561252457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116124f9575b50505050509050915091509091565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082015260a0810182905260c08101919091526040805160e08082018352600a805461ffff808216855262010000820468ffffffffffffffffff166020808701919091526b010000000000000000000000830490941b7fffffffff0000000000000000000000000000000000000000000000000000000016858701526f01000000000000000000000000000000909104166060840152600b805485518185028101850190965280865293949193608086019383018282801561266557602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116126285790505b50505091835250506002919091015461ffff8116602083015262010000900468ffffffffffffffffff16604090910152919050565b60006126a4613689565b6126ac6141b5565b600280546000906126c69067ffffffffffffffff16615be3565b825467ffffffffffffffff8083166101009490940a93840293021916919091179091556040805160c081018252600080825233602083015291810182905260608101829052919250608082019060405190808252806020026020018201604052801561273c578160200160208202803683370190505b5081526000602091820181905267ffffffffffffffff841681526003825260409081902083518484015173ffffffffffffffffffffffffffffffffffffffff9081166c010000000000000000000000009081026bffffffffffffffffffffffff93841617845593860151606087015190911690930292169190911760018201556080830151805191926127d792600285019290910190614a2d565b5060a0919091015160039182015567ffffffffffffffff82166000818152602092835260408082206002018054600180820183559184528584200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff891690811790915583526004855281832084845285529181902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555133815290917f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf910160405180910390a260405173ffffffffffffffffffffffffffffffffffffffff8316815267ffffffffffffffff8216907f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09060200160405180910390a2919050565b612922613689565b61292b826140ef565b6129336141b5565b61293c82612db7565b15612973576040517f06eb10c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61245f82826001613237565b6129876131b9565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612a14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a389190615c1d565b6000549091506bffffffffffffffffffffffff1681811015611211576000612a608284615ba1565b9050612aa373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001685836142bf565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a150505050565b612b00613689565b60005b81811015611211576000838383818110612b1f57612b1f61587a565b90506101600201803603810190612b369190615c36565b80516080820151600082815260056020908152604091829020549151949550929391929091612b67918691016158db565b6040516020818303038152906040528051906020012014612bb4576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82610140015163ffffffff16421015612bf9576040517fa2376fe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208301516040517f85b214cf0000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff909116906385b214cf90602401600060405180830381600087803b158015612c6757600080fd5b505af1158015612c7b573d6000803e3d6000fd5b50505060408085015167ffffffffffffffff84166000908152600360205291822060010180549193509190612cbf9084906bffffffffffffffffffffffff16615b5d565b82546bffffffffffffffffffffffff9182166101009390930a928302919092021990911617905550606083015173ffffffffffffffffffffffffffffffffffffffff16600090815260046020908152604080832067ffffffffffffffff808616855292529091208054600192600991612d479185916901000000000000000000900416615c53565b825467ffffffffffffffff9182166101009390930a9283029190920219909116179055506000828152600560205260408082208290555183917ff1ca1e9147be737b04a2b018a79405f687a97de8dd8a2559bbe62357343af41491a250505080612db090615b25565b9050612b03565b67ffffffffffffffff8116600090815260036020908152604080832060020180548251818502810185019093528083528493830182828015612e2f57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612e04575b5050505050905060005b8151811015612efc57600060046000848481518110612e5a57612e5a61587a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff808a168352908452908290208251606081018452905460ff8116151582526101008104831694820185905269010000000000000000009004909116918101829052925014612eeb57506001949350505050565b50612ef581615b25565b9050612e39565b5060009392505050565b612f0e613c17565b600955565b60608167ffffffffffffffff168367ffffffffffffffff161180612f46575060025467ffffffffffffffff908116908316115b80612f5b575060025467ffffffffffffffff16155b15612f92576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9c8383615c74565b612fa7906001615c53565b67ffffffffffffffff1667ffffffffffffffff811115612fc957612fc9614ccd565b60405190808252806020026020018201604052801561304657816020015b6040805160c081018252600080825260208083018290529282018190526060808301829052608083015260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181612fe75790505b50905060005b6130568484615c74565b67ffffffffffffffff1681116131a1576003600061307e8367ffffffffffffffff8816615c0a565b67ffffffffffffffff1681526020808201929092526040908101600020815160c08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481168488015260018501549182168487015291900416606082015260028201805484518187028101870190955280855291949293608086019390929083018282801561316057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311613135575b505050505081526020016003820154815250508282815181106131855761318561587a565b60200260200101819052508061319a90615b25565b905061304c565b5092915050565b6131b0613c17565b6107808161441b565b610ea1613c17565b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16610780576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff83166000908152600360209081526040808320815160c08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481168488015260018501549182168487015291900416606082015260028201805484518187028101870190955280855291949293608086019390929083018282801561331857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116132ed575b50505091835250506003919091015460209091015280519091506000805b83608001515181101561342e5760008460800151828151811061335b5761335b61587a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260048352604080822067ffffffffffffffff808e16845294529020549092506133bb9169010000000000000000009091041684615c53565b73ffffffffffffffffffffffffffffffffffffffff909116600090815260046020908152604080832067ffffffffffffffff8c168452909152902080547fffffffffffffffffffffffffffffff0000000000000000000000000000000000169055915061342781615b25565b9050613336565b5067ffffffffffffffff8616600090815260036020526040812081815560018101829055906134606002830182614b51565b50600060039190910155600c5461ffff81169062010000900468ffffffffffffffffff1685801561349e57508161ffff168367ffffffffffffffff16105b1561355a576000846bffffffffffffffffffffffff168268ffffffffffffffffff16116134d6578168ffffffffffffffffff166134d8565b845b90506bffffffffffffffffffffffff81161561355857306000908152600160205260408120805483929061351b9084906bffffffffffffffffffffffff16615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080856135559190615b5d565b94505b505b6bffffffffffffffffffffffff841615613617576000805485919081906135909084906bffffffffffffffffffffffff16615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061361787856bffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166142bf9092919063ffffffff16565b6040805173ffffffffffffffffffffffffffffffffffffffff891681526bffffffffffffffffffffffff8616602082015267ffffffffffffffff8a16917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050505050565b610ea1614517565b60006bffffffffffffffffffffffff82111561372f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f362062697473000000000000000000000000000000000000000000000000000060648201526084016107df565b5090565b60408051606080820183526000808352602083015291810191909152813b1580156137865750506040805160608101825260008082526020808301829052835191825281018352918101919091526138e8565b600a546040516000916b010000000000000000000000900460e01b906137b4908a908a908a90602401615c95565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009590951694909417909352600a548151608480825260c0820190935292945061ffff6f01000000000000000000000000000000909104169260009283928392820181803683370190505090505a8481101561388257600080fd5b8490036040810481038a1061389657600080fd5b505a60008087516020890160008d8ff193505a900391503d60848111156138bb575060845b808252806000602084013e5060408051606081018252931515845260208401929092529082015293505050505b95945050505050565b604080518082019091526000808252602082015260006139118486615a55565b90506000816139208886615a7d565b61392a9190615a7d565b67ffffffffffffffff8b166000908152600360205260409020549091506bffffffffffffffffffffffff80831691161080613991575067ffffffffffffffff8a166000908152600360205260409020600101546bffffffffffffffffffffffff808b169116105b156139f45767ffffffffffffffff8a16600090815260036020526040908190205490517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff90911660048201526024016107df565b67ffffffffffffffff8a1660009081526003602052604081208054839290613a2b9084906bffffffffffffffffffffffff16615b5d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915567ffffffffffffffff8c16600090815260036020526040812060010180548d94509092613a7f91859116615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508184613ab99190615a7d565b3360009081526001602052604081208054909190613ae69084906bffffffffffffffffffffffff16615a7d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915530600090815260016020526040812080548b94509092613b2d91859116615a7d565b82546bffffffffffffffffffffffff9182166101009390930a92830291909202199091161790555073ffffffffffffffffffffffffffffffffffffffff8816600090815260046020908152604080832067ffffffffffffffff808f16855292529091208054600192600991613bb19185916901000000000000000000900416615c53565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040518060400160405280836bffffffffffffffffffffffff168152602001826bffffffffffffffffffffffff1681525092505050979650505050505050565b600654610100900473ffffffffffffffffffffffffffffffffffffffff163314610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107df565b613ca5614584565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000613d24613689565b613d2d856131c1565b613d3733866143a7565b613d418583610783565b8351600003613d7b576040517ec1cfc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000613d8686611f37565b90506000613d94338861147f565b600a54604080516101608101825289815267ffffffffffffffff8b1660009081526003602081815293822001549495506201000090930468ffffffffffffffffff169373ffffffffffffffffffffffffffffffffffffffff8d169263a631571e929190820190815233602082015260408881015189519190920191613e1891615b5d565b6bffffffffffffffffffffffff1681526020018568ffffffffffffffffff1681526020018c67ffffffffffffffff168152602001866020015167ffffffffffffffff1681526020018963ffffffff1681526020018a61ffff168152602001866040015167ffffffffffffffff168152602001876020015173ffffffffffffffffffffffffffffffffffffffff168152506040518263ffffffff1660e01b8152600401613ec49190615cc0565b610160604051808303816000875af1158015613ee4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f089190615e25565b805160009081526005602052604090205490915015613f595780516040517f304f32e800000000000000000000000000000000000000000000000000000000815260048101919091526024016107df565b604051806101600160405280826000015181526020018b73ffffffffffffffffffffffffffffffffffffffff16815260200182604001516bffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018a67ffffffffffffffff1681526020018763ffffffff1681526020018368ffffffffffffffffff1681526020018260e0015168ffffffffffffffffff16815260200182610100015164ffffffffff16815260200182610120015164ffffffffff16815260200182610140015163ffffffff1681525060405160200161404491906158db565b60405160208183030381529060405280519060200120600560008360000151815260200190815260200160002081905550614084338a83604001516145f0565b8867ffffffffffffffff168b82600001517ff67aec45c9a7ede407974a3e0c3a743dffeab99ee3f2d4c9a8144c2ebf2c7ec9876020015133328e8e8e8a604001516040516140d89796959493929190615ef8565b60405180910390a4519a9950505050505050505050565b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1680614166576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161461245f576040517f5a68151d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095460009081526008602052604090205473ffffffffffffffffffffffffffffffffffffffff16806141e55750565b604080516000815260208101918290527f6b14daf80000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff821690636b14daf89061424690339060248101615f70565b602060405180830381865afa158015614263573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142879190615f9f565b610780576040517f229062630000000000000000000000000000000000000000000000000000000081523360048201526024016107df565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526112119084906146cb565b614354614517565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613cf03390565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260046020908152604080832067ffffffffffffffff8516845290915290205460ff1661245f576040517f71e8313700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82160361449a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107df565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600654604051919261010090910416907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60065460ff1615610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016107df565b60065460ff16610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016107df565b67ffffffffffffffff82166000908152600360205260408120600101805483929061462a9084906bffffffffffffffffffffffff16615a7d565b82546bffffffffffffffffffffffff91821661010093840a908102920219161790915573ffffffffffffffffffffffffffffffffffffffff8516600090815260046020908152604080832067ffffffffffffffff80891685529252909120805460019450909284926146a0928492900416615c53565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505050565b600061472d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166147d79092919063ffffffff16565b805190915015611211578080602001905181019061474b9190615f9f565b611211576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107df565b60606147e684846000856147ee565b949350505050565b606082471015614880576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107df565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516148a99190615fc1565b60006040518083038185875af1925050503d80600081146148e6576040519150601f19603f3d011682016040523d82523d6000602084013e6148eb565b606091505b50915091506148fc87838387614907565b979650505050505050565b6060831561499d5782516000036149965773ffffffffffffffffffffffffffffffffffffffff85163b614996576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107df565b50816147e6565b6147e683838151156149b25781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df9190614c8e565b828054828255906000526020600020908101928215614a21579160200282015b82811115614a21578251825591602001919060010190614a06565b5061372f929150614b6b565b828054828255906000526020600020908101928215614a21579160200282015b82811115614a2157825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614a4d565b82805482825590600052602060002090600701600890048101928215614a215791602002820160005b83821115614b1457835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614ad0565b8015614b445782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614b14565b505061372f929150614b6b565b508054600082559060005260206000209081019061078091905b5b8082111561372f5760008155600101614b6c565b67ffffffffffffffff8116811461078057600080fd5b8035614ba181614b80565b919050565b600060208284031215614bb857600080fd5b8135614bc381614b80565b9392505050565b63ffffffff8116811461078057600080fd5b8035614ba181614bca565b60008060408385031215614bfa57600080fd5b8235614c0581614b80565b91506020830135614c1581614bca565b809150509250929050565b60005b83811015614c3b578181015183820152602001614c23565b50506000910152565b60008151808452614c5c816020860160208601614c20565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000614bc36020830184614c44565b60008060408385031215614cb457600080fd5b8235614cbf81614b80565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715614d2057614d20614ccd565b60405290565b60405160e0810167ffffffffffffffff81118282101715614d2057614d20614ccd565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614d9057614d90614ccd565b604052919050565b600082601f830112614da957600080fd5b813567ffffffffffffffff811115614dc357614dc3614ccd565b614df460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614d49565b818152846020838601011115614e0957600080fd5b816020850160208301376000918101602001919091529392505050565b6bffffffffffffffffffffffff8116811461078057600080fd5b8035614ba181614e26565b73ffffffffffffffffffffffffffffffffffffffff8116811461078057600080fd5b8035614ba181614e4b565b68ffffffffffffffffff8116811461078057600080fd5b8035614ba181614e78565b64ffffffffff8116811461078057600080fd5b8035614ba181614e9a565b60006101608284031215614ecb57600080fd5b614ed3614cfc565b905081358152614ee560208301614e6d565b6020820152614ef660408301614e40565b6040820152614f0760608301614e6d565b6060820152614f1860808301614b96565b6080820152614f2960a08301614bdc565b60a0820152614f3a60c08301614e8f565b60c0820152614f4b60e08301614e8f565b60e0820152610100614f5e818401614ead565b90820152610120614f70838201614ead565b90820152610140614f82838201614bdc565b9082015292915050565b6000806000806000806102008789031215614fa657600080fd5b863567ffffffffffffffff80821115614fbe57600080fd5b614fca8a838b01614d98565b97506020890135915080821115614fe057600080fd5b50614fed89828a01614d98565b9550506040870135614ffe81614e26565b9350606087013561500e81614e26565b9250608087013561501e81614e4b565b915061502d8860a08901614eb8565b90509295509295509295565b60078110615070577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b604081016150828285615039565b6bffffffffffffffffffffffff831660208301529392505050565b600067ffffffffffffffff8211156150b7576150b7614ccd565b5060051b60200190565b600082601f8301126150d257600080fd5b813560206150e76150e28361509d565b614d49565b82815260059290921b8401810191818101908684111561510657600080fd5b8286015b8481101561512a57803561511d81614e4b565b835291830191830161510a565b509695505050505050565b6000806040838503121561514857600080fd5b823567ffffffffffffffff8082111561516057600080fd5b818501915085601f83011261517457600080fd5b813560206151846150e28361509d565b82815260059290921b840181019181810190898411156151a357600080fd5b948201945b838610156151c1578535825294820194908201906151a8565b965050860135925050808211156151d757600080fd5b506151e4858286016150c1565b9150509250929050565b60008083601f84011261520057600080fd5b50813567ffffffffffffffff81111561521857600080fd5b60208301915083602082850101111561523057600080fd5b9250929050565b803561ffff81168114614ba157600080fd5b60008060008060008060a0878903121561526257600080fd5b863561526d81614b80565b9550602087013567ffffffffffffffff81111561528957600080fd5b61529589828a016151ee565b90965094506152a8905060408801615237565b925060608701356152b881614bca565b80925050608087013590509295509295509295565b600080604083850312156152e057600080fd5b82356152eb81614b80565b91506020830135614c1581614e4b565b6000806040838503121561530e57600080fd5b823561531981614e4b565b91506020830135614c1581614e26565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614ba157600080fd5b600082601f83011261536a57600080fd5b8135602061537a6150e28361509d565b82815260059290921b8401810191818101908684111561539957600080fd5b8286015b8481101561512a5780356153b081614bca565b835291830191830161539d565b6000602082840312156153cf57600080fd5b813567ffffffffffffffff808211156153e757600080fd5b9083019060e082860312156153fb57600080fd5b615403614d26565b61540c83615237565b815261541a60208401614e8f565b602082015261542b60408401615329565b604082015261543c60608401615237565b606082015260808301358281111561545357600080fd5b61545f87828601615359565b60808301525061547160a08401615237565b60a082015261548260c08401614e8f565b60c082015295945050505050565b600080604083850312156154a357600080fd5b82356154ae81614e4b565b91506020830135614c1581614b80565b6000602082840312156154d057600080fd5b5035919050565b600081518084526020808501945080840160005b8381101561551d57815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016154eb565b509495945050505050565b60006bffffffffffffffffffffffff808351168452602083015173ffffffffffffffffffffffffffffffffffffffff8082166020870152826040860151166040870152806060860151166060870152505050608082015160c0608085015261559360c08501826154d7565b60a093840151949093019390935250919050565b602081526000614bc36020830184615528565b600080600080606085870312156155d057600080fd5b84356155db81614e4b565b935060208501359250604085013567ffffffffffffffff8111156155fe57600080fd5b61560a878288016151ee565b95989497509550505050565b604080825283519082018190526000906020906060840190828701845b8281101561564f57815184529284019290840190600101615633565b5050508381038285015261566381866154d7565b9695505050505050565b60006020808352610100830161ffff808651168386015268ffffffffffffffffff838701511660408601527fffffffff00000000000000000000000000000000000000000000000000000000604087015116606086015280606087015116608086015250608085015160e060a0860152818151808452610120870191508483019350600092505b8083101561571a57835163ffffffff1682529284019260019290920191908401906156f4565b5060a087015161ffff811660c0880152935060c087015168ffffffffffffffffff811660e08801529350615663565b60006020828403121561575b57600080fd5b8135614bc381614e4b565b6000806020838503121561577957600080fd5b823567ffffffffffffffff8082111561579157600080fd5b818501915085601f8301126157a557600080fd5b8135818111156157b457600080fd5b866020610160830285010111156157ca57600080fd5b60209290920196919550909350505050565b600080604083850312156157ef57600080fd5b82356154ae81614b80565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561586d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261585b858351615528565b94509285019290850190600101615821565b5092979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff848116825283166020820152606081016147e66040830184615039565b8151815260208083015161016083019161590c9084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015161592c60408401826bffffffffffffffffffffffff169052565b506060830151615954606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615970608084018267ffffffffffffffff169052565b5060a083015161598860a084018263ffffffff169052565b5060c08301516159a560c084018268ffffffffffffffffff169052565b5060e08301516159c260e084018268ffffffffffffffffff169052565b506101008381015164ffffffffff81168483015250506101208381015164ffffffffff81168483015250506101408381015163ffffffff8116848301525b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b64ffffffffff8181168382160190808211156131a1576131a1615a08565b6bffffffffffffffffffffffff818116838216028082169190828114615a0057615a00615a08565b6bffffffffffffffffffffffff8181168382160190808211156131a1576131a1615a08565b6bffffffffffffffffffffffff8716815273ffffffffffffffffffffffffffffffffffffffff86166020820152615adc6040820186615039565b60c060608201526000615af260c0830186614c44565b8281036080840152615b048186614c44565b905082810360a0840152615b188185614c44565b9998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615b5657615b56615a08565b5060010190565b6bffffffffffffffffffffffff8281168282160390808211156131a1576131a1615a08565b600060ff821660ff8103615b9857615b98615a08565b60010192915050565b8181038181111561150957611509615a08565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600067ffffffffffffffff808316818103615c0057615c00615a08565b6001019392505050565b8082018082111561150957611509615a08565b600060208284031215615c2f57600080fd5b5051919050565b60006101608284031215615c4957600080fd5b614bc38383614eb8565b67ffffffffffffffff8181168382160190808211156131a1576131a1615a08565b67ffffffffffffffff8281168282160390808211156131a1576131a1615a08565b838152606060208201526000615cae6060830185614c44565b82810360408401526156638185614c44565b6020815260008251610160806020850152615cdf610180850183614c44565b9150602085015160408501526040850151615d12606086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060608501516bffffffffffffffffffffffff8116608086015250608085015168ffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015167ffffffffffffffff811660e08601525060e0850151610100615d898187018363ffffffff169052565b8601519050610120615da08682018361ffff169052565b8601519050610140615dbd8682018367ffffffffffffffff169052565b9095015173ffffffffffffffffffffffffffffffffffffffff1693019290925250919050565b8051614ba181614e4b565b8051614ba181614e26565b8051614ba181614b80565b8051614ba181614bca565b8051614ba181614e78565b8051614ba181614e9a565b60006101608284031215615e3857600080fd5b615e40614cfc565b82518152615e5060208401615de3565b6020820152615e6160408401615dee565b6040820152615e7260608401615de3565b6060820152615e8360808401615df9565b6080820152615e9460a08401615e04565b60a0820152615ea560c08401615e0f565b60c0820152615eb660e08401615e0f565b60e0820152610100615ec9818501615e1a565b90820152610120615edb848201615e1a565b90820152610140615eed848201615e04565b908201529392505050565b600073ffffffffffffffffffffffffffffffffffffffff808a168352808916602084015280881660408401525060e06060830152615f3960e0830187614c44565b61ffff9590951660808301525063ffffffff9290921660a08301526bffffffffffffffffffffffff1660c090910152949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006147e66040830184614c44565b600060208284031215615fb157600080fd5b81518015158114614bc357600080fd5b60008251615fd3818460208701614c20565b919091019291505056fea164736f6c6343000813000a", } var FunctionsRouterABI = FunctionsRouterMetaData.ABI @@ -677,16 +677,16 @@ func (_FunctionsRouter *FunctionsRouterTransactorSession) CreateSubscriptionWith return _FunctionsRouter.Contract.CreateSubscriptionWithConsumer(&_FunctionsRouter.TransactOpts, consumer) } -func (_FunctionsRouter *FunctionsRouterTransactor) Fulfill(opts *bind.TransactOpts, response []byte, err []byte, juelsPerGas *big.Int, costWithoutCallback *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) { - return _FunctionsRouter.contract.Transact(opts, "fulfill", response, err, juelsPerGas, costWithoutCallback, transmitter, commitment) +func (_FunctionsRouter *FunctionsRouterTransactor) Fulfill(opts *bind.TransactOpts, response []byte, err []byte, juelsPerGas *big.Int, costWithoutFulfillment *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) { + return _FunctionsRouter.contract.Transact(opts, "fulfill", response, err, juelsPerGas, costWithoutFulfillment, transmitter, commitment) } -func (_FunctionsRouter *FunctionsRouterSession) Fulfill(response []byte, err []byte, juelsPerGas *big.Int, costWithoutCallback *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) { - return _FunctionsRouter.Contract.Fulfill(&_FunctionsRouter.TransactOpts, response, err, juelsPerGas, costWithoutCallback, transmitter, commitment) +func (_FunctionsRouter *FunctionsRouterSession) Fulfill(response []byte, err []byte, juelsPerGas *big.Int, costWithoutFulfillment *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) { + return _FunctionsRouter.Contract.Fulfill(&_FunctionsRouter.TransactOpts, response, err, juelsPerGas, costWithoutFulfillment, transmitter, commitment) } -func (_FunctionsRouter *FunctionsRouterTransactorSession) Fulfill(response []byte, err []byte, juelsPerGas *big.Int, costWithoutCallback *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) { - return _FunctionsRouter.Contract.Fulfill(&_FunctionsRouter.TransactOpts, response, err, juelsPerGas, costWithoutCallback, transmitter, commitment) +func (_FunctionsRouter *FunctionsRouterTransactorSession) Fulfill(response []byte, err []byte, juelsPerGas *big.Int, costWithoutFulfillment *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) { + return _FunctionsRouter.Contract.Fulfill(&_FunctionsRouter.TransactOpts, response, err, juelsPerGas, costWithoutFulfillment, transmitter, commitment) } func (_FunctionsRouter *FunctionsRouterTransactor) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { @@ -3510,7 +3510,7 @@ type FunctionsRouterInterface interface { CreateSubscriptionWithConsumer(opts *bind.TransactOpts, consumer common.Address) (*types.Transaction, error) - Fulfill(opts *bind.TransactOpts, response []byte, err []byte, juelsPerGas *big.Int, costWithoutCallback *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) + Fulfill(opts *bind.TransactOpts, response []byte, err []byte, juelsPerGas *big.Int, costWithoutFulfillment *big.Int, transmitter common.Address, commitment FunctionsResponseCommitment) (*types.Transaction, error) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index ac1fc4e83d6..3cdd44cbc5c 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,13 +1,13 @@ GETH_VERSION: 1.12.0 functions: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.bin 3c972870b0afeb6d73a29ebb182f24956a2cebb127b21c4f867d1ecf19a762db -functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.abi ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.bin b2697ad4dfece903a1d34028826a017fa445eb3cd984006f1734fa9d47836ca0 +functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.abi ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.bin 6beec092fbb3b619dfe69f1ad23392b0bbaf00327b335e4080f921c7122a57e4 functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b -functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 97aa7c56d78c703056990eff102279af86b97b11b5855b059e8dd658dc15da8a +functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 7e8a63d56d81fe16a51d4196f5ca3e9623eaa04b56a6e8d7dee1eb0c266944ab functions_load_test_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.bin c8dbbd5ebb34435800d6674700068837c3a252db60046a14b0e61e829db517de functions_oracle_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c -functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 9dedd3a36043605fd9bedf821e7ec5b4281a5c7ae2e4a1955f37aff8ba13519f +functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 1f6d18f9e0846ad74b37a0a6acef5942ab73ace1e84307f201899f69e732e776 functions_v1_events_mock: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsV1EventsMock.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsV1EventsMock.bin 0f0ba42e0cc33c7abc8b8fd4fdfce903748a169886dd5f16cfdd56e75bcf708d ocr2dr: ../../../contracts/solc/v0.8.6/functions/v0_0_0/Functions.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/Functions.bin d9a794b33f47cc57563d216f7cf3a612309fc3062356a27e30005cf1d59e449d ocr2dr_client: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsClient.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsClient.bin 84aa63f9dbc5c7eac240db699b09e613ca4c6cd56dab10bdc25b02461b717e21 From c0a5821a4e3d8d9dafbe7ff0e720ffb06aa2d46d Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 7 Dec 2023 04:56:48 -0500 Subject: [PATCH 020/234] [TT-523] Add Networks for Live Tests (#11358) * Adds A Lot More Networks for Testnet Tests * TT-523 * Enable new network definitions * Clean up reporting * Fix success indicator * Roll-back CTF version * Debug color * Linea contract loader * Reverse time further * tidy * Fix slack colors * Fixes markdown formatting * Fix kuberesolver * Kuberesolver v4 * Tidy * Valid slack blocks * Update CTF * New contract loaders * Fixed contracts and some URLs * Change schedule * Debug * Knocking problems down * Fixed CELO funding * More Cash * Smaller runner * Update actions * Build Tests * Fix runner sizes * Compiled Tests * Control * Update CTF * Network name * Fix selected networks * Moar Cash * Contract Loader * Deprecate Arbitrum Goerli * Upgrade CTF * Fix compile * Enable Scroll * Fix dependencies * Add mockserver logs * Increase timeout * Remove debug * Update CTF * Update Automation Tests * Fix name regex * Actually fix regex * Complete merge --- .github/workflows/live-testnet-tests.yml | 881 ++++++++++++++---- .../contracts/contract_loader.go | 30 +- integration-tests/docker/test_env/test_env.go | 25 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/smoke/ocr_test.go | 3 +- 6 files changed, 767 insertions(+), 178 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index f174e8847bd..2e9809505f2 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -1,7 +1,16 @@ +# *** +# This workflow is a monstrosity of copy-paste, and that's to increase legibility in reporting and running, so the code be damned. +# I suspect this can be cleaned up significantly with some clever trickery of the GitHub actions matrices, but I am not that clever. +# We want each chain to run in parallel, but each test within the chain needs to be able to run sequentially +# (we're trying to eliminate this as a requirement, should make it a lot easier). +# Each chain can have a variety of tests to run. +# We also want reporting to be clear in the start-slack-thread and post-test-results-to-slack jobs. +# *** + name: Live Testnet Tests on: schedule: - - cron: "0 0 * * *" # Run nightly + - cron: "0 0 * * *" # Run every Sunday at midnight push: tags: - "*" @@ -11,7 +20,7 @@ env: CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com MOD_CACHE_VERSION: 2 - CHAINLINK_NODE_FUNDING: .1 + CHAINLINK_NODE_FUNDING: .5 CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} @@ -21,20 +30,53 @@ env: SEPOLIA_URLS: ${{ secrets.QA_SEPOLIA_URLS }} SEPOLIA_HTTP_URLS: ${{ secrets.QA_SEPOLIA_HTTP_URLS }} + BSC_TESTNET_URLS: ${{ secrets.QA_BSC_TESTNET_URLS }} + BSC_TESTNET_HTTP_URLS: ${{ secrets.QA_BSC_TESTNET_HTTP_URLS }} + OPTIMISM_GOERLI_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_URLS }} OPTIMISM_GOERLI_HTTP_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_HTTP_URLS }} - ARBITRUM_GOERLI_URLS: ${{ secrets.QA_ARBITRUM_GOERLI_URLS }} - ARBITRUM_GOERLI_HTTP_URLS: ${{ secrets.QA_ARBITRUM_GOERLI_HTTP_URLS }} + OPTIMISM_SEPOLIA_URLS: ${{ secrets.QA_OPTIMISM_SEPOLIA_URLS }} + OPTIMISM_SEPOLIA_HTTP_URLS: ${{ secrets.QA_OPTIMISM_SEPOLIA_HTTP_URLS }} + + ARBITRUM_SEPOLIA_URLS: ${{ secrets.QA_ARBITRUM_SEPOLIA_URLS }} + ARBITRUM_SEPOLIA_HTTP_URLS: ${{ secrets.QA_ARBITRUM_SEPOLIA_HTTP_URLS }} + + BASE_GOERLI_URLS: ${{ secrets.QA_BASE_GOERLI_URLS }} + BASE_GOERLI_HTTP_URLS: ${{ secrets.QA_BASE_GOERLI_HTTP_URLS }} + + BASE_SEPOLIA_URLS: ${{ secrets.QA_BASE_SEPOLIA_URLS }} + BASE_SEPOLIA_HTTP_URLS: ${{ secrets.QA_BASE_SEPOLIA_HTTP_URLS }} + + POLYGON_MUMBAI_URLS: ${{ secrets.QA_POLYGON_MUMBAI_URLS }} + POLYGON_MUMBAI_HTTP_URLS: ${{ secrets.QA_POLYGON_MUMBAI_HTTP_URLS }} + + AVALANCHE_FUJI_URLS: ${{ secrets.QA_AVALANCHE_FUJI_URLS }} + AVALANCHE_FUJI_HTTP_URLS: ${{ secrets.QA_AVALANCHE_FUJI_HTTP_URLS }} + + FANTOM_TESTNET_URLS: ${{ secrets.QA_FANTOM_TESTNET_URLS }} + FANTOM_TESTNET_HTTP_URLS: ${{ secrets.QA_FANTOM_TESTNET_HTTP_URLS }} + + CELO_ALFAJORES_URLS: ${{ secrets.QA_CELO_ALFAJORES_URLS }} + CELO_ALFAJORES_HTTP_URLS: ${{ secrets.QA_CELO_ALFAJORES_HTTP_URLS }} + + SCROLL_SEPOLIA_URLS: ${{ secrets.QA_SCROLL_SEPOLIA_URLS }} + SCROLL_SEPOLIA_HTTP_URLS: ${{ secrets.QA_SCROLL_SEPOLIA_HTTP_URLS }} + + LINEA_GOERLI_URLS: ${{ secrets.QA_LINEA_GOERLI_URLS }} + LINEA_GOERLI_HTTP_URLS: ${{ secrets.QA_LINEA_GOERLI_HTTP_URLS }} jobs: + + # Build Test Dependencies + build-chainlink: environment: integration permissions: id-token: write contents: read name: Build Chainlink Image - runs-on: ubuntu20.04-16cores-64GB + runs-on: ubuntu-latest steps: - name: Collect Metrics id: collect-gha-metrics @@ -59,188 +101,41 @@ jobs: AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - # TODO: Re-enable when we have secrets properly configured - # sepolia-smoke-tests: - # environment: integration - # permissions: - # checks: write - # pull-requests: write - # id-token: write - # contents: read - # needs: [build-chainlink] - # env: - # SELECTED_NETWORKS: SEPOLIA - # strategy: - # max-parallel: 1 - # fail-fast: false - # matrix: - # include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations - # - product: OCR - # test: TestOCRBasic - # - product: Automation - # test: TestAutomationBasic/registry_2_0 - # name: Sepolia ${{ matrix.product }} Tests - # runs-on: ubuntu-latest - # steps: - # - name: Checkout the repo - # uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - # with: - # ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - # - name: Run Tests - # uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 - # env: - # PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - # PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia - # PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - # with: - # test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 -run ${{ matrix.test }} ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt - # test_download_vendor_packages_command: cd ./integration-tests && go mod download - # cl_repo: ${{ env.CHAINLINK_IMAGE }} - # cl_image_tag: ${{ github.sha }} - # aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - # dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - # dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - # artifacts_location: ./integration-tests/smoke/logs - # publish_check_name: Seplia ${{ matrix.product }} Smoke Test Results - # token: ${{ secrets.GITHUB_TOKEN }} - # go_mod_path: ./integration-tests/go.mod - # cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - # cache_restore_only: "true" - # QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - # QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - # QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - # - name: Collect Metrics - # if: always() - # id: collect-gha-metrics - # uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - # with: - # basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - # hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - # this-job-name: Sepolia ${{ matrix.product }} Tests - # test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - # continue-on-error: true - - optimism-goerli-smoke-tests: + build-tests: environment: integration permissions: - checks: write - pull-requests: write id-token: write contents: read - needs: [build-chainlink] - env: - SELECTED_NETWORKS: OPTIMISM_GOERLI - strategy: - fail-fast: false - max-parallel: 1 - matrix: - include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations - - product: OCR - test: TestOCRBasic - - product: Automation - test: TestAutomationBasic/registry_2_0 - name: Optimism Goerli ${{ matrix.product }} Tests + name: Build Tests Binary runs-on: ubuntu-latest steps: - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-goerli - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 -run ${{ matrix.test }} ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt - test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./integration-tests/smoke/logs - publish_check_name: Seplia ${{ matrix.product }} Smoke Test Results - token: ${{ secrets.GITHUB_TOKEN }} - go_mod_path: ./integration-tests/go.mod - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: "true" - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Collect Metrics - if: always() id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: Optimism Goerli ${{ matrix.product }} Tests - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + this-job-name: Build Tests Binary continue-on-error: true - - arbitrum-goerli-smoke-tests: - environment: integration - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - needs: [build-chainlink] - env: - SELECTED_NETWORKS: ARBITRUM_GOERLI - strategy: - max-parallel: 1 - fail-fast: false - matrix: - include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations - - product: OCR - test: TestOCRBasic - - product: Automation - test: TestAutomationBasic/registry_2_0 - name: Arbitrum Goerli ${{ matrix.product }} Tests - runs-on: ubuntu-latest - steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-goerli - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + - name: Build Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 -run ${{ matrix.test }} ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./integration-tests/smoke/logs - publish_check_name: Arbitrum Goerli ${{ matrix.product }} Smoke Test Results token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} cache_restore_only: "true" - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - - name: Collect Metrics - if: always() - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - with: - basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: Arbitrum Goerli ${{ matrix.product }} Tests - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - continue-on-error: true + binary_name: tests + + # End Build Test Dependencies - testnet-smoke-tests-notify: + # Reporting Jobs + + start-slack-thread: name: Start Slack Thread if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} environment: integration @@ -252,7 +147,7 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: [optimism-goerli-smoke-tests, arbitrum-goerli-smoke-tests] + needs: [sepolia-smoke-tests, bsc-testnet-tests, optimism-goerli-smoke-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, scroll-sepolia-smoke-tests, linea-goerli-smoke-tests] steps: - name: Debug Result run: echo ${{ join(needs.*.result, ',') }} @@ -275,6 +170,13 @@ jobs: "emoji": true } }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "${{ contains(join(needs.*.result, ','), 'failure') && 'Some tests failed, notifying <@U01Q4N37KFG>' || 'All Good!' }}" + } + }, { "type": "divider" }, @@ -292,7 +194,7 @@ jobs: env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} - testnet-smoke-tests-results: + post-test-results-to-slack: name: Post Test Results for ${{ matrix.network }} if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} environment: integration @@ -302,11 +204,11 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: testnet-smoke-tests-notify + needs: start-slack-thread strategy: fail-fast: false matrix: - network: [Optimism Goerli, Arbitrum Goerli] + network: [Sepolia, Optimism Goerli, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Scroll Sepolia, Linea Goerli] steps: - name: Get Results id: test-results @@ -317,7 +219,7 @@ jobs: PARSED_RESULTS=$(curl \ -H "Authorization: Bearer ${{ github.token }}" \ 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ - | jq -r --arg pattern "${{ matrix.network }} (?\\w+) Tests" '.jobs[] + | jq -r --arg pattern "^${{ matrix.network }} (?.*?) Tests$" '.jobs[] | select(.name | test($pattern)) as $job | $job.steps[] | select(.name == "Run Tests") @@ -327,10 +229,13 @@ jobs: echo $PARSED_RESULTS ALL_SUCCESS=true + echo "Checking for failures" + echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")' for row in $(echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")'); do - success=false + ALL_SUCCESS=false break done + echo "Success: $ALL_SUCCESS" echo all_success=$ALL_SUCCESS >> $GITHUB_OUTPUT @@ -360,16 +265,16 @@ jobs: channel-id: ${{ secrets.QA_SLACK_CHANNEL }} payload: | { - "thread_ts": "${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}", + "thread_ts": "${{ needs.start-slack-thread.outputs.thread_ts }}", "attachments": [ { - "color": "${{ steps.test-results.outputs.all_success && '#2E7D32' || '#C62828' }}", + "color": "${{ steps.test-results.outputs.all_success == 'true' && '#2E7D32' || '#C62828' }}", "blocks": [ { "type": "header", "text": { "type": "plain_text", - "text": "${{ matrix.network }} ${{ steps.test-results.outputs.all_success && ':white_check_mark:' || ':x: Notifying <@U01Q4N37KFG>'}}", + "text": "${{ matrix.network }} ${{ steps.test-results.outputs.all_success == 'true' && ':white_check_mark:' || ':x:'}}", "emoji": true } }, @@ -383,3 +288,635 @@ jobs: } env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + + # End Reporting Jobs + + sepolia-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: SEPOLIA + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Sepolia ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + bsc-testnet-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: BSC_TESTNET + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: BSC Testnet ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-bsc-testnet + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + optimism-goerli-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: OPTIMISM_GOERLI + strategy: + fail-fast: false + max-parallel: 1 + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Optimism Goerli ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-goerli + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + optimism-sepolia-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: OPTIMISM_SEPOLIA + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Optimism Sepolia ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + arbitrum-sepolia-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: ARBITRUM_SEPOLIA + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Arbitrum Sepolia ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + base-goerli-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: BASE_GOERLI + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + name: Base Goerli ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-base-goerli + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + base-sepolia-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: BASE_SEPOLIA + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + name: Base Sepolia ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-base-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + polygon-mumbai-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: POLYGON_MUMBAI + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Polygon Mumbai ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-polygon-mumbai + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + avalanche-fuji-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: AVALANCHE_FUJI + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Avalanche Fuji ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-avalanche-fuji + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + fantom-testnet-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: FANTOM_TESTNET + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation Conditional + test: TestAutomationBasic/registry_2_1_conditional + - product: Automation Log Trigger + test: TestAutomationBasic/registry_2_1_logtrigger + name: Fantom Testnet ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-fantom-testnet + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + celo-alfajores-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: CELO_ALFAJORES + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + name: Celo Alfajores ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-celo-alfajores + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + scroll-sepolia-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: SCROLL_SEPOLIA + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + name: Scroll Sepolia ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-scroll-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + + linea-goerli-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink, build-tests] + env: + SELECTED_NETWORKS: LINEA_GOERLI + strategy: + max-parallel: 1 + fail-fast: false + matrix: + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + name: Linea Goerli ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-linea-goerli + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} + binary_name: tests + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs + token: ${{ secrets.GITHUB_TOKEN }} + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} \ No newline at end of file diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index e66c95138b9..0fec424426a 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -71,6 +71,14 @@ func NewContractLoader(bcClient blockchain.EVMClient, logger zerolog.Logger) (Co return &PolygonZkEvmContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil case *blockchain.WeMixClient: return &WeMixContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.LineaClient: + return &LineaContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.CeloClient: + return &CeloContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.ScrollClient: + return &ScrollContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.FantomClient: + return &FantomContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil case *blockchain.BSCClient: return &BSCContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil } @@ -121,7 +129,27 @@ type WeMixContractLoader struct { *EthereumContractLoader } -// BSCContractLoader wraps ethereum contract deployments for BSC +// LineaContractLoader wraps for Linea +type LineaContractLoader struct { + *EthereumContractLoader +} + +// CeloContractLoader wraps for Celo +type CeloContractLoader struct { + *EthereumContractLoader +} + +// ScrollContractLoader wraps for Scroll +type ScrollContractLoader struct { + *EthereumContractLoader +} + +// FantomContractLoader wraps for Fantom +type FantomContractLoader struct { + *EthereumContractLoader +} + +// BSCContractLoader wraps for BSC type BSCContractLoader struct { *EthereumContractLoader } diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index b1b179badc5..b04d24a9593 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "runtime/debug" + "strings" "testing" "time" @@ -262,7 +263,8 @@ func (te *CLClusterTestEnv) logWhetherAllContainersAreRunning() { // collectTestLogs collects the logs from all the Chainlink nodes in the test environment and writes them to local files func (te *CLClusterTestEnv) collectTestLogs() error { te.l.Info().Msg("Collecting test logs") - folder := fmt.Sprintf("./logs/%s-%s", te.t.Name(), time.Now().Format("2006-01-02T15-04-05")) + sanitizedNetworkName := strings.ReplaceAll(te.EVMClient.GetNetworkName(), " ", "-") + folder := fmt.Sprintf("./logs/%s-%s-%s", te.t.Name(), sanitizedNetworkName, time.Now().Format("2006-01-02T15-04-05")) if err := os.MkdirAll(folder, os.ModePerm); err != nil { return err } @@ -290,6 +292,27 @@ func (te *CLClusterTestEnv) collectTestLogs() error { }) } + if te.MockAdapter != nil { + eg.Go(func() error { + logFileName := filepath.Join(folder, "mock-adapter.log") + logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer logFile.Close() + logReader, err := te.MockAdapter.Container.Logs(testcontext.Get(te.t)) + if err != nil { + return err + } + _, err = io.Copy(logFile, logReader) + if err != nil { + return err + } + te.l.Info().Str("Container", te.MockAdapter.ContainerName).Str("File", logFileName).Msg("Wrote Logs") + return nil + }) + } + if err := eg.Wait(); err != nil { return err } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 01d7c4dbef9..32c35c28a9a 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 - github.com/smartcontractkit/chainlink-testing-framework v1.20.0 + github.com/smartcontractkit/chainlink-testing-framework v1.20.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f3d707d18ec..21e237923b9 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1519,8 +1519,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= -github.com/smartcontractkit/chainlink-testing-framework v1.20.0 h1:gQPQRKJuMh6QTAIMkqZ7v5WkjEmbcoMIX/V6WPVrvuI= -github.com/smartcontractkit/chainlink-testing-framework v1.20.0/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= +github.com/smartcontractkit/chainlink-testing-framework v1.20.1 h1:0hxLRts4yIum52MaE95RuM2Xi1S/R0r4UFExpp00iK4= +github.com/smartcontractkit/chainlink-testing-framework v1.20.1/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 9ed692700ad..57afbdc1a4a 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -21,7 +22,7 @@ func TestOCRBasic(t *testing.T) { WithGeth(). WithMockAdapter(). WithCLNodes(6). - WithFunding(big.NewFloat(.01)). + WithFunding(big.NewFloat(.5)). WithStandardCleanup(). Build() require.NoError(t, err) From c545d2c7aa452f4e3c2c6e15c640e4ec39f2cb56 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Thu, 7 Dec 2023 17:02:17 +0300 Subject: [PATCH 021/234] Devspace non-root/build update (#11510) * move root parts to build inside container * update README + add more resources to the build node --- charts/chainlink-cluster/README.md | 12 +++---- charts/chainlink-cluster/devspace.yaml | 34 ++++++++++--------- .../templates/chainlink-node-deployment.yaml | 10 +++--- charts/chainlink-cluster/values.yaml | 10 ++++++ core/chainlink.devspace.Dockerfile | 20 +++++++++++ integration-tests/load/ocr/README.md | 10 +++--- integration-tests/load/ocr/ocr_test.go | 4 +-- 7 files changed, 64 insertions(+), 36 deletions(-) diff --git a/charts/chainlink-cluster/README.md b/charts/chainlink-cluster/README.md index 5fb55536635..3deb37794a6 100644 --- a/charts/chainlink-cluster/README.md +++ b/charts/chainlink-cluster/README.md @@ -16,13 +16,8 @@ nix develop ## New cluster We are using [devspace](https://www.devspace.sh/docs/getting-started/installation?x0=3) -Configure the cluster, see `deployments.app.helm.values` and [values.yaml](./values.yaml) comments +Configure the cluster, see `deployments.app.helm.values` and [values.yaml](./values.yaml) comments for more details -Set your registry for the image, example for `ECR`: -``` -aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin ${aws_account}.dkr.ecr.us-west-2.amazonaws.com -export DEVSPACE_IMAGE="${aws_account}.dkr.ecr.us-west-2.amazonaws.com/chainlink-devspace" -``` Enter the shell and deploy ``` # set your unique namespace if it's a new cluster @@ -45,11 +40,12 @@ Fix something in the code locally, it'd automatically sync, rebuild it inside co make chainlink make chainlink-local-start ``` -If you need to update the whole cluster run `deploy` again with a new set of images + +Reset the pod to original image ``` devspace reset pods -devspace deploy ``` + Destroy the cluster ``` devspace purge diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index cb4c8bfce49..f62b87edf69 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -58,6 +58,16 @@ deployments: - name: node-1 image: ${DEVSPACE_IMAGE} version: latest + # default resources are 300m/1Gi + # first node need more resources to build faster inside container + # at least 2Gi of memory is required otherwise build will fail (OOM) + resources: + requests: + cpu: 2000m + memory: 2048Mi + limits: + cpu: 2000m + memory: 2048Mi # override default config per node # for example, use OCRv2 P2P setup, the whole config # toml: | @@ -105,13 +115,6 @@ deployments: - name: node-6 image: ${DEVSPACE_IMAGE} version: latest - resources: - requests: - cpu: 350m - memory: 1024Mi - limits: - cpu: 350m - memory: 1024Mi # each CL node have a dedicated PostgreSQL 11.15 # use StatefulSet by setting: @@ -231,7 +234,7 @@ profiles: patches: - op: replace path: dev.app.workingDir - value: /home/root/chainlink/integration-tests + value: /home/chainlink/integration-tests - op: replace path: dev.app.container value: runner @@ -256,21 +259,20 @@ profiles: # This is a list of `dev` containers that are based on the containers created by your deployments dev: app: - workingDir: /home/root/chainlink + workingDir: /home/chainlink container: node labelSelector: instance: node-1 # Sync files between the local filesystem and the development container sync: - - path: ../../core/services/chainlink:/home/root/chainlink/core/services/chainlink + - path: ../../core/services/chainlink:/home/chainlink/core/services/chainlink printLogs: true disableDownload: true - - path: ../..:/home/root/chainlink + - path: ../..:/home/chainlink printLogs: true disableDownload: true uploadExcludePaths: - integration-tests/ - - .git/ - .github/ - belt/ - charts/ @@ -280,16 +282,16 @@ dev: - integration-scripts/ - testdata/ - evm-test-helpers/ - - tools/ # Open a terminal and use the following command terminal: command: bash ssh: enabled: true proxyCommands: - - command: devspace - - command: kubectl - - command: helm +# TODO: access issues +# - command: devspace +# - command: kubectl +# - command: helm - gitCredentials: true ports: - port: "2345" diff --git a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml index 463453aff93..a08c31c2c42 100644 --- a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml @@ -71,14 +71,14 @@ spec: initialDelaySeconds: 15 periodSeconds: 5 failureThreshold: 20 - {{ if (hasKey $.Values.chainlink "resources") }} + {{ if (hasKey $cfg "resources") }} resources: requests: - memory: {{ default "1024Mi" $.Values.chainlink.resources.requests.memory }} - cpu: {{ default "500m" $.Values.chainlink.resources.requests.cpu }} + memory: {{ default "1024Mi" $cfg.resources.requests.memory }} + cpu: {{ default "300m" $cfg.resources.requests.cpu }} limits: - memory: {{ default "1024Mi" $.Values.chainlink.resources.limits.memory }} - cpu: {{ default "500m" $.Values.chainlink.resources.limits.cpu }} + memory: {{ default "1024Mi" $cfg.resources.limits.memory }} + cpu: {{ default "300m" $cfg.resources.limits.cpu }} {{ else }} {{ end }} {{- with $.Values.nodeSelector }} diff --git a/charts/chainlink-cluster/values.yaml b/charts/chainlink-cluster/values.yaml index eb93e6cefcf..feba1414444 100644 --- a/charts/chainlink-cluster/values.yaml +++ b/charts/chainlink-cluster/values.yaml @@ -25,6 +25,16 @@ chainlink: nodes: - name: node-1 image: "public.ecr.aws/chainlink/chainlink:latest" + # default resources are 300m/1Gi + # first node need more resources to build faster inside container + # at least 2Gi of memory is required otherwise build will fail (OOM) + resources: + requests: + cpu: 2000m + memory: 2048Mi + limits: + cpu: 2000m + memory: 2048Mi # override default config per node # for example, use OCRv2 P2P setup, the whole config # toml: | diff --git a/core/chainlink.devspace.Dockerfile b/core/chainlink.devspace.Dockerfile index 88d3cec16ad..c639190a80f 100644 --- a/core/chainlink.devspace.Dockerfile +++ b/core/chainlink.devspace.Dockerfile @@ -17,6 +17,22 @@ COPY . . # Build the golang binary RUN make install-chainlink +# Link LOOP Plugin source dirs with simple names +RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-feeds | xargs -I % ln -s % /chainlink-feeds +RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-solana | xargs -I % ln -s % /chainlink-solana + +# Build image: Plugins +FROM golang:1.21-bullseye as buildplugins +RUN go version + +WORKDIR /chainlink-feeds +COPY --from=buildgo /chainlink-feeds . +RUN go install ./cmd/chainlink-feeds + +WORKDIR /chainlink-solana +COPY --from=buildgo /chainlink-solana . +RUN go install ./pkg/solana/cmd/chainlink-solana + # Final image: ubuntu with chainlink binary FROM golang:1.21-bullseye @@ -32,6 +48,10 @@ RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ COPY --from=buildgo /go/bin/chainlink /usr/local/bin/ +# Install (but don't enable) LOOP Plugins +COPY --from=buildplugins /go/bin/chainlink-feeds /usr/local/bin/ +COPY --from=buildplugins /go/bin/chainlink-solana /usr/local/bin/ + # Dependency of CosmWasm/wasmd COPY --from=buildgo /go/pkg/mod/github.com/\!cosm\!wasm/wasmvm@v*/internal/api/libwasmvm.*.so /usr/lib/ RUN chmod 755 /usr/lib/libwasmvm.*.so diff --git a/integration-tests/load/ocr/README.md b/integration-tests/load/ocr/README.md index 20446992dc2..61951ba700f 100644 --- a/integration-tests/load/ocr/README.md +++ b/integration-tests/load/ocr/README.md @@ -3,15 +3,15 @@ ## Setup These tests can connect to any cluster create with [chainlink-cluster](../../../charts/chainlink-cluster/README.md) -Create your cluster +Create your cluster, if you already have one just use `kubefwd` ``` -kubectl create ns my-cluster -devspace use namespace my-cluster +kubectl create ns cl-cluster +devspace use namespace cl-cluster devspace deploy -sudo kubefwd svc -n my-cluster +sudo kubefwd svc -n cl-cluster ``` -Change environment connection configuration [here](connection.toml) +Change environment connection configuration [here](../../../charts/chainlink-cluster/connect.toml) If you haven't changed anything in [devspace.yaml](../../../charts/chainlink-cluster/devspace.yaml) then default connection configuration will work diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index 6bf1487125d..13aea441b2a 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -18,7 +18,7 @@ var ( } ) -func TestOCRPerformance(t *testing.T) { +func TestOCRLoad(t *testing.T) { l := logging.GetTestLogger(t) cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) require.NoError(t, err) @@ -46,7 +46,7 @@ func TestOCRPerformance(t *testing.T) { require.NoError(t, err) } -func TestOCRCapacity(t *testing.T) { +func TestOCRVolume(t *testing.T) { l := logging.GetTestLogger(t) cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) require.NoError(t, err) From 6bb80e0f9ae87e6faa20ccc3ab3e09196157673f Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 7 Dec 2023 08:03:53 -0600 Subject: [PATCH 022/234] bump mockery to v2.38.0 (#11509) --- .tool-versions | 2 +- GNUmakefile | 2 +- common/client/mock_head_test.go | 10 +- common/client/mock_node_client_test.go | 26 +- common/client/mock_node_selector_test.go | 10 +- common/client/mock_node_test.go | 42 +- common/client/mock_rpc_test.go | 102 ++++- common/client/mock_send_only_client_test.go | 10 +- common/client/mock_send_only_node_test.go | 30 +- common/headtracker/types/mocks/head.go | 50 ++- common/mocks/head_broadcaster.go | 26 +- common/mocks/head_tracker.go | 30 +- common/txmgr/mocks/tx_manager.go | 66 ++- common/txmgr/types/mocks/forwarder_manager.go | 30 +- common/txmgr/types/mocks/key_store.go | 14 +- .../txmgr/types/mocks/reaper_chain_config.go | 6 +- .../txmgr/types/mocks/tx_attempt_builder.go | 42 +- common/txmgr/types/mocks/tx_store.go | 202 ++++++++- common/txmgr/types/mocks/tx_strategy.go | 10 +- common/types/mocks/head.go | 38 +- common/types/mocks/head_trackable.go | 2 +- common/types/mocks/subscription.go | 6 +- core/bridges/mocks/orm.go | 54 ++- core/chains/evm/client/mocks/batch_sender.go | 6 +- core/chains/evm/client/mocks/client.go | 134 +++++- core/chains/evm/client/mocks/tx_sender.go | 10 +- .../evm/config/mocks/chain_scoped_config.go | 138 +++++- core/chains/evm/config/mocks/gas_estimator.go | 78 +++- core/chains/evm/forwarders/mocks/orm.go | 22 +- core/chains/evm/gas/mocks/config.go | 14 +- core/chains/evm/gas/mocks/eth_client.go | 6 +- core/chains/evm/gas/mocks/evm_estimator.go | 38 +- .../chains/evm/gas/mocks/evm_fee_estimator.go | 38 +- core/chains/evm/gas/mocks/rpc_client.go | 6 +- .../evm/gas/rollups/mocks/eth_client.go | 6 +- .../chains/evm/gas/rollups/mocks/l1_oracle.go | 26 +- core/chains/evm/headtracker/mocks/config.go | 10 +- core/chains/evm/log/mocks/abigen_contract.go | 10 +- core/chains/evm/log/mocks/broadcast.go | 42 +- core/chains/evm/log/mocks/broadcaster.go | 46 +- core/chains/evm/logpoller/mocks/log_poller.go | 110 ++++- core/chains/evm/mocks/balance_monitor.go | 26 +- core/chains/evm/mocks/node.go | 126 +++++- core/chains/evm/mocks/send_only_node.go | 34 +- core/chains/evm/txmgr/mocks/config.go | 22 +- core/chains/evm/txmgr/mocks/evm_tx_store.go | 230 +++++++++- core/chains/legacyevm/mocks/chain.go | 78 +++- .../legacyevm/mocks/legacy_chain_container.go | 22 +- core/cmd/mocks/prompter.go | 14 +- core/config/mocks/telemetry_ingress.go | 42 +- .../mocks/telemetry_ingress_endpoint.go | 18 +- core/internal/mocks/application.go | 122 +++++- core/internal/mocks/flags.go | 182 +++++++- core/internal/mocks/flux_aggregator.go | 318 +++++++++++++- core/internal/mocks/prometheus_backend.go | 2 +- core/logger/logger_mock_test.go | 22 +- core/logger/mocks/logger.go | 22 +- core/services/blockhashstore/mocks/bhs.go | 22 +- core/services/blockhashstore/mocks/timer.go | 6 +- .../chainlink/mocks/general_config.go | 154 ++++++- .../feeds/mocks/connections_manager.go | 14 +- .../feeds/mocks/feeds_manager_client.go | 22 +- core/services/feeds/mocks/orm.go | 138 +++++- core/services/feeds/mocks/service.go | 114 ++++- .../fluxmonitorv2/mocks/contract_submitter.go | 6 +- core/services/fluxmonitorv2/mocks/flags.go | 18 +- .../mocks/key_store_interface.go | 10 +- core/services/fluxmonitorv2/mocks/orm.go | 26 +- .../functions/mocks/bridge_accessor.go | 6 +- .../mocks/external_adapter_client.go | 10 +- .../functions/mocks/functions_listener.go | 14 +- .../functions/mocks/offchain_transmitter.go | 10 +- core/services/functions/mocks/orm.go | 38 +- .../connector/mocks/gateway_connector.go | 22 +- .../mocks/gateway_connector_handler.go | 10 +- .../gateway/connector/mocks/signer.go | 6 +- .../functions/mocks/onchain_allowlist.go | 18 +- .../functions/mocks/onchain_subscriptions.go | 14 +- core/services/gateway/handlers/mocks/don.go | 6 +- .../gateway/handlers/mocks/handler.go | 18 +- .../network/mocks/connection_acceptor.go | 10 +- .../network/mocks/connection_initiator.go | 10 +- .../network/mocks/http_request_handler.go | 6 +- .../gateway/network/mocks/http_server.go | 14 +- .../network/mocks/web_socket_server.go | 14 +- core/services/job/mocks/orm.go | 102 ++++- core/services/job/mocks/service_ctx.go | 10 +- core/services/job/mocks/spawner.go | 38 +- core/services/keystore/mocks/cosmos.go | 34 +- core/services/keystore/mocks/csa.go | 34 +- core/services/keystore/mocks/dkg_encrypt.go | 34 +- core/services/keystore/mocks/dkg_sign.go | 34 +- core/services/keystore/mocks/eth.go | 82 +++- core/services/keystore/mocks/master.go | 54 ++- core/services/keystore/mocks/ocr.go | 34 +- core/services/keystore/mocks/ocr2.go | 38 +- core/services/keystore/mocks/p2p.go | 38 +- core/services/keystore/mocks/solana.go | 38 +- core/services/keystore/mocks/starknet.go | 34 +- core/services/keystore/mocks/vrf.go | 34 +- core/services/mocks/checker.go | 26 +- .../ocr/mocks/ocr_contract_tracker_db.go | 10 +- .../evmregistry/v20/mocks/registry.go | 18 +- .../v21/core/mocks/upkeep_state_reader.go | 6 +- .../evmregistry/v21/mocks/http_client.go | 6 +- .../evmregistry/v21/mocks/registry.go | 30 +- .../ocr2vrf/coordinator/mocks/vrf_beacon.go | 290 +++++++++++- .../mocks/vrf_beacon_coordinator.go | 22 +- .../coordinator/mocks/vrf_coordinator.go | 414 +++++++++++++++++- .../promwrapper/mocks/prometheus_backend.go | 2 +- .../ocr2/plugins/threshold/mocks/decryptor.go | 6 +- core/services/pg/mocks/event_broadcaster.go | 30 +- core/services/pg/mocks/subscription.go | 14 +- core/services/pipeline/mocks/config.go | 22 +- core/services/pipeline/mocks/orm.go | 74 +++- .../mocks/pipeline_param_unmarshaler.go | 6 +- core/services/pipeline/mocks/runner.go | 46 +- .../relay/evm/mercury/mocks/async_deleter.go | 2 +- .../relay/evm/mocks/loop_relay_adapter.go | 46 +- .../relay/evm/mocks/request_round_db.go | 10 +- .../evm/types/mocks/log_poller_wrapper.go | 26 +- core/services/s4/mocks/orm.go | 22 +- core/services/s4/mocks/storage.go | 18 +- .../mocks/telemetry_service.go | 22 +- .../vrf/mocks/aggregator_v3_interface.go | 26 +- core/services/vrf/mocks/config.go | 10 +- core/services/vrf/mocks/fee_config.go | 14 +- core/services/vrf/mocks/vrf_coordinator_v2.go | 334 +++++++++++++- .../mocks/external_initiator_manager.go | 14 +- core/services/webhook/mocks/http_client.go | 6 +- core/sessions/ldapauth/mocks/ldap_client.go | 6 +- core/sessions/ldapauth/mocks/ldap_conn.go | 14 +- .../sessions/mocks/authentication_provider.go | 78 +++- core/sessions/mocks/basic_admin_users_orm.go | 14 +- 134 files changed, 5774 insertions(+), 134 deletions(-) diff --git a/.tool-versions b/.tool-versions index c60396ccb86..d78ce677cda 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,5 +1,5 @@ golang 1.21.4 -mockery 2.35.4 +mockery 2.38.0 nodejs 16.16.0 postgres 13.3 helm 3.10.3 diff --git a/GNUmakefile b/GNUmakefile index 6cd5ab7143e..7d0753911a3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -112,7 +112,7 @@ presubmit: ## Format go files and imports. .PHONY: mockery mockery: $(mockery) ## Install mockery. - go install github.com/vektra/mockery/v2@v2.35.4 + go install github.com/vektra/mockery/v2@v2.38.0 .PHONY: codecgen codecgen: $(codecgen) ## Install codecgen diff --git a/common/client/mock_head_test.go b/common/client/mock_head_test.go index 1b69eedf438..747770480f5 100644 --- a/common/client/mock_head_test.go +++ b/common/client/mock_head_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -17,6 +17,10 @@ type mockHead struct { func (_m *mockHead) BlockDifficulty() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockDifficulty") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -33,6 +37,10 @@ func (_m *mockHead) BlockDifficulty() *big.Int { func (_m *mockHead) BlockNumber() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockNumber") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index 7c8eb69171f..661ad68ede5 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -18,6 +18,10 @@ type mockNodeClient[CHAIN_ID types.ID, HEAD Head] struct { 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 { @@ -42,6 +46,10 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID 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 { @@ -71,6 +79,10 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) Close() { 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) @@ -85,6 +97,10 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { 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() @@ -112,6 +128,10 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) Subscribe(ctx context.Context, channel _ca = append(_ca, args...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 types.Subscription var r1 error if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) (types.Subscription, error)); ok { @@ -138,6 +158,10 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) Subscribe(ctx context.Context, channel 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() diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go index e7b8d9ecb8d..bd0805fa4e6 100644 --- a/common/client/mock_node_selector_test.go +++ b/common/client/mock_node_selector_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -16,6 +16,10 @@ type mockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEA func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -30,6 +34,10 @@ func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, 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 { r0 = rf() diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index ea0e7d1a120..56132b2cee8 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -20,6 +20,10 @@ type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] stru func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ConfiguredChainID") + } + var r0 CHAIN_ID if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { r0 = rf() @@ -48,6 +56,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -62,6 +74,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Order") + } + var r0 int32 if rf, ok := ret.Get(0).(func() int32); ok { r0 = rf() @@ -76,6 +92,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RPC") + } + var r0 RPC if rf, ok := ret.Get(0).(func() RPC); ok { r0 = rf() @@ -90,6 +110,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -104,6 +128,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for State") + } + var r0 nodeState if rf, ok := ret.Get(0).(func() nodeState); ok { r0 = rf() @@ -118,6 +146,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *big.Int) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StateAndLatest") + } + var r0 nodeState var r1 int64 var r2 *big.Int @@ -151,6 +183,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *bi func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for String") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -165,6 +201,10 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { 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() diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index d5e8db82836..d87a02d47c1 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -25,6 +25,10 @@ type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_H func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -51,6 +55,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BatchCallContext(ctx context.Context, b []interface{}) 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, []interface{}) error); ok { r0 = rf(ctx, b) @@ -65,6 +73,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -89,6 +101,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -116,6 +132,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS _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...) @@ -130,6 +150,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -156,6 +180,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, 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 { @@ -180,6 +208,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, 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 { @@ -209,6 +241,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -235,6 +271,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, 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) @@ -249,6 +289,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, 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() @@ -268,6 +312,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -292,6 +340,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -318,6 +370,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -344,6 +400,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -370,6 +430,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -394,6 +458,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -418,6 +486,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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) @@ -432,6 +504,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -461,6 +537,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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) @@ -478,6 +558,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS _ca = append(_ca, args...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 types.Subscription var r1 error if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) (types.Subscription, error)); ok { @@ -504,6 +588,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, 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() @@ -518,6 +606,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -544,6 +636,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { @@ -568,6 +664,10 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) 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 { diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go index 481b2602ea3..b667a2ceb59 100644 --- a/common/client/mock_send_only_client_test.go +++ b/common/client/mock_send_only_client_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -18,6 +18,10 @@ type mockSendOnlyClient[CHAIN_ID types.ID] struct { func (_m *mockSendOnlyClient[CHAIN_ID]) ChainID(_a0 context.Context) (CHAIN_ID, error) { ret := _m.Called(_a0) + 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 { @@ -47,6 +51,10 @@ func (_m *mockSendOnlyClient[CHAIN_ID]) Close() { func (_m *mockSendOnlyClient[CHAIN_ID]) 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() diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go index 524d7d8a6c5..0a319db5f78 100644 --- a/common/client/mock_send_only_node_test.go +++ b/common/client/mock_send_only_node_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package client @@ -18,6 +18,10 @@ type mockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ConfiguredChainID") + } + var r0 CHAIN_ID if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { r0 = rf() @@ -46,6 +54,10 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -60,6 +72,10 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RPC") + } + var r0 RPC if rf, ok := ret.Get(0).(func() RPC); ok { r0 = rf() @@ -74,6 +90,10 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -88,6 +108,10 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for State") + } + var r0 nodeState if rf, ok := ret.Get(0).(func() nodeState); ok { r0 = rf() @@ -102,6 +126,10 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState { func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for String") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() diff --git a/common/headtracker/types/mocks/head.go b/common/headtracker/types/mocks/head.go index 79c483c9978..f86df1d7fce 100644 --- a/common/headtracker/types/mocks/head.go +++ b/common/headtracker/types/mocks/head.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type Head[BLOCK_HASH types.Hashable, CHAIN_ID types.ID] struct { func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockDifficulty() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockDifficulty") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -37,6 +41,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockDifficulty() *big.Int { func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockHash() BLOCK_HASH { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockHash") + } + var r0 BLOCK_HASH if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok { r0 = rf() @@ -51,6 +59,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockHash() BLOCK_HASH { func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockNumber() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockNumber") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -65,6 +77,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockNumber() int64 { func (_m *Head[BLOCK_HASH, CHAIN_ID]) ChainID() CHAIN_ID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + var r0 CHAIN_ID if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { r0 = rf() @@ -79,6 +95,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) ChainID() CHAIN_ID { func (_m *Head[BLOCK_HASH, CHAIN_ID]) ChainLength() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainLength") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -93,6 +113,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) ChainLength() uint32 { func (_m *Head[BLOCK_HASH, CHAIN_ID]) EarliestHeadInChain() types.Head[BLOCK_HASH] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EarliestHeadInChain") + } + var r0 types.Head[BLOCK_HASH] if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { r0 = rf() @@ -109,6 +133,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) EarliestHeadInChain() types.Head[BLOCK_HAS func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetParent() types.Head[BLOCK_HASH] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetParent") + } + var r0 types.Head[BLOCK_HASH] if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { r0 = rf() @@ -125,6 +153,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetParent() types.Head[BLOCK_HASH] { func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetParentHash() BLOCK_HASH { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetParentHash") + } + var r0 BLOCK_HASH if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok { r0 = rf() @@ -139,6 +171,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetParentHash() BLOCK_HASH { func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetTimestamp() time.Time { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetTimestamp") + } + var r0 time.Time if rf, ok := ret.Get(0).(func() time.Time); ok { r0 = rf() @@ -153,6 +189,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetTimestamp() time.Time { func (_m *Head[BLOCK_HASH, CHAIN_ID]) HasChainID() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HasChainID") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -167,6 +207,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) HasChainID() bool { func (_m *Head[BLOCK_HASH, CHAIN_ID]) HashAtHeight(blockNum int64) BLOCK_HASH { ret := _m.Called(blockNum) + if len(ret) == 0 { + panic("no return value specified for HashAtHeight") + } + var r0 BLOCK_HASH if rf, ok := ret.Get(0).(func(int64) BLOCK_HASH); ok { r0 = rf(blockNum) @@ -181,6 +225,10 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) HashAtHeight(blockNum int64) BLOCK_HASH { func (_m *Head[BLOCK_HASH, CHAIN_ID]) IsValid() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsValid") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() diff --git a/common/mocks/head_broadcaster.go b/common/mocks/head_broadcaster.go index 12036f67843..265fceae91e 100644 --- a/common/mocks/head_broadcaster.go +++ b/common/mocks/head_broadcaster.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -23,6 +23,10 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) BroadcastNewLongestChain(_a0 H) { func (_m *HeadBroadcaster[H, BLOCK_HASH]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -37,6 +41,10 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Close() error { func (_m *HeadBroadcaster[H, BLOCK_HASH]) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -53,6 +61,10 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) HealthReport() map[string]error { func (_m *HeadBroadcaster[H, BLOCK_HASH]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -67,6 +79,10 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Name() string { func (_m *HeadBroadcaster[H, BLOCK_HASH]) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -81,6 +97,10 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Ready() error { func (_m *HeadBroadcaster[H, BLOCK_HASH]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -95,6 +115,10 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Start(_a0 context.Context) error { func (_m *HeadBroadcaster[H, BLOCK_HASH]) Subscribe(callback types.HeadTrackable[H, BLOCK_HASH]) (H, func()) { ret := _m.Called(callback) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 H var r1 func() if rf, ok := ret.Get(0).(func(types.HeadTrackable[H, BLOCK_HASH]) (H, func())); ok { diff --git a/common/mocks/head_tracker.go b/common/mocks/head_tracker.go index 2a1f64eeeb7..83ee54b1847 100644 --- a/common/mocks/head_tracker.go +++ b/common/mocks/head_tracker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain H, depth uint) error { ret := _m.Called(ctx, headWithChain, depth) + if len(ret) == 0 { + panic("no return value specified for Backfill") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, H, uint) error); ok { r0 = rf(ctx, headWithChain, depth) @@ -32,6 +36,10 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChai func (_m *HeadTracker[H, BLOCK_HASH]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -46,6 +54,10 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Close() error { func (_m *HeadTracker[H, BLOCK_HASH]) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -62,6 +74,10 @@ func (_m *HeadTracker[H, BLOCK_HASH]) HealthReport() map[string]error { func (_m *HeadTracker[H, BLOCK_HASH]) LatestChain() H { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestChain") + } + var r0 H if rf, ok := ret.Get(0).(func() H); ok { r0 = rf() @@ -76,6 +92,10 @@ func (_m *HeadTracker[H, BLOCK_HASH]) LatestChain() H { func (_m *HeadTracker[H, BLOCK_HASH]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -90,6 +110,10 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Name() string { func (_m *HeadTracker[H, BLOCK_HASH]) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -104,6 +128,10 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Ready() error { func (_m *HeadTracker[H, BLOCK_HASH]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/common/txmgr/mocks/tx_manager.go b/common/txmgr/mocks/tx_manager.go index 27077218f6e..45a3675aced 100644 --- a/common/txmgr/mocks/tx_manager.go +++ b/common/txmgr/mocks/tx_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -27,6 +27,10 @@ type TxManager[CHAIN_ID types.ID, HEAD types.Head[BLOCK_HASH], ADDR types.Hashab func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -41,6 +45,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Close( func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState) (uint32, error) { ret := _m.Called(ctx, state) + if len(ret) == 0 { + panic("no return value specified for CountTransactionsByState") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxState) (uint32, error)); ok { @@ -65,6 +73,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) CountT func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) CreateTransaction(ctx context.Context, txRequest txmgrtypes.TxRequest[ADDR, TX_HASH]) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, txRequest) + if len(ret) == 0 { + panic("no return value specified for CreateTransaction") + } + var r0 txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxRequest[ADDR, TX_HASH]) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -89,6 +101,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Create func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEarliestUnconfirmedBroadcastTime(ctx context.Context) (null.Time, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for FindEarliestUnconfirmedBroadcastTime") + } + var r0 null.Time var r1 error if rf, ok := ret.Get(0).(func(context.Context) (null.Time, error)); ok { @@ -113,6 +129,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEa func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context) (null.Int, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for FindEarliestUnconfirmedTxAttemptBlock") + } + var r0 null.Int var r1 error if rf, ok := ret.Get(0).(func(context.Context) (null.Int, error)); ok { @@ -137,6 +157,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEa func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, metaValue, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesByMetaFieldAndStates") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -163,6 +187,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTx func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, ids, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithAttemptsAndReceiptsByIdsAndState") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -189,6 +217,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTx func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithMetaFieldByReceiptBlockNum") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -215,6 +247,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTx func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithMetaFieldByStates") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -241,6 +277,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTx func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetForwarderForEOA(eoa ADDR) (ADDR, error) { ret := _m.Called(eoa) + if len(ret) == 0 { + panic("no return value specified for GetForwarderForEOA") + } + var r0 ADDR var r1 error if rf, ok := ret.Get(0).(func(ADDR) (ADDR, error)); ok { @@ -265,6 +305,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetFor func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -281,6 +325,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Health func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -300,6 +348,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) OnNewL func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -319,6 +371,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Regist func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Reset(addr ADDR, abandon bool) error { ret := _m.Called(addr, abandon) + if len(ret) == 0 { + panic("no return value specified for Reset") + } + var r0 error if rf, ok := ret.Get(0).(func(ADDR, bool) error); ok { r0 = rf(addr, abandon) @@ -333,6 +389,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Reset( func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) SendNativeToken(ctx context.Context, chainID CHAIN_ID, from ADDR, to ADDR, value big.Int, gasLimit uint32) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, chainID, from, to, value, gasLimit) + if len(ret) == 0 { + panic("no return value specified for SendNativeToken") + } + var r0 txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID, ADDR, ADDR, big.Int, uint32) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -357,6 +417,10 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) SendNa func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/common/txmgr/types/mocks/forwarder_manager.go b/common/txmgr/types/mocks/forwarder_manager.go index abf176550b2..bf61d0c3d27 100644 --- a/common/txmgr/types/mocks/forwarder_manager.go +++ b/common/txmgr/types/mocks/forwarder_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type ForwarderManager[ADDR types.Hashable] struct { func (_m *ForwarderManager[ADDR]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -33,6 +37,10 @@ func (_m *ForwarderManager[ADDR]) Close() error { func (_m *ForwarderManager[ADDR]) ConvertPayload(dest ADDR, origPayload []byte) ([]byte, error) { ret := _m.Called(dest, origPayload) + if len(ret) == 0 { + panic("no return value specified for ConvertPayload") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(ADDR, []byte) ([]byte, error)); ok { @@ -59,6 +67,10 @@ func (_m *ForwarderManager[ADDR]) ConvertPayload(dest ADDR, origPayload []byte) func (_m *ForwarderManager[ADDR]) ForwarderFor(addr ADDR) (ADDR, error) { ret := _m.Called(addr) + if len(ret) == 0 { + panic("no return value specified for ForwarderFor") + } + var r0 ADDR var r1 error if rf, ok := ret.Get(0).(func(ADDR) (ADDR, error)); ok { @@ -83,6 +95,10 @@ func (_m *ForwarderManager[ADDR]) ForwarderFor(addr ADDR) (ADDR, error) { func (_m *ForwarderManager[ADDR]) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -99,6 +115,10 @@ func (_m *ForwarderManager[ADDR]) HealthReport() map[string]error { func (_m *ForwarderManager[ADDR]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -113,6 +133,10 @@ func (_m *ForwarderManager[ADDR]) Name() string { func (_m *ForwarderManager[ADDR]) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -127,6 +151,10 @@ func (_m *ForwarderManager[ADDR]) Ready() error { func (_m *ForwarderManager[ADDR]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/common/txmgr/types/mocks/key_store.go b/common/txmgr/types/mocks/key_store.go index ad5178c09d1..d440528a41d 100644 --- a/common/txmgr/types/mocks/key_store.go +++ b/common/txmgr/types/mocks/key_store.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type KeyStore[ADDR types.Hashable, CHAIN_ID types.ID, SEQ types.Sequence] struct func (_m *KeyStore[ADDR, CHAIN_ID, SEQ]) CheckEnabled(address ADDR, chainID CHAIN_ID) error { ret := _m.Called(address, chainID) + if len(ret) == 0 { + panic("no return value specified for CheckEnabled") + } + var r0 error if rf, ok := ret.Get(0).(func(ADDR, CHAIN_ID) error); ok { r0 = rf(address, chainID) @@ -31,6 +35,10 @@ func (_m *KeyStore[ADDR, CHAIN_ID, SEQ]) CheckEnabled(address ADDR, chainID CHAI func (_m *KeyStore[ADDR, CHAIN_ID, SEQ]) EnabledAddressesForChain(chainId CHAIN_ID) ([]ADDR, error) { ret := _m.Called(chainId) + if len(ret) == 0 { + panic("no return value specified for EnabledAddressesForChain") + } + var r0 []ADDR var r1 error if rf, ok := ret.Get(0).(func(CHAIN_ID) ([]ADDR, error)); ok { @@ -57,6 +65,10 @@ func (_m *KeyStore[ADDR, CHAIN_ID, SEQ]) EnabledAddressesForChain(chainId CHAIN_ func (_m *KeyStore[ADDR, CHAIN_ID, SEQ]) SubscribeToKeyChanges() (chan struct{}, func()) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SubscribeToKeyChanges") + } + var r0 chan struct{} var r1 func() if rf, ok := ret.Get(0).(func() (chan struct{}, func())); ok { diff --git a/common/txmgr/types/mocks/reaper_chain_config.go b/common/txmgr/types/mocks/reaper_chain_config.go index a733b223701..041214b80c6 100644 --- a/common/txmgr/types/mocks/reaper_chain_config.go +++ b/common/txmgr/types/mocks/reaper_chain_config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type ReaperConfig struct { func (_m *ReaperConfig) FinalityDepth() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityDepth") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() diff --git a/common/txmgr/types/mocks/tx_attempt_builder.go b/common/txmgr/types/mocks/tx_attempt_builder.go index 0f3d3e3fba1..b3b6ff761fb 100644 --- a/common/txmgr/types/mocks/tx_attempt_builder.go +++ b/common/txmgr/types/mocks/tx_attempt_builder.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type TxAttemptBuilder[CHAIN_ID types.ID, HEAD types.Head[BLOCK_HASH], ADDR types func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -38,6 +42,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -54,6 +62,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -68,6 +80,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) NewBumpTxAttempt(ctx context.Context, tx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], previousAttempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], priorAttempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], lggr logger.Logger) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], FEE, uint32, bool, error) { ret := _m.Called(ctx, tx, previousAttempt, priorAttempts, lggr) + if len(ret) == 0 { + panic("no return value specified for NewBumpTxAttempt") + } + var r0 txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 FEE var r2 uint32 @@ -113,6 +129,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) NewCustomTxAttempt(tx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], fee FEE, gasLimit uint32, txType int, lggr logger.Logger) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], bool, error) { ret := _m.Called(tx, fee, gasLimit, txType, lggr) + if len(ret) == 0 { + panic("no return value specified for NewCustomTxAttempt") + } + var r0 txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 bool var r2 error @@ -144,6 +164,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) NewEmptyTxAttempt(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(seq, feeLimit, fee, fromAddress) + if len(ret) == 0 { + panic("no return value specified for NewEmptyTxAttempt") + } + var r0 txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(SEQ, uint32, FEE, ADDR) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -175,6 +199,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for NewTxAttempt") + } + var r0 txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 FEE var r2 uint32 @@ -227,6 +255,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for NewTxAttemptWithType") + } + var r0 txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 FEE var r2 uint32 @@ -277,6 +309,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -291,6 +327,10 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index df1528a4c24..16c20df31d7 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -29,6 +29,10 @@ type TxStore[ADDR types.Hashable, CHAIN_ID types.ID, TX_HASH types.Hashable, BLO func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Abandon(ctx context.Context, id CHAIN_ID, addr ADDR) error { ret := _m.Called(ctx, id, addr) + if len(ret) == 0 { + panic("no return value specified for Abandon") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID, ADDR) error); ok { r0 = rf(ctx, id, addr) @@ -43,6 +47,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Abandon(ctx func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CheckTxQueueCapacity(ctx context.Context, fromAddress ADDR, maxQueuedTransactions uint64, chainID CHAIN_ID) error { ret := _m.Called(ctx, fromAddress, maxQueuedTransactions, chainID) + if len(ret) == 0 { + panic("no return value specified for CheckTxQueueCapacity") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, uint64, CHAIN_ID) error); ok { r0 = rf(ctx, fromAddress, maxQueuedTransactions, chainID) @@ -62,6 +70,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() { func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState, chainID CHAIN_ID) (uint32, error) { ret := _m.Called(ctx, state, chainID) + if len(ret) == 0 { + panic("no return value specified for CountTransactionsByState") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxState, CHAIN_ID) (uint32, error)); ok { @@ -86,6 +98,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountTransa func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountUnconfirmedTransactions(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (uint32, error) { ret := _m.Called(ctx, fromAddress, chainID) + if len(ret) == 0 { + panic("no return value specified for CountUnconfirmedTransactions") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) (uint32, error)); ok { @@ -110,6 +126,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountUnconf func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountUnstartedTransactions(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (uint32, error) { ret := _m.Called(ctx, fromAddress, chainID) + if len(ret) == 0 { + panic("no return value specified for CountUnstartedTransactions") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) (uint32, error)); ok { @@ -134,6 +154,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountUnstar func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CreateTransaction(ctx context.Context, txRequest txmgrtypes.TxRequest[ADDR, TX_HASH], chainID CHAIN_ID) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, txRequest, chainID) + if len(ret) == 0 { + panic("no return value specified for CreateTransaction") + } + var r0 txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxRequest[ADDR, TX_HASH], CHAIN_ID) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -158,6 +182,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CreateTrans func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) DeleteInProgressAttempt(ctx context.Context, attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, attempt) + if len(ret) == 0 { + panic("no return value specified for DeleteInProgressAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, attempt) @@ -172,6 +200,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) DeleteInPro func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID CHAIN_ID) (null.Time, error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindEarliestUnconfirmedBroadcastTime") + } + var r0 null.Time var r1 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) (null.Time, error)); ok { @@ -196,6 +228,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarlies func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID CHAIN_ID) (null.Int, error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindEarliestUnconfirmedTxAttemptBlock") + } + var r0 null.Int var r1 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) (null.Int, error)); ok { @@ -220,6 +256,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarlies func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindLatestSequence(ctx context.Context, fromAddress ADDR, chainId CHAIN_ID) (SEQ, error) { ret := _m.Called(ctx, fromAddress, chainId) + if len(ret) == 0 { + panic("no return value specified for FindLatestSequence") + } + var r0 SEQ var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) (SEQ, error)); ok { @@ -244,6 +284,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindLatestS func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindNextUnstartedTransactionFromAddress(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], fromAddress ADDR, chainID CHAIN_ID) error { ret := _m.Called(ctx, etx, fromAddress, chainID) + if len(ret) == 0 { + panic("no return value specified for FindNextUnstartedTransactionFromAddress") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], ADDR, CHAIN_ID) error); ok { r0 = rf(ctx, etx, fromAddress, chainID) @@ -258,6 +302,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindNextUns func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber int64, lowBlockNumber int64, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, highBlockNumber, lowBlockNumber, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTransactionsConfirmedInBlockRange") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -284,6 +332,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTransac func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxAttemptsConfirmedMissingReceipt(ctx context.Context, chainID CHAIN_ID) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptsConfirmedMissingReceipt") + } + var r0 []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -310,6 +362,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxAttem func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxAttemptsRequiringReceiptFetch(ctx context.Context, chainID CHAIN_ID) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptsRequiringReceiptFetch") + } + var r0 []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -336,6 +392,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxAttem func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxAttemptsRequiringResend(ctx context.Context, olderThan time.Time, maxInFlightTransactions uint32, chainID CHAIN_ID, address ADDR) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, olderThan, maxInFlightTransactions, chainID, address) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptsRequiringResend") + } + var r0 []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, time.Time, uint32, CHAIN_ID, ADDR) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -362,6 +422,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxAttem func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxWithIdempotencyKey(ctx context.Context, idempotencyKey string, chainID CHAIN_ID) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, idempotencyKey, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxWithIdempotencyKey") + } + var r0 *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, CHAIN_ID) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -388,6 +452,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxWithI func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxWithSequence(ctx context.Context, fromAddress ADDR, seq SEQ) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, fromAddress, seq) + if len(ret) == 0 { + panic("no return value specified for FindTxWithSequence") + } + var r0 *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, SEQ) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -414,6 +482,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxWithS func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, metaValue, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesByMetaFieldAndStates") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -440,6 +512,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesByM func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID CHAIN_ID) ([]txmgrtypes.ReceiptPlus[R], error) { ret := _m.Called(ctx, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesPendingCallback") + } + var r0 []txmgrtypes.ReceiptPlus[R] var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, CHAIN_ID) ([]txmgrtypes.ReceiptPlus[R], error)); ok { @@ -466,6 +542,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesPen func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, ids, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithAttemptsAndReceiptsByIdsAndState") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -492,6 +572,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWit func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithMetaFieldByReceiptBlockNum") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -518,6 +602,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWit func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithMetaFieldByStates") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -544,6 +632,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWit func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxsRequiringGasBump(ctx context.Context, address ADDR, blockNum int64, gasBumpThreshold int64, depth int64, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, address, blockNum, gasBumpThreshold, depth, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxsRequiringGasBump") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, int64, int64, int64, CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -570,6 +662,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxsRequ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxsRequiringResubmissionDueToInsufficientFunds(ctx context.Context, address ADDR, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, address, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxsRequiringResubmissionDueToInsufficientFunds") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -596,6 +692,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxsRequ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetInProgressTxAttempts(ctx context.Context, address ADDR, chainID CHAIN_ID) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, address, chainID) + if len(ret) == 0 { + panic("no return value specified for GetInProgressTxAttempts") + } + var r0 []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) ([]txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -622,6 +722,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetInProgre func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetNonFatalTransactions(ctx context.Context, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for GetNonFatalTransactions") + } + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -648,6 +752,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetNonFatal func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTxByID(ctx context.Context, id int64) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetTxByID") + } + var r0 *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -674,6 +782,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTxByID(c func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTxInProgress(ctx context.Context, fromAddress ADDR) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, fromAddress) + if len(ret) == 0 { + panic("no return value specified for GetTxInProgress") + } + var r0 *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { @@ -700,6 +812,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTxInProg func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HasInProgressTransaction(ctx context.Context, account ADDR, chainID CHAIN_ID) (bool, error) { ret := _m.Called(ctx, account, chainID) + if len(ret) == 0 { + panic("no return value specified for HasInProgressTransaction") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) (bool, error)); ok { @@ -724,6 +840,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HasInProgre func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID CHAIN_ID) (bool, error) { ret := _m.Called(ctx, blockHeight, txID, chainID) + if len(ret) == 0 { + panic("no return value specified for IsTxFinalized") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) (bool, error)); ok { @@ -748,6 +868,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) IsTxFinaliz func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) LoadTxAttempts(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx) + if len(ret) == 0 { + panic("no return value specified for LoadTxAttempts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, etx) @@ -762,6 +886,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) LoadTxAttem func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkAllConfirmedMissingReceipt(ctx context.Context, chainID CHAIN_ID) error { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for MarkAllConfirmedMissingReceipt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) error); ok { r0 = rf(ctx, chainID) @@ -776,6 +904,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkAllConf func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, finalityDepth uint32, chainID CHAIN_ID) error { ret := _m.Called(ctx, blockNum, finalityDepth, chainID) + if len(ret) == 0 { + panic("no return value specified for MarkOldTxesMissingReceiptAsErrored") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, uint32, CHAIN_ID) error); ok { r0 = rf(ctx, blockNum, finalityDepth, chainID) @@ -790,6 +922,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkOldTxes func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) PreloadTxes(ctx context.Context, attempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, attempts) + if len(ret) == 0 { + panic("no return value specified for PreloadTxes") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, attempts) @@ -804,6 +940,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) PreloadTxes func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) PruneUnstartedTxQueue(ctx context.Context, queueSize uint32, subject uuid.UUID) (int64, error) { ret := _m.Called(ctx, queueSize, subject) + if len(ret) == 0 { + panic("no return value specified for PruneUnstartedTxQueue") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, uint32, uuid.UUID) (int64, error)); ok { @@ -828,6 +968,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) PruneUnstar func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ReapTxHistory(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID CHAIN_ID) error { ret := _m.Called(ctx, minBlockNumberToKeep, timeThreshold, chainID) + if len(ret) == 0 { + panic("no return value specified for ReapTxHistory") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, time.Time, CHAIN_ID) error); ok { r0 = rf(ctx, minBlockNumberToKeep, timeThreshold, chainID) @@ -842,6 +986,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ReapTxHisto func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveConfirmedMissingReceiptAttempt(ctx context.Context, timeout time.Duration, attempt *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], broadcastAt time.Time) error { ret := _m.Called(ctx, timeout, attempt, broadcastAt) + if len(ret) == 0 { + panic("no return value specified for SaveConfirmedMissingReceiptAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration, *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], time.Time) error); ok { r0 = rf(ctx, timeout, attempt, broadcastAt) @@ -856,6 +1004,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveConfirm func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveFetchedReceipts(ctx context.Context, receipts []R, chainID CHAIN_ID) error { ret := _m.Called(ctx, receipts, chainID) + if len(ret) == 0 { + panic("no return value specified for SaveFetchedReceipts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []R, CHAIN_ID) error); ok { r0 = rf(ctx, receipts, chainID) @@ -870,6 +1022,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveFetched func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveInProgressAttempt(ctx context.Context, attempt *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, attempt) + if len(ret) == 0 { + panic("no return value specified for SaveInProgressAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, attempt) @@ -884,6 +1040,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveInProgr func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveInsufficientFundsAttempt(ctx context.Context, timeout time.Duration, attempt *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], broadcastAt time.Time) error { ret := _m.Called(ctx, timeout, attempt, broadcastAt) + if len(ret) == 0 { + panic("no return value specified for SaveInsufficientFundsAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration, *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], time.Time) error); ok { r0 = rf(ctx, timeout, attempt, broadcastAt) @@ -898,6 +1058,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveInsuffi func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveReplacementInProgressAttempt(ctx context.Context, oldAttempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], replacementAttempt *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, oldAttempt, replacementAttempt) + if len(ret) == 0 { + panic("no return value specified for SaveReplacementInProgressAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, oldAttempt, replacementAttempt) @@ -912,6 +1076,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveReplace func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveSentAttempt(ctx context.Context, timeout time.Duration, attempt *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], broadcastAt time.Time) error { ret := _m.Called(ctx, timeout, attempt, broadcastAt) + if len(ret) == 0 { + panic("no return value specified for SaveSentAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration, *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], time.Time) error); ok { r0 = rf(ctx, timeout, attempt, broadcastAt) @@ -926,6 +1094,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveSentAtt func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SetBroadcastBeforeBlockNum(ctx context.Context, blockNum int64, chainID CHAIN_ID) error { ret := _m.Called(ctx, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for SetBroadcastBeforeBlockNum") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, CHAIN_ID) error); ok { r0 = rf(ctx, blockNum, chainID) @@ -940,6 +1112,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SetBroadcas func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateBroadcastAts(ctx context.Context, now time.Time, etxIDs []int64) error { ret := _m.Called(ctx, now, etxIDs) + if len(ret) == 0 { + panic("no return value specified for UpdateBroadcastAts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Time, []int64) error); ok { r0 = rf(ctx, now, etxIDs) @@ -954,6 +1130,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateBroad func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxAttemptInProgressToBroadcast(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], NewAttemptState txmgrtypes.TxAttemptState) error { ret := _m.Called(ctx, etx, attempt, NewAttemptState) + if len(ret) == 0 { + panic("no return value specified for UpdateTxAttemptInProgressToBroadcast") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], txmgrtypes.TxAttemptState) error); ok { r0 = rf(ctx, etx, attempt, NewAttemptState) @@ -968,6 +1148,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxAtt func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId CHAIN_ID) error { ret := _m.Called(ctx, pipelineTaskRunRid, chainId) + if len(ret) == 0 { + panic("no return value specified for UpdateTxCallbackCompleted") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, CHAIN_ID) error); ok { r0 = rf(ctx, pipelineTaskRunRid, chainId) @@ -982,6 +1166,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxCal func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxFatalError(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx) + if len(ret) == 0 { + panic("no return value specified for UpdateTxFatalError") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, etx) @@ -996,6 +1184,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxFat func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxForRebroadcast(ctx context.Context, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], etxAttempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx, etxAttempt) + if len(ret) == 0 { + panic("no return value specified for UpdateTxForRebroadcast") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, etx, etxAttempt) @@ -1010,6 +1202,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxFor func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxUnstartedToInProgress(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx, attempt) + if len(ret) == 0 { + panic("no return value specified for UpdateTxUnstartedToInProgress") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], *txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error); ok { r0 = rf(ctx, etx, attempt) @@ -1024,6 +1220,10 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxUns func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxsUnconfirmed(ctx context.Context, ids []int64) error { ret := _m.Called(ctx, ids) + if len(ret) == 0 { + panic("no return value specified for UpdateTxsUnconfirmed") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []int64) error); ok { r0 = rf(ctx, ids) diff --git a/common/txmgr/types/mocks/tx_strategy.go b/common/txmgr/types/mocks/tx_strategy.go index f4ec9bef49a..7992c3fe05f 100644 --- a/common/txmgr/types/mocks/tx_strategy.go +++ b/common/txmgr/types/mocks/tx_strategy.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type TxStrategy struct { func (_m *TxStrategy) PruneQueue(ctx context.Context, pruneService types.UnstartedTxQueuePruner) (int64, error) { ret := _m.Called(ctx, pruneService) + if len(ret) == 0 { + panic("no return value specified for PruneQueue") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, types.UnstartedTxQueuePruner) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *TxStrategy) PruneQueue(ctx context.Context, pruneService types.Unstart func (_m *TxStrategy) Subject() uuid.NullUUID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Subject") + } + var r0 uuid.NullUUID if rf, ok := ret.Get(0).(func() uuid.NullUUID); ok { r0 = rf() diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go index 99d2a265b44..29b6d073656 100644 --- a/common/types/mocks/head.go +++ b/common/types/mocks/head.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type Head[BLOCK_HASH types.Hashable] struct { func (_m *Head[BLOCK_HASH]) BlockDifficulty() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockDifficulty") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -36,6 +40,10 @@ func (_m *Head[BLOCK_HASH]) BlockDifficulty() *big.Int { func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockHash") + } + var r0 BLOCK_HASH if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok { r0 = rf() @@ -50,6 +58,10 @@ func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH { func (_m *Head[BLOCK_HASH]) BlockNumber() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockNumber") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -64,6 +76,10 @@ func (_m *Head[BLOCK_HASH]) BlockNumber() int64 { func (_m *Head[BLOCK_HASH]) ChainLength() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainLength") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -78,6 +94,10 @@ func (_m *Head[BLOCK_HASH]) ChainLength() uint32 { func (_m *Head[BLOCK_HASH]) EarliestHeadInChain() types.Head[BLOCK_HASH] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EarliestHeadInChain") + } + var r0 types.Head[BLOCK_HASH] if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { r0 = rf() @@ -94,6 +114,10 @@ func (_m *Head[BLOCK_HASH]) EarliestHeadInChain() types.Head[BLOCK_HASH] { func (_m *Head[BLOCK_HASH]) GetParent() types.Head[BLOCK_HASH] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetParent") + } + var r0 types.Head[BLOCK_HASH] if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { r0 = rf() @@ -110,6 +134,10 @@ func (_m *Head[BLOCK_HASH]) GetParent() types.Head[BLOCK_HASH] { func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetParentHash") + } + var r0 BLOCK_HASH if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok { r0 = rf() @@ -124,6 +152,10 @@ func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH { func (_m *Head[BLOCK_HASH]) GetTimestamp() time.Time { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetTimestamp") + } + var r0 time.Time if rf, ok := ret.Get(0).(func() time.Time); ok { r0 = rf() @@ -138,6 +170,10 @@ func (_m *Head[BLOCK_HASH]) GetTimestamp() time.Time { func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH { ret := _m.Called(blockNum) + if len(ret) == 0 { + panic("no return value specified for HashAtHeight") + } + var r0 BLOCK_HASH if rf, ok := ret.Get(0).(func(int64) BLOCK_HASH); ok { r0 = rf(blockNum) diff --git a/common/types/mocks/head_trackable.go b/common/types/mocks/head_trackable.go index e63e9ca2497..55f0ebd288e 100644 --- a/common/types/mocks/head_trackable.go +++ b/common/types/mocks/head_trackable.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/common/types/mocks/subscription.go b/common/types/mocks/subscription.go index 5577ee4a62a..32db6dfa769 100644 --- a/common/types/mocks/subscription.go +++ b/common/types/mocks/subscription.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type Subscription struct { func (_m *Subscription) Err() <-chan error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Err") + } + var r0 <-chan error if rf, ok := ret.Get(0).(func() <-chan error); ok { r0 = rf() diff --git a/core/bridges/mocks/orm.go b/core/bridges/mocks/orm.go index ba8c7526d1c..2c92a7e8024 100644 --- a/core/bridges/mocks/orm.go +++ b/core/bridges/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type ORM struct { func (_m *ORM) BridgeTypes(offset int, limit int) ([]bridges.BridgeType, int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for BridgeTypes") + } + var r0 []bridges.BridgeType var r1 int var r2 error @@ -53,6 +57,10 @@ func (_m *ORM) BridgeTypes(offset int, limit int) ([]bridges.BridgeType, int, er func (_m *ORM) CreateBridgeType(bt *bridges.BridgeType) error { ret := _m.Called(bt) + if len(ret) == 0 { + panic("no return value specified for CreateBridgeType") + } + var r0 error if rf, ok := ret.Get(0).(func(*bridges.BridgeType) error); ok { r0 = rf(bt) @@ -67,6 +75,10 @@ func (_m *ORM) CreateBridgeType(bt *bridges.BridgeType) error { func (_m *ORM) CreateExternalInitiator(externalInitiator *bridges.ExternalInitiator) error { ret := _m.Called(externalInitiator) + if len(ret) == 0 { + panic("no return value specified for CreateExternalInitiator") + } + var r0 error if rf, ok := ret.Get(0).(func(*bridges.ExternalInitiator) error); ok { r0 = rf(externalInitiator) @@ -81,6 +93,10 @@ func (_m *ORM) CreateExternalInitiator(externalInitiator *bridges.ExternalInitia func (_m *ORM) DeleteBridgeType(bt *bridges.BridgeType) error { ret := _m.Called(bt) + if len(ret) == 0 { + panic("no return value specified for DeleteBridgeType") + } + var r0 error if rf, ok := ret.Get(0).(func(*bridges.BridgeType) error); ok { r0 = rf(bt) @@ -95,6 +111,10 @@ func (_m *ORM) DeleteBridgeType(bt *bridges.BridgeType) error { func (_m *ORM) DeleteExternalInitiator(name string) error { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for DeleteExternalInitiator") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(name) @@ -109,6 +129,10 @@ func (_m *ORM) DeleteExternalInitiator(name string) error { func (_m *ORM) ExternalInitiators(offset int, limit int) ([]bridges.ExternalInitiator, int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for ExternalInitiators") + } + var r0 []bridges.ExternalInitiator var r1 int var r2 error @@ -142,6 +166,10 @@ func (_m *ORM) ExternalInitiators(offset int, limit int) ([]bridges.ExternalInit func (_m *ORM) FindBridge(name bridges.BridgeName) (bridges.BridgeType, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for FindBridge") + } + var r0 bridges.BridgeType var r1 error if rf, ok := ret.Get(0).(func(bridges.BridgeName) (bridges.BridgeType, error)); ok { @@ -166,6 +194,10 @@ func (_m *ORM) FindBridge(name bridges.BridgeName) (bridges.BridgeType, error) { func (_m *ORM) FindBridges(name []bridges.BridgeName) ([]bridges.BridgeType, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for FindBridges") + } + var r0 []bridges.BridgeType var r1 error if rf, ok := ret.Get(0).(func([]bridges.BridgeName) ([]bridges.BridgeType, error)); ok { @@ -192,6 +224,10 @@ func (_m *ORM) FindBridges(name []bridges.BridgeName) ([]bridges.BridgeType, err func (_m *ORM) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiator, error) { ret := _m.Called(eia) + if len(ret) == 0 { + panic("no return value specified for FindExternalInitiator") + } + var r0 *bridges.ExternalInitiator var r1 error if rf, ok := ret.Get(0).(func(*auth.Token) (*bridges.ExternalInitiator, error)); ok { @@ -218,6 +254,10 @@ func (_m *ORM) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiato func (_m *ORM) FindExternalInitiatorByName(iname string) (bridges.ExternalInitiator, error) { ret := _m.Called(iname) + if len(ret) == 0 { + panic("no return value specified for FindExternalInitiatorByName") + } + var r0 bridges.ExternalInitiator var r1 error if rf, ok := ret.Get(0).(func(string) (bridges.ExternalInitiator, error)); ok { @@ -242,6 +282,10 @@ func (_m *ORM) FindExternalInitiatorByName(iname string) (bridges.ExternalInitia func (_m *ORM) GetCachedResponse(dotId string, specId int32, maxElapsed time.Duration) ([]byte, error) { ret := _m.Called(dotId, specId, maxElapsed) + if len(ret) == 0 { + panic("no return value specified for GetCachedResponse") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, int32, time.Duration) ([]byte, error)); ok { @@ -268,6 +312,10 @@ func (_m *ORM) GetCachedResponse(dotId string, specId int32, maxElapsed time.Dur func (_m *ORM) UpdateBridgeType(bt *bridges.BridgeType, btr *bridges.BridgeTypeRequest) error { ret := _m.Called(bt, btr) + if len(ret) == 0 { + panic("no return value specified for UpdateBridgeType") + } + var r0 error if rf, ok := ret.Get(0).(func(*bridges.BridgeType, *bridges.BridgeTypeRequest) error); ok { r0 = rf(bt, btr) @@ -282,6 +330,10 @@ func (_m *ORM) UpdateBridgeType(bt *bridges.BridgeType, btr *bridges.BridgeTypeR func (_m *ORM) UpsertBridgeResponse(dotId string, specId int32, response []byte) error { ret := _m.Called(dotId, specId, response) + if len(ret) == 0 { + panic("no return value specified for UpsertBridgeResponse") + } + var r0 error if rf, ok := ret.Get(0).(func(string, int32, []byte) error); ok { r0 = rf(dotId, specId, response) diff --git a/core/chains/evm/client/mocks/batch_sender.go b/core/chains/evm/client/mocks/batch_sender.go index 7f1a5bdee69..3d65749b5bc 100644 --- a/core/chains/evm/client/mocks/batch_sender.go +++ b/core/chains/evm/client/mocks/batch_sender.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type BatchSender struct { func (_m *BatchSender) 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) diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index 22498370a2a..0b45894cf28 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -33,6 +33,10 @@ type Client struct { func (_m *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { ret := _m.Called(ctx, account, 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 { @@ -59,6 +63,10 @@ func (_m *Client) BalanceAt(ctx context.Context, account common.Address, blockNu func (_m *Client) 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) @@ -73,6 +81,10 @@ func (_m *Client) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error func (_m *Client) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error { ret := _m.Called(ctx, b) + if len(ret) == 0 { + panic("no return value specified for BatchCallContextAll") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []rpc.BatchElem) error); ok { r0 = rf(ctx, b) @@ -87,6 +99,10 @@ func (_m *Client) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) er func (_m *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { ret := _m.Called(ctx, hash) + if len(ret) == 0 { + panic("no return value specified for BlockByHash") + } + var r0 *types.Block var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Block, error)); ok { @@ -113,6 +129,10 @@ func (_m *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo func (_m *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { ret := _m.Called(ctx, number) + if len(ret) == 0 { + panic("no return value specified for BlockByNumber") + } + var r0 *types.Block var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Block, error)); ok { @@ -142,6 +162,10 @@ func (_m *Client) CallContext(ctx context.Context, result interface{}, method st _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...) @@ -156,6 +180,10 @@ func (_m *Client) CallContext(ctx context.Context, result interface{}, method st func (_m *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, 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, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { @@ -182,6 +210,10 @@ func (_m *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN 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 { @@ -213,6 +245,10 @@ func (_m *Client) Close() { func (_m *Client) 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 { @@ -239,6 +275,10 @@ func (_m *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe func (_m *Client) ConfiguredChainID() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ConfiguredChainID") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -255,6 +295,10 @@ func (_m *Client) ConfiguredChainID() *big.Int { func (_m *Client) 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) @@ -269,6 +313,10 @@ func (_m *Client) Dial(ctx context.Context) error { func (_m *Client) EstimateGas(ctx context.Context, call ethereum.CallMsg) (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, ethereum.CallMsg) (uint64, error)); ok { @@ -293,6 +341,10 @@ func (_m *Client) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint6 func (_m *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { ret := _m.Called(ctx, q) + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + var r0 []types.Log var r1 error if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]types.Log, error)); ok { @@ -319,6 +371,10 @@ func (_m *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]typ func (_m *Client) HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head, error) { ret := _m.Called(ctx, n) + if len(ret) == 0 { + panic("no return value specified for HeadByHash") + } + var r0 *evmtypes.Head var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*evmtypes.Head, error)); ok { @@ -345,6 +401,10 @@ func (_m *Client) HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head func (_m *Client) HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) { ret := _m.Called(ctx, n) + if len(ret) == 0 { + panic("no return value specified for HeadByNumber") + } + var r0 *evmtypes.Head var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*evmtypes.Head, error)); ok { @@ -371,6 +431,10 @@ func (_m *Client) HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, func (_m *Client) HeaderByHash(ctx context.Context, h common.Hash) (*types.Header, error) { ret := _m.Called(ctx, h) + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Header, error)); ok { @@ -397,6 +461,10 @@ func (_m *Client) HeaderByHash(ctx context.Context, h common.Hash) (*types.Heade func (_m *Client) HeaderByNumber(ctx context.Context, n *big.Int) (*types.Header, error) { ret := _m.Called(ctx, n) + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { @@ -423,6 +491,10 @@ func (_m *Client) HeaderByNumber(ctx context.Context, n *big.Int) (*types.Header func (_m *Client) IsL2() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsL2") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -437,6 +509,10 @@ func (_m *Client) IsL2() bool { func (_m *Client) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*assets.Link, error) { ret := _m.Called(ctx, address, 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 { @@ -463,6 +539,10 @@ func (_m *Client) LINKBalance(ctx context.Context, address common.Address, linkA func (_m *Client) LatestBlockHeight(ctx context.Context) (*big.Int, error) { ret := _m.Called(ctx) + 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 { @@ -489,6 +569,10 @@ func (_m *Client) LatestBlockHeight(ctx context.Context) (*big.Int, error) { func (_m *Client) NodeStates() map[string]string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for NodeStates") + } + var r0 map[string]string if rf, ok := ret.Get(0).(func() map[string]string); ok { r0 = rf() @@ -505,6 +589,10 @@ func (_m *Client) NodeStates() map[string]string { func (_m *Client) 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 { @@ -531,6 +619,10 @@ func (_m *Client) PendingCodeAt(ctx context.Context, account common.Address) ([] func (_m *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { ret := _m.Called(ctx, account) + if len(ret) == 0 { + panic("no return value specified for PendingNonceAt") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address) (uint64, error)); ok { @@ -555,6 +647,10 @@ func (_m *Client) PendingNonceAt(ctx context.Context, account common.Address) (u func (_m *Client) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { r0 = rf(ctx, tx) @@ -569,6 +665,10 @@ func (_m *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { ret := _m.Called(ctx, tx, fromAddress) + if len(ret) == 0 { + panic("no return value specified for SendTransactionReturnCode") + } + var r0 commonclient.SendTxReturnCode var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (commonclient.SendTxReturnCode, error)); ok { @@ -593,6 +693,10 @@ func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Trans func (_m *Client) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (evmtypes.Nonce, error) { ret := _m.Called(ctx, account, blockNumber) + if len(ret) == 0 { + panic("no return value specified for SequenceAt") + } + var r0 evmtypes.Nonce var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (evmtypes.Nonce, error)); ok { @@ -617,6 +721,10 @@ func (_m *Client) SequenceAt(ctx context.Context, account common.Address, blockN func (_m *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.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<- types.Log) (ethereum.Subscription, error)); ok { @@ -643,6 +751,10 @@ func (_m *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuer func (_m *Client) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { ret := _m.Called(ctx, ch) + if len(ret) == 0 { + panic("no return value specified for SubscribeNewHead") + } + var r0 ethereum.Subscription var r1 error if rf, ok := ret.Get(0).(func(context.Context, chan<- *evmtypes.Head) (ethereum.Subscription, error)); ok { @@ -669,6 +781,10 @@ func (_m *Client) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head func (_m *Client) 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 { @@ -695,6 +811,10 @@ func (_m *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { func (_m *Client) 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 { @@ -721,6 +841,10 @@ func (_m *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (_m *Client) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { ret := _m.Called(ctx, address, contractAddress) + 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 { @@ -747,6 +871,10 @@ func (_m *Client) TokenBalance(ctx context.Context, address common.Address, cont func (_m *Client) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) { ret := _m.Called(ctx, txHash) + if len(ret) == 0 { + panic("no return value specified for TransactionByHash") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Transaction, error)); ok { @@ -773,6 +901,10 @@ func (_m *Client) TransactionByHash(ctx context.Context, txHash common.Hash) (*t func (_m *Client) 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 { diff --git a/core/chains/evm/client/mocks/tx_sender.go b/core/chains/evm/client/mocks/tx_sender.go index 889069dcfca..a769a786a18 100644 --- a/core/chains/evm/client/mocks/tx_sender.go +++ b/core/chains/evm/client/mocks/tx_sender.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type TxSender struct { func (_m *TxSender) ChainID(_a0 context.Context) (*big.Int, error) { ret := _m.Called(_a0) + 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 { @@ -47,6 +51,10 @@ func (_m *TxSender) ChainID(_a0 context.Context) (*big.Int, error) { func (_m *TxSender) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { r0 = rf(ctx, tx) diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index cb18282f495..badba1d69f3 100644 --- a/core/chains/evm/config/mocks/chain_scoped_config.go +++ b/core/chains/evm/config/mocks/chain_scoped_config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type ChainScopedConfig struct { func (_m *ChainScopedConfig) AppID() uuid.UUID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AppID") + } + var r0 uuid.UUID if rf, ok := ret.Get(0).(func() uuid.UUID); ok { r0 = rf() @@ -40,6 +44,10 @@ func (_m *ChainScopedConfig) AppID() uuid.UUID { func (_m *ChainScopedConfig) AuditLogger() coreconfig.AuditLogger { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AuditLogger") + } + var r0 coreconfig.AuditLogger if rf, ok := ret.Get(0).(func() coreconfig.AuditLogger); ok { r0 = rf() @@ -56,6 +64,10 @@ func (_m *ChainScopedConfig) AuditLogger() coreconfig.AuditLogger { func (_m *ChainScopedConfig) AutoPprof() coreconfig.AutoPprof { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AutoPprof") + } + var r0 coreconfig.AutoPprof if rf, ok := ret.Get(0).(func() coreconfig.AutoPprof); ok { r0 = rf() @@ -72,6 +84,10 @@ func (_m *ChainScopedConfig) AutoPprof() coreconfig.AutoPprof { func (_m *ChainScopedConfig) CosmosEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CosmosEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -86,6 +102,10 @@ func (_m *ChainScopedConfig) CosmosEnabled() bool { func (_m *ChainScopedConfig) Database() coreconfig.Database { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Database") + } + var r0 coreconfig.Database if rf, ok := ret.Get(0).(func() coreconfig.Database); ok { r0 = rf() @@ -102,6 +122,10 @@ func (_m *ChainScopedConfig) Database() coreconfig.Database { func (_m *ChainScopedConfig) EVM() config.EVM { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVM") + } + var r0 config.EVM if rf, ok := ret.Get(0).(func() config.EVM); ok { r0 = rf() @@ -118,6 +142,10 @@ func (_m *ChainScopedConfig) EVM() config.EVM { func (_m *ChainScopedConfig) EVMEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -132,6 +160,10 @@ func (_m *ChainScopedConfig) EVMEnabled() bool { func (_m *ChainScopedConfig) EVMRPCEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMRPCEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -146,6 +178,10 @@ func (_m *ChainScopedConfig) EVMRPCEnabled() bool { func (_m *ChainScopedConfig) Feature() coreconfig.Feature { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Feature") + } + var r0 coreconfig.Feature if rf, ok := ret.Get(0).(func() coreconfig.Feature); ok { r0 = rf() @@ -162,6 +198,10 @@ func (_m *ChainScopedConfig) Feature() coreconfig.Feature { func (_m *ChainScopedConfig) FluxMonitor() coreconfig.FluxMonitor { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FluxMonitor") + } + var r0 coreconfig.FluxMonitor if rf, ok := ret.Get(0).(func() coreconfig.FluxMonitor); ok { r0 = rf() @@ -178,6 +218,10 @@ func (_m *ChainScopedConfig) FluxMonitor() coreconfig.FluxMonitor { func (_m *ChainScopedConfig) Insecure() coreconfig.Insecure { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Insecure") + } + var r0 coreconfig.Insecure if rf, ok := ret.Get(0).(func() coreconfig.Insecure); ok { r0 = rf() @@ -194,6 +238,10 @@ func (_m *ChainScopedConfig) Insecure() coreconfig.Insecure { func (_m *ChainScopedConfig) InsecureFastScrypt() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for InsecureFastScrypt") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -208,6 +256,10 @@ func (_m *ChainScopedConfig) InsecureFastScrypt() bool { func (_m *ChainScopedConfig) JobPipeline() coreconfig.JobPipeline { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for JobPipeline") + } + var r0 coreconfig.JobPipeline if rf, ok := ret.Get(0).(func() coreconfig.JobPipeline); ok { r0 = rf() @@ -224,6 +276,10 @@ func (_m *ChainScopedConfig) JobPipeline() coreconfig.JobPipeline { func (_m *ChainScopedConfig) Keeper() coreconfig.Keeper { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Keeper") + } + var r0 coreconfig.Keeper if rf, ok := ret.Get(0).(func() coreconfig.Keeper); ok { r0 = rf() @@ -240,6 +296,10 @@ func (_m *ChainScopedConfig) Keeper() coreconfig.Keeper { func (_m *ChainScopedConfig) Log() coreconfig.Log { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Log") + } + var r0 coreconfig.Log if rf, ok := ret.Get(0).(func() coreconfig.Log); ok { r0 = rf() @@ -261,6 +321,10 @@ func (_m *ChainScopedConfig) LogConfiguration(log coreconfig.LogfFn, warn coreco func (_m *ChainScopedConfig) Mercury() coreconfig.Mercury { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Mercury") + } + var r0 coreconfig.Mercury if rf, ok := ret.Get(0).(func() coreconfig.Mercury); ok { r0 = rf() @@ -277,6 +341,10 @@ func (_m *ChainScopedConfig) Mercury() coreconfig.Mercury { func (_m *ChainScopedConfig) OCR() coreconfig.OCR { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for OCR") + } + var r0 coreconfig.OCR if rf, ok := ret.Get(0).(func() coreconfig.OCR); ok { r0 = rf() @@ -293,6 +361,10 @@ func (_m *ChainScopedConfig) OCR() coreconfig.OCR { func (_m *ChainScopedConfig) OCR2() coreconfig.OCR2 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for OCR2") + } + var r0 coreconfig.OCR2 if rf, ok := ret.Get(0).(func() coreconfig.OCR2); ok { r0 = rf() @@ -309,6 +381,10 @@ func (_m *ChainScopedConfig) OCR2() coreconfig.OCR2 { func (_m *ChainScopedConfig) P2P() coreconfig.P2P { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for P2P") + } + var r0 coreconfig.P2P if rf, ok := ret.Get(0).(func() coreconfig.P2P); ok { r0 = rf() @@ -325,6 +401,10 @@ func (_m *ChainScopedConfig) P2P() coreconfig.P2P { func (_m *ChainScopedConfig) Password() coreconfig.Password { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Password") + } + var r0 coreconfig.Password if rf, ok := ret.Get(0).(func() coreconfig.Password); ok { r0 = rf() @@ -341,6 +421,10 @@ func (_m *ChainScopedConfig) Password() coreconfig.Password { func (_m *ChainScopedConfig) Prometheus() coreconfig.Prometheus { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Prometheus") + } + var r0 coreconfig.Prometheus if rf, ok := ret.Get(0).(func() coreconfig.Prometheus); ok { r0 = rf() @@ -357,6 +441,10 @@ func (_m *ChainScopedConfig) Prometheus() coreconfig.Prometheus { func (_m *ChainScopedConfig) Pyroscope() coreconfig.Pyroscope { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Pyroscope") + } + var r0 coreconfig.Pyroscope if rf, ok := ret.Get(0).(func() coreconfig.Pyroscope); ok { r0 = rf() @@ -373,6 +461,10 @@ func (_m *ChainScopedConfig) Pyroscope() coreconfig.Pyroscope { func (_m *ChainScopedConfig) RootDir() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RootDir") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -387,6 +479,10 @@ func (_m *ChainScopedConfig) RootDir() string { func (_m *ChainScopedConfig) Sentry() coreconfig.Sentry { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Sentry") + } + var r0 coreconfig.Sentry if rf, ok := ret.Get(0).(func() coreconfig.Sentry); ok { r0 = rf() @@ -403,6 +499,10 @@ func (_m *ChainScopedConfig) Sentry() coreconfig.Sentry { func (_m *ChainScopedConfig) SetLogLevel(lvl zapcore.Level) error { ret := _m.Called(lvl) + if len(ret) == 0 { + panic("no return value specified for SetLogLevel") + } + var r0 error if rf, ok := ret.Get(0).(func(zapcore.Level) error); ok { r0 = rf(lvl) @@ -427,6 +527,10 @@ func (_m *ChainScopedConfig) SetPasswords(keystore *string, vrf *string) { func (_m *ChainScopedConfig) ShutdownGracePeriod() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ShutdownGracePeriod") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -441,6 +545,10 @@ func (_m *ChainScopedConfig) ShutdownGracePeriod() time.Duration { func (_m *ChainScopedConfig) SolanaEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SolanaEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -455,6 +563,10 @@ func (_m *ChainScopedConfig) SolanaEnabled() bool { func (_m *ChainScopedConfig) StarkNetEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StarkNetEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -469,6 +581,10 @@ func (_m *ChainScopedConfig) StarkNetEnabled() bool { func (_m *ChainScopedConfig) TelemetryIngress() coreconfig.TelemetryIngress { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TelemetryIngress") + } + var r0 coreconfig.TelemetryIngress if rf, ok := ret.Get(0).(func() coreconfig.TelemetryIngress); ok { r0 = rf() @@ -485,6 +601,10 @@ func (_m *ChainScopedConfig) TelemetryIngress() coreconfig.TelemetryIngress { func (_m *ChainScopedConfig) Threshold() coreconfig.Threshold { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Threshold") + } + var r0 coreconfig.Threshold if rf, ok := ret.Get(0).(func() coreconfig.Threshold); ok { r0 = rf() @@ -501,6 +621,10 @@ func (_m *ChainScopedConfig) Threshold() coreconfig.Threshold { func (_m *ChainScopedConfig) Tracing() coreconfig.Tracing { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Tracing") + } + var r0 coreconfig.Tracing if rf, ok := ret.Get(0).(func() coreconfig.Tracing); ok { r0 = rf() @@ -517,6 +641,10 @@ func (_m *ChainScopedConfig) Tracing() coreconfig.Tracing { func (_m *ChainScopedConfig) Validate() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Validate") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -531,6 +659,10 @@ func (_m *ChainScopedConfig) Validate() error { func (_m *ChainScopedConfig) ValidateDB() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ValidateDB") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -545,6 +677,10 @@ func (_m *ChainScopedConfig) ValidateDB() error { func (_m *ChainScopedConfig) WebServer() coreconfig.WebServer { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for WebServer") + } + var r0 coreconfig.WebServer if rf, ok := ret.Get(0).(func() coreconfig.WebServer); ok { r0 = rf() diff --git a/core/chains/evm/config/mocks/gas_estimator.go b/core/chains/evm/config/mocks/gas_estimator.go index 6260b3cd501..69a2c852757 100644 --- a/core/chains/evm/config/mocks/gas_estimator.go +++ b/core/chains/evm/config/mocks/gas_estimator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type GasEstimator struct { func (_m *GasEstimator) BlockHistory() config.BlockHistory { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockHistory") + } + var r0 config.BlockHistory if rf, ok := ret.Get(0).(func() config.BlockHistory); ok { r0 = rf() @@ -36,6 +40,10 @@ func (_m *GasEstimator) BlockHistory() config.BlockHistory { func (_m *GasEstimator) BumpMin() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BumpMin") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() @@ -52,6 +60,10 @@ func (_m *GasEstimator) BumpMin() *assets.Wei { func (_m *GasEstimator) BumpPercent() uint16 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BumpPercent") + } + var r0 uint16 if rf, ok := ret.Get(0).(func() uint16); ok { r0 = rf() @@ -66,6 +78,10 @@ func (_m *GasEstimator) BumpPercent() uint16 { func (_m *GasEstimator) BumpThreshold() uint64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BumpThreshold") + } + var r0 uint64 if rf, ok := ret.Get(0).(func() uint64); ok { r0 = rf() @@ -80,6 +96,10 @@ func (_m *GasEstimator) BumpThreshold() uint64 { func (_m *GasEstimator) BumpTxDepth() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BumpTxDepth") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -94,6 +114,10 @@ func (_m *GasEstimator) BumpTxDepth() uint32 { func (_m *GasEstimator) EIP1559DynamicFees() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EIP1559DynamicFees") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -108,6 +132,10 @@ func (_m *GasEstimator) EIP1559DynamicFees() bool { func (_m *GasEstimator) FeeCapDefault() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FeeCapDefault") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() @@ -124,6 +152,10 @@ func (_m *GasEstimator) FeeCapDefault() *assets.Wei { func (_m *GasEstimator) LimitDefault() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitDefault") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -138,6 +170,10 @@ func (_m *GasEstimator) LimitDefault() uint32 { func (_m *GasEstimator) LimitJobType() config.LimitJobType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitJobType") + } + var r0 config.LimitJobType if rf, ok := ret.Get(0).(func() config.LimitJobType); ok { r0 = rf() @@ -154,6 +190,10 @@ func (_m *GasEstimator) LimitJobType() config.LimitJobType { func (_m *GasEstimator) LimitMax() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitMax") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -168,6 +208,10 @@ func (_m *GasEstimator) LimitMax() uint32 { func (_m *GasEstimator) LimitMultiplier() float32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitMultiplier") + } + var r0 float32 if rf, ok := ret.Get(0).(func() float32); ok { r0 = rf() @@ -182,6 +226,10 @@ func (_m *GasEstimator) LimitMultiplier() float32 { func (_m *GasEstimator) LimitTransfer() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitTransfer") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -196,6 +244,10 @@ func (_m *GasEstimator) LimitTransfer() uint32 { func (_m *GasEstimator) Mode() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Mode") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -210,6 +262,10 @@ func (_m *GasEstimator) Mode() string { func (_m *GasEstimator) PriceDefault() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for PriceDefault") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() @@ -226,6 +282,10 @@ func (_m *GasEstimator) PriceDefault() *assets.Wei { func (_m *GasEstimator) PriceMax() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for PriceMax") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() @@ -242,6 +302,10 @@ func (_m *GasEstimator) PriceMax() *assets.Wei { func (_m *GasEstimator) PriceMaxKey(_a0 common.Address) *assets.Wei { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for PriceMaxKey") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func(common.Address) *assets.Wei); ok { r0 = rf(_a0) @@ -258,6 +322,10 @@ func (_m *GasEstimator) PriceMaxKey(_a0 common.Address) *assets.Wei { func (_m *GasEstimator) PriceMin() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for PriceMin") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() @@ -274,6 +342,10 @@ func (_m *GasEstimator) PriceMin() *assets.Wei { func (_m *GasEstimator) TipCapDefault() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TipCapDefault") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() @@ -290,6 +362,10 @@ func (_m *GasEstimator) TipCapDefault() *assets.Wei { func (_m *GasEstimator) TipCapMin() *assets.Wei { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TipCapMin") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func() *assets.Wei); ok { r0 = rf() diff --git a/core/chains/evm/forwarders/mocks/orm.go b/core/chains/evm/forwarders/mocks/orm.go index e8ab62ce7de..c06a04b8eb1 100644 --- a/core/chains/evm/forwarders/mocks/orm.go +++ b/core/chains/evm/forwarders/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type ORM struct { func (_m *ORM) CreateForwarder(addr common.Address, evmChainId utils.Big) (forwarders.Forwarder, error) { ret := _m.Called(addr, evmChainId) + if len(ret) == 0 { + panic("no return value specified for CreateForwarder") + } + var r0 forwarders.Forwarder var r1 error if rf, ok := ret.Get(0).(func(common.Address, utils.Big) (forwarders.Forwarder, error)); ok { @@ -45,6 +49,10 @@ func (_m *ORM) CreateForwarder(addr common.Address, evmChainId utils.Big) (forwa func (_m *ORM) DeleteForwarder(id int64, cleanup func(pg.Queryer, int64, common.Address) error) error { ret := _m.Called(id, cleanup) + if len(ret) == 0 { + panic("no return value specified for DeleteForwarder") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, func(pg.Queryer, int64, common.Address) error) error); ok { r0 = rf(id, cleanup) @@ -59,6 +67,10 @@ func (_m *ORM) DeleteForwarder(id int64, cleanup func(pg.Queryer, int64, common. func (_m *ORM) FindForwarders(offset int, limit int) ([]forwarders.Forwarder, int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for FindForwarders") + } + var r0 []forwarders.Forwarder var r1 int var r2 error @@ -92,6 +104,10 @@ func (_m *ORM) FindForwarders(offset int, limit int) ([]forwarders.Forwarder, in func (_m *ORM) FindForwardersByChain(evmChainId utils.Big) ([]forwarders.Forwarder, error) { ret := _m.Called(evmChainId) + if len(ret) == 0 { + panic("no return value specified for FindForwardersByChain") + } + var r0 []forwarders.Forwarder var r1 error if rf, ok := ret.Get(0).(func(utils.Big) ([]forwarders.Forwarder, error)); ok { @@ -118,6 +134,10 @@ func (_m *ORM) FindForwardersByChain(evmChainId utils.Big) ([]forwarders.Forward func (_m *ORM) FindForwardersInListByChain(evmChainId utils.Big, addrs []common.Address) ([]forwarders.Forwarder, error) { ret := _m.Called(evmChainId, addrs) + if len(ret) == 0 { + panic("no return value specified for FindForwardersInListByChain") + } + var r0 []forwarders.Forwarder var r1 error if rf, ok := ret.Get(0).(func(utils.Big, []common.Address) ([]forwarders.Forwarder, error)); ok { diff --git a/core/chains/evm/gas/mocks/config.go b/core/chains/evm/gas/mocks/config.go index c09005b5e3d..4a7b6f4d7eb 100644 --- a/core/chains/evm/gas/mocks/config.go +++ b/core/chains/evm/gas/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type Config struct { func (_m *Config) ChainType() config.ChainType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainType") + } + var r0 config.ChainType if rf, ok := ret.Get(0).(func() config.ChainType); ok { r0 = rf() @@ -31,6 +35,10 @@ func (_m *Config) ChainType() config.ChainType { func (_m *Config) FinalityDepth() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityDepth") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -45,6 +53,10 @@ func (_m *Config) FinalityDepth() uint32 { func (_m *Config) FinalityTagEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityTagEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() diff --git a/core/chains/evm/gas/mocks/eth_client.go b/core/chains/evm/gas/mocks/eth_client.go index 2b0aaff0257..bb0784f8515 100644 --- a/core/chains/evm/gas/mocks/eth_client.go +++ b/core/chains/evm/gas/mocks/eth_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type ETHClient struct { func (_m *ETHClient) CallContract(ctx context.Context, msg ethereum.CallMsg, 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, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { diff --git a/core/chains/evm/gas/mocks/evm_estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go index f2cb51f8560..29705b2a5d9 100644 --- a/core/chains/evm/gas/mocks/evm_estimator.go +++ b/core/chains/evm/gas/mocks/evm_estimator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -25,6 +25,10 @@ type EvmEstimator struct { func (_m *EvmEstimator) BumpDynamicFee(ctx context.Context, original gas.DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.EvmPriorAttempt) (gas.DynamicFee, uint32, error) { ret := _m.Called(ctx, original, gasLimit, maxGasPriceWei, attempts) + if len(ret) == 0 { + panic("no return value specified for BumpDynamicFee") + } + var r0 gas.DynamicFee var r1 uint32 var r2 error @@ -56,6 +60,10 @@ func (_m *EvmEstimator) BumpDynamicFee(ctx context.Context, original gas.Dynamic func (_m *EvmEstimator) BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.EvmPriorAttempt) (*assets.Wei, uint32, error) { ret := _m.Called(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) + if len(ret) == 0 { + panic("no return value specified for BumpLegacyGas") + } + var r0 *assets.Wei var r1 uint32 var r2 error @@ -89,6 +97,10 @@ func (_m *EvmEstimator) BumpLegacyGas(ctx context.Context, originalGasPrice *ass func (_m *EvmEstimator) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -103,6 +115,10 @@ func (_m *EvmEstimator) Close() error { func (_m *EvmEstimator) GetDynamicFee(ctx context.Context, gasLimit uint32, maxGasPriceWei *assets.Wei) (gas.DynamicFee, uint32, error) { ret := _m.Called(ctx, gasLimit, maxGasPriceWei) + if len(ret) == 0 { + panic("no return value specified for GetDynamicFee") + } + var r0 gas.DynamicFee var r1 uint32 var r2 error @@ -141,6 +157,10 @@ func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLi _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetLegacyGas") + } + var r0 *assets.Wei var r1 uint32 var r2 error @@ -174,6 +194,10 @@ func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLi func (_m *EvmEstimator) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -190,6 +214,10 @@ func (_m *EvmEstimator) HealthReport() map[string]error { func (_m *EvmEstimator) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -209,6 +237,10 @@ func (_m *EvmEstimator) OnNewLongestChain(ctx context.Context, head *evmtypes.He func (_m *EvmEstimator) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -223,6 +255,10 @@ func (_m *EvmEstimator) Ready() error { func (_m *EvmEstimator) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/chains/evm/gas/mocks/evm_fee_estimator.go b/core/chains/evm/gas/mocks/evm_fee_estimator.go index b9b16367796..66acbdbf7ff 100644 --- a/core/chains/evm/gas/mocks/evm_fee_estimator.go +++ b/core/chains/evm/gas/mocks/evm_fee_estimator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -29,6 +29,10 @@ type EvmFeeEstimator struct { func (_m *EvmFeeEstimator) BumpFee(ctx context.Context, originalFee gas.EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []gas.EvmPriorAttempt) (gas.EvmFee, uint32, error) { ret := _m.Called(ctx, originalFee, feeLimit, maxFeePrice, attempts) + if len(ret) == 0 { + panic("no return value specified for BumpFee") + } + var r0 gas.EvmFee var r1 uint32 var r2 error @@ -60,6 +64,10 @@ func (_m *EvmFeeEstimator) BumpFee(ctx context.Context, originalFee gas.EvmFee, func (_m *EvmFeeEstimator) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -81,6 +89,10 @@ func (_m *EvmFeeEstimator) GetFee(ctx context.Context, calldata []byte, feeLimit _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetFee") + } + var r0 gas.EvmFee var r1 uint32 var r2 error @@ -119,6 +131,10 @@ func (_m *EvmFeeEstimator) GetMaxCost(ctx context.Context, amount assets.Eth, ca _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetMaxCost") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(context.Context, assets.Eth, []byte, uint32, *assets.Wei, ...types.Opt) (*big.Int, error)); ok { @@ -145,6 +161,10 @@ func (_m *EvmFeeEstimator) GetMaxCost(ctx context.Context, amount assets.Eth, ca func (_m *EvmFeeEstimator) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -161,6 +181,10 @@ func (_m *EvmFeeEstimator) HealthReport() map[string]error { func (_m *EvmFeeEstimator) L1Oracle() rollups.L1Oracle { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for L1Oracle") + } + var r0 rollups.L1Oracle if rf, ok := ret.Get(0).(func() rollups.L1Oracle); ok { r0 = rf() @@ -177,6 +201,10 @@ func (_m *EvmFeeEstimator) L1Oracle() rollups.L1Oracle { func (_m *EvmFeeEstimator) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -196,6 +224,10 @@ func (_m *EvmFeeEstimator) OnNewLongestChain(ctx context.Context, head *evmtypes func (_m *EvmFeeEstimator) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -210,6 +242,10 @@ func (_m *EvmFeeEstimator) Ready() error { func (_m *EvmFeeEstimator) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/chains/evm/gas/mocks/rpc_client.go b/core/chains/evm/gas/mocks/rpc_client.go index 6f6ac8d6b79..d1262665f66 100644 --- a/core/chains/evm/gas/mocks/rpc_client.go +++ b/core/chains/evm/gas/mocks/rpc_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ func (_m *RPCClient) CallContext(ctx context.Context, result interface{}, 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...) diff --git a/core/chains/evm/gas/rollups/mocks/eth_client.go b/core/chains/evm/gas/rollups/mocks/eth_client.go index 2b0aaff0257..bb0784f8515 100644 --- a/core/chains/evm/gas/rollups/mocks/eth_client.go +++ b/core/chains/evm/gas/rollups/mocks/eth_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type ETHClient struct { func (_m *ETHClient) CallContract(ctx context.Context, msg ethereum.CallMsg, 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, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle.go b/core/chains/evm/gas/rollups/mocks/l1_oracle.go index f6f8cc736af..9e52a3ec38e 100644 --- a/core/chains/evm/gas/rollups/mocks/l1_oracle.go +++ b/core/chains/evm/gas/rollups/mocks/l1_oracle.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type L1Oracle struct { func (_m *L1Oracle) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -33,6 +37,10 @@ func (_m *L1Oracle) Close() error { func (_m *L1Oracle) GasPrice(ctx context.Context) (*assets.Wei, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GasPrice") + } + var r0 *assets.Wei var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*assets.Wei, error)); ok { @@ -59,6 +67,10 @@ func (_m *L1Oracle) GasPrice(ctx context.Context) (*assets.Wei, error) { func (_m *L1Oracle) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -75,6 +87,10 @@ func (_m *L1Oracle) HealthReport() map[string]error { func (_m *L1Oracle) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -89,6 +105,10 @@ func (_m *L1Oracle) Name() string { func (_m *L1Oracle) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -103,6 +123,10 @@ func (_m *L1Oracle) Ready() error { func (_m *L1Oracle) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/chains/evm/headtracker/mocks/config.go b/core/chains/evm/headtracker/mocks/config.go index b505cfc445d..74376a71362 100644 --- a/core/chains/evm/headtracker/mocks/config.go +++ b/core/chains/evm/headtracker/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type Config struct { func (_m *Config) BlockEmissionIdleWarningThreshold() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BlockEmissionIdleWarningThreshold") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -31,6 +35,10 @@ func (_m *Config) BlockEmissionIdleWarningThreshold() time.Duration { func (_m *Config) FinalityDepth() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityDepth") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() diff --git a/core/chains/evm/log/mocks/abigen_contract.go b/core/chains/evm/log/mocks/abigen_contract.go index dcc95a1acdb..fde8949e4fe 100644 --- a/core/chains/evm/log/mocks/abigen_contract.go +++ b/core/chains/evm/log/mocks/abigen_contract.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type AbigenContract struct { func (_m *AbigenContract) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -36,6 +40,10 @@ func (_m *AbigenContract) Address() common.Address { func (_m *AbigenContract) ParseLog(_a0 types.Log) (generated.AbigenLog, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { diff --git a/core/chains/evm/log/mocks/broadcast.go b/core/chains/evm/log/mocks/broadcast.go index a58ce9f30e4..6d9a83716d5 100644 --- a/core/chains/evm/log/mocks/broadcast.go +++ b/core/chains/evm/log/mocks/broadcast.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type Broadcast struct { func (_m *Broadcast) DecodedLog() interface{} { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DecodedLog") + } + var r0 interface{} if rf, ok := ret.Get(0).(func() interface{}); ok { r0 = rf() @@ -37,6 +41,10 @@ func (_m *Broadcast) DecodedLog() interface{} { func (_m *Broadcast) EVMChainID() big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMChainID") + } + var r0 big.Int if rf, ok := ret.Get(0).(func() big.Int); ok { r0 = rf() @@ -51,6 +59,10 @@ func (_m *Broadcast) EVMChainID() big.Int { func (_m *Broadcast) JobID() int32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for JobID") + } + var r0 int32 if rf, ok := ret.Get(0).(func() int32); ok { r0 = rf() @@ -65,6 +77,10 @@ func (_m *Broadcast) JobID() int32 { func (_m *Broadcast) LatestBlockHash() common.Hash { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestBlockHash") + } + var r0 common.Hash if rf, ok := ret.Get(0).(func() common.Hash); ok { r0 = rf() @@ -81,6 +97,10 @@ func (_m *Broadcast) LatestBlockHash() common.Hash { func (_m *Broadcast) LatestBlockNumber() uint64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestBlockNumber") + } + var r0 uint64 if rf, ok := ret.Get(0).(func() uint64); ok { r0 = rf() @@ -95,6 +115,10 @@ func (_m *Broadcast) LatestBlockNumber() uint64 { func (_m *Broadcast) RawLog() types.Log { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RawLog") + } + var r0 types.Log if rf, ok := ret.Get(0).(func() types.Log); ok { r0 = rf() @@ -109,6 +133,10 @@ func (_m *Broadcast) RawLog() types.Log { func (_m *Broadcast) ReceiptsRoot() common.Hash { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReceiptsRoot") + } + var r0 common.Hash if rf, ok := ret.Get(0).(func() common.Hash); ok { r0 = rf() @@ -125,6 +153,10 @@ func (_m *Broadcast) ReceiptsRoot() common.Hash { func (_m *Broadcast) StateRoot() common.Hash { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StateRoot") + } + var r0 common.Hash if rf, ok := ret.Get(0).(func() common.Hash); ok { r0 = rf() @@ -141,6 +173,10 @@ func (_m *Broadcast) StateRoot() common.Hash { func (_m *Broadcast) String() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for String") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -155,6 +191,10 @@ func (_m *Broadcast) String() string { func (_m *Broadcast) TransactionsRoot() common.Hash { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TransactionsRoot") + } + var r0 common.Hash if rf, ok := ret.Get(0).(func() common.Hash); ok { r0 = rf() diff --git a/core/chains/evm/log/mocks/broadcaster.go b/core/chains/evm/log/mocks/broadcaster.go index 84f143b39e4..031bdaaa7d9 100644 --- a/core/chains/evm/log/mocks/broadcaster.go +++ b/core/chains/evm/log/mocks/broadcaster.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -27,6 +27,10 @@ func (_m *Broadcaster) AddDependents(n int) { func (_m *Broadcaster) AwaitDependents() <-chan struct{} { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AwaitDependents") + } + var r0 <-chan struct{} if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { r0 = rf() @@ -43,6 +47,10 @@ func (_m *Broadcaster) AwaitDependents() <-chan struct{} { func (_m *Broadcaster) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -62,6 +70,10 @@ func (_m *Broadcaster) DependentReady() { func (_m *Broadcaster) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -78,6 +90,10 @@ func (_m *Broadcaster) HealthReport() map[string]error { func (_m *Broadcaster) IsConnected() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsConnected") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -99,6 +115,10 @@ func (_m *Broadcaster) MarkConsumed(lb log.Broadcast, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for MarkConsumed") + } + var r0 error if rf, ok := ret.Get(0).(func(log.Broadcast, ...pg.QOpt) error); ok { r0 = rf(lb, qopts...) @@ -120,6 +140,10 @@ func (_m *Broadcaster) MarkManyConsumed(lbs []log.Broadcast, qopts ...pg.QOpt) e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for MarkManyConsumed") + } + var r0 error if rf, ok := ret.Get(0).(func([]log.Broadcast, ...pg.QOpt) error); ok { r0 = rf(lbs, qopts...) @@ -134,6 +158,10 @@ func (_m *Broadcaster) MarkManyConsumed(lbs []log.Broadcast, qopts ...pg.QOpt) e func (_m *Broadcaster) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -153,6 +181,10 @@ func (_m *Broadcaster) OnNewLongestChain(ctx context.Context, head *types.Head) func (_m *Broadcaster) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -167,6 +199,10 @@ func (_m *Broadcaster) Ready() error { func (_m *Broadcaster) Register(listener log.Listener, opts log.ListenerOpts) func() { ret := _m.Called(listener, opts) + if len(ret) == 0 { + panic("no return value specified for Register") + } + var r0 func() if rf, ok := ret.Get(0).(func(log.Listener, log.ListenerOpts) func()); ok { r0 = rf(listener, opts) @@ -188,6 +224,10 @@ func (_m *Broadcaster) ReplayFromBlock(number int64, forceBroadcast bool) { func (_m *Broadcaster) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -209,6 +249,10 @@ func (_m *Broadcaster) WasAlreadyConsumed(lb log.Broadcast, qopts ...pg.QOpt) (b _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for WasAlreadyConsumed") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(log.Broadcast, ...pg.QOpt) (bool, error)); ok { diff --git a/core/chains/evm/logpoller/mocks/log_poller.go b/core/chains/evm/logpoller/mocks/log_poller.go index fe4ccc965cc..65d808b98d5 100644 --- a/core/chains/evm/logpoller/mocks/log_poller.go +++ b/core/chains/evm/logpoller/mocks/log_poller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -25,6 +25,10 @@ type LogPoller struct { func (_m *LogPoller) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -46,6 +50,10 @@ func (_m *LogPoller) GetBlocksRange(ctx context.Context, numbers []uint64, qopts _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetBlocksRange") + } + var r0 []logpoller.LogPollerBlock var r1 error if rf, ok := ret.Get(0).(func(context.Context, []uint64, ...pg.QOpt) ([]logpoller.LogPollerBlock, error)); ok { @@ -72,6 +80,10 @@ func (_m *LogPoller) GetBlocksRange(ctx context.Context, numbers []uint64, qopts func (_m *LogPoller) HasFilter(name string) bool { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for HasFilter") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(name) @@ -86,6 +98,10 @@ func (_m *LogPoller) HasFilter(name string) bool { func (_m *LogPoller) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -109,6 +125,10 @@ func (_m *LogPoller) IndexedLogs(eventSig common.Hash, address common.Address, t _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogs") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, []common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -142,6 +162,10 @@ func (_m *LogPoller) IndexedLogsByBlockRange(start int64, end int64, eventSig co _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogsByBlockRange") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(int64, int64, common.Hash, common.Address, int, []common.Hash, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -175,6 +199,10 @@ func (_m *LogPoller) IndexedLogsByTxHash(eventSig common.Hash, address common.Ad _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogsByTxHash") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, common.Hash, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -208,6 +236,10 @@ func (_m *LogPoller) IndexedLogsCreatedAfter(eventSig common.Hash, address commo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogsCreatedAfter") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, []common.Hash, time.Time, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -241,6 +273,10 @@ func (_m *LogPoller) IndexedLogsTopicGreaterThan(eventSig common.Hash, address c _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogsTopicGreaterThan") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -274,6 +310,10 @@ func (_m *LogPoller) IndexedLogsTopicRange(eventSig common.Hash, address common. _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogsTopicRange") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, common.Hash, common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -307,6 +347,10 @@ func (_m *LogPoller) IndexedLogsWithSigsExcluding(address common.Address, eventS _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IndexedLogsWithSigsExcluding") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Address, common.Hash, common.Hash, int, int64, int64, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -339,6 +383,10 @@ func (_m *LogPoller) LatestBlock(qopts ...pg.QOpt) (logpoller.LogPollerBlock, er _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LatestBlock") + } + var r0 logpoller.LogPollerBlock var r1 error if rf, ok := ret.Get(0).(func(...pg.QOpt) (logpoller.LogPollerBlock, error)); ok { @@ -370,6 +418,10 @@ func (_m *LogPoller) LatestBlockByEventSigsAddrsWithConfs(fromBlock int64, event _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LatestBlockByEventSigsAddrsWithConfs") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(int64, []common.Hash, []common.Address, logpoller.Confirmations, ...pg.QOpt) (int64, error)); ok { @@ -401,6 +453,10 @@ func (_m *LogPoller) LatestLogByEventSigWithConfs(eventSig common.Hash, address _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LatestLogByEventSigWithConfs") + } + var r0 *logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, logpoller.Confirmations, ...pg.QOpt) (*logpoller.Log, error)); ok { @@ -434,6 +490,10 @@ func (_m *LogPoller) LatestLogEventSigsAddrsWithConfs(fromBlock int64, eventSigs _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LatestLogEventSigsAddrsWithConfs") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(int64, []common.Hash, []common.Address, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -467,6 +527,10 @@ func (_m *LogPoller) Logs(start int64, end int64, eventSig common.Hash, address _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Logs") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(int64, int64, common.Hash, common.Address, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -500,6 +564,10 @@ func (_m *LogPoller) LogsCreatedAfter(eventSig common.Hash, address common.Addre _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LogsCreatedAfter") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, time.Time, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -533,6 +601,10 @@ func (_m *LogPoller) LogsDataWordBetween(eventSig common.Hash, address common.Ad _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LogsDataWordBetween") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, int, common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -566,6 +638,10 @@ func (_m *LogPoller) LogsDataWordGreaterThan(eventSig common.Hash, address commo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LogsDataWordGreaterThan") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -599,6 +675,10 @@ func (_m *LogPoller) LogsDataWordRange(eventSig common.Hash, address common.Addr _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LogsDataWordRange") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, common.Hash, common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -632,6 +712,10 @@ func (_m *LogPoller) LogsWithSigs(start int64, end int64, eventSigs []common.Has _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LogsWithSigs") + } + var r0 []logpoller.Log var r1 error if rf, ok := ret.Get(0).(func(int64, int64, []common.Hash, common.Address, ...pg.QOpt) ([]logpoller.Log, error)); ok { @@ -658,6 +742,10 @@ func (_m *LogPoller) LogsWithSigs(start int64, end int64, eventSigs []common.Has func (_m *LogPoller) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -672,6 +760,10 @@ func (_m *LogPoller) Name() string { func (_m *LogPoller) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -693,6 +785,10 @@ func (_m *LogPoller) RegisterFilter(filter logpoller.Filter, qopts ...pg.QOpt) e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for RegisterFilter") + } + var r0 error if rf, ok := ret.Get(0).(func(logpoller.Filter, ...pg.QOpt) error); ok { r0 = rf(filter, qopts...) @@ -707,6 +803,10 @@ func (_m *LogPoller) RegisterFilter(filter logpoller.Filter, qopts ...pg.QOpt) e func (_m *LogPoller) Replay(ctx context.Context, fromBlock int64) error { ret := _m.Called(ctx, fromBlock) + if len(ret) == 0 { + panic("no return value specified for Replay") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, fromBlock) @@ -726,6 +826,10 @@ func (_m *LogPoller) ReplayAsync(fromBlock int64) { func (_m *LogPoller) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -747,6 +851,10 @@ func (_m *LogPoller) UnregisterFilter(name string, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UnregisterFilter") + } + var r0 error if rf, ok := ret.Get(0).(func(string, ...pg.QOpt) error); ok { r0 = rf(name, qopts...) diff --git a/core/chains/evm/mocks/balance_monitor.go b/core/chains/evm/mocks/balance_monitor.go index cdda18b7f03..f03fd8829cc 100644 --- a/core/chains/evm/mocks/balance_monitor.go +++ b/core/chains/evm/mocks/balance_monitor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ type BalanceMonitor struct { func (_m *BalanceMonitor) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -36,6 +40,10 @@ func (_m *BalanceMonitor) Close() error { func (_m *BalanceMonitor) GetEthBalance(_a0 common.Address) *assets.Eth { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetEthBalance") + } + var r0 *assets.Eth if rf, ok := ret.Get(0).(func(common.Address) *assets.Eth); ok { r0 = rf(_a0) @@ -52,6 +60,10 @@ func (_m *BalanceMonitor) GetEthBalance(_a0 common.Address) *assets.Eth { func (_m *BalanceMonitor) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -68,6 +80,10 @@ func (_m *BalanceMonitor) HealthReport() map[string]error { func (_m *BalanceMonitor) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -87,6 +103,10 @@ func (_m *BalanceMonitor) OnNewLongestChain(ctx context.Context, head *types.Hea func (_m *BalanceMonitor) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -101,6 +121,10 @@ func (_m *BalanceMonitor) Ready() error { func (_m *BalanceMonitor) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/chains/evm/mocks/node.go b/core/chains/evm/mocks/node.go index 69020d411f4..8f27218aec7 100644 --- a/core/chains/evm/mocks/node.go +++ b/core/chains/evm/mocks/node.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -30,6 +30,10 @@ type Node struct { func (_m *Node) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { ret := _m.Called(ctx, account, 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 { @@ -56,6 +60,10 @@ func (_m *Node) BalanceAt(ctx context.Context, account common.Address, blockNumb func (_m *Node) 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) @@ -70,6 +78,10 @@ func (_m *Node) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { func (_m *Node) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { ret := _m.Called(ctx, hash) + if len(ret) == 0 { + panic("no return value specified for BlockByHash") + } + var r0 *types.Block var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Block, error)); ok { @@ -96,6 +108,10 @@ func (_m *Node) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block func (_m *Node) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { ret := _m.Called(ctx, number) + if len(ret) == 0 { + panic("no return value specified for BlockByNumber") + } + var r0 *types.Block var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Block, error)); ok { @@ -122,6 +138,10 @@ func (_m *Node) BlockByNumber(ctx context.Context, number *big.Int) (*types.Bloc func (_m *Node) BlockNumber(ctx context.Context) (uint64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for BlockNumber") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { @@ -149,6 +169,10 @@ func (_m *Node) CallContext(ctx context.Context, result interface{}, method stri _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...) @@ -163,6 +187,10 @@ func (_m *Node) CallContext(ctx context.Context, result interface{}, method stri func (_m *Node) CallContract(ctx context.Context, msg ethereum.CallMsg, 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, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { @@ -189,6 +217,10 @@ func (_m *Node) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum func (_m *Node) ChainID() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -205,6 +237,10 @@ func (_m *Node) ChainID() *big.Int { func (_m *Node) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -219,6 +255,10 @@ func (_m *Node) Close() error { func (_m *Node) 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 { @@ -245,6 +285,10 @@ func (_m *Node) CodeAt(ctx context.Context, account common.Address, blockNumber func (_m *Node) EstimateGas(ctx context.Context, call ethereum.CallMsg) (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, ethereum.CallMsg) (uint64, error)); ok { @@ -272,6 +316,10 @@ func (_m *Node) EthSubscribe(ctx context.Context, channel chan<- *evmtypes.Head, _ca = append(_ca, args...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for EthSubscribe") + } + var r0 ethereum.Subscription var r1 error if rf, ok := ret.Get(0).(func(context.Context, chan<- *evmtypes.Head, ...interface{}) (ethereum.Subscription, error)); ok { @@ -298,6 +346,10 @@ func (_m *Node) EthSubscribe(ctx context.Context, channel chan<- *evmtypes.Head, func (_m *Node) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { ret := _m.Called(ctx, q) + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + var r0 []types.Log var r1 error if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]types.Log, error)); ok { @@ -324,6 +376,10 @@ func (_m *Node) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types func (_m *Node) HeaderByHash(_a0 context.Context, _a1 common.Hash) (*types.Header, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Header, error)); ok { @@ -350,6 +406,10 @@ func (_m *Node) HeaderByHash(_a0 context.Context, _a1 common.Hash) (*types.Heade func (_m *Node) HeaderByNumber(_a0 context.Context, _a1 *big.Int) (*types.Header, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + var r0 *types.Header var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { @@ -376,6 +436,10 @@ func (_m *Node) HeaderByNumber(_a0 context.Context, _a1 *big.Int) (*types.Header func (_m *Node) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -390,6 +454,10 @@ func (_m *Node) Name() string { func (_m *Node) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { ret := _m.Called(ctx, account, blockNumber) + if len(ret) == 0 { + panic("no return value specified for NonceAt") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (uint64, error)); ok { @@ -414,6 +482,10 @@ func (_m *Node) NonceAt(ctx context.Context, account common.Address, blockNumber func (_m *Node) Order() int32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Order") + } + var r0 int32 if rf, ok := ret.Get(0).(func() int32); ok { r0 = rf() @@ -428,6 +500,10 @@ func (_m *Node) Order() int32 { func (_m *Node) 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 { @@ -454,6 +530,10 @@ func (_m *Node) PendingCodeAt(ctx context.Context, account common.Address) ([]by func (_m *Node) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { ret := _m.Called(ctx, account) + if len(ret) == 0 { + panic("no return value specified for PendingNonceAt") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address) (uint64, error)); ok { @@ -478,6 +558,10 @@ func (_m *Node) PendingNonceAt(ctx context.Context, account common.Address) (uin func (_m *Node) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { r0 = rf(ctx, tx) @@ -492,6 +576,10 @@ func (_m *Node) SendTransaction(ctx context.Context, tx *types.Transaction) erro func (_m *Node) Start(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -506,6 +594,10 @@ func (_m *Node) Start(ctx context.Context) error { func (_m *Node) State() client.NodeState { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for State") + } + var r0 client.NodeState if rf, ok := ret.Get(0).(func() client.NodeState); ok { r0 = rf() @@ -520,6 +612,10 @@ func (_m *Node) State() client.NodeState { func (_m *Node) StateAndLatest() (client.NodeState, int64, *big.Int) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StateAndLatest") + } + var r0 client.NodeState var r1 int64 var r2 *big.Int @@ -553,6 +649,10 @@ func (_m *Node) StateAndLatest() (client.NodeState, int64, *big.Int) { func (_m *Node) String() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for String") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -567,6 +667,10 @@ func (_m *Node) String() string { func (_m *Node) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.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<- types.Log) (ethereum.Subscription, error)); ok { @@ -593,6 +697,10 @@ func (_m *Node) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, func (_m *Node) 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() @@ -607,6 +715,10 @@ func (_m *Node) SubscribersCount() int32 { func (_m *Node) 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 { @@ -633,6 +745,10 @@ func (_m *Node) SuggestGasPrice(ctx context.Context) (*big.Int, error) { func (_m *Node) 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 { @@ -659,6 +775,10 @@ func (_m *Node) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (_m *Node) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) { ret := _m.Called(ctx, txHash) + if len(ret) == 0 { + panic("no return value specified for TransactionByHash") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Transaction, error)); ok { @@ -685,6 +805,10 @@ func (_m *Node) TransactionByHash(ctx context.Context, txHash common.Hash) (*typ func (_m *Node) 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 { diff --git a/core/chains/evm/mocks/send_only_node.go b/core/chains/evm/mocks/send_only_node.go index c836399a8ba..8ec3270281d 100644 --- a/core/chains/evm/mocks/send_only_node.go +++ b/core/chains/evm/mocks/send_only_node.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type SendOnlyNode struct { func (_m *SendOnlyNode) 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) @@ -38,6 +42,10 @@ func (_m *SendOnlyNode) BatchCallContext(ctx context.Context, b []rpc.BatchElem) func (_m *SendOnlyNode) ChainID() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -54,6 +62,10 @@ func (_m *SendOnlyNode) ChainID() *big.Int { func (_m *SendOnlyNode) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -68,6 +80,10 @@ func (_m *SendOnlyNode) Close() error { func (_m *SendOnlyNode) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -82,6 +98,10 @@ func (_m *SendOnlyNode) Name() string { func (_m *SendOnlyNode) SendTransaction(ctx context.Context, tx *types.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, *types.Transaction) error); ok { r0 = rf(ctx, tx) @@ -96,6 +116,10 @@ func (_m *SendOnlyNode) SendTransaction(ctx context.Context, tx *types.Transacti func (_m *SendOnlyNode) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -110,6 +134,10 @@ func (_m *SendOnlyNode) Start(_a0 context.Context) error { func (_m *SendOnlyNode) State() client.NodeState { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for State") + } + var r0 client.NodeState if rf, ok := ret.Get(0).(func() client.NodeState); ok { r0 = rf() @@ -124,6 +152,10 @@ func (_m *SendOnlyNode) State() client.NodeState { func (_m *SendOnlyNode) String() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for String") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() diff --git a/core/chains/evm/txmgr/mocks/config.go b/core/chains/evm/txmgr/mocks/config.go index ab98d686c85..0a0ece4b90b 100644 --- a/core/chains/evm/txmgr/mocks/config.go +++ b/core/chains/evm/txmgr/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type Config struct { func (_m *Config) ChainType() config.ChainType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainType") + } + var r0 config.ChainType if rf, ok := ret.Get(0).(func() config.ChainType); ok { r0 = rf() @@ -30,6 +34,10 @@ func (_m *Config) ChainType() config.ChainType { func (_m *Config) FinalityDepth() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityDepth") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -44,6 +52,10 @@ func (_m *Config) FinalityDepth() uint32 { func (_m *Config) FinalityTagEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityTagEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -58,6 +70,10 @@ func (_m *Config) FinalityTagEnabled() bool { func (_m *Config) NonceAutoSync() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for NonceAutoSync") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -72,6 +88,10 @@ func (_m *Config) NonceAutoSync() bool { func (_m *Config) RPCDefaultBatchSize() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RPCDefaultBatchSize") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index bf3088b2aa1..a9a7023ac1f 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -32,6 +32,10 @@ type EvmTxStore struct { func (_m *EvmTxStore) Abandon(ctx context.Context, id *big.Int, addr common.Address) error { ret := _m.Called(ctx, id, addr) + if len(ret) == 0 { + panic("no return value specified for Abandon") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int, common.Address) error); ok { r0 = rf(ctx, id, addr) @@ -46,6 +50,10 @@ func (_m *EvmTxStore) Abandon(ctx context.Context, id *big.Int, addr common.Addr func (_m *EvmTxStore) CheckTxQueueCapacity(ctx context.Context, fromAddress common.Address, maxQueuedTransactions uint64, chainID *big.Int) error { ret := _m.Called(ctx, fromAddress, maxQueuedTransactions, chainID) + if len(ret) == 0 { + panic("no return value specified for CheckTxQueueCapacity") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, *big.Int) error); ok { r0 = rf(ctx, fromAddress, maxQueuedTransactions, chainID) @@ -65,6 +73,10 @@ func (_m *EvmTxStore) Close() { func (_m *EvmTxStore) CountTransactionsByState(ctx context.Context, state types.TxState, chainID *big.Int) (uint32, error) { ret := _m.Called(ctx, state, chainID) + if len(ret) == 0 { + panic("no return value specified for CountTransactionsByState") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, types.TxState, *big.Int) (uint32, error)); ok { @@ -89,6 +101,10 @@ func (_m *EvmTxStore) CountTransactionsByState(ctx context.Context, state types. func (_m *EvmTxStore) CountUnconfirmedTransactions(ctx context.Context, fromAddress common.Address, chainID *big.Int) (uint32, error) { ret := _m.Called(ctx, fromAddress, chainID) + if len(ret) == 0 { + panic("no return value specified for CountUnconfirmedTransactions") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (uint32, error)); ok { @@ -113,6 +129,10 @@ func (_m *EvmTxStore) CountUnconfirmedTransactions(ctx context.Context, fromAddr func (_m *EvmTxStore) CountUnstartedTransactions(ctx context.Context, fromAddress common.Address, chainID *big.Int) (uint32, error) { ret := _m.Called(ctx, fromAddress, chainID) + if len(ret) == 0 { + panic("no return value specified for CountUnstartedTransactions") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (uint32, error)); ok { @@ -137,6 +157,10 @@ func (_m *EvmTxStore) CountUnstartedTransactions(ctx context.Context, fromAddres func (_m *EvmTxStore) CreateTransaction(ctx context.Context, txRequest types.TxRequest[common.Address, common.Hash], chainID *big.Int) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, txRequest, chainID) + if len(ret) == 0 { + panic("no return value specified for CreateTransaction") + } + var r0 types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, types.TxRequest[common.Address, common.Hash], *big.Int) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -161,6 +185,10 @@ func (_m *EvmTxStore) CreateTransaction(ctx context.Context, txRequest types.TxR func (_m *EvmTxStore) DeleteInProgressAttempt(ctx context.Context, attempt types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, attempt) + if len(ret) == 0 { + panic("no return value specified for DeleteInProgressAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, attempt) @@ -175,6 +203,10 @@ func (_m *EvmTxStore) DeleteInProgressAttempt(ctx context.Context, attempt types func (_m *EvmTxStore) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID *big.Int) (null.Time, error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindEarliestUnconfirmedBroadcastTime") + } + var r0 null.Time var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (null.Time, error)); ok { @@ -199,6 +231,10 @@ func (_m *EvmTxStore) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, func (_m *EvmTxStore) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID *big.Int) (null.Int, error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindEarliestUnconfirmedTxAttemptBlock") + } + var r0 null.Int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (null.Int, error)); ok { @@ -223,6 +259,10 @@ func (_m *EvmTxStore) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, func (_m *EvmTxStore) FindLatestSequence(ctx context.Context, fromAddress common.Address, chainId *big.Int) (evmtypes.Nonce, error) { ret := _m.Called(ctx, fromAddress, chainId) + if len(ret) == 0 { + panic("no return value specified for FindLatestSequence") + } + var r0 evmtypes.Nonce var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (evmtypes.Nonce, error)); ok { @@ -247,6 +287,10 @@ func (_m *EvmTxStore) FindLatestSequence(ctx context.Context, fromAddress common func (_m *EvmTxStore) FindNextUnstartedTransactionFromAddress(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], fromAddress common.Address, chainID *big.Int) error { ret := _m.Called(ctx, etx, fromAddress, chainID) + if len(ret) == 0 { + panic("no return value specified for FindNextUnstartedTransactionFromAddress") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], common.Address, *big.Int) error); ok { r0 = rf(ctx, etx, fromAddress, chainID) @@ -261,6 +305,10 @@ func (_m *EvmTxStore) FindNextUnstartedTransactionFromAddress(ctx context.Contex func (_m *EvmTxStore) FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber int64, lowBlockNumber int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, highBlockNumber, lowBlockNumber, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTransactionsConfirmedInBlockRange") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -287,6 +335,10 @@ func (_m *EvmTxStore) FindTransactionsConfirmedInBlockRange(ctx context.Context, func (_m *EvmTxStore) FindTxAttempt(hash common.Hash) (*types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(hash) + if len(ret) == 0 { + panic("no return value specified for FindTxAttempt") + } + var r0 *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(common.Hash) (*types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -313,6 +365,10 @@ func (_m *EvmTxStore) FindTxAttempt(hash common.Hash) (*types.TxAttempt[*big.Int func (_m *EvmTxStore) FindTxAttemptConfirmedByTxIDs(ids []int64) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptConfirmedByTxIDs") + } + var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -339,6 +395,10 @@ func (_m *EvmTxStore) FindTxAttemptConfirmedByTxIDs(ids []int64) ([]types.TxAtte func (_m *EvmTxStore) FindTxAttemptsConfirmedMissingReceipt(ctx context.Context, chainID *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptsConfirmedMissingReceipt") + } + var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -365,6 +425,10 @@ func (_m *EvmTxStore) FindTxAttemptsConfirmedMissingReceipt(ctx context.Context, func (_m *EvmTxStore) FindTxAttemptsRequiringReceiptFetch(ctx context.Context, chainID *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptsRequiringReceiptFetch") + } + var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -391,6 +455,10 @@ func (_m *EvmTxStore) FindTxAttemptsRequiringReceiptFetch(ctx context.Context, c func (_m *EvmTxStore) FindTxAttemptsRequiringResend(ctx context.Context, olderThan time.Time, maxInFlightTransactions uint32, chainID *big.Int, address common.Address) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, olderThan, maxInFlightTransactions, chainID, address) + if len(ret) == 0 { + panic("no return value specified for FindTxAttemptsRequiringResend") + } + var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, time.Time, uint32, *big.Int, common.Address) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -417,6 +485,10 @@ func (_m *EvmTxStore) FindTxAttemptsRequiringResend(ctx context.Context, olderTh func (_m *EvmTxStore) FindTxByHash(hash common.Hash) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(hash) + if len(ret) == 0 { + panic("no return value specified for FindTxByHash") + } + var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(common.Hash) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -443,6 +515,10 @@ func (_m *EvmTxStore) FindTxByHash(hash common.Hash) (*types.Tx[*big.Int, common func (_m *EvmTxStore) FindTxWithAttempts(etxID int64) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(etxID) + if len(ret) == 0 { + panic("no return value specified for FindTxWithAttempts") + } + var r0 types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(int64) (types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -467,6 +543,10 @@ func (_m *EvmTxStore) FindTxWithAttempts(etxID int64) (types.Tx[*big.Int, common func (_m *EvmTxStore) FindTxWithIdempotencyKey(ctx context.Context, idempotencyKey string, chainID *big.Int) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, idempotencyKey, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxWithIdempotencyKey") + } + var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, *big.Int) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -493,6 +573,10 @@ func (_m *EvmTxStore) FindTxWithIdempotencyKey(ctx context.Context, idempotencyK func (_m *EvmTxStore) FindTxWithSequence(ctx context.Context, fromAddress common.Address, seq evmtypes.Nonce) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, fromAddress, seq) + if len(ret) == 0 { + panic("no return value specified for FindTxWithSequence") + } + var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, evmtypes.Nonce) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -519,6 +603,10 @@ func (_m *EvmTxStore) FindTxWithSequence(ctx context.Context, fromAddress common func (_m *EvmTxStore) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, metaField, metaValue, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesByMetaFieldAndStates") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, []types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -545,6 +633,10 @@ func (_m *EvmTxStore) FindTxesByMetaFieldAndStates(ctx context.Context, metaFiel func (_m *EvmTxStore) FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error) { ret := _m.Called(ctx, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesPendingCallback") + } + var r0 []types.ReceiptPlus[*evmtypes.Receipt] var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error)); ok { @@ -571,6 +663,10 @@ func (_m *EvmTxStore) FindTxesPendingCallback(ctx context.Context, blockNum int6 func (_m *EvmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, ids, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithAttemptsAndReceiptsByIdsAndState") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -597,6 +693,10 @@ func (_m *EvmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.C func (_m *EvmTxStore) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, metaField, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithMetaFieldByReceiptBlockNum") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -623,6 +723,10 @@ func (_m *EvmTxStore) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context func (_m *EvmTxStore) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, metaField, states, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxesWithMetaFieldByStates") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, []types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -649,6 +753,10 @@ func (_m *EvmTxStore) FindTxesWithMetaFieldByStates(ctx context.Context, metaFie func (_m *EvmTxStore) FindTxsRequiringGasBump(ctx context.Context, address common.Address, blockNum int64, gasBumpThreshold int64, depth int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, address, blockNum, gasBumpThreshold, depth, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxsRequiringGasBump") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, int64, int64, int64, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -675,6 +783,10 @@ func (_m *EvmTxStore) FindTxsRequiringGasBump(ctx context.Context, address commo func (_m *EvmTxStore) FindTxsRequiringResubmissionDueToInsufficientFunds(ctx context.Context, address common.Address, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, address, chainID) + if len(ret) == 0 { + panic("no return value specified for FindTxsRequiringResubmissionDueToInsufficientFunds") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -701,6 +813,10 @@ func (_m *EvmTxStore) FindTxsRequiringResubmissionDueToInsufficientFunds(ctx con func (_m *EvmTxStore) GetInProgressTxAttempts(ctx context.Context, address common.Address, chainID *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, address, chainID) + if len(ret) == 0 { + panic("no return value specified for GetInProgressTxAttempts") + } + var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -727,6 +843,10 @@ func (_m *EvmTxStore) GetInProgressTxAttempts(ctx context.Context, address commo func (_m *EvmTxStore) GetNonFatalTransactions(ctx context.Context, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for GetNonFatalTransactions") + } + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -753,6 +873,10 @@ func (_m *EvmTxStore) GetNonFatalTransactions(ctx context.Context, chainID *big. func (_m *EvmTxStore) GetTxByID(ctx context.Context, id int64) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetTxByID") + } + var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -779,6 +903,10 @@ func (_m *EvmTxStore) GetTxByID(ctx context.Context, id int64) (*types.Tx[*big.I func (_m *EvmTxStore) GetTxInProgress(ctx context.Context, fromAddress common.Address) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, fromAddress) + if len(ret) == 0 { + panic("no return value specified for GetTxInProgress") + } + var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { @@ -805,6 +933,10 @@ func (_m *EvmTxStore) GetTxInProgress(ctx context.Context, fromAddress common.Ad func (_m *EvmTxStore) HasInProgressTransaction(ctx context.Context, account common.Address, chainID *big.Int) (bool, error) { ret := _m.Called(ctx, account, chainID) + if len(ret) == 0 { + panic("no return value specified for HasInProgressTransaction") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (bool, error)); ok { @@ -829,6 +961,10 @@ func (_m *EvmTxStore) HasInProgressTransaction(ctx context.Context, account comm func (_m *EvmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int) (bool, error) { ret := _m.Called(ctx, blockHeight, txID, chainID) + if len(ret) == 0 { + panic("no return value specified for IsTxFinalized") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) (bool, error)); ok { @@ -853,6 +989,10 @@ func (_m *EvmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID func (_m *EvmTxStore) LoadTxAttempts(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx) + if len(ret) == 0 { + panic("no return value specified for LoadTxAttempts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, etx) @@ -867,6 +1007,10 @@ func (_m *EvmTxStore) LoadTxAttempts(ctx context.Context, etx *types.Tx[*big.Int func (_m *EvmTxStore) MarkAllConfirmedMissingReceipt(ctx context.Context, chainID *big.Int) error { ret := _m.Called(ctx, chainID) + if len(ret) == 0 { + panic("no return value specified for MarkAllConfirmedMissingReceipt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int) error); ok { r0 = rf(ctx, chainID) @@ -881,6 +1025,10 @@ func (_m *EvmTxStore) MarkAllConfirmedMissingReceipt(ctx context.Context, chainI func (_m *EvmTxStore) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, finalityDepth uint32, chainID *big.Int) error { ret := _m.Called(ctx, blockNum, finalityDepth, chainID) + if len(ret) == 0 { + panic("no return value specified for MarkOldTxesMissingReceiptAsErrored") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, uint32, *big.Int) error); ok { r0 = rf(ctx, blockNum, finalityDepth, chainID) @@ -895,6 +1043,10 @@ func (_m *EvmTxStore) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, bl func (_m *EvmTxStore) PreloadTxes(ctx context.Context, attempts []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, attempts) + if len(ret) == 0 { + panic("no return value specified for PreloadTxes") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, attempts) @@ -909,6 +1061,10 @@ func (_m *EvmTxStore) PreloadTxes(ctx context.Context, attempts []types.TxAttemp func (_m *EvmTxStore) PruneUnstartedTxQueue(ctx context.Context, queueSize uint32, subject uuid.UUID) (int64, error) { ret := _m.Called(ctx, queueSize, subject) + if len(ret) == 0 { + panic("no return value specified for PruneUnstartedTxQueue") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, uint32, uuid.UUID) (int64, error)); ok { @@ -933,6 +1089,10 @@ func (_m *EvmTxStore) PruneUnstartedTxQueue(ctx context.Context, queueSize uint3 func (_m *EvmTxStore) ReapTxHistory(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID *big.Int) error { ret := _m.Called(ctx, minBlockNumberToKeep, timeThreshold, chainID) + if len(ret) == 0 { + panic("no return value specified for ReapTxHistory") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, time.Time, *big.Int) error); ok { r0 = rf(ctx, minBlockNumberToKeep, timeThreshold, chainID) @@ -947,6 +1107,10 @@ func (_m *EvmTxStore) ReapTxHistory(ctx context.Context, minBlockNumberToKeep in func (_m *EvmTxStore) SaveConfirmedMissingReceiptAttempt(ctx context.Context, timeout time.Duration, attempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], broadcastAt time.Time) error { ret := _m.Called(ctx, timeout, attempt, broadcastAt) + if len(ret) == 0 { + panic("no return value specified for SaveConfirmedMissingReceiptAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration, *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], time.Time) error); ok { r0 = rf(ctx, timeout, attempt, broadcastAt) @@ -961,6 +1125,10 @@ func (_m *EvmTxStore) SaveConfirmedMissingReceiptAttempt(ctx context.Context, ti func (_m *EvmTxStore) SaveFetchedReceipts(ctx context.Context, receipts []*evmtypes.Receipt, chainID *big.Int) error { ret := _m.Called(ctx, receipts, chainID) + if len(ret) == 0 { + panic("no return value specified for SaveFetchedReceipts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []*evmtypes.Receipt, *big.Int) error); ok { r0 = rf(ctx, receipts, chainID) @@ -975,6 +1143,10 @@ func (_m *EvmTxStore) SaveFetchedReceipts(ctx context.Context, receipts []*evmty func (_m *EvmTxStore) SaveInProgressAttempt(ctx context.Context, attempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, attempt) + if len(ret) == 0 { + panic("no return value specified for SaveInProgressAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, attempt) @@ -989,6 +1161,10 @@ func (_m *EvmTxStore) SaveInProgressAttempt(ctx context.Context, attempt *types. func (_m *EvmTxStore) SaveInsufficientFundsAttempt(ctx context.Context, timeout time.Duration, attempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], broadcastAt time.Time) error { ret := _m.Called(ctx, timeout, attempt, broadcastAt) + if len(ret) == 0 { + panic("no return value specified for SaveInsufficientFundsAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration, *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], time.Time) error); ok { r0 = rf(ctx, timeout, attempt, broadcastAt) @@ -1003,6 +1179,10 @@ func (_m *EvmTxStore) SaveInsufficientFundsAttempt(ctx context.Context, timeout func (_m *EvmTxStore) SaveReplacementInProgressAttempt(ctx context.Context, oldAttempt types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], replacementAttempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, oldAttempt, replacementAttempt) + if len(ret) == 0 { + panic("no return value specified for SaveReplacementInProgressAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, oldAttempt, replacementAttempt) @@ -1017,6 +1197,10 @@ func (_m *EvmTxStore) SaveReplacementInProgressAttempt(ctx context.Context, oldA func (_m *EvmTxStore) SaveSentAttempt(ctx context.Context, timeout time.Duration, attempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], broadcastAt time.Time) error { ret := _m.Called(ctx, timeout, attempt, broadcastAt) + if len(ret) == 0 { + panic("no return value specified for SaveSentAttempt") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration, *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], time.Time) error); ok { r0 = rf(ctx, timeout, attempt, broadcastAt) @@ -1031,6 +1215,10 @@ func (_m *EvmTxStore) SaveSentAttempt(ctx context.Context, timeout time.Duration func (_m *EvmTxStore) SetBroadcastBeforeBlockNum(ctx context.Context, blockNum int64, chainID *big.Int) error { ret := _m.Called(ctx, blockNum, chainID) + if len(ret) == 0 { + panic("no return value specified for SetBroadcastBeforeBlockNum") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) error); ok { r0 = rf(ctx, blockNum, chainID) @@ -1045,6 +1233,10 @@ func (_m *EvmTxStore) SetBroadcastBeforeBlockNum(ctx context.Context, blockNum i func (_m *EvmTxStore) Transactions(offset int, limit int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for Transactions") + } + var r0 []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 int var r2 error @@ -1078,6 +1270,10 @@ func (_m *EvmTxStore) Transactions(offset int, limit int) ([]types.Tx[*big.Int, func (_m *EvmTxStore) TransactionsWithAttempts(offset int, limit int) ([]types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for TransactionsWithAttempts") + } + var r0 []types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 int var r2 error @@ -1111,6 +1307,10 @@ func (_m *EvmTxStore) TransactionsWithAttempts(offset int, limit int) ([]types.T func (_m *EvmTxStore) TxAttempts(offset int, limit int) ([]types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for TxAttempts") + } + var r0 []types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] var r1 int var r2 error @@ -1144,6 +1344,10 @@ func (_m *EvmTxStore) TxAttempts(offset int, limit int) ([]types.TxAttempt[*big. func (_m *EvmTxStore) UpdateBroadcastAts(ctx context.Context, now time.Time, etxIDs []int64) error { ret := _m.Called(ctx, now, etxIDs) + if len(ret) == 0 { + panic("no return value specified for UpdateBroadcastAts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Time, []int64) error); ok { r0 = rf(ctx, now, etxIDs) @@ -1158,6 +1362,10 @@ func (_m *EvmTxStore) UpdateBroadcastAts(ctx context.Context, now time.Time, etx func (_m *EvmTxStore) UpdateTxAttemptInProgressToBroadcast(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], attempt types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], NewAttemptState types.TxAttemptState) error { ret := _m.Called(ctx, etx, attempt, NewAttemptState) + if len(ret) == 0 { + panic("no return value specified for UpdateTxAttemptInProgressToBroadcast") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], types.TxAttemptState) error); ok { r0 = rf(ctx, etx, attempt, NewAttemptState) @@ -1172,6 +1380,10 @@ func (_m *EvmTxStore) UpdateTxAttemptInProgressToBroadcast(ctx context.Context, func (_m *EvmTxStore) UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId *big.Int) error { ret := _m.Called(ctx, pipelineTaskRunRid, chainId) + if len(ret) == 0 { + panic("no return value specified for UpdateTxCallbackCompleted") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, *big.Int) error); ok { r0 = rf(ctx, pipelineTaskRunRid, chainId) @@ -1186,6 +1398,10 @@ func (_m *EvmTxStore) UpdateTxCallbackCompleted(ctx context.Context, pipelineTas func (_m *EvmTxStore) UpdateTxFatalError(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx) + if len(ret) == 0 { + panic("no return value specified for UpdateTxFatalError") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, etx) @@ -1200,6 +1416,10 @@ func (_m *EvmTxStore) UpdateTxFatalError(ctx context.Context, etx *types.Tx[*big func (_m *EvmTxStore) UpdateTxForRebroadcast(ctx context.Context, etx types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], etxAttempt types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx, etxAttempt) + if len(ret) == 0 { + panic("no return value specified for UpdateTxForRebroadcast") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, etx, etxAttempt) @@ -1214,6 +1434,10 @@ func (_m *EvmTxStore) UpdateTxForRebroadcast(ctx context.Context, etx types.Tx[* func (_m *EvmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], attempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx, attempt) + if len(ret) == 0 { + panic("no return value specified for UpdateTxUnstartedToInProgress") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error); ok { r0 = rf(ctx, etx, attempt) @@ -1228,6 +1452,10 @@ func (_m *EvmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *ty func (_m *EvmTxStore) UpdateTxsUnconfirmed(ctx context.Context, ids []int64) error { ret := _m.Called(ctx, ids) + if len(ret) == 0 { + panic("no return value specified for UpdateTxsUnconfirmed") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []int64) error); ok { r0 = rf(ctx, ids) diff --git a/core/chains/legacyevm/mocks/chain.go b/core/chains/legacyevm/mocks/chain.go index 3a686887d76..d8cc4895493 100644 --- a/core/chains/legacyevm/mocks/chain.go +++ b/core/chains/legacyevm/mocks/chain.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -42,6 +42,10 @@ type Chain struct { func (_m *Chain) BalanceMonitor() monitor.BalanceMonitor { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BalanceMonitor") + } + var r0 monitor.BalanceMonitor if rf, ok := ret.Get(0).(func() monitor.BalanceMonitor); ok { r0 = rf() @@ -58,6 +62,10 @@ func (_m *Chain) BalanceMonitor() monitor.BalanceMonitor { func (_m *Chain) Client() client.Client { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Client") + } + var r0 client.Client if rf, ok := ret.Get(0).(func() client.Client); ok { r0 = rf() @@ -74,6 +82,10 @@ func (_m *Chain) Client() client.Client { func (_m *Chain) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -88,6 +100,10 @@ func (_m *Chain) Close() error { func (_m *Chain) Config() config.ChainScopedConfig { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Config") + } + var r0 config.ChainScopedConfig if rf, ok := ret.Get(0).(func() config.ChainScopedConfig); ok { r0 = rf() @@ -104,6 +120,10 @@ func (_m *Chain) Config() config.ChainScopedConfig { func (_m *Chain) GasEstimator() gas.EvmFeeEstimator { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GasEstimator") + } + var r0 gas.EvmFeeEstimator if rf, ok := ret.Get(0).(func() gas.EvmFeeEstimator); ok { r0 = rf() @@ -120,6 +140,10 @@ func (_m *Chain) GasEstimator() gas.EvmFeeEstimator { func (_m *Chain) GetChainStatus(ctx context.Context) (types.ChainStatus, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetChainStatus") + } + var r0 types.ChainStatus var r1 error if rf, ok := ret.Get(0).(func(context.Context) (types.ChainStatus, error)); ok { @@ -144,6 +168,10 @@ func (_m *Chain) GetChainStatus(ctx context.Context) (types.ChainStatus, error) func (_m *Chain) HeadBroadcaster() commontypes.HeadBroadcaster[*evmtypes.Head, common.Hash] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HeadBroadcaster") + } + var r0 commontypes.HeadBroadcaster[*evmtypes.Head, common.Hash] if rf, ok := ret.Get(0).(func() commontypes.HeadBroadcaster[*evmtypes.Head, common.Hash]); ok { r0 = rf() @@ -160,6 +188,10 @@ func (_m *Chain) HeadBroadcaster() commontypes.HeadBroadcaster[*evmtypes.Head, c func (_m *Chain) HeadTracker() commontypes.HeadTracker[*evmtypes.Head, common.Hash] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HeadTracker") + } + var r0 commontypes.HeadTracker[*evmtypes.Head, common.Hash] if rf, ok := ret.Get(0).(func() commontypes.HeadTracker[*evmtypes.Head, common.Hash]); ok { r0 = rf() @@ -176,6 +208,10 @@ func (_m *Chain) HeadTracker() commontypes.HeadTracker[*evmtypes.Head, common.Ha func (_m *Chain) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -192,6 +228,10 @@ func (_m *Chain) HealthReport() map[string]error { func (_m *Chain) ID() *big.Int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ID") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() @@ -208,6 +248,10 @@ func (_m *Chain) ID() *big.Int { func (_m *Chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) ([]types.NodeStatus, string, int, error) { ret := _m.Called(ctx, pageSize, pageToken) + if len(ret) == 0 { + panic("no return value specified for ListNodeStatuses") + } + var r0 []types.NodeStatus var r1 string var r2 int @@ -248,6 +292,10 @@ func (_m *Chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken func (_m *Chain) LogBroadcaster() log.Broadcaster { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LogBroadcaster") + } + var r0 log.Broadcaster if rf, ok := ret.Get(0).(func() log.Broadcaster); ok { r0 = rf() @@ -264,6 +312,10 @@ func (_m *Chain) LogBroadcaster() log.Broadcaster { func (_m *Chain) LogPoller() logpoller.LogPoller { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LogPoller") + } + var r0 logpoller.LogPoller if rf, ok := ret.Get(0).(func() logpoller.LogPoller); ok { r0 = rf() @@ -280,6 +332,10 @@ func (_m *Chain) LogPoller() logpoller.LogPoller { func (_m *Chain) Logger() logger.Logger { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Logger") + } + var r0 logger.Logger if rf, ok := ret.Get(0).(func() logger.Logger); ok { r0 = rf() @@ -296,6 +352,10 @@ func (_m *Chain) Logger() logger.Logger { func (_m *Chain) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -310,6 +370,10 @@ func (_m *Chain) Name() string { func (_m *Chain) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -324,6 +388,10 @@ func (_m *Chain) Ready() error { func (_m *Chain) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -338,6 +406,10 @@ func (_m *Chain) Start(_a0 context.Context) error { func (_m *Chain) Transact(ctx context.Context, from string, to string, amount *big.Int, balanceCheck bool) error { ret := _m.Called(ctx, from, to, amount, balanceCheck) + if len(ret) == 0 { + panic("no return value specified for Transact") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, *big.Int, bool) error); ok { r0 = rf(ctx, from, to, amount, balanceCheck) @@ -352,6 +424,10 @@ func (_m *Chain) Transact(ctx context.Context, from string, to string, amount *b func (_m *Chain) TxManager() txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TxManager") + } + var r0 txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] if rf, ok := ret.Get(0).(func() txmgr.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { r0 = rf() diff --git a/core/chains/legacyevm/mocks/legacy_chain_container.go b/core/chains/legacyevm/mocks/legacy_chain_container.go index 9ebacb890aa..812b95d3697 100644 --- a/core/chains/legacyevm/mocks/legacy_chain_container.go +++ b/core/chains/legacyevm/mocks/legacy_chain_container.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type LegacyChainContainer struct { func (_m *LegacyChainContainer) ChainNodeConfigs() types.Configs { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainNodeConfigs") + } + var r0 types.Configs if rf, ok := ret.Get(0).(func() types.Configs); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *LegacyChainContainer) ChainNodeConfigs() types.Configs { func (_m *LegacyChainContainer) Get(id string) (legacyevm.Chain, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 legacyevm.Chain var r1 error if rf, ok := ret.Get(0).(func(string) (legacyevm.Chain, error)); ok { @@ -60,6 +68,10 @@ func (_m *LegacyChainContainer) Get(id string) (legacyevm.Chain, error) { func (_m *LegacyChainContainer) Len() int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Len") + } + var r0 int if rf, ok := ret.Get(0).(func() int); ok { r0 = rf() @@ -80,6 +92,10 @@ func (_m *LegacyChainContainer) List(ids ...string) ([]legacyevm.Chain, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []legacyevm.Chain var r1 error if rf, ok := ret.Get(0).(func(...string) ([]legacyevm.Chain, error)); ok { @@ -106,6 +122,10 @@ func (_m *LegacyChainContainer) List(ids ...string) ([]legacyevm.Chain, error) { func (_m *LegacyChainContainer) Slice() []legacyevm.Chain { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Slice") + } + var r0 []legacyevm.Chain if rf, ok := ret.Get(0).(func() []legacyevm.Chain); ok { r0 = rf() diff --git a/core/cmd/mocks/prompter.go b/core/cmd/mocks/prompter.go index c0f682d5b9f..a05d24d671b 100644 --- a/core/cmd/mocks/prompter.go +++ b/core/cmd/mocks/prompter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type Prompter struct { func (_m *Prompter) IsTerminal() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsTerminal") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -27,6 +31,10 @@ func (_m *Prompter) IsTerminal() bool { func (_m *Prompter) PasswordPrompt(_a0 string) string { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for PasswordPrompt") + } + var r0 string if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(_a0) @@ -41,6 +49,10 @@ func (_m *Prompter) PasswordPrompt(_a0 string) string { func (_m *Prompter) Prompt(_a0 string) string { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Prompt") + } + var r0 string if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(_a0) diff --git a/core/config/mocks/telemetry_ingress.go b/core/config/mocks/telemetry_ingress.go index 59f4ed10dad..ed5dbc4cf52 100644 --- a/core/config/mocks/telemetry_ingress.go +++ b/core/config/mocks/telemetry_ingress.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type TelemetryIngress struct { func (_m *TelemetryIngress) BufferSize() uint { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BufferSize") + } + var r0 uint if rf, ok := ret.Get(0).(func() uint); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *TelemetryIngress) BufferSize() uint { func (_m *TelemetryIngress) Endpoints() []config.TelemetryIngressEndpoint { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Endpoints") + } + var r0 []config.TelemetryIngressEndpoint if rf, ok := ret.Get(0).(func() []config.TelemetryIngressEndpoint); ok { r0 = rf() @@ -50,6 +58,10 @@ func (_m *TelemetryIngress) Endpoints() []config.TelemetryIngressEndpoint { func (_m *TelemetryIngress) Logging() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Logging") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -64,6 +76,10 @@ func (_m *TelemetryIngress) Logging() bool { func (_m *TelemetryIngress) MaxBatchSize() uint { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for MaxBatchSize") + } + var r0 uint if rf, ok := ret.Get(0).(func() uint); ok { r0 = rf() @@ -78,6 +94,10 @@ func (_m *TelemetryIngress) MaxBatchSize() uint { func (_m *TelemetryIngress) SendInterval() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SendInterval") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -92,6 +112,10 @@ func (_m *TelemetryIngress) SendInterval() time.Duration { func (_m *TelemetryIngress) SendTimeout() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SendTimeout") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -106,6 +130,10 @@ func (_m *TelemetryIngress) SendTimeout() time.Duration { func (_m *TelemetryIngress) ServerPubKey() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ServerPubKey") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -120,6 +148,10 @@ func (_m *TelemetryIngress) ServerPubKey() string { func (_m *TelemetryIngress) URL() *url.URL { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for URL") + } + var r0 *url.URL if rf, ok := ret.Get(0).(func() *url.URL); ok { r0 = rf() @@ -136,6 +168,10 @@ func (_m *TelemetryIngress) URL() *url.URL { func (_m *TelemetryIngress) UniConn() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for UniConn") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -150,6 +186,10 @@ func (_m *TelemetryIngress) UniConn() bool { func (_m *TelemetryIngress) UseBatchSend() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for UseBatchSend") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() diff --git a/core/config/mocks/telemetry_ingress_endpoint.go b/core/config/mocks/telemetry_ingress_endpoint.go index 7aa6f46aa2b..08432cfe0ee 100644 --- a/core/config/mocks/telemetry_ingress_endpoint.go +++ b/core/config/mocks/telemetry_ingress_endpoint.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type TelemetryIngressEndpoint struct { func (_m *TelemetryIngressEndpoint) ChainID() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -31,6 +35,10 @@ func (_m *TelemetryIngressEndpoint) ChainID() string { func (_m *TelemetryIngressEndpoint) Network() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Network") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -45,6 +53,10 @@ func (_m *TelemetryIngressEndpoint) Network() string { func (_m *TelemetryIngressEndpoint) ServerPubKey() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ServerPubKey") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -59,6 +71,10 @@ func (_m *TelemetryIngressEndpoint) ServerPubKey() string { func (_m *TelemetryIngressEndpoint) URL() *url.URL { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for URL") + } + var r0 *url.URL if rf, ok := ret.Get(0).(func() *url.URL); ok { r0 = rf() diff --git a/core/internal/mocks/application.go b/core/internal/mocks/application.go index 48f8e12dac3..20874e4b60e 100644 --- a/core/internal/mocks/application.go +++ b/core/internal/mocks/application.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -53,6 +53,10 @@ type Application struct { func (_m *Application) AddJobV2(ctx context.Context, _a1 *job.Job) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for AddJobV2") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *job.Job) error); ok { r0 = rf(ctx, _a1) @@ -67,6 +71,10 @@ func (_m *Application) AddJobV2(ctx context.Context, _a1 *job.Job) error { func (_m *Application) AuthenticationProvider() sessions.AuthenticationProvider { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AuthenticationProvider") + } + var r0 sessions.AuthenticationProvider if rf, ok := ret.Get(0).(func() sessions.AuthenticationProvider); ok { r0 = rf() @@ -83,6 +91,10 @@ func (_m *Application) AuthenticationProvider() sessions.AuthenticationProvider func (_m *Application) BasicAdminUsersORM() sessions.BasicAdminUsersORM { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BasicAdminUsersORM") + } + var r0 sessions.BasicAdminUsersORM if rf, ok := ret.Get(0).(func() sessions.BasicAdminUsersORM); ok { r0 = rf() @@ -99,6 +111,10 @@ func (_m *Application) BasicAdminUsersORM() sessions.BasicAdminUsersORM { func (_m *Application) BridgeORM() bridges.ORM { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for BridgeORM") + } + var r0 bridges.ORM if rf, ok := ret.Get(0).(func() bridges.ORM); ok { r0 = rf() @@ -115,6 +131,10 @@ func (_m *Application) BridgeORM() bridges.ORM { func (_m *Application) DeleteJob(ctx context.Context, jobID int32) error { ret := _m.Called(ctx, jobID) + if len(ret) == 0 { + panic("no return value specified for DeleteJob") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int32) error); ok { r0 = rf(ctx, jobID) @@ -129,6 +149,10 @@ func (_m *Application) DeleteJob(ctx context.Context, jobID int32) error { func (_m *Application) EVMORM() types.Configs { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMORM") + } + var r0 types.Configs if rf, ok := ret.Get(0).(func() types.Configs); ok { r0 = rf() @@ -145,6 +169,10 @@ func (_m *Application) EVMORM() types.Configs { func (_m *Application) GetAuditLogger() audit.AuditLogger { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAuditLogger") + } + var r0 audit.AuditLogger if rf, ok := ret.Get(0).(func() audit.AuditLogger); ok { r0 = rf() @@ -161,6 +189,10 @@ func (_m *Application) GetAuditLogger() audit.AuditLogger { func (_m *Application) GetConfig() chainlink.GeneralConfig { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetConfig") + } + var r0 chainlink.GeneralConfig if rf, ok := ret.Get(0).(func() chainlink.GeneralConfig); ok { r0 = rf() @@ -177,6 +209,10 @@ func (_m *Application) GetConfig() chainlink.GeneralConfig { func (_m *Application) GetExternalInitiatorManager() webhook.ExternalInitiatorManager { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetExternalInitiatorManager") + } + var r0 webhook.ExternalInitiatorManager if rf, ok := ret.Get(0).(func() webhook.ExternalInitiatorManager); ok { r0 = rf() @@ -193,6 +229,10 @@ func (_m *Application) GetExternalInitiatorManager() webhook.ExternalInitiatorMa func (_m *Application) GetFeedsService() feeds.Service { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetFeedsService") + } + var r0 feeds.Service if rf, ok := ret.Get(0).(func() feeds.Service); ok { r0 = rf() @@ -209,6 +249,10 @@ func (_m *Application) GetFeedsService() feeds.Service { func (_m *Application) GetHealthChecker() services.Checker { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetHealthChecker") + } + var r0 services.Checker if rf, ok := ret.Get(0).(func() services.Checker); ok { r0 = rf() @@ -225,6 +269,10 @@ func (_m *Application) GetHealthChecker() services.Checker { func (_m *Application) GetKeyStore() keystore.Master { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetKeyStore") + } + var r0 keystore.Master if rf, ok := ret.Get(0).(func() keystore.Master); ok { r0 = rf() @@ -241,6 +289,10 @@ func (_m *Application) GetKeyStore() keystore.Master { func (_m *Application) GetLogger() logger.SugaredLogger { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetLogger") + } + var r0 logger.SugaredLogger if rf, ok := ret.Get(0).(func() logger.SugaredLogger); ok { r0 = rf() @@ -257,6 +309,10 @@ func (_m *Application) GetLogger() logger.SugaredLogger { func (_m *Application) GetLoopRegistry() *plugins.LoopRegistry { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetLoopRegistry") + } + var r0 *plugins.LoopRegistry if rf, ok := ret.Get(0).(func() *plugins.LoopRegistry); ok { r0 = rf() @@ -273,6 +329,10 @@ func (_m *Application) GetLoopRegistry() *plugins.LoopRegistry { func (_m *Application) GetRelayers() chainlink.RelayerChainInteroperators { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetRelayers") + } + var r0 chainlink.RelayerChainInteroperators if rf, ok := ret.Get(0).(func() chainlink.RelayerChainInteroperators); ok { r0 = rf() @@ -289,6 +349,10 @@ func (_m *Application) GetRelayers() chainlink.RelayerChainInteroperators { func (_m *Application) GetSqlxDB() *sqlx.DB { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetSqlxDB") + } + var r0 *sqlx.DB if rf, ok := ret.Get(0).(func() *sqlx.DB); ok { r0 = rf() @@ -305,6 +369,10 @@ func (_m *Application) GetSqlxDB() *sqlx.DB { func (_m *Application) GetWebAuthnConfiguration() sessions.WebAuthnConfiguration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetWebAuthnConfiguration") + } + var r0 sessions.WebAuthnConfiguration if rf, ok := ret.Get(0).(func() sessions.WebAuthnConfiguration); ok { r0 = rf() @@ -319,6 +387,10 @@ func (_m *Application) GetWebAuthnConfiguration() sessions.WebAuthnConfiguration func (_m *Application) ID() uuid.UUID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ID") + } + var r0 uuid.UUID if rf, ok := ret.Get(0).(func() uuid.UUID); ok { r0 = rf() @@ -335,6 +407,10 @@ func (_m *Application) ID() uuid.UUID { func (_m *Application) JobORM() job.ORM { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for JobORM") + } + var r0 job.ORM if rf, ok := ret.Get(0).(func() job.ORM); ok { r0 = rf() @@ -351,6 +427,10 @@ func (_m *Application) JobORM() job.ORM { func (_m *Application) JobSpawner() job.Spawner { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for JobSpawner") + } + var r0 job.Spawner if rf, ok := ret.Get(0).(func() job.Spawner); ok { r0 = rf() @@ -367,6 +447,10 @@ func (_m *Application) JobSpawner() job.Spawner { func (_m *Application) PipelineORM() pipeline.ORM { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for PipelineORM") + } + var r0 pipeline.ORM if rf, ok := ret.Get(0).(func() pipeline.ORM); ok { r0 = rf() @@ -383,6 +467,10 @@ func (_m *Application) PipelineORM() pipeline.ORM { func (_m *Application) ReplayFromBlock(chainID *big.Int, number uint64, forceBroadcast bool) error { ret := _m.Called(chainID, number, forceBroadcast) + if len(ret) == 0 { + panic("no return value specified for ReplayFromBlock") + } + var r0 error if rf, ok := ret.Get(0).(func(*big.Int, uint64, bool) error); ok { r0 = rf(chainID, number, forceBroadcast) @@ -397,6 +485,10 @@ func (_m *Application) ReplayFromBlock(chainID *big.Int, number uint64, forceBro func (_m *Application) ResumeJobV2(ctx context.Context, taskID uuid.UUID, result pipeline.Result) error { ret := _m.Called(ctx, taskID, result) + if len(ret) == 0 { + panic("no return value specified for ResumeJobV2") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, pipeline.Result) error); ok { r0 = rf(ctx, taskID, result) @@ -411,6 +503,10 @@ func (_m *Application) ResumeJobV2(ctx context.Context, taskID uuid.UUID, result func (_m *Application) RunJobV2(ctx context.Context, jobID int32, meta map[string]interface{}) (int64, error) { ret := _m.Called(ctx, jobID, meta) + if len(ret) == 0 { + panic("no return value specified for RunJobV2") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int32, map[string]interface{}) (int64, error)); ok { @@ -435,6 +531,10 @@ func (_m *Application) RunJobV2(ctx context.Context, jobID int32, meta map[strin func (_m *Application) RunWebhookJobV2(ctx context.Context, jobUUID uuid.UUID, requestBody string, meta pipeline.JSONSerializable) (int64, error) { ret := _m.Called(ctx, jobUUID, requestBody, meta) + if len(ret) == 0 { + panic("no return value specified for RunWebhookJobV2") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, string, pipeline.JSONSerializable) (int64, error)); ok { @@ -459,6 +559,10 @@ func (_m *Application) RunWebhookJobV2(ctx context.Context, jobUUID uuid.UUID, r func (_m *Application) SecretGenerator() chainlink.SecretGenerator { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SecretGenerator") + } + var r0 chainlink.SecretGenerator if rf, ok := ret.Get(0).(func() chainlink.SecretGenerator); ok { r0 = rf() @@ -475,6 +579,10 @@ func (_m *Application) SecretGenerator() chainlink.SecretGenerator { func (_m *Application) SetLogLevel(lvl zapcore.Level) error { ret := _m.Called(lvl) + if len(ret) == 0 { + panic("no return value specified for SetLogLevel") + } + var r0 error if rf, ok := ret.Get(0).(func(zapcore.Level) error); ok { r0 = rf(lvl) @@ -489,6 +597,10 @@ func (_m *Application) SetLogLevel(lvl zapcore.Level) error { func (_m *Application) Start(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -503,6 +615,10 @@ func (_m *Application) Start(ctx context.Context) error { func (_m *Application) Stop() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -517,6 +633,10 @@ func (_m *Application) Stop() error { func (_m *Application) TxmStorageService() txmgr.EvmTxStore { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TxmStorageService") + } + var r0 txmgr.EvmTxStore if rf, ok := ret.Get(0).(func() txmgr.EvmTxStore); ok { r0 = rf() diff --git a/core/internal/mocks/flags.go b/core/internal/mocks/flags.go index 5b5aa812680..32d21326ba0 100644 --- a/core/internal/mocks/flags.go +++ b/core/internal/mocks/flags.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -26,6 +26,10 @@ type Flags struct { func (_m *Flags) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AcceptOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -52,6 +56,10 @@ func (_m *Flags) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, e func (_m *Flags) AddAccess(opts *bind.TransactOpts, _user common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _user) + if len(ret) == 0 { + panic("no return value specified for AddAccess") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -78,6 +86,10 @@ func (_m *Flags) AddAccess(opts *bind.TransactOpts, _user common.Address) (*type func (_m *Flags) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -94,6 +106,10 @@ func (_m *Flags) Address() common.Address { func (_m *Flags) CheckEnabled(opts *bind.CallOpts) (bool, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for CheckEnabled") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (bool, error)); ok { @@ -118,6 +134,10 @@ func (_m *Flags) CheckEnabled(opts *bind.CallOpts) (bool, error) { func (_m *Flags) DisableAccessCheck(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for DisableAccessCheck") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -144,6 +164,10 @@ func (_m *Flags) DisableAccessCheck(opts *bind.TransactOpts) (*types.Transaction func (_m *Flags) EnableAccessCheck(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for EnableAccessCheck") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -170,6 +194,10 @@ func (_m *Flags) EnableAccessCheck(opts *bind.TransactOpts) (*types.Transaction, func (_m *Flags) FilterAddedAccess(opts *bind.FilterOpts) (*flags_wrapper.FlagsAddedAccessIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterAddedAccess") + } + var r0 *flags_wrapper.FlagsAddedAccessIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*flags_wrapper.FlagsAddedAccessIterator, error)); ok { @@ -196,6 +224,10 @@ func (_m *Flags) FilterAddedAccess(opts *bind.FilterOpts) (*flags_wrapper.FlagsA func (_m *Flags) FilterCheckAccessDisabled(opts *bind.FilterOpts) (*flags_wrapper.FlagsCheckAccessDisabledIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterCheckAccessDisabled") + } + var r0 *flags_wrapper.FlagsCheckAccessDisabledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*flags_wrapper.FlagsCheckAccessDisabledIterator, error)); ok { @@ -222,6 +254,10 @@ func (_m *Flags) FilterCheckAccessDisabled(opts *bind.FilterOpts) (*flags_wrappe func (_m *Flags) FilterCheckAccessEnabled(opts *bind.FilterOpts) (*flags_wrapper.FlagsCheckAccessEnabledIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterCheckAccessEnabled") + } + var r0 *flags_wrapper.FlagsCheckAccessEnabledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*flags_wrapper.FlagsCheckAccessEnabledIterator, error)); ok { @@ -248,6 +284,10 @@ func (_m *Flags) FilterCheckAccessEnabled(opts *bind.FilterOpts) (*flags_wrapper func (_m *Flags) FilterFlagLowered(opts *bind.FilterOpts, subject []common.Address) (*flags_wrapper.FlagsFlagLoweredIterator, error) { ret := _m.Called(opts, subject) + if len(ret) == 0 { + panic("no return value specified for FilterFlagLowered") + } + var r0 *flags_wrapper.FlagsFlagLoweredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*flags_wrapper.FlagsFlagLoweredIterator, error)); ok { @@ -274,6 +314,10 @@ func (_m *Flags) FilterFlagLowered(opts *bind.FilterOpts, subject []common.Addre func (_m *Flags) FilterFlagRaised(opts *bind.FilterOpts, subject []common.Address) (*flags_wrapper.FlagsFlagRaisedIterator, error) { ret := _m.Called(opts, subject) + if len(ret) == 0 { + panic("no return value specified for FilterFlagRaised") + } + var r0 *flags_wrapper.FlagsFlagRaisedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*flags_wrapper.FlagsFlagRaisedIterator, error)); ok { @@ -300,6 +344,10 @@ func (_m *Flags) FilterFlagRaised(opts *bind.FilterOpts, subject []common.Addres func (_m *Flags) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*flags_wrapper.FlagsOwnershipTransferRequestedIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferRequested") + } + var r0 *flags_wrapper.FlagsOwnershipTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flags_wrapper.FlagsOwnershipTransferRequestedIterator, error)); ok { @@ -326,6 +374,10 @@ func (_m *Flags) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from [] func (_m *Flags) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*flags_wrapper.FlagsOwnershipTransferredIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferred") + } + var r0 *flags_wrapper.FlagsOwnershipTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flags_wrapper.FlagsOwnershipTransferredIterator, error)); ok { @@ -352,6 +404,10 @@ func (_m *Flags) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common func (_m *Flags) FilterRaisingAccessControllerUpdated(opts *bind.FilterOpts, previous []common.Address, current []common.Address) (*flags_wrapper.FlagsRaisingAccessControllerUpdatedIterator, error) { ret := _m.Called(opts, previous, current) + if len(ret) == 0 { + panic("no return value specified for FilterRaisingAccessControllerUpdated") + } + var r0 *flags_wrapper.FlagsRaisingAccessControllerUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flags_wrapper.FlagsRaisingAccessControllerUpdatedIterator, error)); ok { @@ -378,6 +434,10 @@ func (_m *Flags) FilterRaisingAccessControllerUpdated(opts *bind.FilterOpts, pre func (_m *Flags) FilterRemovedAccess(opts *bind.FilterOpts) (*flags_wrapper.FlagsRemovedAccessIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterRemovedAccess") + } + var r0 *flags_wrapper.FlagsRemovedAccessIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*flags_wrapper.FlagsRemovedAccessIterator, error)); ok { @@ -404,6 +464,10 @@ func (_m *Flags) FilterRemovedAccess(opts *bind.FilterOpts) (*flags_wrapper.Flag func (_m *Flags) GetFlag(opts *bind.CallOpts, subject common.Address) (bool, error) { ret := _m.Called(opts, subject) + if len(ret) == 0 { + panic("no return value specified for GetFlag") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (bool, error)); ok { @@ -428,6 +492,10 @@ func (_m *Flags) GetFlag(opts *bind.CallOpts, subject common.Address) (bool, err func (_m *Flags) GetFlags(opts *bind.CallOpts, subjects []common.Address) ([]bool, error) { ret := _m.Called(opts, subjects) + if len(ret) == 0 { + panic("no return value specified for GetFlags") + } + var r0 []bool var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, []common.Address) ([]bool, error)); ok { @@ -454,6 +522,10 @@ func (_m *Flags) GetFlags(opts *bind.CallOpts, subjects []common.Address) ([]boo func (_m *Flags) HasAccess(opts *bind.CallOpts, _user common.Address, _calldata []byte) (bool, error) { ret := _m.Called(opts, _user, _calldata) + if len(ret) == 0 { + panic("no return value specified for HasAccess") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address, []byte) (bool, error)); ok { @@ -478,6 +550,10 @@ func (_m *Flags) HasAccess(opts *bind.CallOpts, _user common.Address, _calldata func (_m *Flags) LowerFlags(opts *bind.TransactOpts, subjects []common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subjects) + if len(ret) == 0 { + panic("no return value specified for LowerFlags") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address) (*types.Transaction, error)); ok { @@ -504,6 +580,10 @@ func (_m *Flags) LowerFlags(opts *bind.TransactOpts, subjects []common.Address) func (_m *Flags) Owner(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Owner") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -530,6 +610,10 @@ func (_m *Flags) Owner(opts *bind.CallOpts) (common.Address, error) { func (_m *Flags) ParseAddedAccess(log types.Log) (*flags_wrapper.FlagsAddedAccess, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseAddedAccess") + } + var r0 *flags_wrapper.FlagsAddedAccess var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsAddedAccess, error)); ok { @@ -556,6 +640,10 @@ func (_m *Flags) ParseAddedAccess(log types.Log) (*flags_wrapper.FlagsAddedAcces func (_m *Flags) ParseCheckAccessDisabled(log types.Log) (*flags_wrapper.FlagsCheckAccessDisabled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseCheckAccessDisabled") + } + var r0 *flags_wrapper.FlagsCheckAccessDisabled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsCheckAccessDisabled, error)); ok { @@ -582,6 +670,10 @@ func (_m *Flags) ParseCheckAccessDisabled(log types.Log) (*flags_wrapper.FlagsCh func (_m *Flags) ParseCheckAccessEnabled(log types.Log) (*flags_wrapper.FlagsCheckAccessEnabled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseCheckAccessEnabled") + } + var r0 *flags_wrapper.FlagsCheckAccessEnabled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsCheckAccessEnabled, error)); ok { @@ -608,6 +700,10 @@ func (_m *Flags) ParseCheckAccessEnabled(log types.Log) (*flags_wrapper.FlagsChe func (_m *Flags) ParseFlagLowered(log types.Log) (*flags_wrapper.FlagsFlagLowered, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseFlagLowered") + } + var r0 *flags_wrapper.FlagsFlagLowered var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsFlagLowered, error)); ok { @@ -634,6 +730,10 @@ func (_m *Flags) ParseFlagLowered(log types.Log) (*flags_wrapper.FlagsFlagLowere func (_m *Flags) ParseFlagRaised(log types.Log) (*flags_wrapper.FlagsFlagRaised, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseFlagRaised") + } + var r0 *flags_wrapper.FlagsFlagRaised var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsFlagRaised, error)); ok { @@ -660,6 +760,10 @@ func (_m *Flags) ParseFlagRaised(log types.Log) (*flags_wrapper.FlagsFlagRaised, func (_m *Flags) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { @@ -686,6 +790,10 @@ func (_m *Flags) ParseLog(log types.Log) (generated.AbigenLog, error) { func (_m *Flags) ParseOwnershipTransferRequested(log types.Log) (*flags_wrapper.FlagsOwnershipTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferRequested") + } + var r0 *flags_wrapper.FlagsOwnershipTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsOwnershipTransferRequested, error)); ok { @@ -712,6 +820,10 @@ func (_m *Flags) ParseOwnershipTransferRequested(log types.Log) (*flags_wrapper. func (_m *Flags) ParseOwnershipTransferred(log types.Log) (*flags_wrapper.FlagsOwnershipTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferred") + } + var r0 *flags_wrapper.FlagsOwnershipTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsOwnershipTransferred, error)); ok { @@ -738,6 +850,10 @@ func (_m *Flags) ParseOwnershipTransferred(log types.Log) (*flags_wrapper.FlagsO func (_m *Flags) ParseRaisingAccessControllerUpdated(log types.Log) (*flags_wrapper.FlagsRaisingAccessControllerUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRaisingAccessControllerUpdated") + } + var r0 *flags_wrapper.FlagsRaisingAccessControllerUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsRaisingAccessControllerUpdated, error)); ok { @@ -764,6 +880,10 @@ func (_m *Flags) ParseRaisingAccessControllerUpdated(log types.Log) (*flags_wrap func (_m *Flags) ParseRemovedAccess(log types.Log) (*flags_wrapper.FlagsRemovedAccess, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRemovedAccess") + } + var r0 *flags_wrapper.FlagsRemovedAccess var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flags_wrapper.FlagsRemovedAccess, error)); ok { @@ -790,6 +910,10 @@ func (_m *Flags) ParseRemovedAccess(log types.Log) (*flags_wrapper.FlagsRemovedA func (_m *Flags) RaiseFlag(opts *bind.TransactOpts, subject common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subject) + if len(ret) == 0 { + panic("no return value specified for RaiseFlag") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -816,6 +940,10 @@ func (_m *Flags) RaiseFlag(opts *bind.TransactOpts, subject common.Address) (*ty func (_m *Flags) RaiseFlags(opts *bind.TransactOpts, subjects []common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subjects) + if len(ret) == 0 { + panic("no return value specified for RaiseFlags") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address) (*types.Transaction, error)); ok { @@ -842,6 +970,10 @@ func (_m *Flags) RaiseFlags(opts *bind.TransactOpts, subjects []common.Address) func (_m *Flags) RaisingAccessController(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for RaisingAccessController") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -868,6 +1000,10 @@ func (_m *Flags) RaisingAccessController(opts *bind.CallOpts) (common.Address, e func (_m *Flags) RemoveAccess(opts *bind.TransactOpts, _user common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _user) + if len(ret) == 0 { + panic("no return value specified for RemoveAccess") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -894,6 +1030,10 @@ func (_m *Flags) RemoveAccess(opts *bind.TransactOpts, _user common.Address) (*t func (_m *Flags) SetRaisingAccessController(opts *bind.TransactOpts, racAddress common.Address) (*types.Transaction, error) { ret := _m.Called(opts, racAddress) + if len(ret) == 0 { + panic("no return value specified for SetRaisingAccessController") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -920,6 +1060,10 @@ func (_m *Flags) SetRaisingAccessController(opts *bind.TransactOpts, racAddress func (_m *Flags) TransferOwnership(opts *bind.TransactOpts, _to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _to) + if len(ret) == 0 { + panic("no return value specified for TransferOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -946,6 +1090,10 @@ func (_m *Flags) TransferOwnership(opts *bind.TransactOpts, _to common.Address) func (_m *Flags) WatchAddedAccess(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsAddedAccess) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchAddedAccess") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsAddedAccess) (event.Subscription, error)); ok { @@ -972,6 +1120,10 @@ func (_m *Flags) WatchAddedAccess(opts *bind.WatchOpts, sink chan<- *flags_wrapp func (_m *Flags) WatchCheckAccessDisabled(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsCheckAccessDisabled) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchCheckAccessDisabled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsCheckAccessDisabled) (event.Subscription, error)); ok { @@ -998,6 +1150,10 @@ func (_m *Flags) WatchCheckAccessDisabled(opts *bind.WatchOpts, sink chan<- *fla func (_m *Flags) WatchCheckAccessEnabled(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsCheckAccessEnabled) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchCheckAccessEnabled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsCheckAccessEnabled) (event.Subscription, error)); ok { @@ -1024,6 +1180,10 @@ func (_m *Flags) WatchCheckAccessEnabled(opts *bind.WatchOpts, sink chan<- *flag func (_m *Flags) WatchFlagLowered(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsFlagLowered, subject []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, subject) + if len(ret) == 0 { + panic("no return value specified for WatchFlagLowered") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsFlagLowered, []common.Address) (event.Subscription, error)); ok { @@ -1050,6 +1210,10 @@ func (_m *Flags) WatchFlagLowered(opts *bind.WatchOpts, sink chan<- *flags_wrapp func (_m *Flags) WatchFlagRaised(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsFlagRaised, subject []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, subject) + if len(ret) == 0 { + panic("no return value specified for WatchFlagRaised") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsFlagRaised, []common.Address) (event.Subscription, error)); ok { @@ -1076,6 +1240,10 @@ func (_m *Flags) WatchFlagRaised(opts *bind.WatchOpts, sink chan<- *flags_wrappe func (_m *Flags) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsOwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1102,6 +1270,10 @@ func (_m *Flags) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan func (_m *Flags) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsOwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1128,6 +1300,10 @@ func (_m *Flags) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *fl func (_m *Flags) WatchRaisingAccessControllerUpdated(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsRaisingAccessControllerUpdated, previous []common.Address, current []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, previous, current) + if len(ret) == 0 { + panic("no return value specified for WatchRaisingAccessControllerUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsRaisingAccessControllerUpdated, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1154,6 +1330,10 @@ func (_m *Flags) WatchRaisingAccessControllerUpdated(opts *bind.WatchOpts, sink func (_m *Flags) WatchRemovedAccess(opts *bind.WatchOpts, sink chan<- *flags_wrapper.FlagsRemovedAccess) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchRemovedAccess") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flags_wrapper.FlagsRemovedAccess) (event.Subscription, error)); ok { diff --git a/core/internal/mocks/flux_aggregator.go b/core/internal/mocks/flux_aggregator.go index e3da1b83dff..ac72bd07db8 100644 --- a/core/internal/mocks/flux_aggregator.go +++ b/core/internal/mocks/flux_aggregator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ type FluxAggregator struct { func (_m *FluxAggregator) AcceptAdmin(opts *bind.TransactOpts, _oracle common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _oracle) + if len(ret) == 0 { + panic("no return value specified for AcceptAdmin") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -54,6 +58,10 @@ func (_m *FluxAggregator) AcceptAdmin(opts *bind.TransactOpts, _oracle common.Ad func (_m *FluxAggregator) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AcceptOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -80,6 +88,10 @@ func (_m *FluxAggregator) AcceptOwnership(opts *bind.TransactOpts) (*types.Trans func (_m *FluxAggregator) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -96,6 +108,10 @@ func (_m *FluxAggregator) Address() common.Address { func (_m *FluxAggregator) AllocatedFunds(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AllocatedFunds") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -122,6 +138,10 @@ func (_m *FluxAggregator) AllocatedFunds(opts *bind.CallOpts) (*big.Int, error) func (_m *FluxAggregator) AvailableFunds(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AvailableFunds") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -148,6 +168,10 @@ func (_m *FluxAggregator) AvailableFunds(opts *bind.CallOpts) (*big.Int, error) func (_m *FluxAggregator) ChangeOracles(opts *bind.TransactOpts, _removed []common.Address, _added []common.Address, _addedAdmins []common.Address, _minSubmissions uint32, _maxSubmissions uint32, _restartDelay uint32) (*types.Transaction, error) { ret := _m.Called(opts, _removed, _added, _addedAdmins, _minSubmissions, _maxSubmissions, _restartDelay) + if len(ret) == 0 { + panic("no return value specified for ChangeOracles") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address, []common.Address, []common.Address, uint32, uint32, uint32) (*types.Transaction, error)); ok { @@ -174,6 +198,10 @@ func (_m *FluxAggregator) ChangeOracles(opts *bind.TransactOpts, _removed []comm func (_m *FluxAggregator) Decimals(opts *bind.CallOpts) (uint8, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Decimals") + } + var r0 uint8 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint8, error)); ok { @@ -198,6 +226,10 @@ func (_m *FluxAggregator) Decimals(opts *bind.CallOpts) (uint8, error) { func (_m *FluxAggregator) Description(opts *bind.CallOpts) (string, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Description") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (string, error)); ok { @@ -222,6 +254,10 @@ func (_m *FluxAggregator) Description(opts *bind.CallOpts) (string, error) { func (_m *FluxAggregator) FilterAnswerUpdated(opts *bind.FilterOpts, current []*big.Int, roundId []*big.Int) (*flux_aggregator_wrapper.FluxAggregatorAnswerUpdatedIterator, error) { ret := _m.Called(opts, current, roundId) + if len(ret) == 0 { + panic("no return value specified for FilterAnswerUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorAnswerUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []*big.Int) (*flux_aggregator_wrapper.FluxAggregatorAnswerUpdatedIterator, error)); ok { @@ -248,6 +284,10 @@ func (_m *FluxAggregator) FilterAnswerUpdated(opts *bind.FilterOpts, current []* func (_m *FluxAggregator) FilterAvailableFundsUpdated(opts *bind.FilterOpts, amount []*big.Int) (*flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdatedIterator, error) { ret := _m.Called(opts, amount) + if len(ret) == 0 { + panic("no return value specified for FilterAvailableFundsUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdatedIterator, error)); ok { @@ -274,6 +314,10 @@ func (_m *FluxAggregator) FilterAvailableFundsUpdated(opts *bind.FilterOpts, amo func (_m *FluxAggregator) FilterNewRound(opts *bind.FilterOpts, roundId []*big.Int, startedBy []common.Address) (*flux_aggregator_wrapper.FluxAggregatorNewRoundIterator, error) { ret := _m.Called(opts, roundId, startedBy) + if len(ret) == 0 { + panic("no return value specified for FilterNewRound") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorNewRoundIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorNewRoundIterator, error)); ok { @@ -300,6 +344,10 @@ func (_m *FluxAggregator) FilterNewRound(opts *bind.FilterOpts, roundId []*big.I func (_m *FluxAggregator) FilterOracleAdminUpdateRequested(opts *bind.FilterOpts, oracle []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequestedIterator, error) { ret := _m.Called(opts, oracle) + if len(ret) == 0 { + panic("no return value specified for FilterOracleAdminUpdateRequested") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequestedIterator, error)); ok { @@ -326,6 +374,10 @@ func (_m *FluxAggregator) FilterOracleAdminUpdateRequested(opts *bind.FilterOpts func (_m *FluxAggregator) FilterOracleAdminUpdated(opts *bind.FilterOpts, oracle []common.Address, newAdmin []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdatedIterator, error) { ret := _m.Called(opts, oracle, newAdmin) + if len(ret) == 0 { + panic("no return value specified for FilterOracleAdminUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdatedIterator, error)); ok { @@ -352,6 +404,10 @@ func (_m *FluxAggregator) FilterOracleAdminUpdated(opts *bind.FilterOpts, oracle func (_m *FluxAggregator) FilterOraclePermissionsUpdated(opts *bind.FilterOpts, oracle []common.Address, whitelisted []bool) (*flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdatedIterator, error) { ret := _m.Called(opts, oracle, whitelisted) + if len(ret) == 0 { + panic("no return value specified for FilterOraclePermissionsUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []bool) (*flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdatedIterator, error)); ok { @@ -378,6 +434,10 @@ func (_m *FluxAggregator) FilterOraclePermissionsUpdated(opts *bind.FilterOpts, func (_m *FluxAggregator) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequestedIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferRequested") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequestedIterator, error)); ok { @@ -404,6 +464,10 @@ func (_m *FluxAggregator) FilterOwnershipTransferRequested(opts *bind.FilterOpts func (_m *FluxAggregator) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferredIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferred") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferredIterator, error)); ok { @@ -430,6 +494,10 @@ func (_m *FluxAggregator) FilterOwnershipTransferred(opts *bind.FilterOpts, from func (_m *FluxAggregator) FilterRequesterPermissionsSet(opts *bind.FilterOpts, requester []common.Address) (*flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSetIterator, error) { ret := _m.Called(opts, requester) + if len(ret) == 0 { + panic("no return value specified for FilterRequesterPermissionsSet") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSetIterator, error)); ok { @@ -456,6 +524,10 @@ func (_m *FluxAggregator) FilterRequesterPermissionsSet(opts *bind.FilterOpts, r func (_m *FluxAggregator) FilterRoundDetailsUpdated(opts *bind.FilterOpts, paymentAmount []*big.Int, minSubmissionCount []uint32, maxSubmissionCount []uint32) (*flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdatedIterator, error) { ret := _m.Called(opts, paymentAmount, minSubmissionCount, maxSubmissionCount) + if len(ret) == 0 { + panic("no return value specified for FilterRoundDetailsUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []uint32, []uint32) (*flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdatedIterator, error)); ok { @@ -482,6 +554,10 @@ func (_m *FluxAggregator) FilterRoundDetailsUpdated(opts *bind.FilterOpts, payme func (_m *FluxAggregator) FilterSubmissionReceived(opts *bind.FilterOpts, submission []*big.Int, round []uint32, oracle []common.Address) (*flux_aggregator_wrapper.FluxAggregatorSubmissionReceivedIterator, error) { ret := _m.Called(opts, submission, round, oracle) + if len(ret) == 0 { + panic("no return value specified for FilterSubmissionReceived") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorSubmissionReceivedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []uint32, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorSubmissionReceivedIterator, error)); ok { @@ -508,6 +584,10 @@ func (_m *FluxAggregator) FilterSubmissionReceived(opts *bind.FilterOpts, submis func (_m *FluxAggregator) FilterValidatorUpdated(opts *bind.FilterOpts, previous []common.Address, current []common.Address) (*flux_aggregator_wrapper.FluxAggregatorValidatorUpdatedIterator, error) { ret := _m.Called(opts, previous, current) + if len(ret) == 0 { + panic("no return value specified for FilterValidatorUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorValidatorUpdatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*flux_aggregator_wrapper.FluxAggregatorValidatorUpdatedIterator, error)); ok { @@ -534,6 +614,10 @@ func (_m *FluxAggregator) FilterValidatorUpdated(opts *bind.FilterOpts, previous func (_m *FluxAggregator) GetAdmin(opts *bind.CallOpts, _oracle common.Address) (common.Address, error) { ret := _m.Called(opts, _oracle) + if len(ret) == 0 { + panic("no return value specified for GetAdmin") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (common.Address, error)); ok { @@ -560,6 +644,10 @@ func (_m *FluxAggregator) GetAdmin(opts *bind.CallOpts, _oracle common.Address) func (_m *FluxAggregator) GetAnswer(opts *bind.CallOpts, _roundId *big.Int) (*big.Int, error) { ret := _m.Called(opts, _roundId) + if len(ret) == 0 { + panic("no return value specified for GetAnswer") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (*big.Int, error)); ok { @@ -586,6 +674,10 @@ func (_m *FluxAggregator) GetAnswer(opts *bind.CallOpts, _roundId *big.Int) (*bi func (_m *FluxAggregator) GetOracles(opts *bind.CallOpts) ([]common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetOracles") + } + var r0 []common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([]common.Address, error)); ok { @@ -612,6 +704,10 @@ func (_m *FluxAggregator) GetOracles(opts *bind.CallOpts) ([]common.Address, err func (_m *FluxAggregator) GetRoundData(opts *bind.CallOpts, _roundId *big.Int) (flux_aggregator_wrapper.GetRoundData, error) { ret := _m.Called(opts, _roundId) + if len(ret) == 0 { + panic("no return value specified for GetRoundData") + } + var r0 flux_aggregator_wrapper.GetRoundData var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (flux_aggregator_wrapper.GetRoundData, error)); ok { @@ -636,6 +732,10 @@ func (_m *FluxAggregator) GetRoundData(opts *bind.CallOpts, _roundId *big.Int) ( func (_m *FluxAggregator) GetTimestamp(opts *bind.CallOpts, _roundId *big.Int) (*big.Int, error) { ret := _m.Called(opts, _roundId) + if len(ret) == 0 { + panic("no return value specified for GetTimestamp") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (*big.Int, error)); ok { @@ -662,6 +762,10 @@ func (_m *FluxAggregator) GetTimestamp(opts *bind.CallOpts, _roundId *big.Int) ( func (_m *FluxAggregator) LatestAnswer(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestAnswer") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -688,6 +792,10 @@ func (_m *FluxAggregator) LatestAnswer(opts *bind.CallOpts) (*big.Int, error) { func (_m *FluxAggregator) LatestRound(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestRound") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -714,6 +822,10 @@ func (_m *FluxAggregator) LatestRound(opts *bind.CallOpts) (*big.Int, error) { func (_m *FluxAggregator) LatestRoundData(opts *bind.CallOpts) (flux_aggregator_wrapper.LatestRoundData, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestRoundData") + } + var r0 flux_aggregator_wrapper.LatestRoundData var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (flux_aggregator_wrapper.LatestRoundData, error)); ok { @@ -738,6 +850,10 @@ func (_m *FluxAggregator) LatestRoundData(opts *bind.CallOpts) (flux_aggregator_ func (_m *FluxAggregator) LatestTimestamp(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestTimestamp") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -764,6 +880,10 @@ func (_m *FluxAggregator) LatestTimestamp(opts *bind.CallOpts) (*big.Int, error) func (_m *FluxAggregator) LinkToken(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LinkToken") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -790,6 +910,10 @@ func (_m *FluxAggregator) LinkToken(opts *bind.CallOpts) (common.Address, error) func (_m *FluxAggregator) MaxSubmissionCount(opts *bind.CallOpts) (uint32, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MaxSubmissionCount") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint32, error)); ok { @@ -814,6 +938,10 @@ func (_m *FluxAggregator) MaxSubmissionCount(opts *bind.CallOpts) (uint32, error func (_m *FluxAggregator) MaxSubmissionValue(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MaxSubmissionValue") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -840,6 +968,10 @@ func (_m *FluxAggregator) MaxSubmissionValue(opts *bind.CallOpts) (*big.Int, err func (_m *FluxAggregator) MinSubmissionCount(opts *bind.CallOpts) (uint32, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MinSubmissionCount") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint32, error)); ok { @@ -864,6 +996,10 @@ func (_m *FluxAggregator) MinSubmissionCount(opts *bind.CallOpts) (uint32, error func (_m *FluxAggregator) MinSubmissionValue(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MinSubmissionValue") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -890,6 +1026,10 @@ func (_m *FluxAggregator) MinSubmissionValue(opts *bind.CallOpts) (*big.Int, err func (_m *FluxAggregator) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, arg1 *big.Int, _data []byte) (*types.Transaction, error) { ret := _m.Called(opts, arg0, arg1, _data) + if len(ret) == 0 { + panic("no return value specified for OnTokenTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int, []byte) (*types.Transaction, error)); ok { @@ -916,6 +1056,10 @@ func (_m *FluxAggregator) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.A func (_m *FluxAggregator) OracleCount(opts *bind.CallOpts) (uint8, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for OracleCount") + } + var r0 uint8 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint8, error)); ok { @@ -940,6 +1084,10 @@ func (_m *FluxAggregator) OracleCount(opts *bind.CallOpts) (uint8, error) { func (_m *FluxAggregator) OracleRoundState(opts *bind.CallOpts, _oracle common.Address, _queriedRoundId uint32) (flux_aggregator_wrapper.OracleRoundState, error) { ret := _m.Called(opts, _oracle, _queriedRoundId) + if len(ret) == 0 { + panic("no return value specified for OracleRoundState") + } + var r0 flux_aggregator_wrapper.OracleRoundState var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address, uint32) (flux_aggregator_wrapper.OracleRoundState, error)); ok { @@ -964,6 +1112,10 @@ func (_m *FluxAggregator) OracleRoundState(opts *bind.CallOpts, _oracle common.A func (_m *FluxAggregator) Owner(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Owner") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -990,6 +1142,10 @@ func (_m *FluxAggregator) Owner(opts *bind.CallOpts) (common.Address, error) { func (_m *FluxAggregator) ParseAnswerUpdated(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorAnswerUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseAnswerUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorAnswerUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorAnswerUpdated, error)); ok { @@ -1016,6 +1172,10 @@ func (_m *FluxAggregator) ParseAnswerUpdated(log types.Log) (*flux_aggregator_wr func (_m *FluxAggregator) ParseAvailableFundsUpdated(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseAvailableFundsUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdated, error)); ok { @@ -1042,6 +1202,10 @@ func (_m *FluxAggregator) ParseAvailableFundsUpdated(log types.Log) (*flux_aggre func (_m *FluxAggregator) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { @@ -1068,6 +1232,10 @@ func (_m *FluxAggregator) ParseLog(log types.Log) (generated.AbigenLog, error) { func (_m *FluxAggregator) ParseNewRound(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorNewRound, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseNewRound") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorNewRound var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorNewRound, error)); ok { @@ -1094,6 +1262,10 @@ func (_m *FluxAggregator) ParseNewRound(log types.Log) (*flux_aggregator_wrapper func (_m *FluxAggregator) ParseOracleAdminUpdateRequested(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOracleAdminUpdateRequested") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequested, error)); ok { @@ -1120,6 +1292,10 @@ func (_m *FluxAggregator) ParseOracleAdminUpdateRequested(log types.Log) (*flux_ func (_m *FluxAggregator) ParseOracleAdminUpdated(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOracleAdminUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdated, error)); ok { @@ -1146,6 +1322,10 @@ func (_m *FluxAggregator) ParseOracleAdminUpdated(log types.Log) (*flux_aggregat func (_m *FluxAggregator) ParseOraclePermissionsUpdated(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOraclePermissionsUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdated, error)); ok { @@ -1172,6 +1352,10 @@ func (_m *FluxAggregator) ParseOraclePermissionsUpdated(log types.Log) (*flux_ag func (_m *FluxAggregator) ParseOwnershipTransferRequested(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferRequested") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequested, error)); ok { @@ -1198,6 +1382,10 @@ func (_m *FluxAggregator) ParseOwnershipTransferRequested(log types.Log) (*flux_ func (_m *FluxAggregator) ParseOwnershipTransferred(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferred") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorOwnershipTransferred, error)); ok { @@ -1224,6 +1412,10 @@ func (_m *FluxAggregator) ParseOwnershipTransferred(log types.Log) (*flux_aggreg func (_m *FluxAggregator) ParseRequesterPermissionsSet(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRequesterPermissionsSet") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSet, error)); ok { @@ -1250,6 +1442,10 @@ func (_m *FluxAggregator) ParseRequesterPermissionsSet(log types.Log) (*flux_agg func (_m *FluxAggregator) ParseRoundDetailsUpdated(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRoundDetailsUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdated, error)); ok { @@ -1276,6 +1472,10 @@ func (_m *FluxAggregator) ParseRoundDetailsUpdated(log types.Log) (*flux_aggrega func (_m *FluxAggregator) ParseSubmissionReceived(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorSubmissionReceived, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubmissionReceived") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorSubmissionReceived var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorSubmissionReceived, error)); ok { @@ -1302,6 +1502,10 @@ func (_m *FluxAggregator) ParseSubmissionReceived(log types.Log) (*flux_aggregat func (_m *FluxAggregator) ParseValidatorUpdated(log types.Log) (*flux_aggregator_wrapper.FluxAggregatorValidatorUpdated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseValidatorUpdated") + } + var r0 *flux_aggregator_wrapper.FluxAggregatorValidatorUpdated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*flux_aggregator_wrapper.FluxAggregatorValidatorUpdated, error)); ok { @@ -1328,6 +1532,10 @@ func (_m *FluxAggregator) ParseValidatorUpdated(log types.Log) (*flux_aggregator func (_m *FluxAggregator) PaymentAmount(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for PaymentAmount") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -1354,6 +1562,10 @@ func (_m *FluxAggregator) PaymentAmount(opts *bind.CallOpts) (*big.Int, error) { func (_m *FluxAggregator) RequestNewRound(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for RequestNewRound") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -1380,6 +1592,10 @@ func (_m *FluxAggregator) RequestNewRound(opts *bind.TransactOpts) (*types.Trans func (_m *FluxAggregator) RestartDelay(opts *bind.CallOpts) (uint32, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for RestartDelay") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint32, error)); ok { @@ -1404,6 +1620,10 @@ func (_m *FluxAggregator) RestartDelay(opts *bind.CallOpts) (uint32, error) { func (_m *FluxAggregator) SetRequesterPermissions(opts *bind.TransactOpts, _requester common.Address, _authorized bool, _delay uint32) (*types.Transaction, error) { ret := _m.Called(opts, _requester, _authorized, _delay) + if len(ret) == 0 { + panic("no return value specified for SetRequesterPermissions") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, bool, uint32) (*types.Transaction, error)); ok { @@ -1430,6 +1650,10 @@ func (_m *FluxAggregator) SetRequesterPermissions(opts *bind.TransactOpts, _requ func (_m *FluxAggregator) SetValidator(opts *bind.TransactOpts, _newValidator common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _newValidator) + if len(ret) == 0 { + panic("no return value specified for SetValidator") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1456,6 +1680,10 @@ func (_m *FluxAggregator) SetValidator(opts *bind.TransactOpts, _newValidator co func (_m *FluxAggregator) Submit(opts *bind.TransactOpts, _roundId *big.Int, _submission *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, _roundId, _submission) + if len(ret) == 0 { + panic("no return value specified for Submit") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, *big.Int) (*types.Transaction, error)); ok { @@ -1482,6 +1710,10 @@ func (_m *FluxAggregator) Submit(opts *bind.TransactOpts, _roundId *big.Int, _su func (_m *FluxAggregator) Timeout(opts *bind.CallOpts) (uint32, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Timeout") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint32, error)); ok { @@ -1506,6 +1738,10 @@ func (_m *FluxAggregator) Timeout(opts *bind.CallOpts) (uint32, error) { func (_m *FluxAggregator) TransferAdmin(opts *bind.TransactOpts, _oracle common.Address, _newAdmin common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _oracle, _newAdmin) + if len(ret) == 0 { + panic("no return value specified for TransferAdmin") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, common.Address) (*types.Transaction, error)); ok { @@ -1532,6 +1768,10 @@ func (_m *FluxAggregator) TransferAdmin(opts *bind.TransactOpts, _oracle common. func (_m *FluxAggregator) TransferOwnership(opts *bind.TransactOpts, _to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _to) + if len(ret) == 0 { + panic("no return value specified for TransferOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1558,6 +1798,10 @@ func (_m *FluxAggregator) TransferOwnership(opts *bind.TransactOpts, _to common. func (_m *FluxAggregator) UpdateAvailableFunds(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for UpdateAvailableFunds") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -1584,6 +1828,10 @@ func (_m *FluxAggregator) UpdateAvailableFunds(opts *bind.TransactOpts) (*types. func (_m *FluxAggregator) UpdateFutureRounds(opts *bind.TransactOpts, _paymentAmount *big.Int, _minSubmissions uint32, _maxSubmissions uint32, _restartDelay uint32, _timeout uint32) (*types.Transaction, error) { ret := _m.Called(opts, _paymentAmount, _minSubmissions, _maxSubmissions, _restartDelay, _timeout) + if len(ret) == 0 { + panic("no return value specified for UpdateFutureRounds") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, uint32, uint32, uint32, uint32) (*types.Transaction, error)); ok { @@ -1610,6 +1858,10 @@ func (_m *FluxAggregator) UpdateFutureRounds(opts *bind.TransactOpts, _paymentAm func (_m *FluxAggregator) Validator(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Validator") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -1636,6 +1888,10 @@ func (_m *FluxAggregator) Validator(opts *bind.CallOpts) (common.Address, error) func (_m *FluxAggregator) Version(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Version") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -1662,6 +1918,10 @@ func (_m *FluxAggregator) Version(opts *bind.CallOpts) (*big.Int, error) { func (_m *FluxAggregator) WatchAnswerUpdated(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorAnswerUpdated, current []*big.Int, roundId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, current, roundId) + if len(ret) == 0 { + panic("no return value specified for WatchAnswerUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorAnswerUpdated, []*big.Int, []*big.Int) (event.Subscription, error)); ok { @@ -1688,6 +1948,10 @@ func (_m *FluxAggregator) WatchAnswerUpdated(opts *bind.WatchOpts, sink chan<- * func (_m *FluxAggregator) WatchAvailableFundsUpdated(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdated, amount []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, amount) + if len(ret) == 0 { + panic("no return value specified for WatchAvailableFundsUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorAvailableFundsUpdated, []*big.Int) (event.Subscription, error)); ok { @@ -1714,6 +1978,10 @@ func (_m *FluxAggregator) WatchAvailableFundsUpdated(opts *bind.WatchOpts, sink func (_m *FluxAggregator) WatchNewRound(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorNewRound, roundId []*big.Int, startedBy []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, roundId, startedBy) + if len(ret) == 0 { + panic("no return value specified for WatchNewRound") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorNewRound, []*big.Int, []common.Address) (event.Subscription, error)); ok { @@ -1740,6 +2008,10 @@ func (_m *FluxAggregator) WatchNewRound(opts *bind.WatchOpts, sink chan<- *flux_ func (_m *FluxAggregator) WatchOracleAdminUpdateRequested(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequested, oracle []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, oracle) + if len(ret) == 0 { + panic("no return value specified for WatchOracleAdminUpdateRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdateRequested, []common.Address) (event.Subscription, error)); ok { @@ -1766,6 +2038,10 @@ func (_m *FluxAggregator) WatchOracleAdminUpdateRequested(opts *bind.WatchOpts, func (_m *FluxAggregator) WatchOracleAdminUpdated(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdated, oracle []common.Address, newAdmin []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, oracle, newAdmin) + if len(ret) == 0 { + panic("no return value specified for WatchOracleAdminUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorOracleAdminUpdated, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1792,6 +2068,10 @@ func (_m *FluxAggregator) WatchOracleAdminUpdated(opts *bind.WatchOpts, sink cha func (_m *FluxAggregator) WatchOraclePermissionsUpdated(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdated, oracle []common.Address, whitelisted []bool) (event.Subscription, error) { ret := _m.Called(opts, sink, oracle, whitelisted) + if len(ret) == 0 { + panic("no return value specified for WatchOraclePermissionsUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorOraclePermissionsUpdated, []common.Address, []bool) (event.Subscription, error)); ok { @@ -1818,6 +2098,10 @@ func (_m *FluxAggregator) WatchOraclePermissionsUpdated(opts *bind.WatchOpts, si func (_m *FluxAggregator) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1844,6 +2128,10 @@ func (_m *FluxAggregator) WatchOwnershipTransferRequested(opts *bind.WatchOpts, func (_m *FluxAggregator) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorOwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1870,6 +2158,10 @@ func (_m *FluxAggregator) WatchOwnershipTransferred(opts *bind.WatchOpts, sink c func (_m *FluxAggregator) WatchRequesterPermissionsSet(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSet, requester []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, requester) + if len(ret) == 0 { + panic("no return value specified for WatchRequesterPermissionsSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorRequesterPermissionsSet, []common.Address) (event.Subscription, error)); ok { @@ -1896,6 +2188,10 @@ func (_m *FluxAggregator) WatchRequesterPermissionsSet(opts *bind.WatchOpts, sin func (_m *FluxAggregator) WatchRoundDetailsUpdated(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdated, paymentAmount []*big.Int, minSubmissionCount []uint32, maxSubmissionCount []uint32) (event.Subscription, error) { ret := _m.Called(opts, sink, paymentAmount, minSubmissionCount, maxSubmissionCount) + if len(ret) == 0 { + panic("no return value specified for WatchRoundDetailsUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorRoundDetailsUpdated, []*big.Int, []uint32, []uint32) (event.Subscription, error)); ok { @@ -1922,6 +2218,10 @@ func (_m *FluxAggregator) WatchRoundDetailsUpdated(opts *bind.WatchOpts, sink ch func (_m *FluxAggregator) WatchSubmissionReceived(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorSubmissionReceived, submission []*big.Int, round []uint32, oracle []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, submission, round, oracle) + if len(ret) == 0 { + panic("no return value specified for WatchSubmissionReceived") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorSubmissionReceived, []*big.Int, []uint32, []common.Address) (event.Subscription, error)); ok { @@ -1948,6 +2248,10 @@ func (_m *FluxAggregator) WatchSubmissionReceived(opts *bind.WatchOpts, sink cha func (_m *FluxAggregator) WatchValidatorUpdated(opts *bind.WatchOpts, sink chan<- *flux_aggregator_wrapper.FluxAggregatorValidatorUpdated, previous []common.Address, current []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, previous, current) + if len(ret) == 0 { + panic("no return value specified for WatchValidatorUpdated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *flux_aggregator_wrapper.FluxAggregatorValidatorUpdated, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1974,6 +2278,10 @@ func (_m *FluxAggregator) WatchValidatorUpdated(opts *bind.WatchOpts, sink chan< func (_m *FluxAggregator) WithdrawFunds(opts *bind.TransactOpts, _recipient common.Address, _amount *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, _recipient, _amount) + if len(ret) == 0 { + panic("no return value specified for WithdrawFunds") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int) (*types.Transaction, error)); ok { @@ -2000,6 +2308,10 @@ func (_m *FluxAggregator) WithdrawFunds(opts *bind.TransactOpts, _recipient comm func (_m *FluxAggregator) WithdrawPayment(opts *bind.TransactOpts, _oracle common.Address, _recipient common.Address, _amount *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, _oracle, _recipient, _amount) + if len(ret) == 0 { + panic("no return value specified for WithdrawPayment") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, common.Address, *big.Int) (*types.Transaction, error)); ok { @@ -2026,6 +2338,10 @@ func (_m *FluxAggregator) WithdrawPayment(opts *bind.TransactOpts, _oracle commo func (_m *FluxAggregator) WithdrawablePayment(opts *bind.CallOpts, _oracle common.Address) (*big.Int, error) { ret := _m.Called(opts, _oracle) + if len(ret) == 0 { + panic("no return value specified for WithdrawablePayment") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (*big.Int, error)); ok { diff --git a/core/internal/mocks/prometheus_backend.go b/core/internal/mocks/prometheus_backend.go index b2556a0bad4..6573dbaf03f 100644 --- a/core/internal/mocks/prometheus_backend.go +++ b/core/internal/mocks/prometheus_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/core/logger/logger_mock_test.go b/core/logger/logger_mock_test.go index 2b6dfcf3b22..afddd031888 100644 --- a/core/logger/logger_mock_test.go +++ b/core/logger/logger_mock_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package logger @@ -108,6 +108,10 @@ func (_m *MockLogger) Fatalw(msg string, keysAndValues ...interface{}) { func (_m *MockLogger) Helper(skip int) Logger { ret := _m.Called(skip) + if len(ret) == 0 { + panic("no return value specified for Helper") + } + var r0 Logger if rf, ok := ret.Get(0).(func(int) Logger); ok { r0 = rf(skip) @@ -147,6 +151,10 @@ func (_m *MockLogger) Infow(msg string, keysAndValues ...interface{}) { func (_m *MockLogger) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -161,6 +169,10 @@ func (_m *MockLogger) Name() string { func (_m *MockLogger) Named(name string) Logger { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for Named") + } + var r0 Logger if rf, ok := ret.Get(0).(func(string) Logger); ok { r0 = rf(name) @@ -210,6 +222,10 @@ func (_m *MockLogger) SetLogLevel(_a0 zapcore.Level) { func (_m *MockLogger) Sync() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Sync") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -272,6 +288,10 @@ func (_m *MockLogger) With(args ...interface{}) Logger { _ca = append(_ca, args...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for With") + } + var r0 Logger if rf, ok := ret.Get(0).(func(...interface{}) Logger); ok { r0 = rf(args...) diff --git a/core/logger/mocks/logger.go b/core/logger/mocks/logger.go index 965bd57baaf..316f6216b90 100644 --- a/core/logger/mocks/logger.go +++ b/core/logger/mocks/logger.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -110,6 +110,10 @@ func (_m *Logger) Fatalw(msg string, keysAndValues ...interface{}) { func (_m *Logger) Helper(skip int) logger.Logger { ret := _m.Called(skip) + if len(ret) == 0 { + panic("no return value specified for Helper") + } + var r0 logger.Logger if rf, ok := ret.Get(0).(func(int) logger.Logger); ok { r0 = rf(skip) @@ -149,6 +153,10 @@ func (_m *Logger) Infow(msg string, keysAndValues ...interface{}) { func (_m *Logger) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -163,6 +171,10 @@ func (_m *Logger) Name() string { func (_m *Logger) Named(name string) logger.Logger { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for Named") + } + var r0 logger.Logger if rf, ok := ret.Get(0).(func(string) logger.Logger); ok { r0 = rf(name) @@ -212,6 +224,10 @@ func (_m *Logger) SetLogLevel(_a0 zapcore.Level) { func (_m *Logger) Sync() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Sync") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -274,6 +290,10 @@ func (_m *Logger) With(args ...interface{}) logger.Logger { _ca = append(_ca, args...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for With") + } + var r0 logger.Logger if rf, ok := ret.Get(0).(func(...interface{}) logger.Logger); ok { r0 = rf(args...) diff --git a/core/services/blockhashstore/mocks/bhs.go b/core/services/blockhashstore/mocks/bhs.go index 51ddca46a0d..a69016c8026 100644 --- a/core/services/blockhashstore/mocks/bhs.go +++ b/core/services/blockhashstore/mocks/bhs.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type BHS struct { func (_m *BHS) IsStored(ctx context.Context, blockNum uint64) (bool, error) { ret := _m.Called(ctx, blockNum) + if len(ret) == 0 { + panic("no return value specified for IsStored") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, uint64) (bool, error)); ok { @@ -43,6 +47,10 @@ func (_m *BHS) IsStored(ctx context.Context, blockNum uint64) (bool, error) { func (_m *BHS) IsTrusted() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsTrusted") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -57,6 +65,10 @@ func (_m *BHS) IsTrusted() bool { func (_m *BHS) Store(ctx context.Context, blockNum uint64) error { ret := _m.Called(ctx, blockNum) + if len(ret) == 0 { + panic("no return value specified for Store") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, uint64) error); ok { r0 = rf(ctx, blockNum) @@ -71,6 +83,10 @@ func (_m *BHS) Store(ctx context.Context, blockNum uint64) error { func (_m *BHS) StoreEarliest(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for StoreEarliest") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -85,6 +101,10 @@ func (_m *BHS) StoreEarliest(ctx context.Context) error { func (_m *BHS) StoreTrusted(ctx context.Context, blockNums []uint64, blockhashes []common.Hash, recentBlock uint64, recentBlockhash common.Hash) error { ret := _m.Called(ctx, blockNums, blockhashes, recentBlock, recentBlockhash) + if len(ret) == 0 { + panic("no return value specified for StoreTrusted") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []uint64, []common.Hash, uint64, common.Hash) error); ok { r0 = rf(ctx, blockNums, blockhashes, recentBlock, recentBlockhash) diff --git a/core/services/blockhashstore/mocks/timer.go b/core/services/blockhashstore/mocks/timer.go index c116163a3df..4236bdf8d92 100644 --- a/core/services/blockhashstore/mocks/timer.go +++ b/core/services/blockhashstore/mocks/timer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type Timer struct { func (_m *Timer) After(d time.Duration) <-chan time.Time { ret := _m.Called(d) + if len(ret) == 0 { + panic("no return value specified for After") + } + var r0 <-chan time.Time if rf, ok := ret.Get(0).(func(time.Duration) <-chan time.Time); ok { r0 = rf(d) diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index 98796e90053..1dd85875395 100644 --- a/core/services/chainlink/mocks/general_config.go +++ b/core/services/chainlink/mocks/general_config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -30,6 +30,10 @@ type GeneralConfig struct { func (_m *GeneralConfig) AppID() uuid.UUID { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AppID") + } + var r0 uuid.UUID if rf, ok := ret.Get(0).(func() uuid.UUID); ok { r0 = rf() @@ -46,6 +50,10 @@ func (_m *GeneralConfig) AppID() uuid.UUID { func (_m *GeneralConfig) AuditLogger() config.AuditLogger { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AuditLogger") + } + var r0 config.AuditLogger if rf, ok := ret.Get(0).(func() config.AuditLogger); ok { r0 = rf() @@ -62,6 +70,10 @@ func (_m *GeneralConfig) AuditLogger() config.AuditLogger { func (_m *GeneralConfig) AutoPprof() config.AutoPprof { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AutoPprof") + } + var r0 config.AutoPprof if rf, ok := ret.Get(0).(func() config.AutoPprof); ok { r0 = rf() @@ -78,6 +90,10 @@ func (_m *GeneralConfig) AutoPprof() config.AutoPprof { func (_m *GeneralConfig) ConfigTOML() (string, string) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ConfigTOML") + } + var r0 string var r1 string if rf, ok := ret.Get(0).(func() (string, string)); ok { @@ -102,6 +118,10 @@ func (_m *GeneralConfig) ConfigTOML() (string, string) { func (_m *GeneralConfig) CosmosConfigs() cosmosconfig.TOMLConfigs { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CosmosConfigs") + } + var r0 cosmosconfig.TOMLConfigs if rf, ok := ret.Get(0).(func() cosmosconfig.TOMLConfigs); ok { r0 = rf() @@ -118,6 +138,10 @@ func (_m *GeneralConfig) CosmosConfigs() cosmosconfig.TOMLConfigs { func (_m *GeneralConfig) CosmosEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CosmosEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -132,6 +156,10 @@ func (_m *GeneralConfig) CosmosEnabled() bool { func (_m *GeneralConfig) Database() config.Database { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Database") + } + var r0 config.Database if rf, ok := ret.Get(0).(func() config.Database); ok { r0 = rf() @@ -148,6 +176,10 @@ func (_m *GeneralConfig) Database() config.Database { func (_m *GeneralConfig) EVMConfigs() toml.EVMConfigs { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMConfigs") + } + var r0 toml.EVMConfigs if rf, ok := ret.Get(0).(func() toml.EVMConfigs); ok { r0 = rf() @@ -164,6 +196,10 @@ func (_m *GeneralConfig) EVMConfigs() toml.EVMConfigs { func (_m *GeneralConfig) EVMEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -178,6 +214,10 @@ func (_m *GeneralConfig) EVMEnabled() bool { func (_m *GeneralConfig) EVMRPCEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EVMRPCEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -192,6 +232,10 @@ func (_m *GeneralConfig) EVMRPCEnabled() bool { func (_m *GeneralConfig) Feature() config.Feature { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Feature") + } + var r0 config.Feature if rf, ok := ret.Get(0).(func() config.Feature); ok { r0 = rf() @@ -208,6 +252,10 @@ func (_m *GeneralConfig) Feature() config.Feature { func (_m *GeneralConfig) FluxMonitor() config.FluxMonitor { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FluxMonitor") + } + var r0 config.FluxMonitor if rf, ok := ret.Get(0).(func() config.FluxMonitor); ok { r0 = rf() @@ -224,6 +272,10 @@ func (_m *GeneralConfig) FluxMonitor() config.FluxMonitor { func (_m *GeneralConfig) Insecure() config.Insecure { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Insecure") + } + var r0 config.Insecure if rf, ok := ret.Get(0).(func() config.Insecure); ok { r0 = rf() @@ -240,6 +292,10 @@ func (_m *GeneralConfig) Insecure() config.Insecure { func (_m *GeneralConfig) InsecureFastScrypt() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for InsecureFastScrypt") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -254,6 +310,10 @@ func (_m *GeneralConfig) InsecureFastScrypt() bool { func (_m *GeneralConfig) JobPipeline() config.JobPipeline { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for JobPipeline") + } + var r0 config.JobPipeline if rf, ok := ret.Get(0).(func() config.JobPipeline); ok { r0 = rf() @@ -270,6 +330,10 @@ func (_m *GeneralConfig) JobPipeline() config.JobPipeline { func (_m *GeneralConfig) Keeper() config.Keeper { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Keeper") + } + var r0 config.Keeper if rf, ok := ret.Get(0).(func() config.Keeper); ok { r0 = rf() @@ -286,6 +350,10 @@ func (_m *GeneralConfig) Keeper() config.Keeper { func (_m *GeneralConfig) Log() config.Log { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Log") + } + var r0 config.Log if rf, ok := ret.Get(0).(func() config.Log); ok { r0 = rf() @@ -307,6 +375,10 @@ func (_m *GeneralConfig) LogConfiguration(log config.LogfFn, warn config.LogfFn) func (_m *GeneralConfig) Mercury() config.Mercury { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Mercury") + } + var r0 config.Mercury if rf, ok := ret.Get(0).(func() config.Mercury); ok { r0 = rf() @@ -323,6 +395,10 @@ func (_m *GeneralConfig) Mercury() config.Mercury { func (_m *GeneralConfig) OCR() config.OCR { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for OCR") + } + var r0 config.OCR if rf, ok := ret.Get(0).(func() config.OCR); ok { r0 = rf() @@ -339,6 +415,10 @@ func (_m *GeneralConfig) OCR() config.OCR { func (_m *GeneralConfig) OCR2() config.OCR2 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for OCR2") + } + var r0 config.OCR2 if rf, ok := ret.Get(0).(func() config.OCR2); ok { r0 = rf() @@ -355,6 +435,10 @@ func (_m *GeneralConfig) OCR2() config.OCR2 { func (_m *GeneralConfig) P2P() config.P2P { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for P2P") + } + var r0 config.P2P if rf, ok := ret.Get(0).(func() config.P2P); ok { r0 = rf() @@ -371,6 +455,10 @@ func (_m *GeneralConfig) P2P() config.P2P { func (_m *GeneralConfig) Password() config.Password { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Password") + } + var r0 config.Password if rf, ok := ret.Get(0).(func() config.Password); ok { r0 = rf() @@ -387,6 +475,10 @@ func (_m *GeneralConfig) Password() config.Password { func (_m *GeneralConfig) Prometheus() config.Prometheus { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Prometheus") + } + var r0 config.Prometheus if rf, ok := ret.Get(0).(func() config.Prometheus); ok { r0 = rf() @@ -403,6 +495,10 @@ func (_m *GeneralConfig) Prometheus() config.Prometheus { func (_m *GeneralConfig) Pyroscope() config.Pyroscope { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Pyroscope") + } + var r0 config.Pyroscope if rf, ok := ret.Get(0).(func() config.Pyroscope); ok { r0 = rf() @@ -419,6 +515,10 @@ func (_m *GeneralConfig) Pyroscope() config.Pyroscope { func (_m *GeneralConfig) RootDir() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RootDir") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -433,6 +533,10 @@ func (_m *GeneralConfig) RootDir() string { func (_m *GeneralConfig) Sentry() config.Sentry { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Sentry") + } + var r0 config.Sentry if rf, ok := ret.Get(0).(func() config.Sentry); ok { r0 = rf() @@ -449,6 +553,10 @@ func (_m *GeneralConfig) Sentry() config.Sentry { func (_m *GeneralConfig) SetLogLevel(lvl zapcore.Level) error { ret := _m.Called(lvl) + if len(ret) == 0 { + panic("no return value specified for SetLogLevel") + } + var r0 error if rf, ok := ret.Get(0).(func(zapcore.Level) error); ok { r0 = rf(lvl) @@ -473,6 +581,10 @@ func (_m *GeneralConfig) SetPasswords(keystore *string, vrf *string) { func (_m *GeneralConfig) ShutdownGracePeriod() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ShutdownGracePeriod") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -487,6 +599,10 @@ func (_m *GeneralConfig) ShutdownGracePeriod() time.Duration { func (_m *GeneralConfig) SolanaConfigs() solana.TOMLConfigs { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SolanaConfigs") + } + var r0 solana.TOMLConfigs if rf, ok := ret.Get(0).(func() solana.TOMLConfigs); ok { r0 = rf() @@ -503,6 +619,10 @@ func (_m *GeneralConfig) SolanaConfigs() solana.TOMLConfigs { func (_m *GeneralConfig) SolanaEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SolanaEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -517,6 +637,10 @@ func (_m *GeneralConfig) SolanaEnabled() bool { func (_m *GeneralConfig) StarkNetEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StarkNetEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -531,6 +655,10 @@ func (_m *GeneralConfig) StarkNetEnabled() bool { func (_m *GeneralConfig) StarknetConfigs() chainlinkconfig.TOMLConfigs { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StarknetConfigs") + } + var r0 chainlinkconfig.TOMLConfigs if rf, ok := ret.Get(0).(func() chainlinkconfig.TOMLConfigs); ok { r0 = rf() @@ -547,6 +675,10 @@ func (_m *GeneralConfig) StarknetConfigs() chainlinkconfig.TOMLConfigs { func (_m *GeneralConfig) TelemetryIngress() config.TelemetryIngress { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TelemetryIngress") + } + var r0 config.TelemetryIngress if rf, ok := ret.Get(0).(func() config.TelemetryIngress); ok { r0 = rf() @@ -563,6 +695,10 @@ func (_m *GeneralConfig) TelemetryIngress() config.TelemetryIngress { func (_m *GeneralConfig) Threshold() config.Threshold { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Threshold") + } + var r0 config.Threshold if rf, ok := ret.Get(0).(func() config.Threshold); ok { r0 = rf() @@ -579,6 +715,10 @@ func (_m *GeneralConfig) Threshold() config.Threshold { func (_m *GeneralConfig) Tracing() config.Tracing { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Tracing") + } + var r0 config.Tracing if rf, ok := ret.Get(0).(func() config.Tracing); ok { r0 = rf() @@ -595,6 +735,10 @@ func (_m *GeneralConfig) Tracing() config.Tracing { func (_m *GeneralConfig) Validate() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Validate") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -609,6 +753,10 @@ func (_m *GeneralConfig) Validate() error { func (_m *GeneralConfig) ValidateDB() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ValidateDB") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -623,6 +771,10 @@ func (_m *GeneralConfig) ValidateDB() error { func (_m *GeneralConfig) WebServer() config.WebServer { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for WebServer") + } + var r0 config.WebServer if rf, ok := ret.Get(0).(func() config.WebServer); ok { r0 = rf() diff --git a/core/services/feeds/mocks/connections_manager.go b/core/services/feeds/mocks/connections_manager.go index e72c98a987a..5bdc5087108 100644 --- a/core/services/feeds/mocks/connections_manager.go +++ b/core/services/feeds/mocks/connections_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ func (_m *ConnectionsManager) Connect(opts feeds.ConnectOpts) { func (_m *ConnectionsManager) Disconnect(id int64) error { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Disconnect") + } + var r0 error if rf, ok := ret.Get(0).(func(int64) error); ok { r0 = rf(id) @@ -42,6 +46,10 @@ func (_m *ConnectionsManager) Disconnect(id int64) error { func (_m *ConnectionsManager) GetClient(id int64) (proto.FeedsManagerClient, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetClient") + } + var r0 proto.FeedsManagerClient var r1 error if rf, ok := ret.Get(0).(func(int64) (proto.FeedsManagerClient, error)); ok { @@ -68,6 +76,10 @@ func (_m *ConnectionsManager) GetClient(id int64) (proto.FeedsManagerClient, err func (_m *ConnectionsManager) IsConnected(id int64) bool { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for IsConnected") + } + var r0 bool if rf, ok := ret.Get(0).(func(int64) bool); ok { r0 = rf(id) diff --git a/core/services/feeds/mocks/feeds_manager_client.go b/core/services/feeds/mocks/feeds_manager_client.go index 9d0037ceabc..f07200cc8fd 100644 --- a/core/services/feeds/mocks/feeds_manager_client.go +++ b/core/services/feeds/mocks/feeds_manager_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type FeedsManagerClient struct { func (_m *FeedsManagerClient) ApprovedJob(ctx context.Context, in *proto.ApprovedJobRequest) (*proto.ApprovedJobResponse, error) { ret := _m.Called(ctx, in) + if len(ret) == 0 { + panic("no return value specified for ApprovedJob") + } + var r0 *proto.ApprovedJobResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *proto.ApprovedJobRequest) (*proto.ApprovedJobResponse, error)); ok { @@ -44,6 +48,10 @@ func (_m *FeedsManagerClient) ApprovedJob(ctx context.Context, in *proto.Approve func (_m *FeedsManagerClient) CancelledJob(ctx context.Context, in *proto.CancelledJobRequest) (*proto.CancelledJobResponse, error) { ret := _m.Called(ctx, in) + if len(ret) == 0 { + panic("no return value specified for CancelledJob") + } + var r0 *proto.CancelledJobResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *proto.CancelledJobRequest) (*proto.CancelledJobResponse, error)); ok { @@ -70,6 +78,10 @@ func (_m *FeedsManagerClient) CancelledJob(ctx context.Context, in *proto.Cancel func (_m *FeedsManagerClient) Healthcheck(ctx context.Context, in *proto.HealthcheckRequest) (*proto.HealthcheckResponse, error) { ret := _m.Called(ctx, in) + if len(ret) == 0 { + panic("no return value specified for Healthcheck") + } + var r0 *proto.HealthcheckResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *proto.HealthcheckRequest) (*proto.HealthcheckResponse, error)); ok { @@ -96,6 +108,10 @@ func (_m *FeedsManagerClient) Healthcheck(ctx context.Context, in *proto.Healthc func (_m *FeedsManagerClient) RejectedJob(ctx context.Context, in *proto.RejectedJobRequest) (*proto.RejectedJobResponse, error) { ret := _m.Called(ctx, in) + if len(ret) == 0 { + panic("no return value specified for RejectedJob") + } + var r0 *proto.RejectedJobResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *proto.RejectedJobRequest) (*proto.RejectedJobResponse, error)); ok { @@ -122,6 +138,10 @@ func (_m *FeedsManagerClient) RejectedJob(ctx context.Context, in *proto.Rejecte func (_m *FeedsManagerClient) UpdateNode(ctx context.Context, in *proto.UpdateNodeRequest) (*proto.UpdateNodeResponse, error) { ret := _m.Called(ctx, in) + if len(ret) == 0 { + panic("no return value specified for UpdateNode") + } + var r0 *proto.UpdateNodeResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *proto.UpdateNodeRequest) (*proto.UpdateNodeResponse, error)); ok { diff --git a/core/services/feeds/mocks/orm.go b/core/services/feeds/mocks/orm.go index 09326ada518..73bc4c4d4a0 100644 --- a/core/services/feeds/mocks/orm.go +++ b/core/services/feeds/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -35,6 +35,10 @@ func (_m *ORM) ApproveSpec(id int64, externalJobID uuid.UUID, qopts ...pg.QOpt) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ApproveSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, uuid.UUID, ...pg.QOpt) error); ok { r0 = rf(id, externalJobID, qopts...) @@ -93,6 +97,10 @@ func (_m *ORM) CancelSpec(id int64, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CancelSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) error); ok { r0 = rf(id, qopts...) @@ -143,6 +151,10 @@ func (_c *ORM_CancelSpec_Call) RunAndReturn(run func(int64, ...pg.QOpt) error) * func (_m *ORM) CountJobProposals() (int64, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CountJobProposals") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func() (int64, error)); ok { @@ -194,6 +206,10 @@ func (_c *ORM_CountJobProposals_Call) RunAndReturn(run func() (int64, error)) *O func (_m *ORM) CountJobProposalsByStatus() (*feeds.JobProposalCounts, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CountJobProposalsByStatus") + } + var r0 *feeds.JobProposalCounts var r1 error if rf, ok := ret.Get(0).(func() (*feeds.JobProposalCounts, error)); ok { @@ -247,6 +263,10 @@ func (_c *ORM_CountJobProposalsByStatus_Call) RunAndReturn(run func() (*feeds.Jo func (_m *ORM) CountManagers() (int64, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CountManagers") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func() (int64, error)); ok { @@ -305,6 +325,10 @@ func (_m *ORM) CreateBatchChainConfig(cfgs []feeds.ChainConfig, qopts ...pg.QOpt _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateBatchChainConfig") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func([]feeds.ChainConfig, ...pg.QOpt) ([]int64, error)); ok { @@ -374,6 +398,10 @@ func (_m *ORM) CreateChainConfig(cfg feeds.ChainConfig, qopts ...pg.QOpt) (int64 _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateChainConfig") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(feeds.ChainConfig, ...pg.QOpt) (int64, error)); ok { @@ -434,6 +462,10 @@ func (_c *ORM_CreateChainConfig_Call) RunAndReturn(run func(feeds.ChainConfig, . func (_m *ORM) CreateJobProposal(jp *feeds.JobProposal) (int64, error) { ret := _m.Called(jp) + if len(ret) == 0 { + panic("no return value specified for CreateJobProposal") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(*feeds.JobProposal) (int64, error)); ok { @@ -493,6 +525,10 @@ func (_m *ORM) CreateManager(ms *feeds.FeedsManager, qopts ...pg.QOpt) (int64, e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateManager") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(*feeds.FeedsManager, ...pg.QOpt) (int64, error)); ok { @@ -560,6 +596,10 @@ func (_m *ORM) CreateSpec(spec feeds.JobProposalSpec, qopts ...pg.QOpt) (int64, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateSpec") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(feeds.JobProposalSpec, ...pg.QOpt) (int64, error)); ok { @@ -620,6 +660,10 @@ func (_c *ORM_CreateSpec_Call) RunAndReturn(run func(feeds.JobProposalSpec, ...p func (_m *ORM) DeleteChainConfig(id int64) (int64, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for DeleteChainConfig") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(int64) (int64, error)); ok { @@ -679,6 +723,10 @@ func (_m *ORM) DeleteProposal(id int64, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DeleteProposal") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) error); ok { r0 = rf(id, qopts...) @@ -736,6 +784,10 @@ func (_m *ORM) ExistsSpecByJobProposalIDAndVersion(jpID int64, version int32, qo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ExistsSpecByJobProposalIDAndVersion") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(int64, int32, ...pg.QOpt) (bool, error)); ok { @@ -804,6 +856,10 @@ func (_m *ORM) GetApprovedSpec(jpID int64, qopts ...pg.QOpt) (*feeds.JobProposal _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetApprovedSpec") + } + var r0 *feeds.JobProposalSpec var r1 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) (*feeds.JobProposalSpec, error)); ok { @@ -866,6 +922,10 @@ func (_c *ORM_GetApprovedSpec_Call) RunAndReturn(run func(int64, ...pg.QOpt) (*f func (_m *ORM) GetChainConfig(id int64) (*feeds.ChainConfig, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetChainConfig") + } + var r0 *feeds.ChainConfig var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.ChainConfig, error)); ok { @@ -927,6 +987,10 @@ func (_m *ORM) GetJobProposal(id int64, qopts ...pg.QOpt) (*feeds.JobProposal, e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetJobProposal") + } + var r0 *feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) (*feeds.JobProposal, error)); ok { @@ -989,6 +1053,10 @@ func (_c *ORM_GetJobProposal_Call) RunAndReturn(run func(int64, ...pg.QOpt) (*fe func (_m *ORM) GetJobProposalByRemoteUUID(_a0 uuid.UUID) (*feeds.JobProposal, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetJobProposalByRemoteUUID") + } + var r0 *feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func(uuid.UUID) (*feeds.JobProposal, error)); ok { @@ -1043,6 +1111,10 @@ func (_c *ORM_GetJobProposalByRemoteUUID_Call) RunAndReturn(run func(uuid.UUID) func (_m *ORM) GetLatestSpec(jpID int64) (*feeds.JobProposalSpec, error) { ret := _m.Called(jpID) + if len(ret) == 0 { + panic("no return value specified for GetLatestSpec") + } + var r0 *feeds.JobProposalSpec var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.JobProposalSpec, error)); ok { @@ -1097,6 +1169,10 @@ func (_c *ORM_GetLatestSpec_Call) RunAndReturn(run func(int64) (*feeds.JobPropos func (_m *ORM) GetManager(id int64) (*feeds.FeedsManager, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetManager") + } + var r0 *feeds.FeedsManager var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.FeedsManager, error)); ok { @@ -1158,6 +1234,10 @@ func (_m *ORM) GetSpec(id int64, qopts ...pg.QOpt) (*feeds.JobProposalSpec, erro _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetSpec") + } + var r0 *feeds.JobProposalSpec var r1 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) (*feeds.JobProposalSpec, error)); ok { @@ -1227,6 +1307,10 @@ func (_m *ORM) IsJobManaged(jobID int64, qopts ...pg.QOpt) (bool, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IsJobManaged") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) (bool, error)); ok { @@ -1287,6 +1371,10 @@ func (_c *ORM_IsJobManaged_Call) RunAndReturn(run func(int64, ...pg.QOpt) (bool, func (_m *ORM) ListChainConfigsByManagerIDs(mgrIDs []int64) ([]feeds.ChainConfig, error) { ret := _m.Called(mgrIDs) + if len(ret) == 0 { + panic("no return value specified for ListChainConfigsByManagerIDs") + } + var r0 []feeds.ChainConfig var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]feeds.ChainConfig, error)); ok { @@ -1341,6 +1429,10 @@ func (_c *ORM_ListChainConfigsByManagerIDs_Call) RunAndReturn(run func([]int64) func (_m *ORM) ListJobProposals() ([]feeds.JobProposal, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ListJobProposals") + } + var r0 []feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func() ([]feeds.JobProposal, error)); ok { @@ -1401,6 +1493,10 @@ func (_m *ORM) ListJobProposalsByManagersIDs(ids []int64, qopts ...pg.QOpt) ([]f _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ListJobProposalsByManagersIDs") + } + var r0 []feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func([]int64, ...pg.QOpt) ([]feeds.JobProposal, error)); ok { @@ -1463,6 +1559,10 @@ func (_c *ORM_ListJobProposalsByManagersIDs_Call) RunAndReturn(run func([]int64, func (_m *ORM) ListManagers() ([]feeds.FeedsManager, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ListManagers") + } + var r0 []feeds.FeedsManager var r1 error if rf, ok := ret.Get(0).(func() ([]feeds.FeedsManager, error)); ok { @@ -1516,6 +1616,10 @@ func (_c *ORM_ListManagers_Call) RunAndReturn(run func() ([]feeds.FeedsManager, func (_m *ORM) ListManagersByIDs(ids []int64) ([]feeds.FeedsManager, error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for ListManagersByIDs") + } + var r0 []feeds.FeedsManager var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]feeds.FeedsManager, error)); ok { @@ -1577,6 +1681,10 @@ func (_m *ORM) ListSpecsByJobProposalIDs(ids []int64, qopts ...pg.QOpt) ([]feeds _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ListSpecsByJobProposalIDs") + } + var r0 []feeds.JobProposalSpec var r1 error if rf, ok := ret.Get(0).(func([]int64, ...pg.QOpt) ([]feeds.JobProposalSpec, error)); ok { @@ -1646,6 +1754,10 @@ func (_m *ORM) RejectSpec(id int64, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for RejectSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) error); ok { r0 = rf(id, qopts...) @@ -1703,6 +1815,10 @@ func (_m *ORM) RevokeSpec(id int64, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for RevokeSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) error); ok { r0 = rf(id, qopts...) @@ -1753,6 +1869,10 @@ func (_c *ORM_RevokeSpec_Call) RunAndReturn(run func(int64, ...pg.QOpt) error) * func (_m *ORM) UpdateChainConfig(cfg feeds.ChainConfig) (int64, error) { ret := _m.Called(cfg) + if len(ret) == 0 { + panic("no return value specified for UpdateChainConfig") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(feeds.ChainConfig) (int64, error)); ok { @@ -1812,6 +1932,10 @@ func (_m *ORM) UpdateJobProposalStatus(id int64, status feeds.JobProposalStatus, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateJobProposalStatus") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, feeds.JobProposalStatus, ...pg.QOpt) error); ok { r0 = rf(id, status, qopts...) @@ -1870,6 +1994,10 @@ func (_m *ORM) UpdateManager(mgr feeds.FeedsManager, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateManager") + } + var r0 error if rf, ok := ret.Get(0).(func(feeds.FeedsManager, ...pg.QOpt) error); ok { r0 = rf(mgr, qopts...) @@ -1927,6 +2055,10 @@ func (_m *ORM) UpdateSpecDefinition(id int64, spec string, qopts ...pg.QOpt) err _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateSpecDefinition") + } + var r0 error if rf, ok := ret.Get(0).(func(int64, string, ...pg.QOpt) error); ok { r0 = rf(id, spec, qopts...) @@ -1985,6 +2117,10 @@ func (_m *ORM) UpsertJobProposal(jp *feeds.JobProposal, qopts ...pg.QOpt) (int64 _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpsertJobProposal") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(*feeds.JobProposal, ...pg.QOpt) (int64, error)); ok { diff --git a/core/services/feeds/mocks/service.go b/core/services/feeds/mocks/service.go index 1681918bb74..d8bc88c8159 100644 --- a/core/services/feeds/mocks/service.go +++ b/core/services/feeds/mocks/service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type Service struct { func (_m *Service) ApproveSpec(ctx context.Context, id int64, force bool) error { ret := _m.Called(ctx, id, force) + if len(ret) == 0 { + panic("no return value specified for ApproveSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, bool) error); ok { r0 = rf(ctx, id, force) @@ -32,6 +36,10 @@ func (_m *Service) ApproveSpec(ctx context.Context, id int64, force bool) error func (_m *Service) CancelSpec(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for CancelSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -46,6 +54,10 @@ func (_m *Service) CancelSpec(ctx context.Context, id int64) error { func (_m *Service) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -60,6 +72,10 @@ func (_m *Service) Close() error { func (_m *Service) CountJobProposalsByStatus() (*feeds.JobProposalCounts, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CountJobProposalsByStatus") + } + var r0 *feeds.JobProposalCounts var r1 error if rf, ok := ret.Get(0).(func() (*feeds.JobProposalCounts, error)); ok { @@ -86,6 +102,10 @@ func (_m *Service) CountJobProposalsByStatus() (*feeds.JobProposalCounts, error) func (_m *Service) CountManagers() (int64, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CountManagers") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func() (int64, error)); ok { @@ -110,6 +130,10 @@ func (_m *Service) CountManagers() (int64, error) { func (_m *Service) CreateChainConfig(ctx context.Context, cfg feeds.ChainConfig) (int64, error) { ret := _m.Called(ctx, cfg) + if len(ret) == 0 { + panic("no return value specified for CreateChainConfig") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, feeds.ChainConfig) (int64, error)); ok { @@ -134,6 +158,10 @@ func (_m *Service) CreateChainConfig(ctx context.Context, cfg feeds.ChainConfig) func (_m *Service) DeleteChainConfig(ctx context.Context, id int64) (int64, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteChainConfig") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (int64, error)); ok { @@ -158,6 +186,10 @@ func (_m *Service) DeleteChainConfig(ctx context.Context, id int64) (int64, erro func (_m *Service) DeleteJob(ctx context.Context, args *feeds.DeleteJobArgs) (int64, error) { ret := _m.Called(ctx, args) + if len(ret) == 0 { + panic("no return value specified for DeleteJob") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *feeds.DeleteJobArgs) (int64, error)); ok { @@ -182,6 +214,10 @@ func (_m *Service) DeleteJob(ctx context.Context, args *feeds.DeleteJobArgs) (in func (_m *Service) GetChainConfig(id int64) (*feeds.ChainConfig, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetChainConfig") + } + var r0 *feeds.ChainConfig var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.ChainConfig, error)); ok { @@ -208,6 +244,10 @@ func (_m *Service) GetChainConfig(id int64) (*feeds.ChainConfig, error) { func (_m *Service) GetJobProposal(id int64) (*feeds.JobProposal, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetJobProposal") + } + var r0 *feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.JobProposal, error)); ok { @@ -234,6 +274,10 @@ func (_m *Service) GetJobProposal(id int64) (*feeds.JobProposal, error) { func (_m *Service) GetManager(id int64) (*feeds.FeedsManager, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetManager") + } + var r0 *feeds.FeedsManager var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.FeedsManager, error)); ok { @@ -260,6 +304,10 @@ func (_m *Service) GetManager(id int64) (*feeds.FeedsManager, error) { func (_m *Service) GetSpec(id int64) (*feeds.JobProposalSpec, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetSpec") + } + var r0 *feeds.JobProposalSpec var r1 error if rf, ok := ret.Get(0).(func(int64) (*feeds.JobProposalSpec, error)); ok { @@ -286,6 +334,10 @@ func (_m *Service) GetSpec(id int64) (*feeds.JobProposalSpec, error) { func (_m *Service) IsJobManaged(ctx context.Context, jobID int64) (bool, error) { ret := _m.Called(ctx, jobID) + if len(ret) == 0 { + panic("no return value specified for IsJobManaged") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (bool, error)); ok { @@ -310,6 +362,10 @@ func (_m *Service) IsJobManaged(ctx context.Context, jobID int64) (bool, error) func (_m *Service) ListChainConfigsByManagerIDs(mgrIDs []int64) ([]feeds.ChainConfig, error) { ret := _m.Called(mgrIDs) + if len(ret) == 0 { + panic("no return value specified for ListChainConfigsByManagerIDs") + } + var r0 []feeds.ChainConfig var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]feeds.ChainConfig, error)); ok { @@ -336,6 +392,10 @@ func (_m *Service) ListChainConfigsByManagerIDs(mgrIDs []int64) ([]feeds.ChainCo func (_m *Service) ListJobProposals() ([]feeds.JobProposal, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ListJobProposals") + } + var r0 []feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func() ([]feeds.JobProposal, error)); ok { @@ -362,6 +422,10 @@ func (_m *Service) ListJobProposals() ([]feeds.JobProposal, error) { func (_m *Service) ListJobProposalsByManagersIDs(ids []int64) ([]feeds.JobProposal, error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for ListJobProposalsByManagersIDs") + } + var r0 []feeds.JobProposal var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]feeds.JobProposal, error)); ok { @@ -388,6 +452,10 @@ func (_m *Service) ListJobProposalsByManagersIDs(ids []int64) ([]feeds.JobPropos func (_m *Service) ListManagers() ([]feeds.FeedsManager, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ListManagers") + } + var r0 []feeds.FeedsManager var r1 error if rf, ok := ret.Get(0).(func() ([]feeds.FeedsManager, error)); ok { @@ -414,6 +482,10 @@ func (_m *Service) ListManagers() ([]feeds.FeedsManager, error) { func (_m *Service) ListManagersByIDs(ids []int64) ([]feeds.FeedsManager, error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for ListManagersByIDs") + } + var r0 []feeds.FeedsManager var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]feeds.FeedsManager, error)); ok { @@ -440,6 +512,10 @@ func (_m *Service) ListManagersByIDs(ids []int64) ([]feeds.FeedsManager, error) func (_m *Service) ListSpecsByJobProposalIDs(ids []int64) ([]feeds.JobProposalSpec, error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for ListSpecsByJobProposalIDs") + } + var r0 []feeds.JobProposalSpec var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]feeds.JobProposalSpec, error)); ok { @@ -466,6 +542,10 @@ func (_m *Service) ListSpecsByJobProposalIDs(ids []int64) ([]feeds.JobProposalSp func (_m *Service) ProposeJob(ctx context.Context, args *feeds.ProposeJobArgs) (int64, error) { ret := _m.Called(ctx, args) + if len(ret) == 0 { + panic("no return value specified for ProposeJob") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *feeds.ProposeJobArgs) (int64, error)); ok { @@ -490,6 +570,10 @@ func (_m *Service) ProposeJob(ctx context.Context, args *feeds.ProposeJobArgs) ( func (_m *Service) RegisterManager(ctx context.Context, params feeds.RegisterManagerParams) (int64, error) { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for RegisterManager") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, feeds.RegisterManagerParams) (int64, error)); ok { @@ -514,6 +598,10 @@ func (_m *Service) RegisterManager(ctx context.Context, params feeds.RegisterMan func (_m *Service) RejectSpec(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for RejectSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -528,6 +616,10 @@ func (_m *Service) RejectSpec(ctx context.Context, id int64) error { func (_m *Service) RevokeJob(ctx context.Context, args *feeds.RevokeJobArgs) (int64, error) { ret := _m.Called(ctx, args) + if len(ret) == 0 { + panic("no return value specified for RevokeJob") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *feeds.RevokeJobArgs) (int64, error)); ok { @@ -552,6 +644,10 @@ func (_m *Service) RevokeJob(ctx context.Context, args *feeds.RevokeJobArgs) (in func (_m *Service) Start(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -566,6 +662,10 @@ func (_m *Service) Start(ctx context.Context) error { func (_m *Service) SyncNodeInfo(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for SyncNodeInfo") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -585,6 +685,10 @@ func (_m *Service) Unsafe_SetConnectionsManager(_a0 feeds.ConnectionsManager) { func (_m *Service) UpdateChainConfig(ctx context.Context, cfg feeds.ChainConfig) (int64, error) { ret := _m.Called(ctx, cfg) + if len(ret) == 0 { + panic("no return value specified for UpdateChainConfig") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, feeds.ChainConfig) (int64, error)); ok { @@ -609,6 +713,10 @@ func (_m *Service) UpdateChainConfig(ctx context.Context, cfg feeds.ChainConfig) func (_m *Service) UpdateManager(ctx context.Context, mgr feeds.FeedsManager) error { ret := _m.Called(ctx, mgr) + if len(ret) == 0 { + panic("no return value specified for UpdateManager") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, feeds.FeedsManager) error); ok { r0 = rf(ctx, mgr) @@ -623,6 +731,10 @@ func (_m *Service) UpdateManager(ctx context.Context, mgr feeds.FeedsManager) er func (_m *Service) UpdateSpecDefinition(ctx context.Context, id int64, spec string) error { ret := _m.Called(ctx, id, spec) + if len(ret) == 0 { + panic("no return value specified for UpdateSpecDefinition") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok { r0 = rf(ctx, id, spec) diff --git a/core/services/fluxmonitorv2/mocks/contract_submitter.go b/core/services/fluxmonitorv2/mocks/contract_submitter.go index 03540a6cb1c..3154b4c86ee 100644 --- a/core/services/fluxmonitorv2/mocks/contract_submitter.go +++ b/core/services/fluxmonitorv2/mocks/contract_submitter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type ContractSubmitter struct { func (_m *ContractSubmitter) Submit(ctx context.Context, roundID *big.Int, submission *big.Int, idempotencyKey *string) error { ret := _m.Called(ctx, roundID, submission, idempotencyKey) + if len(ret) == 0 { + panic("no return value specified for Submit") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *big.Int, *big.Int, *string) error); ok { r0 = rf(ctx, roundID, submission, idempotencyKey) diff --git a/core/services/fluxmonitorv2/mocks/flags.go b/core/services/fluxmonitorv2/mocks/flags.go index 08ad0f5b3f7..6ff1616111b 100644 --- a/core/services/fluxmonitorv2/mocks/flags.go +++ b/core/services/fluxmonitorv2/mocks/flags.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type Flags struct { func (_m *Flags) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -37,6 +41,10 @@ func (_m *Flags) Address() common.Address { func (_m *Flags) ContractExists() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ContractExists") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -51,6 +59,10 @@ func (_m *Flags) ContractExists() bool { func (_m *Flags) IsLowered(contractAddr common.Address) (bool, error) { ret := _m.Called(contractAddr) + if len(ret) == 0 { + panic("no return value specified for IsLowered") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(common.Address) (bool, error)); ok { @@ -75,6 +87,10 @@ func (_m *Flags) IsLowered(contractAddr common.Address) (bool, error) { func (_m *Flags) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { diff --git a/core/services/fluxmonitorv2/mocks/key_store_interface.go b/core/services/fluxmonitorv2/mocks/key_store_interface.go index c409a987e02..98f5ab71020 100644 --- a/core/services/fluxmonitorv2/mocks/key_store_interface.go +++ b/core/services/fluxmonitorv2/mocks/key_store_interface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type KeyStoreInterface struct { func (_m *KeyStoreInterface) EnabledKeysForChain(chainID *big.Int) ([]ethkey.KeyV2, error) { ret := _m.Called(chainID) + if len(ret) == 0 { + panic("no return value specified for EnabledKeysForChain") + } + var r0 []ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(*big.Int) ([]ethkey.KeyV2, error)); ok { @@ -53,6 +57,10 @@ func (_m *KeyStoreInterface) GetRoundRobinAddress(chainID *big.Int, addrs ...com _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetRoundRobinAddress") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*big.Int, ...common.Address) (common.Address, error)); ok { diff --git a/core/services/fluxmonitorv2/mocks/orm.go b/core/services/fluxmonitorv2/mocks/orm.go index 5080f19edf0..8d277d61d2e 100644 --- a/core/services/fluxmonitorv2/mocks/orm.go +++ b/core/services/fluxmonitorv2/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -23,6 +23,10 @@ type ORM struct { func (_m *ORM) CountFluxMonitorRoundStats() (int, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CountFluxMonitorRoundStats") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func() (int, error)); ok { @@ -47,6 +51,10 @@ func (_m *ORM) CountFluxMonitorRoundStats() (int, error) { func (_m *ORM) CreateEthTransaction(ctx context.Context, fromAddress common.Address, toAddress common.Address, payload []byte, gasLimit uint32, idempotencyKey *string) error { ret := _m.Called(ctx, fromAddress, toAddress, payload, gasLimit, idempotencyKey) + if len(ret) == 0 { + panic("no return value specified for CreateEthTransaction") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address, []byte, uint32, *string) error); ok { r0 = rf(ctx, fromAddress, toAddress, payload, gasLimit, idempotencyKey) @@ -61,6 +69,10 @@ func (_m *ORM) CreateEthTransaction(ctx context.Context, fromAddress common.Addr func (_m *ORM) DeleteFluxMonitorRoundsBackThrough(aggregator common.Address, roundID uint32) error { ret := _m.Called(aggregator, roundID) + if len(ret) == 0 { + panic("no return value specified for DeleteFluxMonitorRoundsBackThrough") + } + var r0 error if rf, ok := ret.Get(0).(func(common.Address, uint32) error); ok { r0 = rf(aggregator, roundID) @@ -75,6 +87,10 @@ func (_m *ORM) DeleteFluxMonitorRoundsBackThrough(aggregator common.Address, rou func (_m *ORM) FindOrCreateFluxMonitorRoundStats(aggregator common.Address, roundID uint32, newRoundLogs uint) (fluxmonitorv2.FluxMonitorRoundStatsV2, error) { ret := _m.Called(aggregator, roundID, newRoundLogs) + if len(ret) == 0 { + panic("no return value specified for FindOrCreateFluxMonitorRoundStats") + } + var r0 fluxmonitorv2.FluxMonitorRoundStatsV2 var r1 error if rf, ok := ret.Get(0).(func(common.Address, uint32, uint) (fluxmonitorv2.FluxMonitorRoundStatsV2, error)); ok { @@ -99,6 +115,10 @@ func (_m *ORM) FindOrCreateFluxMonitorRoundStats(aggregator common.Address, roun func (_m *ORM) MostRecentFluxMonitorRoundID(aggregator common.Address) (uint32, error) { ret := _m.Called(aggregator) + if len(ret) == 0 { + panic("no return value specified for MostRecentFluxMonitorRoundID") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(common.Address) (uint32, error)); ok { @@ -130,6 +150,10 @@ func (_m *ORM) UpdateFluxMonitorRoundStats(aggregator common.Address, roundID ui _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateFluxMonitorRoundStats") + } + var r0 error if rf, ok := ret.Get(0).(func(common.Address, uint32, int64, uint, ...pg.QOpt) error); ok { r0 = rf(aggregator, roundID, runID, newRoundLogsAddition, qopts...) diff --git a/core/services/functions/mocks/bridge_accessor.go b/core/services/functions/mocks/bridge_accessor.go index 65e81ab8b83..fa765287c44 100644 --- a/core/services/functions/mocks/bridge_accessor.go +++ b/core/services/functions/mocks/bridge_accessor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type BridgeAccessor struct { func (_m *BridgeAccessor) NewExternalAdapterClient() (functions.ExternalAdapterClient, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for NewExternalAdapterClient") + } + var r0 functions.ExternalAdapterClient var r1 error if rf, ok := ret.Get(0).(func() (functions.ExternalAdapterClient, error)); ok { diff --git a/core/services/functions/mocks/external_adapter_client.go b/core/services/functions/mocks/external_adapter_client.go index b06f13fdea7..dbf4081c95d 100644 --- a/core/services/functions/mocks/external_adapter_client.go +++ b/core/services/functions/mocks/external_adapter_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type ExternalAdapterClient struct { func (_m *ExternalAdapterClient) FetchEncryptedSecrets(ctx context.Context, encryptedSecretsUrls []byte, requestId string, jobName string) ([]byte, []byte, error) { ret := _m.Called(ctx, encryptedSecretsUrls, requestId, jobName) + if len(ret) == 0 { + panic("no return value specified for FetchEncryptedSecrets") + } + var r0 []byte var r1 []byte var r2 error @@ -53,6 +57,10 @@ func (_m *ExternalAdapterClient) FetchEncryptedSecrets(ctx context.Context, encr func (_m *ExternalAdapterClient) RunComputation(ctx context.Context, requestId string, jobName string, subscriptionOwner string, subscriptionId uint64, flags functions.RequestFlags, nodeProvidedSecrets string, requestData *functions.RequestData) ([]byte, []byte, []string, error) { ret := _m.Called(ctx, requestId, jobName, subscriptionOwner, subscriptionId, flags, nodeProvidedSecrets, requestData) + if len(ret) == 0 { + panic("no return value specified for RunComputation") + } + var r0 []byte var r1 []byte var r2 []string diff --git a/core/services/functions/mocks/functions_listener.go b/core/services/functions/mocks/functions_listener.go index d2aeb2ddab8..d63248f00cf 100644 --- a/core/services/functions/mocks/functions_listener.go +++ b/core/services/functions/mocks/functions_listener.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type FunctionsListener struct { func (_m *FunctionsListener) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *FunctionsListener) Close() error { func (_m *FunctionsListener) HandleOffchainRequest(ctx context.Context, request *functions.OffchainRequest) error { ret := _m.Called(ctx, request) + if len(ret) == 0 { + panic("no return value specified for HandleOffchainRequest") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *functions.OffchainRequest) error); ok { r0 = rf(ctx, request) @@ -46,6 +54,10 @@ func (_m *FunctionsListener) HandleOffchainRequest(ctx context.Context, request func (_m *FunctionsListener) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/functions/mocks/offchain_transmitter.go b/core/services/functions/mocks/offchain_transmitter.go index d9a7be04dd4..5eee967e685 100644 --- a/core/services/functions/mocks/offchain_transmitter.go +++ b/core/services/functions/mocks/offchain_transmitter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type OffchainTransmitter struct { func (_m *OffchainTransmitter) ReportChannel() chan *functions.OffchainResponse { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReportChannel") + } + var r0 chan *functions.OffchainResponse if rf, ok := ret.Get(0).(func() chan *functions.OffchainResponse); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *OffchainTransmitter) ReportChannel() chan *functions.OffchainResponse func (_m *OffchainTransmitter) TransmitReport(ctx context.Context, report *functions.OffchainResponse) error { ret := _m.Called(ctx, report) + if len(ret) == 0 { + panic("no return value specified for TransmitReport") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *functions.OffchainResponse) error); ok { r0 = rf(ctx, report) diff --git a/core/services/functions/mocks/orm.go b/core/services/functions/mocks/orm.go index 8d11b0b9817..90055fe6286 100644 --- a/core/services/functions/mocks/orm.go +++ b/core/services/functions/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -27,6 +27,10 @@ func (_m *ORM) CreateRequest(request *functions.Request, qopts ...pg.QOpt) error _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateRequest") + } + var r0 error if rf, ok := ret.Get(0).(func(*functions.Request, ...pg.QOpt) error); ok { r0 = rf(request, qopts...) @@ -48,6 +52,10 @@ func (_m *ORM) FindById(requestID functions.RequestID, qopts ...pg.QOpt) (*funct _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindById") + } + var r0 *functions.Request var r1 error if rf, ok := ret.Get(0).(func(functions.RequestID, ...pg.QOpt) (*functions.Request, error)); ok { @@ -81,6 +89,10 @@ func (_m *ORM) FindOldestEntriesByState(state functions.RequestState, limit uint _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindOldestEntriesByState") + } + var r0 []functions.Request var r1 error if rf, ok := ret.Get(0).(func(functions.RequestState, uint32, ...pg.QOpt) ([]functions.Request, error)); ok { @@ -114,6 +126,10 @@ func (_m *ORM) PruneOldestRequests(maxRequestsInDB uint32, batchSize uint32, qop _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for PruneOldestRequests") + } + var r0 uint32 var r1 uint32 var r2 error @@ -152,6 +168,10 @@ func (_m *ORM) SetConfirmed(requestID functions.RequestID, qopts ...pg.QOpt) err _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SetConfirmed") + } + var r0 error if rf, ok := ret.Get(0).(func(functions.RequestID, ...pg.QOpt) error); ok { r0 = rf(requestID, qopts...) @@ -173,6 +193,10 @@ func (_m *ORM) SetError(requestID functions.RequestID, errorType functions.ErrTy _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SetError") + } + var r0 error if rf, ok := ret.Get(0).(func(functions.RequestID, functions.ErrType, []byte, time.Time, bool, ...pg.QOpt) error); ok { r0 = rf(requestID, errorType, computationError, readyAt, readyForProcessing, qopts...) @@ -194,6 +218,10 @@ func (_m *ORM) SetFinalized(requestID functions.RequestID, reportedResult []byte _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SetFinalized") + } + var r0 error if rf, ok := ret.Get(0).(func(functions.RequestID, []byte, []byte, ...pg.QOpt) error); ok { r0 = rf(requestID, reportedResult, reportedError, qopts...) @@ -215,6 +243,10 @@ func (_m *ORM) SetResult(requestID functions.RequestID, computationResult []byte _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SetResult") + } + var r0 error if rf, ok := ret.Get(0).(func(functions.RequestID, []byte, time.Time, ...pg.QOpt) error); ok { r0 = rf(requestID, computationResult, readyAt, qopts...) @@ -236,6 +268,10 @@ func (_m *ORM) TimeoutExpiredResults(cutoff time.Time, limit uint32, qopts ...pg _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for TimeoutExpiredResults") + } + var r0 []functions.RequestID var r1 error if rf, ok := ret.Get(0).(func(time.Time, uint32, ...pg.QOpt) ([]functions.RequestID, error)); ok { diff --git a/core/services/gateway/connector/mocks/gateway_connector.go b/core/services/gateway/connector/mocks/gateway_connector.go index a9fa69e1e3b..ba972425f66 100644 --- a/core/services/gateway/connector/mocks/gateway_connector.go +++ b/core/services/gateway/connector/mocks/gateway_connector.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type GatewayConnector struct { func (_m *GatewayConnector) ChallengeResponse(_a0 *url.URL, challenge []byte) ([]byte, error) { ret := _m.Called(_a0, challenge) + if len(ret) == 0 { + panic("no return value specified for ChallengeResponse") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(*url.URL, []byte) ([]byte, error)); ok { @@ -47,6 +51,10 @@ func (_m *GatewayConnector) ChallengeResponse(_a0 *url.URL, challenge []byte) ([ func (_m *GatewayConnector) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -61,6 +69,10 @@ func (_m *GatewayConnector) Close() error { func (_m *GatewayConnector) NewAuthHeader(_a0 *url.URL) ([]byte, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for NewAuthHeader") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(*url.URL) ([]byte, error)); ok { @@ -87,6 +99,10 @@ func (_m *GatewayConnector) NewAuthHeader(_a0 *url.URL) ([]byte, error) { func (_m *GatewayConnector) SendToGateway(ctx context.Context, gatewayId string, msg *api.Message) error { ret := _m.Called(ctx, gatewayId, msg) + if len(ret) == 0 { + panic("no return value specified for SendToGateway") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, *api.Message) error); ok { r0 = rf(ctx, gatewayId, msg) @@ -101,6 +117,10 @@ func (_m *GatewayConnector) SendToGateway(ctx context.Context, gatewayId string, func (_m *GatewayConnector) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/gateway/connector/mocks/gateway_connector_handler.go b/core/services/gateway/connector/mocks/gateway_connector_handler.go index 8eb27708a5a..e83e06b60e3 100644 --- a/core/services/gateway/connector/mocks/gateway_connector_handler.go +++ b/core/services/gateway/connector/mocks/gateway_connector_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type GatewayConnectorHandler struct { func (_m *GatewayConnectorHandler) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -38,6 +42,10 @@ func (_m *GatewayConnectorHandler) HandleGatewayMessage(ctx context.Context, gat func (_m *GatewayConnectorHandler) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/gateway/connector/mocks/signer.go b/core/services/gateway/connector/mocks/signer.go index 497b25d8df3..18c7186f7fc 100644 --- a/core/services/gateway/connector/mocks/signer.go +++ b/core/services/gateway/connector/mocks/signer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *Signer) Sign(data ...[]byte) ([]byte, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Sign") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(...[]byte) ([]byte, error)); ok { diff --git a/core/services/gateway/handlers/functions/mocks/onchain_allowlist.go b/core/services/gateway/handlers/functions/mocks/onchain_allowlist.go index 8cbf301f0ea..6668a3c76ff 100644 --- a/core/services/gateway/handlers/functions/mocks/onchain_allowlist.go +++ b/core/services/gateway/handlers/functions/mocks/onchain_allowlist.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type OnchainAllowlist struct { func (_m *OnchainAllowlist) Allow(_a0 common.Address) bool { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Allow") + } + var r0 bool if rf, ok := ret.Get(0).(func(common.Address) bool); ok { r0 = rf(_a0) @@ -33,6 +37,10 @@ func (_m *OnchainAllowlist) Allow(_a0 common.Address) bool { func (_m *OnchainAllowlist) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -47,6 +55,10 @@ func (_m *OnchainAllowlist) Close() error { func (_m *OnchainAllowlist) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -61,6 +73,10 @@ func (_m *OnchainAllowlist) Start(_a0 context.Context) error { func (_m *OnchainAllowlist) UpdateFromContract(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for UpdateFromContract") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) diff --git a/core/services/gateway/handlers/functions/mocks/onchain_subscriptions.go b/core/services/gateway/handlers/functions/mocks/onchain_subscriptions.go index 64e2960f949..5f2054c4e47 100644 --- a/core/services/gateway/handlers/functions/mocks/onchain_subscriptions.go +++ b/core/services/gateway/handlers/functions/mocks/onchain_subscriptions.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type OnchainSubscriptions struct { func (_m *OnchainSubscriptions) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *OnchainSubscriptions) Close() error { func (_m *OnchainSubscriptions) GetMaxUserBalance(_a0 common.Address) (*big.Int, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetMaxUserBalance") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(common.Address) (*big.Int, error)); ok { @@ -60,6 +68,10 @@ func (_m *OnchainSubscriptions) GetMaxUserBalance(_a0 common.Address) (*big.Int, func (_m *OnchainSubscriptions) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/gateway/handlers/mocks/don.go b/core/services/gateway/handlers/mocks/don.go index 02df7c0334f..6e88708dd7d 100644 --- a/core/services/gateway/handlers/mocks/don.go +++ b/core/services/gateway/handlers/mocks/don.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type DON struct { func (_m *DON) SendToNode(ctx context.Context, nodeAddress string, msg *api.Message) error { ret := _m.Called(ctx, nodeAddress, msg) + if len(ret) == 0 { + panic("no return value specified for SendToNode") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, *api.Message) error); ok { r0 = rf(ctx, nodeAddress, msg) diff --git a/core/services/gateway/handlers/mocks/handler.go b/core/services/gateway/handlers/mocks/handler.go index 10a31c6d76e..7dfe1eae784 100644 --- a/core/services/gateway/handlers/mocks/handler.go +++ b/core/services/gateway/handlers/mocks/handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type Handler struct { func (_m *Handler) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -35,6 +39,10 @@ func (_m *Handler) Close() error { func (_m *Handler) HandleNodeMessage(ctx context.Context, msg *api.Message, nodeAddr string) error { ret := _m.Called(ctx, msg, nodeAddr) + if len(ret) == 0 { + panic("no return value specified for HandleNodeMessage") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *api.Message, string) error); ok { r0 = rf(ctx, msg, nodeAddr) @@ -49,6 +57,10 @@ func (_m *Handler) HandleNodeMessage(ctx context.Context, msg *api.Message, node func (_m *Handler) HandleUserMessage(ctx context.Context, msg *api.Message, callbackCh chan<- handlers.UserCallbackPayload) error { ret := _m.Called(ctx, msg, callbackCh) + if len(ret) == 0 { + panic("no return value specified for HandleUserMessage") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *api.Message, chan<- handlers.UserCallbackPayload) error); ok { r0 = rf(ctx, msg, callbackCh) @@ -63,6 +75,10 @@ func (_m *Handler) HandleUserMessage(ctx context.Context, msg *api.Message, call func (_m *Handler) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/gateway/network/mocks/connection_acceptor.go b/core/services/gateway/network/mocks/connection_acceptor.go index 738904984af..c45cc7fbe3e 100644 --- a/core/services/gateway/network/mocks/connection_acceptor.go +++ b/core/services/gateway/network/mocks/connection_acceptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ func (_m *ConnectionAcceptor) AbortHandshake(attemptId string) { func (_m *ConnectionAcceptor) FinalizeHandshake(attemptId string, response []byte, conn *websocket.Conn) error { ret := _m.Called(attemptId, response, conn) + if len(ret) == 0 { + panic("no return value specified for FinalizeHandshake") + } + var r0 error if rf, ok := ret.Get(0).(func(string, []byte, *websocket.Conn) error); ok { r0 = rf(attemptId, response, conn) @@ -36,6 +40,10 @@ func (_m *ConnectionAcceptor) FinalizeHandshake(attemptId string, response []byt func (_m *ConnectionAcceptor) StartHandshake(authHeader []byte) (string, []byte, error) { ret := _m.Called(authHeader) + if len(ret) == 0 { + panic("no return value specified for StartHandshake") + } + var r0 string var r1 []byte var r2 error diff --git a/core/services/gateway/network/mocks/connection_initiator.go b/core/services/gateway/network/mocks/connection_initiator.go index 3ff60e61398..87e4f407328 100644 --- a/core/services/gateway/network/mocks/connection_initiator.go +++ b/core/services/gateway/network/mocks/connection_initiator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type ConnectionInitiator struct { func (_m *ConnectionInitiator) ChallengeResponse(_a0 *url.URL, challenge []byte) ([]byte, error) { ret := _m.Called(_a0, challenge) + if len(ret) == 0 { + panic("no return value specified for ChallengeResponse") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(*url.URL, []byte) ([]byte, error)); ok { @@ -43,6 +47,10 @@ func (_m *ConnectionInitiator) ChallengeResponse(_a0 *url.URL, challenge []byte) func (_m *ConnectionInitiator) NewAuthHeader(_a0 *url.URL) ([]byte, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for NewAuthHeader") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(*url.URL) ([]byte, error)); ok { diff --git a/core/services/gateway/network/mocks/http_request_handler.go b/core/services/gateway/network/mocks/http_request_handler.go index 7716626ac72..7c5ff4025cf 100644 --- a/core/services/gateway/network/mocks/http_request_handler.go +++ b/core/services/gateway/network/mocks/http_request_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type HTTPRequestHandler struct { func (_m *HTTPRequestHandler) ProcessRequest(ctx context.Context, rawRequest []byte) ([]byte, int) { ret := _m.Called(ctx, rawRequest) + if len(ret) == 0 { + panic("no return value specified for ProcessRequest") + } + var r0 []byte var r1 int if rf, ok := ret.Get(0).(func(context.Context, []byte) ([]byte, int)); ok { diff --git a/core/services/gateway/network/mocks/http_server.go b/core/services/gateway/network/mocks/http_server.go index 197e77f1b8a..81e180e7b8d 100644 --- a/core/services/gateway/network/mocks/http_server.go +++ b/core/services/gateway/network/mocks/http_server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type HttpServer struct { func (_m *HttpServer) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *HttpServer) Close() error { func (_m *HttpServer) GetPort() int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetPort") + } + var r0 int if rf, ok := ret.Get(0).(func() int); ok { r0 = rf() @@ -51,6 +59,10 @@ func (_m *HttpServer) SetHTTPRequestHandler(handler network.HTTPRequestHandler) func (_m *HttpServer) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/gateway/network/mocks/web_socket_server.go b/core/services/gateway/network/mocks/web_socket_server.go index d88cd5ba4f7..4f75f3b7d0f 100644 --- a/core/services/gateway/network/mocks/web_socket_server.go +++ b/core/services/gateway/network/mocks/web_socket_server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type WebSocketServer struct { func (_m *WebSocketServer) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -31,6 +35,10 @@ func (_m *WebSocketServer) Close() error { func (_m *WebSocketServer) GetPort() int { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetPort") + } + var r0 int if rf, ok := ret.Get(0).(func() int); ok { r0 = rf() @@ -45,6 +53,10 @@ func (_m *WebSocketServer) GetPort() int { func (_m *WebSocketServer) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/job/mocks/orm.go b/core/services/job/mocks/orm.go index 9e18573f4e5..66602c60053 100644 --- a/core/services/job/mocks/orm.go +++ b/core/services/job/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -31,6 +31,10 @@ type ORM struct { func (_m *ORM) AssertBridgesExist(p pipeline.Pipeline) error { ret := _m.Called(p) + if len(ret) == 0 { + panic("no return value specified for AssertBridgesExist") + } + var r0 error if rf, ok := ret.Get(0).(func(pipeline.Pipeline) error); ok { r0 = rf(p) @@ -45,6 +49,10 @@ func (_m *ORM) AssertBridgesExist(p pipeline.Pipeline) error { func (_m *ORM) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -59,6 +67,10 @@ func (_m *ORM) Close() error { func (_m *ORM) CountPipelineRunsByJobID(jobID int32) (int32, error) { ret := _m.Called(jobID) + if len(ret) == 0 { + panic("no return value specified for CountPipelineRunsByJobID") + } + var r0 int32 var r1 error if rf, ok := ret.Get(0).(func(int32) (int32, error)); ok { @@ -90,6 +102,10 @@ func (_m *ORM) CreateJob(jb *job.Job, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateJob") + } + var r0 error if rf, ok := ret.Get(0).(func(*job.Job, ...pg.QOpt) error); ok { r0 = rf(jb, qopts...) @@ -111,6 +127,10 @@ func (_m *ORM) DeleteJob(id int32, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DeleteJob") + } + var r0 error if rf, ok := ret.Get(0).(func(int32, ...pg.QOpt) error); ok { r0 = rf(id, qopts...) @@ -125,6 +145,10 @@ func (_m *ORM) DeleteJob(id int32, qopts ...pg.QOpt) error { func (_m *ORM) DismissError(ctx context.Context, errorID int64) error { ret := _m.Called(ctx, errorID) + if len(ret) == 0 { + panic("no return value specified for DismissError") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, errorID) @@ -139,6 +163,10 @@ func (_m *ORM) DismissError(ctx context.Context, errorID int64) error { func (_m *ORM) FindJob(ctx context.Context, id int32) (job.Job, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for FindJob") + } + var r0 job.Job var r1 error if rf, ok := ret.Get(0).(func(context.Context, int32) (job.Job, error)); ok { @@ -170,6 +198,10 @@ func (_m *ORM) FindJobByExternalJobID(_a0 uuid.UUID, qopts ...pg.QOpt) (job.Job, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindJobByExternalJobID") + } + var r0 job.Job var r1 error if rf, ok := ret.Get(0).(func(uuid.UUID, ...pg.QOpt) (job.Job, error)); ok { @@ -201,6 +233,10 @@ func (_m *ORM) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindJobIDByAddress") + } + var r0 int32 var r1 error if rf, ok := ret.Get(0).(func(ethkey.EIP55Address, *utils.Big, ...pg.QOpt) (int32, error)); ok { @@ -225,6 +261,10 @@ func (_m *ORM) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils func (_m *ORM) FindJobIDsWithBridge(name string) ([]int32, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for FindJobIDsWithBridge") + } + var r0 []int32 var r1 error if rf, ok := ret.Get(0).(func(string) ([]int32, error)); ok { @@ -251,6 +291,10 @@ func (_m *ORM) FindJobIDsWithBridge(name string) ([]int32, error) { func (_m *ORM) FindJobTx(ctx context.Context, id int32) (job.Job, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for FindJobTx") + } + var r0 job.Job var r1 error if rf, ok := ret.Get(0).(func(context.Context, int32) (job.Job, error)); ok { @@ -275,6 +319,10 @@ func (_m *ORM) FindJobTx(ctx context.Context, id int32) (job.Job, error) { func (_m *ORM) FindJobWithoutSpecErrors(id int32) (job.Job, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for FindJobWithoutSpecErrors") + } + var r0 job.Job var r1 error if rf, ok := ret.Get(0).(func(int32) (job.Job, error)); ok { @@ -299,6 +347,10 @@ func (_m *ORM) FindJobWithoutSpecErrors(id int32) (job.Job, error) { func (_m *ORM) FindJobs(offset int, limit int) ([]job.Job, int, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for FindJobs") + } + var r0 []job.Job var r1 int var r2 error @@ -332,6 +384,10 @@ func (_m *ORM) FindJobs(offset int, limit int) ([]job.Job, int, error) { func (_m *ORM) FindJobsByPipelineSpecIDs(ids []int32) ([]job.Job, error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for FindJobsByPipelineSpecIDs") + } + var r0 []job.Job var r1 error if rf, ok := ret.Get(0).(func([]int32) ([]job.Job, error)); ok { @@ -365,6 +421,10 @@ func (_m *ORM) FindOCR2JobIDByAddress(contractID string, feedID *common.Hash, qo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindOCR2JobIDByAddress") + } + var r0 int32 var r1 error if rf, ok := ret.Get(0).(func(string, *common.Hash, ...pg.QOpt) (int32, error)); ok { @@ -389,6 +449,10 @@ func (_m *ORM) FindOCR2JobIDByAddress(contractID string, feedID *common.Hash, qo func (_m *ORM) FindPipelineRunByID(id int64) (pipeline.Run, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for FindPipelineRunByID") + } + var r0 pipeline.Run var r1 error if rf, ok := ret.Get(0).(func(int64) (pipeline.Run, error)); ok { @@ -413,6 +477,10 @@ func (_m *ORM) FindPipelineRunByID(id int64) (pipeline.Run, error) { func (_m *ORM) FindPipelineRunIDsByJobID(jobID int32, offset int, limit int) ([]int64, error) { ret := _m.Called(jobID, offset, limit) + if len(ret) == 0 { + panic("no return value specified for FindPipelineRunIDsByJobID") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func(int32, int, int) ([]int64, error)); ok { @@ -439,6 +507,10 @@ func (_m *ORM) FindPipelineRunIDsByJobID(jobID int32, offset int, limit int) ([] func (_m *ORM) FindPipelineRunsByIDs(ids []int64) ([]pipeline.Run, error) { ret := _m.Called(ids) + if len(ret) == 0 { + panic("no return value specified for FindPipelineRunsByIDs") + } + var r0 []pipeline.Run var r1 error if rf, ok := ret.Get(0).(func([]int64) ([]pipeline.Run, error)); ok { @@ -472,6 +544,10 @@ func (_m *ORM) FindSpecError(id int64, qopts ...pg.QOpt) (job.SpecError, error) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindSpecError") + } + var r0 job.SpecError var r1 error if rf, ok := ret.Get(0).(func(int64, ...pg.QOpt) (job.SpecError, error)); ok { @@ -503,6 +579,10 @@ func (_m *ORM) FindSpecErrorsByJobIDs(ids []int32, qopts ...pg.QOpt) ([]job.Spec _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindSpecErrorsByJobIDs") + } + var r0 []job.SpecError var r1 error if rf, ok := ret.Get(0).(func([]int32, ...pg.QOpt) ([]job.SpecError, error)); ok { @@ -536,6 +616,10 @@ func (_m *ORM) FindTaskResultByRunIDAndTaskName(runID int64, taskName string, qo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for FindTaskResultByRunIDAndTaskName") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(int64, string, ...pg.QOpt) ([]byte, error)); ok { @@ -569,6 +653,10 @@ func (_m *ORM) InsertJob(_a0 *job.Job, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertJob") + } + var r0 error if rf, ok := ret.Get(0).(func(*job.Job, ...pg.QOpt) error); ok { r0 = rf(_a0, qopts...) @@ -590,6 +678,10 @@ func (_m *ORM) InsertWebhookSpec(webhookSpec *job.WebhookSpec, qopts ...pg.QOpt) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertWebhookSpec") + } + var r0 error if rf, ok := ret.Get(0).(func(*job.WebhookSpec, ...pg.QOpt) error); ok { r0 = rf(webhookSpec, qopts...) @@ -604,6 +696,10 @@ func (_m *ORM) InsertWebhookSpec(webhookSpec *job.WebhookSpec, qopts ...pg.QOpt) func (_m *ORM) PipelineRuns(jobID *int32, offset int, size int) ([]pipeline.Run, int, error) { ret := _m.Called(jobID, offset, size) + if len(ret) == 0 { + panic("no return value specified for PipelineRuns") + } + var r0 []pipeline.Run var r1 int var r2 error @@ -644,6 +740,10 @@ func (_m *ORM) RecordError(jobID int32, description string, qopts ...pg.QOpt) er _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for RecordError") + } + var r0 error if rf, ok := ret.Get(0).(func(int32, string, ...pg.QOpt) error); ok { r0 = rf(jobID, description, qopts...) diff --git a/core/services/job/mocks/service_ctx.go b/core/services/job/mocks/service_ctx.go index 93ef76619d9..43c28632963 100644 --- a/core/services/job/mocks/service_ctx.go +++ b/core/services/job/mocks/service_ctx.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type ServiceCtx struct { func (_m *ServiceCtx) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -31,6 +35,10 @@ func (_m *ServiceCtx) Close() error { func (_m *ServiceCtx) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/job/mocks/spawner.go b/core/services/job/mocks/spawner.go index 6866f1fc156..60d36b18fa5 100644 --- a/core/services/job/mocks/spawner.go +++ b/core/services/job/mocks/spawner.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type Spawner struct { func (_m *Spawner) ActiveJobs() map[int32]job.Job { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ActiveJobs") + } + var r0 map[int32]job.Job if rf, ok := ret.Get(0).(func() map[int32]job.Job); ok { r0 = rf() @@ -36,6 +40,10 @@ func (_m *Spawner) ActiveJobs() map[int32]job.Job { func (_m *Spawner) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -57,6 +65,10 @@ func (_m *Spawner) CreateJob(jb *job.Job, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateJob") + } + var r0 error if rf, ok := ret.Get(0).(func(*job.Job, ...pg.QOpt) error); ok { r0 = rf(jb, qopts...) @@ -78,6 +90,10 @@ func (_m *Spawner) DeleteJob(jobID int32, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DeleteJob") + } + var r0 error if rf, ok := ret.Get(0).(func(int32, ...pg.QOpt) error); ok { r0 = rf(jobID, qopts...) @@ -92,6 +108,10 @@ func (_m *Spawner) DeleteJob(jobID int32, qopts ...pg.QOpt) error { func (_m *Spawner) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -108,6 +128,10 @@ func (_m *Spawner) HealthReport() map[string]error { func (_m *Spawner) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -122,6 +146,10 @@ func (_m *Spawner) Name() string { func (_m *Spawner) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -136,6 +164,10 @@ func (_m *Spawner) Ready() error { func (_m *Spawner) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -157,6 +189,10 @@ func (_m *Spawner) StartService(ctx context.Context, spec job.Job, qopts ...pg.Q _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for StartService") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, job.Job, ...pg.QOpt) error); ok { r0 = rf(ctx, spec, qopts...) diff --git a/core/services/keystore/mocks/cosmos.go b/core/services/keystore/mocks/cosmos.go index b8d5d56c373..40ba12d15d7 100644 --- a/core/services/keystore/mocks/cosmos.go +++ b/core/services/keystore/mocks/cosmos.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type Cosmos struct { func (_m *Cosmos) Add(key cosmoskey.Key) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(cosmoskey.Key) error); ok { r0 = rf(key) @@ -31,6 +35,10 @@ func (_m *Cosmos) Add(key cosmoskey.Key) error { func (_m *Cosmos) Create() (cosmoskey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 cosmoskey.Key var r1 error if rf, ok := ret.Get(0).(func() (cosmoskey.Key, error)); ok { @@ -55,6 +63,10 @@ func (_m *Cosmos) Create() (cosmoskey.Key, error) { func (_m *Cosmos) Delete(id string) (cosmoskey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 cosmoskey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (cosmoskey.Key, error)); ok { @@ -79,6 +91,10 @@ func (_m *Cosmos) Delete(id string) (cosmoskey.Key, error) { func (_m *Cosmos) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -93,6 +109,10 @@ func (_m *Cosmos) EnsureKey() error { func (_m *Cosmos) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -119,6 +139,10 @@ func (_m *Cosmos) Export(id string, password string) ([]byte, error) { func (_m *Cosmos) Get(id string) (cosmoskey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 cosmoskey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (cosmoskey.Key, error)); ok { @@ -143,6 +167,10 @@ func (_m *Cosmos) Get(id string) (cosmoskey.Key, error) { func (_m *Cosmos) GetAll() ([]cosmoskey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []cosmoskey.Key var r1 error if rf, ok := ret.Get(0).(func() ([]cosmoskey.Key, error)); ok { @@ -169,6 +197,10 @@ func (_m *Cosmos) GetAll() ([]cosmoskey.Key, error) { func (_m *Cosmos) Import(keyJSON []byte, password string) (cosmoskey.Key, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 cosmoskey.Key var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (cosmoskey.Key, error)); ok { diff --git a/core/services/keystore/mocks/csa.go b/core/services/keystore/mocks/csa.go index 4f4e02a2fe6..ad5b25a27bd 100644 --- a/core/services/keystore/mocks/csa.go +++ b/core/services/keystore/mocks/csa.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type CSA struct { func (_m *CSA) Add(key csakey.KeyV2) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(csakey.KeyV2) error); ok { r0 = rf(key) @@ -31,6 +35,10 @@ func (_m *CSA) Add(key csakey.KeyV2) error { func (_m *CSA) Create() (csakey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 csakey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() (csakey.KeyV2, error)); ok { @@ -55,6 +63,10 @@ func (_m *CSA) Create() (csakey.KeyV2, error) { func (_m *CSA) Delete(id string) (csakey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 csakey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (csakey.KeyV2, error)); ok { @@ -79,6 +91,10 @@ func (_m *CSA) Delete(id string) (csakey.KeyV2, error) { func (_m *CSA) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -93,6 +109,10 @@ func (_m *CSA) EnsureKey() error { func (_m *CSA) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -119,6 +139,10 @@ func (_m *CSA) Export(id string, password string) ([]byte, error) { func (_m *CSA) Get(id string) (csakey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 csakey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (csakey.KeyV2, error)); ok { @@ -143,6 +167,10 @@ func (_m *CSA) Get(id string) (csakey.KeyV2, error) { func (_m *CSA) GetAll() ([]csakey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []csakey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() ([]csakey.KeyV2, error)); ok { @@ -169,6 +197,10 @@ func (_m *CSA) GetAll() ([]csakey.KeyV2, error) { func (_m *CSA) Import(keyJSON []byte, password string) (csakey.KeyV2, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 csakey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (csakey.KeyV2, error)); ok { diff --git a/core/services/keystore/mocks/dkg_encrypt.go b/core/services/keystore/mocks/dkg_encrypt.go index e1f83888c48..e7e52bada25 100644 --- a/core/services/keystore/mocks/dkg_encrypt.go +++ b/core/services/keystore/mocks/dkg_encrypt.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type DKGEncrypt struct { func (_m *DKGEncrypt) Add(key dkgencryptkey.Key) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(dkgencryptkey.Key) error); ok { r0 = rf(key) @@ -31,6 +35,10 @@ func (_m *DKGEncrypt) Add(key dkgencryptkey.Key) error { func (_m *DKGEncrypt) Create() (dkgencryptkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 dkgencryptkey.Key var r1 error if rf, ok := ret.Get(0).(func() (dkgencryptkey.Key, error)); ok { @@ -55,6 +63,10 @@ func (_m *DKGEncrypt) Create() (dkgencryptkey.Key, error) { func (_m *DKGEncrypt) Delete(id string) (dkgencryptkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 dkgencryptkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (dkgencryptkey.Key, error)); ok { @@ -79,6 +91,10 @@ func (_m *DKGEncrypt) Delete(id string) (dkgencryptkey.Key, error) { func (_m *DKGEncrypt) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -93,6 +109,10 @@ func (_m *DKGEncrypt) EnsureKey() error { func (_m *DKGEncrypt) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -119,6 +139,10 @@ func (_m *DKGEncrypt) Export(id string, password string) ([]byte, error) { func (_m *DKGEncrypt) Get(id string) (dkgencryptkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 dkgencryptkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (dkgencryptkey.Key, error)); ok { @@ -143,6 +167,10 @@ func (_m *DKGEncrypt) Get(id string) (dkgencryptkey.Key, error) { func (_m *DKGEncrypt) GetAll() ([]dkgencryptkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []dkgencryptkey.Key var r1 error if rf, ok := ret.Get(0).(func() ([]dkgencryptkey.Key, error)); ok { @@ -169,6 +197,10 @@ func (_m *DKGEncrypt) GetAll() ([]dkgencryptkey.Key, error) { func (_m *DKGEncrypt) Import(keyJSON []byte, password string) (dkgencryptkey.Key, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 dkgencryptkey.Key var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (dkgencryptkey.Key, error)); ok { diff --git a/core/services/keystore/mocks/dkg_sign.go b/core/services/keystore/mocks/dkg_sign.go index ed1aa756a6d..e5c6434d90d 100644 --- a/core/services/keystore/mocks/dkg_sign.go +++ b/core/services/keystore/mocks/dkg_sign.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type DKGSign struct { func (_m *DKGSign) Add(key dkgsignkey.Key) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(dkgsignkey.Key) error); ok { r0 = rf(key) @@ -31,6 +35,10 @@ func (_m *DKGSign) Add(key dkgsignkey.Key) error { func (_m *DKGSign) Create() (dkgsignkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 dkgsignkey.Key var r1 error if rf, ok := ret.Get(0).(func() (dkgsignkey.Key, error)); ok { @@ -55,6 +63,10 @@ func (_m *DKGSign) Create() (dkgsignkey.Key, error) { func (_m *DKGSign) Delete(id string) (dkgsignkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 dkgsignkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (dkgsignkey.Key, error)); ok { @@ -79,6 +91,10 @@ func (_m *DKGSign) Delete(id string) (dkgsignkey.Key, error) { func (_m *DKGSign) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -93,6 +109,10 @@ func (_m *DKGSign) EnsureKey() error { func (_m *DKGSign) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -119,6 +139,10 @@ func (_m *DKGSign) Export(id string, password string) ([]byte, error) { func (_m *DKGSign) Get(id string) (dkgsignkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 dkgsignkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (dkgsignkey.Key, error)); ok { @@ -143,6 +167,10 @@ func (_m *DKGSign) Get(id string) (dkgsignkey.Key, error) { func (_m *DKGSign) GetAll() ([]dkgsignkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []dkgsignkey.Key var r1 error if rf, ok := ret.Get(0).(func() ([]dkgsignkey.Key, error)); ok { @@ -169,6 +197,10 @@ func (_m *DKGSign) GetAll() ([]dkgsignkey.Key, error) { func (_m *DKGSign) Import(keyJSON []byte, password string) (dkgsignkey.Key, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 dkgsignkey.Key var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (dkgsignkey.Key, error)); ok { diff --git a/core/services/keystore/mocks/eth.go b/core/services/keystore/mocks/eth.go index 6a076e130d1..b3827398fd5 100644 --- a/core/services/keystore/mocks/eth.go +++ b/core/services/keystore/mocks/eth.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -31,6 +31,10 @@ func (_m *Eth) Add(address common.Address, chainID *big.Int, qopts ...pg.QOpt) e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(common.Address, *big.Int, ...pg.QOpt) error); ok { r0 = rf(address, chainID, qopts...) @@ -45,6 +49,10 @@ func (_m *Eth) Add(address common.Address, chainID *big.Int, qopts ...pg.QOpt) e func (_m *Eth) CheckEnabled(address common.Address, chainID *big.Int) error { ret := _m.Called(address, chainID) + if len(ret) == 0 { + panic("no return value specified for CheckEnabled") + } + var r0 error if rf, ok := ret.Get(0).(func(common.Address, *big.Int) error); ok { r0 = rf(address, chainID) @@ -65,6 +73,10 @@ func (_m *Eth) Create(chainIDs ...*big.Int) (ethkey.KeyV2, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(...*big.Int) (ethkey.KeyV2, error)); ok { @@ -89,6 +101,10 @@ func (_m *Eth) Create(chainIDs ...*big.Int) (ethkey.KeyV2, error) { func (_m *Eth) Delete(id string) (ethkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (ethkey.KeyV2, error)); ok { @@ -120,6 +136,10 @@ func (_m *Eth) Disable(address common.Address, chainID *big.Int, qopts ...pg.QOp _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Disable") + } + var r0 error if rf, ok := ret.Get(0).(func(common.Address, *big.Int, ...pg.QOpt) error); ok { r0 = rf(address, chainID, qopts...) @@ -141,6 +161,10 @@ func (_m *Eth) Enable(address common.Address, chainID *big.Int, qopts ...pg.QOpt _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Enable") + } + var r0 error if rf, ok := ret.Get(0).(func(common.Address, *big.Int, ...pg.QOpt) error); ok { r0 = rf(address, chainID, qopts...) @@ -155,6 +179,10 @@ func (_m *Eth) Enable(address common.Address, chainID *big.Int, qopts ...pg.QOpt func (_m *Eth) EnabledAddressesForChain(chainID *big.Int) ([]common.Address, error) { ret := _m.Called(chainID) + if len(ret) == 0 { + panic("no return value specified for EnabledAddressesForChain") + } + var r0 []common.Address var r1 error if rf, ok := ret.Get(0).(func(*big.Int) ([]common.Address, error)); ok { @@ -181,6 +209,10 @@ func (_m *Eth) EnabledAddressesForChain(chainID *big.Int) ([]common.Address, err func (_m *Eth) EnabledKeysForChain(chainID *big.Int) ([]ethkey.KeyV2, error) { ret := _m.Called(chainID) + if len(ret) == 0 { + panic("no return value specified for EnabledKeysForChain") + } + var r0 []ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(*big.Int) ([]ethkey.KeyV2, error)); ok { @@ -213,6 +245,10 @@ func (_m *Eth) EnsureKeys(chainIDs ...*big.Int) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for EnsureKeys") + } + var r0 error if rf, ok := ret.Get(0).(func(...*big.Int) error); ok { r0 = rf(chainIDs...) @@ -227,6 +263,10 @@ func (_m *Eth) EnsureKeys(chainIDs ...*big.Int) error { func (_m *Eth) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -253,6 +293,10 @@ func (_m *Eth) Export(id string, password string) ([]byte, error) { func (_m *Eth) Get(id string) (ethkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (ethkey.KeyV2, error)); ok { @@ -277,6 +321,10 @@ func (_m *Eth) Get(id string) (ethkey.KeyV2, error) { func (_m *Eth) GetAll() ([]ethkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() ([]ethkey.KeyV2, error)); ok { @@ -310,6 +358,10 @@ func (_m *Eth) GetRoundRobinAddress(chainID *big.Int, addresses ...common.Addres _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetRoundRobinAddress") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*big.Int, ...common.Address) (common.Address, error)); ok { @@ -336,6 +388,10 @@ func (_m *Eth) GetRoundRobinAddress(chainID *big.Int, addresses ...common.Addres func (_m *Eth) GetState(id string, chainID *big.Int) (ethkey.State, error) { ret := _m.Called(id, chainID) + if len(ret) == 0 { + panic("no return value specified for GetState") + } + var r0 ethkey.State var r1 error if rf, ok := ret.Get(0).(func(string, *big.Int) (ethkey.State, error)); ok { @@ -360,6 +416,10 @@ func (_m *Eth) GetState(id string, chainID *big.Int) (ethkey.State, error) { func (_m *Eth) GetStateForKey(_a0 ethkey.KeyV2) (ethkey.State, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetStateForKey") + } + var r0 ethkey.State var r1 error if rf, ok := ret.Get(0).(func(ethkey.KeyV2) (ethkey.State, error)); ok { @@ -384,6 +444,10 @@ func (_m *Eth) GetStateForKey(_a0 ethkey.KeyV2) (ethkey.State, error) { func (_m *Eth) GetStatesForChain(chainID *big.Int) ([]ethkey.State, error) { ret := _m.Called(chainID) + if len(ret) == 0 { + panic("no return value specified for GetStatesForChain") + } + var r0 []ethkey.State var r1 error if rf, ok := ret.Get(0).(func(*big.Int) ([]ethkey.State, error)); ok { @@ -410,6 +474,10 @@ func (_m *Eth) GetStatesForChain(chainID *big.Int) ([]ethkey.State, error) { func (_m *Eth) GetStatesForKeys(_a0 []ethkey.KeyV2) ([]ethkey.State, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetStatesForKeys") + } + var r0 []ethkey.State var r1 error if rf, ok := ret.Get(0).(func([]ethkey.KeyV2) ([]ethkey.State, error)); ok { @@ -443,6 +511,10 @@ func (_m *Eth) Import(keyJSON []byte, password string, chainIDs ...*big.Int) (et _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 ethkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func([]byte, string, ...*big.Int) (ethkey.KeyV2, error)); ok { @@ -467,6 +539,10 @@ func (_m *Eth) Import(keyJSON []byte, password string, chainIDs ...*big.Int) (et func (_m *Eth) SignTx(fromAddress common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { ret := _m.Called(fromAddress, tx, chainID) + if len(ret) == 0 { + panic("no return value specified for SignTx") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(common.Address, *types.Transaction, *big.Int) (*types.Transaction, error)); ok { @@ -493,6 +569,10 @@ func (_m *Eth) SignTx(fromAddress common.Address, tx *types.Transaction, chainID func (_m *Eth) SubscribeToKeyChanges() (chan struct{}, func()) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SubscribeToKeyChanges") + } + var r0 chan struct{} var r1 func() if rf, ok := ret.Get(0).(func() (chan struct{}, func())); ok { diff --git a/core/services/keystore/mocks/master.go b/core/services/keystore/mocks/master.go index d29d2fa4692..3025f5b103a 100644 --- a/core/services/keystore/mocks/master.go +++ b/core/services/keystore/mocks/master.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type Master struct { func (_m *Master) CSA() keystore.CSA { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CSA") + } + var r0 keystore.CSA if rf, ok := ret.Get(0).(func() keystore.CSA); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *Master) CSA() keystore.CSA { func (_m *Master) Cosmos() keystore.Cosmos { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Cosmos") + } + var r0 keystore.Cosmos if rf, ok := ret.Get(0).(func() keystore.Cosmos); ok { r0 = rf() @@ -48,6 +56,10 @@ func (_m *Master) Cosmos() keystore.Cosmos { func (_m *Master) DKGEncrypt() keystore.DKGEncrypt { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DKGEncrypt") + } + var r0 keystore.DKGEncrypt if rf, ok := ret.Get(0).(func() keystore.DKGEncrypt); ok { r0 = rf() @@ -64,6 +76,10 @@ func (_m *Master) DKGEncrypt() keystore.DKGEncrypt { func (_m *Master) DKGSign() keystore.DKGSign { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DKGSign") + } + var r0 keystore.DKGSign if rf, ok := ret.Get(0).(func() keystore.DKGSign); ok { r0 = rf() @@ -80,6 +96,10 @@ func (_m *Master) DKGSign() keystore.DKGSign { func (_m *Master) Eth() keystore.Eth { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Eth") + } + var r0 keystore.Eth if rf, ok := ret.Get(0).(func() keystore.Eth); ok { r0 = rf() @@ -96,6 +116,10 @@ func (_m *Master) Eth() keystore.Eth { func (_m *Master) IsEmpty() (bool, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsEmpty") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func() (bool, error)); ok { @@ -120,6 +144,10 @@ func (_m *Master) IsEmpty() (bool, error) { func (_m *Master) OCR() keystore.OCR { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for OCR") + } + var r0 keystore.OCR if rf, ok := ret.Get(0).(func() keystore.OCR); ok { r0 = rf() @@ -136,6 +164,10 @@ func (_m *Master) OCR() keystore.OCR { func (_m *Master) OCR2() keystore.OCR2 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for OCR2") + } + var r0 keystore.OCR2 if rf, ok := ret.Get(0).(func() keystore.OCR2); ok { r0 = rf() @@ -152,6 +184,10 @@ func (_m *Master) OCR2() keystore.OCR2 { func (_m *Master) P2P() keystore.P2P { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for P2P") + } + var r0 keystore.P2P if rf, ok := ret.Get(0).(func() keystore.P2P); ok { r0 = rf() @@ -168,6 +204,10 @@ func (_m *Master) P2P() keystore.P2P { func (_m *Master) Solana() keystore.Solana { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Solana") + } + var r0 keystore.Solana if rf, ok := ret.Get(0).(func() keystore.Solana); ok { r0 = rf() @@ -184,6 +224,10 @@ func (_m *Master) Solana() keystore.Solana { func (_m *Master) StarkNet() keystore.StarkNet { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for StarkNet") + } + var r0 keystore.StarkNet if rf, ok := ret.Get(0).(func() keystore.StarkNet); ok { r0 = rf() @@ -200,6 +244,10 @@ func (_m *Master) StarkNet() keystore.StarkNet { func (_m *Master) Unlock(password string) error { ret := _m.Called(password) + if len(ret) == 0 { + panic("no return value specified for Unlock") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(password) @@ -214,6 +262,10 @@ func (_m *Master) Unlock(password string) error { func (_m *Master) VRF() keystore.VRF { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for VRF") + } + var r0 keystore.VRF if rf, ok := ret.Get(0).(func() keystore.VRF); ok { r0 = rf() diff --git a/core/services/keystore/mocks/ocr.go b/core/services/keystore/mocks/ocr.go index 505eaa0e46e..e1c4d588330 100644 --- a/core/services/keystore/mocks/ocr.go +++ b/core/services/keystore/mocks/ocr.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type OCR struct { func (_m *OCR) Add(key ocrkey.KeyV2) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(ocrkey.KeyV2) error); ok { r0 = rf(key) @@ -30,6 +34,10 @@ func (_m *OCR) Add(key ocrkey.KeyV2) error { func (_m *OCR) Create() (ocrkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 ocrkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() (ocrkey.KeyV2, error)); ok { @@ -54,6 +62,10 @@ func (_m *OCR) Create() (ocrkey.KeyV2, error) { func (_m *OCR) Delete(id string) (ocrkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 ocrkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (ocrkey.KeyV2, error)); ok { @@ -78,6 +90,10 @@ func (_m *OCR) Delete(id string) (ocrkey.KeyV2, error) { func (_m *OCR) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -92,6 +108,10 @@ func (_m *OCR) EnsureKey() error { func (_m *OCR) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -118,6 +138,10 @@ func (_m *OCR) Export(id string, password string) ([]byte, error) { func (_m *OCR) Get(id string) (ocrkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 ocrkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (ocrkey.KeyV2, error)); ok { @@ -142,6 +166,10 @@ func (_m *OCR) Get(id string) (ocrkey.KeyV2, error) { func (_m *OCR) GetAll() ([]ocrkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []ocrkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() ([]ocrkey.KeyV2, error)); ok { @@ -168,6 +196,10 @@ func (_m *OCR) GetAll() ([]ocrkey.KeyV2, error) { func (_m *OCR) Import(keyJSON []byte, password string) (ocrkey.KeyV2, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 ocrkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (ocrkey.KeyV2, error)); ok { diff --git a/core/services/keystore/mocks/ocr2.go b/core/services/keystore/mocks/ocr2.go index 30d870dcdc7..d44e739deed 100644 --- a/core/services/keystore/mocks/ocr2.go +++ b/core/services/keystore/mocks/ocr2.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type OCR2 struct { func (_m *OCR2) Add(key ocr2key.KeyBundle) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(ocr2key.KeyBundle) error); ok { r0 = rf(key) @@ -33,6 +37,10 @@ func (_m *OCR2) Add(key ocr2key.KeyBundle) error { func (_m *OCR2) Create(_a0 chaintype.ChainType) (ocr2key.KeyBundle, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 ocr2key.KeyBundle var r1 error if rf, ok := ret.Get(0).(func(chaintype.ChainType) (ocr2key.KeyBundle, error)); ok { @@ -59,6 +67,10 @@ func (_m *OCR2) Create(_a0 chaintype.ChainType) (ocr2key.KeyBundle, error) { func (_m *OCR2) Delete(id string) error { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(id) @@ -79,6 +91,10 @@ func (_m *OCR2) EnsureKeys(enabledChains ...chaintype.ChainType) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for EnsureKeys") + } + var r0 error if rf, ok := ret.Get(0).(func(...chaintype.ChainType) error); ok { r0 = rf(enabledChains...) @@ -93,6 +109,10 @@ func (_m *OCR2) EnsureKeys(enabledChains ...chaintype.ChainType) error { func (_m *OCR2) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -119,6 +139,10 @@ func (_m *OCR2) Export(id string, password string) ([]byte, error) { func (_m *OCR2) Get(id string) (ocr2key.KeyBundle, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 ocr2key.KeyBundle var r1 error if rf, ok := ret.Get(0).(func(string) (ocr2key.KeyBundle, error)); ok { @@ -145,6 +169,10 @@ func (_m *OCR2) Get(id string) (ocr2key.KeyBundle, error) { func (_m *OCR2) GetAll() ([]ocr2key.KeyBundle, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []ocr2key.KeyBundle var r1 error if rf, ok := ret.Get(0).(func() ([]ocr2key.KeyBundle, error)); ok { @@ -171,6 +199,10 @@ func (_m *OCR2) GetAll() ([]ocr2key.KeyBundle, error) { func (_m *OCR2) GetAllOfType(_a0 chaintype.ChainType) ([]ocr2key.KeyBundle, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetAllOfType") + } + var r0 []ocr2key.KeyBundle var r1 error if rf, ok := ret.Get(0).(func(chaintype.ChainType) ([]ocr2key.KeyBundle, error)); ok { @@ -197,6 +229,10 @@ func (_m *OCR2) GetAllOfType(_a0 chaintype.ChainType) ([]ocr2key.KeyBundle, erro func (_m *OCR2) Import(keyJSON []byte, password string) (ocr2key.KeyBundle, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 ocr2key.KeyBundle var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (ocr2key.KeyBundle, error)); ok { diff --git a/core/services/keystore/mocks/p2p.go b/core/services/keystore/mocks/p2p.go index c91be5a4a92..fa2a1b6ceeb 100644 --- a/core/services/keystore/mocks/p2p.go +++ b/core/services/keystore/mocks/p2p.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type P2P struct { func (_m *P2P) Add(key p2pkey.KeyV2) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(p2pkey.KeyV2) error); ok { r0 = rf(key) @@ -30,6 +34,10 @@ func (_m *P2P) Add(key p2pkey.KeyV2) error { func (_m *P2P) Create() (p2pkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 p2pkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() (p2pkey.KeyV2, error)); ok { @@ -54,6 +62,10 @@ func (_m *P2P) Create() (p2pkey.KeyV2, error) { func (_m *P2P) Delete(id p2pkey.PeerID) (p2pkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 p2pkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(p2pkey.PeerID) (p2pkey.KeyV2, error)); ok { @@ -78,6 +90,10 @@ func (_m *P2P) Delete(id p2pkey.PeerID) (p2pkey.KeyV2, error) { func (_m *P2P) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -92,6 +108,10 @@ func (_m *P2P) EnsureKey() error { func (_m *P2P) Export(id p2pkey.PeerID, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(p2pkey.PeerID, string) ([]byte, error)); ok { @@ -118,6 +138,10 @@ func (_m *P2P) Export(id p2pkey.PeerID, password string) ([]byte, error) { func (_m *P2P) Get(id p2pkey.PeerID) (p2pkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 p2pkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(p2pkey.PeerID) (p2pkey.KeyV2, error)); ok { @@ -142,6 +166,10 @@ func (_m *P2P) Get(id p2pkey.PeerID) (p2pkey.KeyV2, error) { func (_m *P2P) GetAll() ([]p2pkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []p2pkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() ([]p2pkey.KeyV2, error)); ok { @@ -168,6 +196,10 @@ func (_m *P2P) GetAll() ([]p2pkey.KeyV2, error) { func (_m *P2P) GetOrFirst(id p2pkey.PeerID) (p2pkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetOrFirst") + } + var r0 p2pkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(p2pkey.PeerID) (p2pkey.KeyV2, error)); ok { @@ -192,6 +224,10 @@ func (_m *P2P) GetOrFirst(id p2pkey.PeerID) (p2pkey.KeyV2, error) { func (_m *P2P) Import(keyJSON []byte, password string) (p2pkey.KeyV2, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 p2pkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (p2pkey.KeyV2, error)); ok { diff --git a/core/services/keystore/mocks/solana.go b/core/services/keystore/mocks/solana.go index 66357e32b93..c2cc4139bab 100644 --- a/core/services/keystore/mocks/solana.go +++ b/core/services/keystore/mocks/solana.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type Solana struct { func (_m *Solana) Add(key solkey.Key) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(solkey.Key) error); ok { r0 = rf(key) @@ -33,6 +37,10 @@ func (_m *Solana) Add(key solkey.Key) error { func (_m *Solana) Create() (solkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 solkey.Key var r1 error if rf, ok := ret.Get(0).(func() (solkey.Key, error)); ok { @@ -57,6 +65,10 @@ func (_m *Solana) Create() (solkey.Key, error) { func (_m *Solana) Delete(id string) (solkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 solkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (solkey.Key, error)); ok { @@ -81,6 +93,10 @@ func (_m *Solana) Delete(id string) (solkey.Key, error) { func (_m *Solana) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -95,6 +111,10 @@ func (_m *Solana) EnsureKey() error { func (_m *Solana) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -121,6 +141,10 @@ func (_m *Solana) Export(id string, password string) ([]byte, error) { func (_m *Solana) Get(id string) (solkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 solkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (solkey.Key, error)); ok { @@ -145,6 +169,10 @@ func (_m *Solana) Get(id string) (solkey.Key, error) { func (_m *Solana) GetAll() ([]solkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []solkey.Key var r1 error if rf, ok := ret.Get(0).(func() ([]solkey.Key, error)); ok { @@ -171,6 +199,10 @@ func (_m *Solana) GetAll() ([]solkey.Key, error) { func (_m *Solana) Import(keyJSON []byte, password string) (solkey.Key, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 solkey.Key var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (solkey.Key, error)); ok { @@ -195,6 +227,10 @@ func (_m *Solana) Import(keyJSON []byte, password string) (solkey.Key, error) { func (_m *Solana) Sign(ctx context.Context, id string, msg []byte) ([]byte, error) { ret := _m.Called(ctx, id, msg) + if len(ret) == 0 { + panic("no return value specified for Sign") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, []byte) ([]byte, error)); ok { diff --git a/core/services/keystore/mocks/starknet.go b/core/services/keystore/mocks/starknet.go index ff9b52d7136..c3d74a8389f 100644 --- a/core/services/keystore/mocks/starknet.go +++ b/core/services/keystore/mocks/starknet.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type StarkNet struct { func (_m *StarkNet) Add(key starkkey.Key) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(starkkey.Key) error); ok { r0 = rf(key) @@ -30,6 +34,10 @@ func (_m *StarkNet) Add(key starkkey.Key) error { func (_m *StarkNet) Create() (starkkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 starkkey.Key var r1 error if rf, ok := ret.Get(0).(func() (starkkey.Key, error)); ok { @@ -54,6 +62,10 @@ func (_m *StarkNet) Create() (starkkey.Key, error) { func (_m *StarkNet) Delete(id string) (starkkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 starkkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (starkkey.Key, error)); ok { @@ -78,6 +90,10 @@ func (_m *StarkNet) Delete(id string) (starkkey.Key, error) { func (_m *StarkNet) EnsureKey() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EnsureKey") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -92,6 +108,10 @@ func (_m *StarkNet) EnsureKey() error { func (_m *StarkNet) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -118,6 +138,10 @@ func (_m *StarkNet) Export(id string, password string) ([]byte, error) { func (_m *StarkNet) Get(id string) (starkkey.Key, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 starkkey.Key var r1 error if rf, ok := ret.Get(0).(func(string) (starkkey.Key, error)); ok { @@ -142,6 +166,10 @@ func (_m *StarkNet) Get(id string) (starkkey.Key, error) { func (_m *StarkNet) GetAll() ([]starkkey.Key, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []starkkey.Key var r1 error if rf, ok := ret.Get(0).(func() ([]starkkey.Key, error)); ok { @@ -168,6 +196,10 @@ func (_m *StarkNet) GetAll() ([]starkkey.Key, error) { func (_m *StarkNet) Import(keyJSON []byte, password string) (starkkey.Key, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 starkkey.Key var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (starkkey.Key, error)); ok { diff --git a/core/services/keystore/mocks/vrf.go b/core/services/keystore/mocks/vrf.go index 5aa15dca59e..ab730ebec67 100644 --- a/core/services/keystore/mocks/vrf.go +++ b/core/services/keystore/mocks/vrf.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type VRF struct { func (_m *VRF) Add(key vrfkey.KeyV2) error { ret := _m.Called(key) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(vrfkey.KeyV2) error); ok { r0 = rf(key) @@ -33,6 +37,10 @@ func (_m *VRF) Add(key vrfkey.KeyV2) error { func (_m *VRF) Create() (vrfkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 vrfkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() (vrfkey.KeyV2, error)); ok { @@ -57,6 +65,10 @@ func (_m *VRF) Create() (vrfkey.KeyV2, error) { func (_m *VRF) Delete(id string) (vrfkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 vrfkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (vrfkey.KeyV2, error)); ok { @@ -81,6 +93,10 @@ func (_m *VRF) Delete(id string) (vrfkey.KeyV2, error) { func (_m *VRF) Export(id string, password string) ([]byte, error) { ret := _m.Called(id, password) + if len(ret) == 0 { + panic("no return value specified for Export") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { @@ -107,6 +123,10 @@ func (_m *VRF) Export(id string, password string) ([]byte, error) { func (_m *VRF) GenerateProof(id string, seed *big.Int) (vrfkey.Proof, error) { ret := _m.Called(id, seed) + if len(ret) == 0 { + panic("no return value specified for GenerateProof") + } + var r0 vrfkey.Proof var r1 error if rf, ok := ret.Get(0).(func(string, *big.Int) (vrfkey.Proof, error)); ok { @@ -131,6 +151,10 @@ func (_m *VRF) GenerateProof(id string, seed *big.Int) (vrfkey.Proof, error) { func (_m *VRF) Get(id string) (vrfkey.KeyV2, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 vrfkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func(string) (vrfkey.KeyV2, error)); ok { @@ -155,6 +179,10 @@ func (_m *VRF) Get(id string) (vrfkey.KeyV2, error) { func (_m *VRF) GetAll() ([]vrfkey.KeyV2, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 []vrfkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func() ([]vrfkey.KeyV2, error)); ok { @@ -181,6 +209,10 @@ func (_m *VRF) GetAll() ([]vrfkey.KeyV2, error) { func (_m *VRF) Import(keyJSON []byte, password string) (vrfkey.KeyV2, error) { ret := _m.Called(keyJSON, password) + if len(ret) == 0 { + panic("no return value specified for Import") + } + var r0 vrfkey.KeyV2 var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (vrfkey.KeyV2, error)); ok { diff --git a/core/services/mocks/checker.go b/core/services/mocks/checker.go index e0c209d8afb..2572efb1822 100644 --- a/core/services/mocks/checker.go +++ b/core/services/mocks/checker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type Checker struct { func (_m *Checker) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -30,6 +34,10 @@ func (_m *Checker) Close() error { func (_m *Checker) IsHealthy() (bool, map[string]error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsHealthy") + } + var r0 bool var r1 map[string]error if rf, ok := ret.Get(0).(func() (bool, map[string]error)); ok { @@ -56,6 +64,10 @@ func (_m *Checker) IsHealthy() (bool, map[string]error) { func (_m *Checker) IsReady() (bool, map[string]error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsReady") + } + var r0 bool var r1 map[string]error if rf, ok := ret.Get(0).(func() (bool, map[string]error)); ok { @@ -82,6 +94,10 @@ func (_m *Checker) IsReady() (bool, map[string]error) { func (_m *Checker) Register(service pkgservices.HealthReporter) error { ret := _m.Called(service) + if len(ret) == 0 { + panic("no return value specified for Register") + } + var r0 error if rf, ok := ret.Get(0).(func(pkgservices.HealthReporter) error); ok { r0 = rf(service) @@ -96,6 +112,10 @@ func (_m *Checker) Register(service pkgservices.HealthReporter) error { func (_m *Checker) Start() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -110,6 +130,10 @@ func (_m *Checker) Start() error { func (_m *Checker) Unregister(name string) error { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for Unregister") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(name) diff --git a/core/services/ocr/mocks/ocr_contract_tracker_db.go b/core/services/ocr/mocks/ocr_contract_tracker_db.go index a1d2f523ccb..6724e418014 100644 --- a/core/services/ocr/mocks/ocr_contract_tracker_db.go +++ b/core/services/ocr/mocks/ocr_contract_tracker_db.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type OCRContractTrackerDB struct { func (_m *OCRContractTrackerDB) LoadLatestRoundRequested() (offchainaggregator.OffchainAggregatorRoundRequested, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LoadLatestRoundRequested") + } + var r0 offchainaggregator.OffchainAggregatorRoundRequested var r1 error if rf, ok := ret.Get(0).(func() (offchainaggregator.OffchainAggregatorRoundRequested, error)); ok { @@ -43,6 +47,10 @@ func (_m *OCRContractTrackerDB) LoadLatestRoundRequested() (offchainaggregator.O func (_m *OCRContractTrackerDB) SaveLatestRoundRequested(tx pg.Queryer, rr offchainaggregator.OffchainAggregatorRoundRequested) error { ret := _m.Called(tx, rr) + if len(ret) == 0 { + panic("no return value specified for SaveLatestRoundRequested") + } + var r0 error if rf, ok := ret.Get(0).(func(pg.Queryer, offchainaggregator.OffchainAggregatorRoundRequested) error); ok { r0 = rf(tx, rr) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go index bc30ac781de..8c8ea721d73 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -25,6 +25,10 @@ type Registry struct { func (_m *Registry) GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { ret := _m.Called(opts, startIndex, maxCount) + if len(ret) == 0 { + panic("no return value specified for GetActiveUpkeepIDs") + } + var r0 []*big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int, *big.Int) ([]*big.Int, error)); ok { @@ -51,6 +55,10 @@ func (_m *Registry) GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, func (_m *Registry) GetState(opts *bind.CallOpts) (keeper_registry_wrapper2_0.GetState, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetState") + } + var r0 keeper_registry_wrapper2_0.GetState var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (keeper_registry_wrapper2_0.GetState, error)); ok { @@ -75,6 +83,10 @@ func (_m *Registry) GetState(opts *bind.CallOpts) (keeper_registry_wrapper2_0.Ge func (_m *Registry) GetUpkeep(opts *bind.CallOpts, id *big.Int) (keeper_registry_wrapper2_0.UpkeepInfo, error) { ret := _m.Called(opts, id) + if len(ret) == 0 { + panic("no return value specified for GetUpkeep") + } + var r0 keeper_registry_wrapper2_0.UpkeepInfo var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (keeper_registry_wrapper2_0.UpkeepInfo, error)); ok { @@ -99,6 +111,10 @@ func (_m *Registry) GetUpkeep(opts *bind.CallOpts, id *big.Int) (keeper_registry func (_m *Registry) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go index b036fbb26c3..3167cf97aad 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -26,6 +26,10 @@ func (_m *UpkeepStateReader) SelectByWorkIDs(ctx context.Context, workIDs ...str _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SelectByWorkIDs") + } + var r0 []types.UpkeepState var r1 error if rf, ok := ret.Get(0).(func(context.Context, ...string) ([]types.UpkeepState, error)); ok { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go index 53d347b1c06..d6982e9dbdb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type HttpClient struct { func (_m *HttpClient) Do(req *http.Request) (*http.Response, error) { ret := _m.Called(req) + if len(ret) == 0 { + panic("no return value specified for Do") + } + var r0 *http.Response var r1 error if rf, ok := ret.Get(0).(func(*http.Request) (*http.Response, error)); ok { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go index a3cbd772b1d..1bb4cb2a325 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -25,6 +25,10 @@ type Registry struct { func (_m *Registry) CheckCallback(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (i_keeper_registry_master_wrapper_2_1.CheckCallback, error) { ret := _m.Called(opts, id, values, extraData) + if len(ret) == 0 { + panic("no return value specified for CheckCallback") + } + var r0 i_keeper_registry_master_wrapper_2_1.CheckCallback var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int, [][]byte, []byte) (i_keeper_registry_master_wrapper_2_1.CheckCallback, error)); ok { @@ -49,6 +53,10 @@ func (_m *Registry) CheckCallback(opts *bind.CallOpts, id *big.Int, values [][]b func (_m *Registry) GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { ret := _m.Called(opts, startIndex, maxCount) + if len(ret) == 0 { + panic("no return value specified for GetActiveUpkeepIDs") + } + var r0 []*big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int, *big.Int) ([]*big.Int, error)); ok { @@ -75,6 +83,10 @@ func (_m *Registry) GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, func (_m *Registry) GetState(opts *bind.CallOpts) (i_keeper_registry_master_wrapper_2_1.GetState, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetState") + } + var r0 i_keeper_registry_master_wrapper_2_1.GetState var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (i_keeper_registry_master_wrapper_2_1.GetState, error)); ok { @@ -99,6 +111,10 @@ func (_m *Registry) GetState(opts *bind.CallOpts) (i_keeper_registry_master_wrap func (_m *Registry) GetUpkeep(opts *bind.CallOpts, id *big.Int) (i_keeper_registry_master_wrapper_2_1.KeeperRegistryBase21UpkeepInfo, error) { ret := _m.Called(opts, id) + if len(ret) == 0 { + panic("no return value specified for GetUpkeep") + } + var r0 i_keeper_registry_master_wrapper_2_1.KeeperRegistryBase21UpkeepInfo var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (i_keeper_registry_master_wrapper_2_1.KeeperRegistryBase21UpkeepInfo, error)); ok { @@ -123,6 +139,10 @@ func (_m *Registry) GetUpkeep(opts *bind.CallOpts, id *big.Int) (i_keeper_regist func (_m *Registry) GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { ret := _m.Called(opts, upkeepId) + if len(ret) == 0 { + panic("no return value specified for GetUpkeepPrivilegeConfig") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) ([]byte, error)); ok { @@ -149,6 +169,10 @@ func (_m *Registry) GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big. func (_m *Registry) GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { ret := _m.Called(opts, upkeepId) + if len(ret) == 0 { + panic("no return value specified for GetUpkeepTriggerConfig") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) ([]byte, error)); ok { @@ -175,6 +199,10 @@ func (_m *Registry) GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.In func (_m *Registry) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go index d3fe5c91195..57521566a71 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ type VRFBeaconInterface struct { func (_m *VRFBeaconInterface) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AcceptOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -54,6 +58,10 @@ func (_m *VRFBeaconInterface) AcceptOwnership(opts *bind.TransactOpts) (*types.T func (_m *VRFBeaconInterface) AcceptPayeeship(opts *bind.TransactOpts, transmitter common.Address) (*types.Transaction, error) { ret := _m.Called(opts, transmitter) + if len(ret) == 0 { + panic("no return value specified for AcceptPayeeship") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -80,6 +88,10 @@ func (_m *VRFBeaconInterface) AcceptPayeeship(opts *bind.TransactOpts, transmitt func (_m *VRFBeaconInterface) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -96,6 +108,10 @@ func (_m *VRFBeaconInterface) Address() common.Address { func (_m *VRFBeaconInterface) ExposeType(opts *bind.TransactOpts, arg0 vrf_beacon.VRFBeaconReportReport) (*types.Transaction, error) { ret := _m.Called(opts, arg0) + if len(ret) == 0 { + panic("no return value specified for ExposeType") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, vrf_beacon.VRFBeaconReportReport) (*types.Transaction, error)); ok { @@ -122,6 +138,10 @@ func (_m *VRFBeaconInterface) ExposeType(opts *bind.TransactOpts, arg0 vrf_beaco func (_m *VRFBeaconInterface) FilterBillingAccessControllerSet(opts *bind.FilterOpts) (*vrf_beacon.VRFBeaconBillingAccessControllerSetIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterBillingAccessControllerSet") + } + var r0 *vrf_beacon.VRFBeaconBillingAccessControllerSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_beacon.VRFBeaconBillingAccessControllerSetIterator, error)); ok { @@ -148,6 +168,10 @@ func (_m *VRFBeaconInterface) FilterBillingAccessControllerSet(opts *bind.Filter func (_m *VRFBeaconInterface) FilterBillingSet(opts *bind.FilterOpts) (*vrf_beacon.VRFBeaconBillingSetIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterBillingSet") + } + var r0 *vrf_beacon.VRFBeaconBillingSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_beacon.VRFBeaconBillingSetIterator, error)); ok { @@ -174,6 +198,10 @@ func (_m *VRFBeaconInterface) FilterBillingSet(opts *bind.FilterOpts) (*vrf_beac func (_m *VRFBeaconInterface) FilterConfigSet(opts *bind.FilterOpts) (*vrf_beacon.VRFBeaconConfigSetIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterConfigSet") + } + var r0 *vrf_beacon.VRFBeaconConfigSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_beacon.VRFBeaconConfigSetIterator, error)); ok { @@ -200,6 +228,10 @@ func (_m *VRFBeaconInterface) FilterConfigSet(opts *bind.FilterOpts) (*vrf_beaco func (_m *VRFBeaconInterface) FilterNewTransmission(opts *bind.FilterOpts, epochAndRound []*big.Int) (*vrf_beacon.VRFBeaconNewTransmissionIterator, error) { ret := _m.Called(opts, epochAndRound) + if len(ret) == 0 { + panic("no return value specified for FilterNewTransmission") + } + var r0 *vrf_beacon.VRFBeaconNewTransmissionIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_beacon.VRFBeaconNewTransmissionIterator, error)); ok { @@ -226,6 +258,10 @@ func (_m *VRFBeaconInterface) FilterNewTransmission(opts *bind.FilterOpts, epoch func (_m *VRFBeaconInterface) FilterOraclePaid(opts *bind.FilterOpts, transmitter []common.Address, payee []common.Address, linkToken []common.Address) (*vrf_beacon.VRFBeaconOraclePaidIterator, error) { ret := _m.Called(opts, transmitter, payee, linkToken) + if len(ret) == 0 { + panic("no return value specified for FilterOraclePaid") + } + var r0 *vrf_beacon.VRFBeaconOraclePaidIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address, []common.Address) (*vrf_beacon.VRFBeaconOraclePaidIterator, error)); ok { @@ -252,6 +288,10 @@ func (_m *VRFBeaconInterface) FilterOraclePaid(opts *bind.FilterOpts, transmitte func (_m *VRFBeaconInterface) FilterOutputsServed(opts *bind.FilterOpts) (*vrf_beacon.VRFBeaconOutputsServedIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterOutputsServed") + } + var r0 *vrf_beacon.VRFBeaconOutputsServedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_beacon.VRFBeaconOutputsServedIterator, error)); ok { @@ -278,6 +318,10 @@ func (_m *VRFBeaconInterface) FilterOutputsServed(opts *bind.FilterOpts) (*vrf_b func (_m *VRFBeaconInterface) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*vrf_beacon.VRFBeaconOwnershipTransferRequestedIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferRequested") + } + var r0 *vrf_beacon.VRFBeaconOwnershipTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*vrf_beacon.VRFBeaconOwnershipTransferRequestedIterator, error)); ok { @@ -304,6 +348,10 @@ func (_m *VRFBeaconInterface) FilterOwnershipTransferRequested(opts *bind.Filter func (_m *VRFBeaconInterface) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*vrf_beacon.VRFBeaconOwnershipTransferredIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferred") + } + var r0 *vrf_beacon.VRFBeaconOwnershipTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*vrf_beacon.VRFBeaconOwnershipTransferredIterator, error)); ok { @@ -330,6 +378,10 @@ func (_m *VRFBeaconInterface) FilterOwnershipTransferred(opts *bind.FilterOpts, func (_m *VRFBeaconInterface) FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, current []common.Address, proposed []common.Address) (*vrf_beacon.VRFBeaconPayeeshipTransferRequestedIterator, error) { ret := _m.Called(opts, transmitter, current, proposed) + if len(ret) == 0 { + panic("no return value specified for FilterPayeeshipTransferRequested") + } + var r0 *vrf_beacon.VRFBeaconPayeeshipTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address, []common.Address) (*vrf_beacon.VRFBeaconPayeeshipTransferRequestedIterator, error)); ok { @@ -356,6 +408,10 @@ func (_m *VRFBeaconInterface) FilterPayeeshipTransferRequested(opts *bind.Filter func (_m *VRFBeaconInterface) FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, previous []common.Address, current []common.Address) (*vrf_beacon.VRFBeaconPayeeshipTransferredIterator, error) { ret := _m.Called(opts, transmitter, previous, current) + if len(ret) == 0 { + panic("no return value specified for FilterPayeeshipTransferred") + } + var r0 *vrf_beacon.VRFBeaconPayeeshipTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address, []common.Address) (*vrf_beacon.VRFBeaconPayeeshipTransferredIterator, error)); ok { @@ -382,6 +438,10 @@ func (_m *VRFBeaconInterface) FilterPayeeshipTransferred(opts *bind.FilterOpts, func (_m *VRFBeaconInterface) FilterRandomWordsFulfilled(opts *bind.FilterOpts) (*vrf_beacon.VRFBeaconRandomWordsFulfilledIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterRandomWordsFulfilled") + } + var r0 *vrf_beacon.VRFBeaconRandomWordsFulfilledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_beacon.VRFBeaconRandomWordsFulfilledIterator, error)); ok { @@ -408,6 +468,10 @@ func (_m *VRFBeaconInterface) FilterRandomWordsFulfilled(opts *bind.FilterOpts) func (_m *VRFBeaconInterface) FilterRandomnessFulfillmentRequested(opts *bind.FilterOpts, requestID []*big.Int) (*vrf_beacon.VRFBeaconRandomnessFulfillmentRequestedIterator, error) { ret := _m.Called(opts, requestID) + if len(ret) == 0 { + panic("no return value specified for FilterRandomnessFulfillmentRequested") + } + var r0 *vrf_beacon.VRFBeaconRandomnessFulfillmentRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_beacon.VRFBeaconRandomnessFulfillmentRequestedIterator, error)); ok { @@ -434,6 +498,10 @@ func (_m *VRFBeaconInterface) FilterRandomnessFulfillmentRequested(opts *bind.Fi func (_m *VRFBeaconInterface) FilterRandomnessRedeemed(opts *bind.FilterOpts, requestID []*big.Int, requester []common.Address) (*vrf_beacon.VRFBeaconRandomnessRedeemedIterator, error) { ret := _m.Called(opts, requestID, requester) + if len(ret) == 0 { + panic("no return value specified for FilterRandomnessRedeemed") + } + var r0 *vrf_beacon.VRFBeaconRandomnessRedeemedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []common.Address) (*vrf_beacon.VRFBeaconRandomnessRedeemedIterator, error)); ok { @@ -460,6 +528,10 @@ func (_m *VRFBeaconInterface) FilterRandomnessRedeemed(opts *bind.FilterOpts, re func (_m *VRFBeaconInterface) FilterRandomnessRequested(opts *bind.FilterOpts, requestID []*big.Int) (*vrf_beacon.VRFBeaconRandomnessRequestedIterator, error) { ret := _m.Called(opts, requestID) + if len(ret) == 0 { + panic("no return value specified for FilterRandomnessRequested") + } + var r0 *vrf_beacon.VRFBeaconRandomnessRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_beacon.VRFBeaconRandomnessRequestedIterator, error)); ok { @@ -486,6 +558,10 @@ func (_m *VRFBeaconInterface) FilterRandomnessRequested(opts *bind.FilterOpts, r func (_m *VRFBeaconInterface) GetBilling(opts *bind.CallOpts) (vrf_beacon.GetBilling, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetBilling") + } + var r0 vrf_beacon.GetBilling var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_beacon.GetBilling, error)); ok { @@ -510,6 +586,10 @@ func (_m *VRFBeaconInterface) GetBilling(opts *bind.CallOpts) (vrf_beacon.GetBil func (_m *VRFBeaconInterface) GetBillingAccessController(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetBillingAccessController") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -536,6 +616,10 @@ func (_m *VRFBeaconInterface) GetBillingAccessController(opts *bind.CallOpts) (c func (_m *VRFBeaconInterface) ICoordinator(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for ICoordinator") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -562,6 +646,10 @@ func (_m *VRFBeaconInterface) ICoordinator(opts *bind.CallOpts) (common.Address, func (_m *VRFBeaconInterface) ILink(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for ILink") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -588,6 +676,10 @@ func (_m *VRFBeaconInterface) ILink(opts *bind.CallOpts) (common.Address, error) func (_m *VRFBeaconInterface) KeyGenerated(opts *bind.TransactOpts, kd vrf_beacon.KeyDataStructKeyData) (*types.Transaction, error) { ret := _m.Called(opts, kd) + if len(ret) == 0 { + panic("no return value specified for KeyGenerated") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, vrf_beacon.KeyDataStructKeyData) (*types.Transaction, error)); ok { @@ -614,6 +706,10 @@ func (_m *VRFBeaconInterface) KeyGenerated(opts *bind.TransactOpts, kd vrf_beaco func (_m *VRFBeaconInterface) LatestConfigDetails(opts *bind.CallOpts) (vrf_beacon.LatestConfigDetails, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestConfigDetails") + } + var r0 vrf_beacon.LatestConfigDetails var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_beacon.LatestConfigDetails, error)); ok { @@ -638,6 +734,10 @@ func (_m *VRFBeaconInterface) LatestConfigDetails(opts *bind.CallOpts) (vrf_beac func (_m *VRFBeaconInterface) LatestConfigDigestAndEpoch(opts *bind.CallOpts) (vrf_beacon.LatestConfigDigestAndEpoch, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestConfigDigestAndEpoch") + } + var r0 vrf_beacon.LatestConfigDigestAndEpoch var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_beacon.LatestConfigDigestAndEpoch, error)); ok { @@ -662,6 +762,10 @@ func (_m *VRFBeaconInterface) LatestConfigDigestAndEpoch(opts *bind.CallOpts) (v func (_m *VRFBeaconInterface) LinkAvailableForPayment(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LinkAvailableForPayment") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -688,6 +792,10 @@ func (_m *VRFBeaconInterface) LinkAvailableForPayment(opts *bind.CallOpts) (*big func (_m *VRFBeaconInterface) NUMCONFDELAYS(opts *bind.CallOpts) (uint8, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for NUMCONFDELAYS") + } + var r0 uint8 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint8, error)); ok { @@ -712,6 +820,10 @@ func (_m *VRFBeaconInterface) NUMCONFDELAYS(opts *bind.CallOpts) (uint8, error) func (_m *VRFBeaconInterface) NewKeyRequested(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for NewKeyRequested") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -738,6 +850,10 @@ func (_m *VRFBeaconInterface) NewKeyRequested(opts *bind.TransactOpts) (*types.T func (_m *VRFBeaconInterface) OwedPayment(opts *bind.CallOpts, transmitterAddress common.Address) (*big.Int, error) { ret := _m.Called(opts, transmitterAddress) + if len(ret) == 0 { + panic("no return value specified for OwedPayment") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (*big.Int, error)); ok { @@ -764,6 +880,10 @@ func (_m *VRFBeaconInterface) OwedPayment(opts *bind.CallOpts, transmitterAddres func (_m *VRFBeaconInterface) Owner(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Owner") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -790,6 +910,10 @@ func (_m *VRFBeaconInterface) Owner(opts *bind.CallOpts) (common.Address, error) func (_m *VRFBeaconInterface) ParseBillingAccessControllerSet(log types.Log) (*vrf_beacon.VRFBeaconBillingAccessControllerSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseBillingAccessControllerSet") + } + var r0 *vrf_beacon.VRFBeaconBillingAccessControllerSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconBillingAccessControllerSet, error)); ok { @@ -816,6 +940,10 @@ func (_m *VRFBeaconInterface) ParseBillingAccessControllerSet(log types.Log) (*v func (_m *VRFBeaconInterface) ParseBillingSet(log types.Log) (*vrf_beacon.VRFBeaconBillingSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseBillingSet") + } + var r0 *vrf_beacon.VRFBeaconBillingSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconBillingSet, error)); ok { @@ -842,6 +970,10 @@ func (_m *VRFBeaconInterface) ParseBillingSet(log types.Log) (*vrf_beacon.VRFBea func (_m *VRFBeaconInterface) ParseConfigSet(log types.Log) (*vrf_beacon.VRFBeaconConfigSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseConfigSet") + } + var r0 *vrf_beacon.VRFBeaconConfigSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconConfigSet, error)); ok { @@ -868,6 +1000,10 @@ func (_m *VRFBeaconInterface) ParseConfigSet(log types.Log) (*vrf_beacon.VRFBeac func (_m *VRFBeaconInterface) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { @@ -894,6 +1030,10 @@ func (_m *VRFBeaconInterface) ParseLog(log types.Log) (generated.AbigenLog, erro func (_m *VRFBeaconInterface) ParseNewTransmission(log types.Log) (*vrf_beacon.VRFBeaconNewTransmission, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseNewTransmission") + } + var r0 *vrf_beacon.VRFBeaconNewTransmission var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconNewTransmission, error)); ok { @@ -920,6 +1060,10 @@ func (_m *VRFBeaconInterface) ParseNewTransmission(log types.Log) (*vrf_beacon.V func (_m *VRFBeaconInterface) ParseOraclePaid(log types.Log) (*vrf_beacon.VRFBeaconOraclePaid, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOraclePaid") + } + var r0 *vrf_beacon.VRFBeaconOraclePaid var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconOraclePaid, error)); ok { @@ -946,6 +1090,10 @@ func (_m *VRFBeaconInterface) ParseOraclePaid(log types.Log) (*vrf_beacon.VRFBea func (_m *VRFBeaconInterface) ParseOutputsServed(log types.Log) (*vrf_beacon.VRFBeaconOutputsServed, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOutputsServed") + } + var r0 *vrf_beacon.VRFBeaconOutputsServed var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconOutputsServed, error)); ok { @@ -972,6 +1120,10 @@ func (_m *VRFBeaconInterface) ParseOutputsServed(log types.Log) (*vrf_beacon.VRF func (_m *VRFBeaconInterface) ParseOwnershipTransferRequested(log types.Log) (*vrf_beacon.VRFBeaconOwnershipTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferRequested") + } + var r0 *vrf_beacon.VRFBeaconOwnershipTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconOwnershipTransferRequested, error)); ok { @@ -998,6 +1150,10 @@ func (_m *VRFBeaconInterface) ParseOwnershipTransferRequested(log types.Log) (*v func (_m *VRFBeaconInterface) ParseOwnershipTransferred(log types.Log) (*vrf_beacon.VRFBeaconOwnershipTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferred") + } + var r0 *vrf_beacon.VRFBeaconOwnershipTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconOwnershipTransferred, error)); ok { @@ -1024,6 +1180,10 @@ func (_m *VRFBeaconInterface) ParseOwnershipTransferred(log types.Log) (*vrf_bea func (_m *VRFBeaconInterface) ParsePayeeshipTransferRequested(log types.Log) (*vrf_beacon.VRFBeaconPayeeshipTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParsePayeeshipTransferRequested") + } + var r0 *vrf_beacon.VRFBeaconPayeeshipTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconPayeeshipTransferRequested, error)); ok { @@ -1050,6 +1210,10 @@ func (_m *VRFBeaconInterface) ParsePayeeshipTransferRequested(log types.Log) (*v func (_m *VRFBeaconInterface) ParsePayeeshipTransferred(log types.Log) (*vrf_beacon.VRFBeaconPayeeshipTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParsePayeeshipTransferred") + } + var r0 *vrf_beacon.VRFBeaconPayeeshipTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconPayeeshipTransferred, error)); ok { @@ -1076,6 +1240,10 @@ func (_m *VRFBeaconInterface) ParsePayeeshipTransferred(log types.Log) (*vrf_bea func (_m *VRFBeaconInterface) ParseRandomWordsFulfilled(log types.Log) (*vrf_beacon.VRFBeaconRandomWordsFulfilled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomWordsFulfilled") + } + var r0 *vrf_beacon.VRFBeaconRandomWordsFulfilled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconRandomWordsFulfilled, error)); ok { @@ -1102,6 +1270,10 @@ func (_m *VRFBeaconInterface) ParseRandomWordsFulfilled(log types.Log) (*vrf_bea func (_m *VRFBeaconInterface) ParseRandomnessFulfillmentRequested(log types.Log) (*vrf_beacon.VRFBeaconRandomnessFulfillmentRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomnessFulfillmentRequested") + } + var r0 *vrf_beacon.VRFBeaconRandomnessFulfillmentRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconRandomnessFulfillmentRequested, error)); ok { @@ -1128,6 +1300,10 @@ func (_m *VRFBeaconInterface) ParseRandomnessFulfillmentRequested(log types.Log) func (_m *VRFBeaconInterface) ParseRandomnessRedeemed(log types.Log) (*vrf_beacon.VRFBeaconRandomnessRedeemed, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomnessRedeemed") + } + var r0 *vrf_beacon.VRFBeaconRandomnessRedeemed var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconRandomnessRedeemed, error)); ok { @@ -1154,6 +1330,10 @@ func (_m *VRFBeaconInterface) ParseRandomnessRedeemed(log types.Log) (*vrf_beaco func (_m *VRFBeaconInterface) ParseRandomnessRequested(log types.Log) (*vrf_beacon.VRFBeaconRandomnessRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomnessRequested") + } + var r0 *vrf_beacon.VRFBeaconRandomnessRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_beacon.VRFBeaconRandomnessRequested, error)); ok { @@ -1180,6 +1360,10 @@ func (_m *VRFBeaconInterface) ParseRandomnessRequested(log types.Log) (*vrf_beac func (_m *VRFBeaconInterface) SKeyID(opts *bind.CallOpts) ([32]byte, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SKeyID") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { @@ -1206,6 +1390,10 @@ func (_m *VRFBeaconInterface) SKeyID(opts *bind.CallOpts) ([32]byte, error) { func (_m *VRFBeaconInterface) SKeyProvider(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SKeyProvider") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -1232,6 +1420,10 @@ func (_m *VRFBeaconInterface) SKeyProvider(opts *bind.CallOpts) (common.Address, func (_m *VRFBeaconInterface) SProvingKeyHash(opts *bind.CallOpts) ([32]byte, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SProvingKeyHash") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { @@ -1258,6 +1450,10 @@ func (_m *VRFBeaconInterface) SProvingKeyHash(opts *bind.CallOpts) ([32]byte, er func (_m *VRFBeaconInterface) SetBilling(opts *bind.TransactOpts, maximumGasPrice uint64, reasonableGasPrice uint64, observationPayment uint64, transmissionPayment uint64, accountingGas *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, maximumGasPrice, reasonableGasPrice, observationPayment, transmissionPayment, accountingGas) + if len(ret) == 0 { + panic("no return value specified for SetBilling") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64, uint64, uint64, uint64, *big.Int) (*types.Transaction, error)); ok { @@ -1284,6 +1480,10 @@ func (_m *VRFBeaconInterface) SetBilling(opts *bind.TransactOpts, maximumGasPric func (_m *VRFBeaconInterface) SetBillingAccessController(opts *bind.TransactOpts, _billingAccessController common.Address) (*types.Transaction, error) { ret := _m.Called(opts, _billingAccessController) + if len(ret) == 0 { + panic("no return value specified for SetBillingAccessController") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1310,6 +1510,10 @@ func (_m *VRFBeaconInterface) SetBillingAccessController(opts *bind.TransactOpts func (_m *VRFBeaconInterface) SetConfig(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfig []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { ret := _m.Called(opts, signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) + if len(ret) == 0 { + panic("no return value specified for SetConfig") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address, []common.Address, uint8, []byte, uint64, []byte) (*types.Transaction, error)); ok { @@ -1336,6 +1540,10 @@ func (_m *VRFBeaconInterface) SetConfig(opts *bind.TransactOpts, signers []commo func (_m *VRFBeaconInterface) SetPayees(opts *bind.TransactOpts, transmitters []common.Address, payees []common.Address) (*types.Transaction, error) { ret := _m.Called(opts, transmitters, payees) + if len(ret) == 0 { + panic("no return value specified for SetPayees") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address, []common.Address) (*types.Transaction, error)); ok { @@ -1362,6 +1570,10 @@ func (_m *VRFBeaconInterface) SetPayees(opts *bind.TransactOpts, transmitters [] func (_m *VRFBeaconInterface) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, to) + if len(ret) == 0 { + panic("no return value specified for TransferOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1388,6 +1600,10 @@ func (_m *VRFBeaconInterface) TransferOwnership(opts *bind.TransactOpts, to comm func (_m *VRFBeaconInterface) TransferPayeeship(opts *bind.TransactOpts, transmitter common.Address, proposed common.Address) (*types.Transaction, error) { ret := _m.Called(opts, transmitter, proposed) + if len(ret) == 0 { + panic("no return value specified for TransferPayeeship") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, common.Address) (*types.Transaction, error)); ok { @@ -1414,6 +1630,10 @@ func (_m *VRFBeaconInterface) TransferPayeeship(opts *bind.TransactOpts, transmi func (_m *VRFBeaconInterface) Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { ret := _m.Called(opts, reportContext, report, rs, ss, rawVs) + if len(ret) == 0 { + panic("no return value specified for Transmit") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, [3][32]byte, []byte, [][32]byte, [][32]byte, [32]byte) (*types.Transaction, error)); ok { @@ -1440,6 +1660,10 @@ func (_m *VRFBeaconInterface) Transmit(opts *bind.TransactOpts, reportContext [3 func (_m *VRFBeaconInterface) TypeAndVersion(opts *bind.CallOpts) (string, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for TypeAndVersion") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (string, error)); ok { @@ -1464,6 +1688,10 @@ func (_m *VRFBeaconInterface) TypeAndVersion(opts *bind.CallOpts) (string, error func (_m *VRFBeaconInterface) WatchBillingAccessControllerSet(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconBillingAccessControllerSet) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchBillingAccessControllerSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconBillingAccessControllerSet) (event.Subscription, error)); ok { @@ -1490,6 +1718,10 @@ func (_m *VRFBeaconInterface) WatchBillingAccessControllerSet(opts *bind.WatchOp func (_m *VRFBeaconInterface) WatchBillingSet(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconBillingSet) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchBillingSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconBillingSet) (event.Subscription, error)); ok { @@ -1516,6 +1748,10 @@ func (_m *VRFBeaconInterface) WatchBillingSet(opts *bind.WatchOpts, sink chan<- func (_m *VRFBeaconInterface) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconConfigSet) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchConfigSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconConfigSet) (event.Subscription, error)); ok { @@ -1542,6 +1778,10 @@ func (_m *VRFBeaconInterface) WatchConfigSet(opts *bind.WatchOpts, sink chan<- * func (_m *VRFBeaconInterface) WatchNewTransmission(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconNewTransmission, epochAndRound []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, epochAndRound) + if len(ret) == 0 { + panic("no return value specified for WatchNewTransmission") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconNewTransmission, []*big.Int) (event.Subscription, error)); ok { @@ -1568,6 +1808,10 @@ func (_m *VRFBeaconInterface) WatchNewTransmission(opts *bind.WatchOpts, sink ch func (_m *VRFBeaconInterface) WatchOraclePaid(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconOraclePaid, transmitter []common.Address, payee []common.Address, linkToken []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, transmitter, payee, linkToken) + if len(ret) == 0 { + panic("no return value specified for WatchOraclePaid") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconOraclePaid, []common.Address, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1594,6 +1838,10 @@ func (_m *VRFBeaconInterface) WatchOraclePaid(opts *bind.WatchOpts, sink chan<- func (_m *VRFBeaconInterface) WatchOutputsServed(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconOutputsServed) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchOutputsServed") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconOutputsServed) (event.Subscription, error)); ok { @@ -1620,6 +1868,10 @@ func (_m *VRFBeaconInterface) WatchOutputsServed(opts *bind.WatchOpts, sink chan func (_m *VRFBeaconInterface) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconOwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1646,6 +1898,10 @@ func (_m *VRFBeaconInterface) WatchOwnershipTransferRequested(opts *bind.WatchOp func (_m *VRFBeaconInterface) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconOwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1672,6 +1928,10 @@ func (_m *VRFBeaconInterface) WatchOwnershipTransferred(opts *bind.WatchOpts, si func (_m *VRFBeaconInterface) WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconPayeeshipTransferRequested, transmitter []common.Address, current []common.Address, proposed []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, transmitter, current, proposed) + if len(ret) == 0 { + panic("no return value specified for WatchPayeeshipTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconPayeeshipTransferRequested, []common.Address, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1698,6 +1958,10 @@ func (_m *VRFBeaconInterface) WatchPayeeshipTransferRequested(opts *bind.WatchOp func (_m *VRFBeaconInterface) WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconPayeeshipTransferred, transmitter []common.Address, previous []common.Address, current []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, transmitter, previous, current) + if len(ret) == 0 { + panic("no return value specified for WatchPayeeshipTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconPayeeshipTransferred, []common.Address, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1724,6 +1988,10 @@ func (_m *VRFBeaconInterface) WatchPayeeshipTransferred(opts *bind.WatchOpts, si func (_m *VRFBeaconInterface) WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconRandomWordsFulfilled) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchRandomWordsFulfilled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconRandomWordsFulfilled) (event.Subscription, error)); ok { @@ -1750,6 +2018,10 @@ func (_m *VRFBeaconInterface) WatchRandomWordsFulfilled(opts *bind.WatchOpts, si func (_m *VRFBeaconInterface) WatchRandomnessFulfillmentRequested(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconRandomnessFulfillmentRequested, requestID []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, requestID) + if len(ret) == 0 { + panic("no return value specified for WatchRandomnessFulfillmentRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconRandomnessFulfillmentRequested, []*big.Int) (event.Subscription, error)); ok { @@ -1776,6 +2048,10 @@ func (_m *VRFBeaconInterface) WatchRandomnessFulfillmentRequested(opts *bind.Wat func (_m *VRFBeaconInterface) WatchRandomnessRedeemed(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconRandomnessRedeemed, requestID []*big.Int, requester []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, requestID, requester) + if len(ret) == 0 { + panic("no return value specified for WatchRandomnessRedeemed") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconRandomnessRedeemed, []*big.Int, []common.Address) (event.Subscription, error)); ok { @@ -1802,6 +2078,10 @@ func (_m *VRFBeaconInterface) WatchRandomnessRedeemed(opts *bind.WatchOpts, sink func (_m *VRFBeaconInterface) WatchRandomnessRequested(opts *bind.WatchOpts, sink chan<- *vrf_beacon.VRFBeaconRandomnessRequested, requestID []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, requestID) + if len(ret) == 0 { + panic("no return value specified for WatchRandomnessRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_beacon.VRFBeaconRandomnessRequested, []*big.Int) (event.Subscription, error)); ok { @@ -1828,6 +2108,10 @@ func (_m *VRFBeaconInterface) WatchRandomnessRequested(opts *bind.WatchOpts, sin func (_m *VRFBeaconInterface) WithdrawFunds(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, recipient, amount) + if len(ret) == 0 { + panic("no return value specified for WithdrawFunds") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int) (*types.Transaction, error)); ok { @@ -1854,6 +2138,10 @@ func (_m *VRFBeaconInterface) WithdrawFunds(opts *bind.TransactOpts, recipient c func (_m *VRFBeaconInterface) WithdrawPayment(opts *bind.TransactOpts, transmitter common.Address) (*types.Transaction, error) { ret := _m.Called(opts, transmitter) + if len(ret) == 0 { + panic("no return value specified for WithdrawPayment") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go index 835702d136a..c0f85d67e06 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_beacon_coordinator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -23,6 +23,10 @@ type VRFBeaconCoordinator struct { func (_m *VRFBeaconCoordinator) GetConfirmationDelays(opts *bind.CallOpts) ([8]*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetConfirmationDelays") + } + var r0 [8]*big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([8]*big.Int, error)); ok { @@ -49,6 +53,10 @@ func (_m *VRFBeaconCoordinator) GetConfirmationDelays(opts *bind.CallOpts) ([8]* func (_m *VRFBeaconCoordinator) IBeaconPeriodBlocks(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for IBeaconPeriodBlocks") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -75,6 +83,10 @@ func (_m *VRFBeaconCoordinator) IBeaconPeriodBlocks(opts *bind.CallOpts) (*big.I func (_m *VRFBeaconCoordinator) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { @@ -101,6 +113,10 @@ func (_m *VRFBeaconCoordinator) ParseLog(log types.Log) (generated.AbigenLog, er func (_m *VRFBeaconCoordinator) SKeyID(opts *bind.CallOpts) ([32]byte, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SKeyID") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { @@ -127,6 +143,10 @@ func (_m *VRFBeaconCoordinator) SKeyID(opts *bind.CallOpts) ([32]byte, error) { func (_m *VRFBeaconCoordinator) SProvingKeyHash(opts *bind.CallOpts) ([32]byte, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SProvingKeyHash") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([32]byte, error)); ok { diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go index d5e373f4bca..4b2155bb4e2 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/mocks/vrf_coordinator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ type VRFCoordinatorInterface struct { func (_m *VRFCoordinatorInterface) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AcceptOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -54,6 +58,10 @@ func (_m *VRFCoordinatorInterface) AcceptOwnership(opts *bind.TransactOpts) (*ty func (_m *VRFCoordinatorInterface) AcceptSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for AcceptSubscriptionOwnerTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int) (*types.Transaction, error)); ok { @@ -80,6 +88,10 @@ func (_m *VRFCoordinatorInterface) AcceptSubscriptionOwnerTransfer(opts *bind.Tr func (_m *VRFCoordinatorInterface) AddConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, consumer) + if len(ret) == 0 { + panic("no return value specified for AddConsumer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, common.Address) (*types.Transaction, error)); ok { @@ -106,6 +118,10 @@ func (_m *VRFCoordinatorInterface) AddConsumer(opts *bind.TransactOpts, subId *b func (_m *VRFCoordinatorInterface) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -122,6 +138,10 @@ func (_m *VRFCoordinatorInterface) Address() common.Address { func (_m *VRFCoordinatorInterface) BatchTransferLink(opts *bind.TransactOpts, recipients []common.Address, paymentsInJuels []*big.Int) (*types.Transaction, error) { ret := _m.Called(opts, recipients, paymentsInJuels) + if len(ret) == 0 { + panic("no return value specified for BatchTransferLink") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address, []*big.Int) (*types.Transaction, error)); ok { @@ -148,6 +168,10 @@ func (_m *VRFCoordinatorInterface) BatchTransferLink(opts *bind.TransactOpts, re func (_m *VRFCoordinatorInterface) CancelSubscription(opts *bind.TransactOpts, subId *big.Int, to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, to) + if len(ret) == 0 { + panic("no return value specified for CancelSubscription") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, common.Address) (*types.Transaction, error)); ok { @@ -174,6 +198,10 @@ func (_m *VRFCoordinatorInterface) CancelSubscription(opts *bind.TransactOpts, s func (_m *VRFCoordinatorInterface) CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for CreateSubscription") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -200,6 +228,10 @@ func (_m *VRFCoordinatorInterface) CreateSubscription(opts *bind.TransactOpts) ( func (_m *VRFCoordinatorInterface) DeregisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) { ret := _m.Called(opts, target) + if len(ret) == 0 { + panic("no return value specified for DeregisterMigratableCoordinator") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -226,6 +258,10 @@ func (_m *VRFCoordinatorInterface) DeregisterMigratableCoordinator(opts *bind.Tr func (_m *VRFCoordinatorInterface) FilterCallbackConfigSet(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCallbackConfigSetIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterCallbackConfigSet") + } + var r0 *vrf_coordinator.VRFCoordinatorCallbackConfigSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCallbackConfigSetIterator, error)); ok { @@ -252,6 +288,10 @@ func (_m *VRFCoordinatorInterface) FilterCallbackConfigSet(opts *bind.FilterOpts func (_m *VRFCoordinatorInterface) FilterCoordinatorConfigSet(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCoordinatorConfigSetIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterCoordinatorConfigSet") + } + var r0 *vrf_coordinator.VRFCoordinatorCoordinatorConfigSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCoordinatorConfigSetIterator, error)); ok { @@ -278,6 +318,10 @@ func (_m *VRFCoordinatorInterface) FilterCoordinatorConfigSet(opts *bind.FilterO func (_m *VRFCoordinatorInterface) FilterCoordinatorDeregistered(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCoordinatorDeregisteredIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterCoordinatorDeregistered") + } + var r0 *vrf_coordinator.VRFCoordinatorCoordinatorDeregisteredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCoordinatorDeregisteredIterator, error)); ok { @@ -304,6 +348,10 @@ func (_m *VRFCoordinatorInterface) FilterCoordinatorDeregistered(opts *bind.Filt func (_m *VRFCoordinatorInterface) FilterCoordinatorRegistered(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCoordinatorRegisteredIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterCoordinatorRegistered") + } + var r0 *vrf_coordinator.VRFCoordinatorCoordinatorRegisteredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorCoordinatorRegisteredIterator, error)); ok { @@ -330,6 +378,10 @@ func (_m *VRFCoordinatorInterface) FilterCoordinatorRegistered(opts *bind.Filter func (_m *VRFCoordinatorInterface) FilterMigrationCompleted(opts *bind.FilterOpts, newVersion []uint8, subID []*big.Int) (*vrf_coordinator.VRFCoordinatorMigrationCompletedIterator, error) { ret := _m.Called(opts, newVersion, subID) + if len(ret) == 0 { + panic("no return value specified for FilterMigrationCompleted") + } + var r0 *vrf_coordinator.VRFCoordinatorMigrationCompletedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint8, []*big.Int) (*vrf_coordinator.VRFCoordinatorMigrationCompletedIterator, error)); ok { @@ -356,6 +408,10 @@ func (_m *VRFCoordinatorInterface) FilterMigrationCompleted(opts *bind.FilterOpt func (_m *VRFCoordinatorInterface) FilterOutputsServed(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorOutputsServedIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterOutputsServed") + } + var r0 *vrf_coordinator.VRFCoordinatorOutputsServedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorOutputsServedIterator, error)); ok { @@ -382,6 +438,10 @@ func (_m *VRFCoordinatorInterface) FilterOutputsServed(opts *bind.FilterOpts) (* func (_m *VRFCoordinatorInterface) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*vrf_coordinator.VRFCoordinatorOwnershipTransferRequestedIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorOwnershipTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*vrf_coordinator.VRFCoordinatorOwnershipTransferRequestedIterator, error)); ok { @@ -408,6 +468,10 @@ func (_m *VRFCoordinatorInterface) FilterOwnershipTransferRequested(opts *bind.F func (_m *VRFCoordinatorInterface) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*vrf_coordinator.VRFCoordinatorOwnershipTransferredIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferred") + } + var r0 *vrf_coordinator.VRFCoordinatorOwnershipTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*vrf_coordinator.VRFCoordinatorOwnershipTransferredIterator, error)); ok { @@ -434,6 +498,10 @@ func (_m *VRFCoordinatorInterface) FilterOwnershipTransferred(opts *bind.FilterO func (_m *VRFCoordinatorInterface) FilterPauseFlagChanged(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorPauseFlagChangedIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterPauseFlagChanged") + } + var r0 *vrf_coordinator.VRFCoordinatorPauseFlagChangedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorPauseFlagChangedIterator, error)); ok { @@ -460,6 +528,10 @@ func (_m *VRFCoordinatorInterface) FilterPauseFlagChanged(opts *bind.FilterOpts) func (_m *VRFCoordinatorInterface) FilterRandomWordsFulfilled(opts *bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorRandomWordsFulfilledIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterRandomWordsFulfilled") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomWordsFulfilledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator.VRFCoordinatorRandomWordsFulfilledIterator, error)); ok { @@ -486,6 +558,10 @@ func (_m *VRFCoordinatorInterface) FilterRandomWordsFulfilled(opts *bind.FilterO func (_m *VRFCoordinatorInterface) FilterRandomnessFulfillmentRequested(opts *bind.FilterOpts, requestID []*big.Int) (*vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequestedIterator, error) { ret := _m.Called(opts, requestID) + if len(ret) == 0 { + panic("no return value specified for FilterRandomnessFulfillmentRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequestedIterator, error)); ok { @@ -512,6 +588,10 @@ func (_m *VRFCoordinatorInterface) FilterRandomnessFulfillmentRequested(opts *bi func (_m *VRFCoordinatorInterface) FilterRandomnessRedeemed(opts *bind.FilterOpts, requestID []*big.Int, requester []common.Address) (*vrf_coordinator.VRFCoordinatorRandomnessRedeemedIterator, error) { ret := _m.Called(opts, requestID, requester) + if len(ret) == 0 { + panic("no return value specified for FilterRandomnessRedeemed") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomnessRedeemedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []common.Address) (*vrf_coordinator.VRFCoordinatorRandomnessRedeemedIterator, error)); ok { @@ -538,6 +618,10 @@ func (_m *VRFCoordinatorInterface) FilterRandomnessRedeemed(opts *bind.FilterOpt func (_m *VRFCoordinatorInterface) FilterRandomnessRequested(opts *bind.FilterOpts, requestID []*big.Int) (*vrf_coordinator.VRFCoordinatorRandomnessRequestedIterator, error) { ret := _m.Called(opts, requestID) + if len(ret) == 0 { + panic("no return value specified for FilterRandomnessRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomnessRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorRandomnessRequestedIterator, error)); ok { @@ -564,6 +648,10 @@ func (_m *VRFCoordinatorInterface) FilterRandomnessRequested(opts *bind.FilterOp func (_m *VRFCoordinatorInterface) FilterSubscriptionCanceled(opts *bind.FilterOpts, subId []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionCanceledIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionCanceled") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionCanceledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionCanceledIterator, error)); ok { @@ -590,6 +678,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionCanceled(opts *bind.FilterO func (_m *VRFCoordinatorInterface) FilterSubscriptionConsumerAdded(opts *bind.FilterOpts, subId []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerAddedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionConsumerAdded") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionConsumerAddedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerAddedIterator, error)); ok { @@ -616,6 +708,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionConsumerAdded(opts *bind.Fi func (_m *VRFCoordinatorInterface) FilterSubscriptionConsumerRemoved(opts *bind.FilterOpts, subId []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemovedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionConsumerRemoved") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemovedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemovedIterator, error)); ok { @@ -642,6 +738,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionConsumerRemoved(opts *bind. func (_m *VRFCoordinatorInterface) FilterSubscriptionCreated(opts *bind.FilterOpts, subId []*big.Int, owner []common.Address) (*vrf_coordinator.VRFCoordinatorSubscriptionCreatedIterator, error) { ret := _m.Called(opts, subId, owner) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionCreated") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionCreatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int, []common.Address) (*vrf_coordinator.VRFCoordinatorSubscriptionCreatedIterator, error)); ok { @@ -668,6 +768,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionCreated(opts *bind.FilterOp func (_m *VRFCoordinatorInterface) FilterSubscriptionFunded(opts *bind.FilterOpts, subId []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionFundedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionFunded") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionFundedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionFundedIterator, error)); ok { @@ -694,6 +798,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionFunded(opts *bind.FilterOpt func (_m *VRFCoordinatorInterface) FilterSubscriptionOwnerTransferRequested(opts *bind.FilterOpts, subId []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequestedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionOwnerTransferRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequestedIterator, error)); ok { @@ -720,6 +828,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionOwnerTransferRequested(opts func (_m *VRFCoordinatorInterface) FilterSubscriptionOwnerTransferred(opts *bind.FilterOpts, subId []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferredIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionOwnerTransferred") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferredIterator, error)); ok { @@ -746,6 +858,10 @@ func (_m *VRFCoordinatorInterface) FilterSubscriptionOwnerTransferred(opts *bind func (_m *VRFCoordinatorInterface) GetCallbackMemo(opts *bind.CallOpts, requestId *big.Int) ([32]byte, error) { ret := _m.Called(opts, requestId) + if len(ret) == 0 { + panic("no return value specified for GetCallbackMemo") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) ([32]byte, error)); ok { @@ -772,6 +888,10 @@ func (_m *VRFCoordinatorInterface) GetCallbackMemo(opts *bind.CallOpts, requestI func (_m *VRFCoordinatorInterface) GetConfirmationDelays(opts *bind.CallOpts) ([8]*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetConfirmationDelays") + } + var r0 [8]*big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([8]*big.Int, error)); ok { @@ -798,6 +918,10 @@ func (_m *VRFCoordinatorInterface) GetConfirmationDelays(opts *bind.CallOpts) ([ func (_m *VRFCoordinatorInterface) GetFee(opts *bind.CallOpts, arg0 *big.Int, arg1 []byte) (*big.Int, error) { ret := _m.Called(opts, arg0, arg1) + if len(ret) == 0 { + panic("no return value specified for GetFee") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int, []byte) (*big.Int, error)); ok { @@ -824,6 +948,10 @@ func (_m *VRFCoordinatorInterface) GetFee(opts *bind.CallOpts, arg0 *big.Int, ar func (_m *VRFCoordinatorInterface) GetFulfillmentFee(opts *bind.CallOpts, arg0 *big.Int, callbackGasLimit uint32, arguments []byte, arg3 []byte) (*big.Int, error) { ret := _m.Called(opts, arg0, callbackGasLimit, arguments, arg3) + if len(ret) == 0 { + panic("no return value specified for GetFulfillmentFee") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int, uint32, []byte, []byte) (*big.Int, error)); ok { @@ -850,6 +978,10 @@ func (_m *VRFCoordinatorInterface) GetFulfillmentFee(opts *bind.CallOpts, arg0 * func (_m *VRFCoordinatorInterface) GetSubscription(opts *bind.CallOpts, subId *big.Int) (vrf_coordinator.GetSubscription, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for GetSubscription") + } + var r0 vrf_coordinator.GetSubscription var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (vrf_coordinator.GetSubscription, error)); ok { @@ -874,6 +1006,10 @@ func (_m *VRFCoordinatorInterface) GetSubscription(opts *bind.CallOpts, subId *b func (_m *VRFCoordinatorInterface) GetSubscriptionLinkBalance(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetSubscriptionLinkBalance") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -900,6 +1036,10 @@ func (_m *VRFCoordinatorInterface) GetSubscriptionLinkBalance(opts *bind.CallOpt func (_m *VRFCoordinatorInterface) IBeaconPeriodBlocks(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for IBeaconPeriodBlocks") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -926,6 +1066,10 @@ func (_m *VRFCoordinatorInterface) IBeaconPeriodBlocks(opts *bind.CallOpts) (*bi func (_m *VRFCoordinatorInterface) ILink(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for ILink") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -952,6 +1096,10 @@ func (_m *VRFCoordinatorInterface) ILink(opts *bind.CallOpts) (common.Address, e func (_m *VRFCoordinatorInterface) MAXCONSUMERS(opts *bind.CallOpts) (uint16, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MAXCONSUMERS") + } + var r0 uint16 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint16, error)); ok { @@ -976,6 +1124,10 @@ func (_m *VRFCoordinatorInterface) MAXCONSUMERS(opts *bind.CallOpts) (uint16, er func (_m *VRFCoordinatorInterface) MAXNUMWORDS(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MAXNUMWORDS") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -1002,6 +1154,10 @@ func (_m *VRFCoordinatorInterface) MAXNUMWORDS(opts *bind.CallOpts) (*big.Int, e func (_m *VRFCoordinatorInterface) Migrate(opts *bind.TransactOpts, newCoordinator common.Address, encodedRequest []byte) (*types.Transaction, error) { ret := _m.Called(opts, newCoordinator, encodedRequest) + if len(ret) == 0 { + panic("no return value specified for Migrate") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, []byte) (*types.Transaction, error)); ok { @@ -1028,6 +1184,10 @@ func (_m *VRFCoordinatorInterface) Migrate(opts *bind.TransactOpts, newCoordinat func (_m *VRFCoordinatorInterface) MigrationVersion(opts *bind.CallOpts) (uint8, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MigrationVersion") + } + var r0 uint8 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint8, error)); ok { @@ -1052,6 +1212,10 @@ func (_m *VRFCoordinatorInterface) MigrationVersion(opts *bind.CallOpts) (uint8, func (_m *VRFCoordinatorInterface) NUMCONFDELAYS(opts *bind.CallOpts) (uint8, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for NUMCONFDELAYS") + } + var r0 uint8 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint8, error)); ok { @@ -1076,6 +1240,10 @@ func (_m *VRFCoordinatorInterface) NUMCONFDELAYS(opts *bind.CallOpts) (uint8, er func (_m *VRFCoordinatorInterface) OnMigration(opts *bind.CallOpts, arg0 []byte) error { ret := _m.Called(opts, arg0) + if len(ret) == 0 { + panic("no return value specified for OnMigration") + } + var r0 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, []byte) error); ok { r0 = rf(opts, arg0) @@ -1090,6 +1258,10 @@ func (_m *VRFCoordinatorInterface) OnMigration(opts *bind.CallOpts, arg0 []byte) func (_m *VRFCoordinatorInterface) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { ret := _m.Called(opts, arg0, amount, data) + if len(ret) == 0 { + panic("no return value specified for OnTokenTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int, []byte) (*types.Transaction, error)); ok { @@ -1116,6 +1288,10 @@ func (_m *VRFCoordinatorInterface) OnTokenTransfer(opts *bind.TransactOpts, arg0 func (_m *VRFCoordinatorInterface) Owner(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Owner") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -1142,6 +1318,10 @@ func (_m *VRFCoordinatorInterface) Owner(opts *bind.CallOpts) (common.Address, e func (_m *VRFCoordinatorInterface) ParseCallbackConfigSet(log types.Log) (*vrf_coordinator.VRFCoordinatorCallbackConfigSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseCallbackConfigSet") + } + var r0 *vrf_coordinator.VRFCoordinatorCallbackConfigSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorCallbackConfigSet, error)); ok { @@ -1168,6 +1348,10 @@ func (_m *VRFCoordinatorInterface) ParseCallbackConfigSet(log types.Log) (*vrf_c func (_m *VRFCoordinatorInterface) ParseCoordinatorConfigSet(log types.Log) (*vrf_coordinator.VRFCoordinatorCoordinatorConfigSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseCoordinatorConfigSet") + } + var r0 *vrf_coordinator.VRFCoordinatorCoordinatorConfigSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorCoordinatorConfigSet, error)); ok { @@ -1194,6 +1378,10 @@ func (_m *VRFCoordinatorInterface) ParseCoordinatorConfigSet(log types.Log) (*vr func (_m *VRFCoordinatorInterface) ParseCoordinatorDeregistered(log types.Log) (*vrf_coordinator.VRFCoordinatorCoordinatorDeregistered, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseCoordinatorDeregistered") + } + var r0 *vrf_coordinator.VRFCoordinatorCoordinatorDeregistered var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorCoordinatorDeregistered, error)); ok { @@ -1220,6 +1408,10 @@ func (_m *VRFCoordinatorInterface) ParseCoordinatorDeregistered(log types.Log) ( func (_m *VRFCoordinatorInterface) ParseCoordinatorRegistered(log types.Log) (*vrf_coordinator.VRFCoordinatorCoordinatorRegistered, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseCoordinatorRegistered") + } + var r0 *vrf_coordinator.VRFCoordinatorCoordinatorRegistered var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorCoordinatorRegistered, error)); ok { @@ -1246,6 +1438,10 @@ func (_m *VRFCoordinatorInterface) ParseCoordinatorRegistered(log types.Log) (*v func (_m *VRFCoordinatorInterface) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { @@ -1272,6 +1468,10 @@ func (_m *VRFCoordinatorInterface) ParseLog(log types.Log) (generated.AbigenLog, func (_m *VRFCoordinatorInterface) ParseMigrationCompleted(log types.Log) (*vrf_coordinator.VRFCoordinatorMigrationCompleted, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseMigrationCompleted") + } + var r0 *vrf_coordinator.VRFCoordinatorMigrationCompleted var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorMigrationCompleted, error)); ok { @@ -1298,6 +1498,10 @@ func (_m *VRFCoordinatorInterface) ParseMigrationCompleted(log types.Log) (*vrf_ func (_m *VRFCoordinatorInterface) ParseOutputsServed(log types.Log) (*vrf_coordinator.VRFCoordinatorOutputsServed, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOutputsServed") + } + var r0 *vrf_coordinator.VRFCoordinatorOutputsServed var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorOutputsServed, error)); ok { @@ -1324,6 +1528,10 @@ func (_m *VRFCoordinatorInterface) ParseOutputsServed(log types.Log) (*vrf_coord func (_m *VRFCoordinatorInterface) ParseOwnershipTransferRequested(log types.Log) (*vrf_coordinator.VRFCoordinatorOwnershipTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorOwnershipTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorOwnershipTransferRequested, error)); ok { @@ -1350,6 +1558,10 @@ func (_m *VRFCoordinatorInterface) ParseOwnershipTransferRequested(log types.Log func (_m *VRFCoordinatorInterface) ParseOwnershipTransferred(log types.Log) (*vrf_coordinator.VRFCoordinatorOwnershipTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferred") + } + var r0 *vrf_coordinator.VRFCoordinatorOwnershipTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorOwnershipTransferred, error)); ok { @@ -1376,6 +1588,10 @@ func (_m *VRFCoordinatorInterface) ParseOwnershipTransferred(log types.Log) (*vr func (_m *VRFCoordinatorInterface) ParsePauseFlagChanged(log types.Log) (*vrf_coordinator.VRFCoordinatorPauseFlagChanged, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParsePauseFlagChanged") + } + var r0 *vrf_coordinator.VRFCoordinatorPauseFlagChanged var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorPauseFlagChanged, error)); ok { @@ -1402,6 +1618,10 @@ func (_m *VRFCoordinatorInterface) ParsePauseFlagChanged(log types.Log) (*vrf_co func (_m *VRFCoordinatorInterface) ParseRandomWordsFulfilled(log types.Log) (*vrf_coordinator.VRFCoordinatorRandomWordsFulfilled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomWordsFulfilled") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomWordsFulfilled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorRandomWordsFulfilled, error)); ok { @@ -1428,6 +1648,10 @@ func (_m *VRFCoordinatorInterface) ParseRandomWordsFulfilled(log types.Log) (*vr func (_m *VRFCoordinatorInterface) ParseRandomnessFulfillmentRequested(log types.Log) (*vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomnessFulfillmentRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequested, error)); ok { @@ -1454,6 +1678,10 @@ func (_m *VRFCoordinatorInterface) ParseRandomnessFulfillmentRequested(log types func (_m *VRFCoordinatorInterface) ParseRandomnessRedeemed(log types.Log) (*vrf_coordinator.VRFCoordinatorRandomnessRedeemed, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomnessRedeemed") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomnessRedeemed var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorRandomnessRedeemed, error)); ok { @@ -1480,6 +1708,10 @@ func (_m *VRFCoordinatorInterface) ParseRandomnessRedeemed(log types.Log) (*vrf_ func (_m *VRFCoordinatorInterface) ParseRandomnessRequested(log types.Log) (*vrf_coordinator.VRFCoordinatorRandomnessRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomnessRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorRandomnessRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorRandomnessRequested, error)); ok { @@ -1506,6 +1738,10 @@ func (_m *VRFCoordinatorInterface) ParseRandomnessRequested(log types.Log) (*vrf func (_m *VRFCoordinatorInterface) ParseSubscriptionCanceled(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionCanceled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionCanceled") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionCanceled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionCanceled, error)); ok { @@ -1532,6 +1768,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionCanceled(log types.Log) (*vr func (_m *VRFCoordinatorInterface) ParseSubscriptionConsumerAdded(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerAdded, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionConsumerAdded") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionConsumerAdded var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerAdded, error)); ok { @@ -1558,6 +1798,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionConsumerAdded(log types.Log) func (_m *VRFCoordinatorInterface) ParseSubscriptionConsumerRemoved(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemoved, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionConsumerRemoved") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemoved var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemoved, error)); ok { @@ -1584,6 +1828,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionConsumerRemoved(log types.Lo func (_m *VRFCoordinatorInterface) ParseSubscriptionCreated(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionCreated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionCreated") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionCreated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionCreated, error)); ok { @@ -1610,6 +1858,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionCreated(log types.Log) (*vrf func (_m *VRFCoordinatorInterface) ParseSubscriptionFunded(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionFunded, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionFunded") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionFunded var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionFunded, error)); ok { @@ -1636,6 +1888,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionFunded(log types.Log) (*vrf_ func (_m *VRFCoordinatorInterface) ParseSubscriptionOwnerTransferRequested(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionOwnerTransferRequested") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequested, error)); ok { @@ -1662,6 +1918,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionOwnerTransferRequested(log t func (_m *VRFCoordinatorInterface) ParseSubscriptionOwnerTransferred(log types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionOwnerTransferred") + } + var r0 *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferred, error)); ok { @@ -1688,6 +1948,10 @@ func (_m *VRFCoordinatorInterface) ParseSubscriptionOwnerTransferred(log types.L func (_m *VRFCoordinatorInterface) ProcessVRFOutputs(opts *bind.TransactOpts, vrfOutputs []vrf_coordinator.VRFBeaconTypesVRFOutput, juelsPerFeeCoin *big.Int, reasonableGasPrice uint64, blockHeight uint64) (*types.Transaction, error) { ret := _m.Called(opts, vrfOutputs, juelsPerFeeCoin, reasonableGasPrice, blockHeight) + if len(ret) == 0 { + panic("no return value specified for ProcessVRFOutputs") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []vrf_coordinator.VRFBeaconTypesVRFOutput, *big.Int, uint64, uint64) (*types.Transaction, error)); ok { @@ -1714,6 +1978,10 @@ func (_m *VRFCoordinatorInterface) ProcessVRFOutputs(opts *bind.TransactOpts, vr func (_m *VRFCoordinatorInterface) RedeemRandomness(opts *bind.TransactOpts, subID *big.Int, requestID *big.Int, arg2 []byte) (*types.Transaction, error) { ret := _m.Called(opts, subID, requestID, arg2) + if len(ret) == 0 { + panic("no return value specified for RedeemRandomness") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, *big.Int, []byte) (*types.Transaction, error)); ok { @@ -1740,6 +2008,10 @@ func (_m *VRFCoordinatorInterface) RedeemRandomness(opts *bind.TransactOpts, sub func (_m *VRFCoordinatorInterface) RegisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) { ret := _m.Called(opts, target) + if len(ret) == 0 { + panic("no return value specified for RegisterMigratableCoordinator") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1766,6 +2038,10 @@ func (_m *VRFCoordinatorInterface) RegisterMigratableCoordinator(opts *bind.Tran func (_m *VRFCoordinatorInterface) RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, consumer) + if len(ret) == 0 { + panic("no return value specified for RemoveConsumer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, common.Address) (*types.Transaction, error)); ok { @@ -1792,6 +2068,10 @@ func (_m *VRFCoordinatorInterface) RemoveConsumer(opts *bind.TransactOpts, subId func (_m *VRFCoordinatorInterface) RequestRandomness(opts *bind.TransactOpts, subID *big.Int, numWords uint16, confDelay *big.Int, arg3 []byte) (*types.Transaction, error) { ret := _m.Called(opts, subID, numWords, confDelay, arg3) + if len(ret) == 0 { + panic("no return value specified for RequestRandomness") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, uint16, *big.Int, []byte) (*types.Transaction, error)); ok { @@ -1818,6 +2098,10 @@ func (_m *VRFCoordinatorInterface) RequestRandomness(opts *bind.TransactOpts, su func (_m *VRFCoordinatorInterface) RequestRandomnessFulfillment(opts *bind.TransactOpts, subID *big.Int, numWords uint16, confDelay *big.Int, callbackGasLimit uint32, arguments []byte, arg5 []byte) (*types.Transaction, error) { ret := _m.Called(opts, subID, numWords, confDelay, callbackGasLimit, arguments, arg5) + if len(ret) == 0 { + panic("no return value specified for RequestRandomnessFulfillment") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, uint16, *big.Int, uint32, []byte, []byte) (*types.Transaction, error)); ok { @@ -1844,6 +2128,10 @@ func (_m *VRFCoordinatorInterface) RequestRandomnessFulfillment(opts *bind.Trans func (_m *VRFCoordinatorInterface) RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId *big.Int, newOwner common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, newOwner) + if len(ret) == 0 { + panic("no return value specified for RequestSubscriptionOwnerTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, *big.Int, common.Address) (*types.Transaction, error)); ok { @@ -1870,6 +2158,10 @@ func (_m *VRFCoordinatorInterface) RequestSubscriptionOwnerTransfer(opts *bind.T func (_m *VRFCoordinatorInterface) SCallbackConfig(opts *bind.CallOpts) (vrf_coordinator.SCallbackConfig, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SCallbackConfig") + } + var r0 vrf_coordinator.SCallbackConfig var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_coordinator.SCallbackConfig, error)); ok { @@ -1894,6 +2186,10 @@ func (_m *VRFCoordinatorInterface) SCallbackConfig(opts *bind.CallOpts) (vrf_coo func (_m *VRFCoordinatorInterface) SCoordinatorConfig(opts *bind.CallOpts) (vrf_coordinator.SCoordinatorConfig, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SCoordinatorConfig") + } + var r0 vrf_coordinator.SCoordinatorConfig var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_coordinator.SCoordinatorConfig, error)); ok { @@ -1918,6 +2214,10 @@ func (_m *VRFCoordinatorInterface) SCoordinatorConfig(opts *bind.CallOpts) (vrf_ func (_m *VRFCoordinatorInterface) SPendingRequests(opts *bind.CallOpts, arg0 *big.Int) (vrf_coordinator.SPendingRequests, error) { ret := _m.Called(opts, arg0) + if len(ret) == 0 { + panic("no return value specified for SPendingRequests") + } + var r0 vrf_coordinator.SPendingRequests var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (vrf_coordinator.SPendingRequests, error)); ok { @@ -1942,6 +2242,10 @@ func (_m *VRFCoordinatorInterface) SPendingRequests(opts *bind.CallOpts, arg0 *b func (_m *VRFCoordinatorInterface) SProducer(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for SProducer") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -1968,6 +2272,10 @@ func (_m *VRFCoordinatorInterface) SProducer(opts *bind.CallOpts) (common.Addres func (_m *VRFCoordinatorInterface) SetCallbackConfig(opts *bind.TransactOpts, config vrf_coordinator.VRFCoordinatorCallbackConfig) (*types.Transaction, error) { ret := _m.Called(opts, config) + if len(ret) == 0 { + panic("no return value specified for SetCallbackConfig") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, vrf_coordinator.VRFCoordinatorCallbackConfig) (*types.Transaction, error)); ok { @@ -1994,6 +2302,10 @@ func (_m *VRFCoordinatorInterface) SetCallbackConfig(opts *bind.TransactOpts, co func (_m *VRFCoordinatorInterface) SetConfirmationDelays(opts *bind.TransactOpts, confDelays [8]*big.Int) (*types.Transaction, error) { ret := _m.Called(opts, confDelays) + if len(ret) == 0 { + panic("no return value specified for SetConfirmationDelays") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, [8]*big.Int) (*types.Transaction, error)); ok { @@ -2020,6 +2332,10 @@ func (_m *VRFCoordinatorInterface) SetConfirmationDelays(opts *bind.TransactOpts func (_m *VRFCoordinatorInterface) SetCoordinatorConfig(opts *bind.TransactOpts, coordinatorConfig vrf_coordinator.VRFBeaconTypesCoordinatorConfig) (*types.Transaction, error) { ret := _m.Called(opts, coordinatorConfig) + if len(ret) == 0 { + panic("no return value specified for SetCoordinatorConfig") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, vrf_coordinator.VRFBeaconTypesCoordinatorConfig) (*types.Transaction, error)); ok { @@ -2046,6 +2362,10 @@ func (_m *VRFCoordinatorInterface) SetCoordinatorConfig(opts *bind.TransactOpts, func (_m *VRFCoordinatorInterface) SetPauseFlag(opts *bind.TransactOpts, pause bool) (*types.Transaction, error) { ret := _m.Called(opts, pause) + if len(ret) == 0 { + panic("no return value specified for SetPauseFlag") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, bool) (*types.Transaction, error)); ok { @@ -2072,6 +2392,10 @@ func (_m *VRFCoordinatorInterface) SetPauseFlag(opts *bind.TransactOpts, pause b func (_m *VRFCoordinatorInterface) SetProducer(opts *bind.TransactOpts, producer common.Address) (*types.Transaction, error) { ret := _m.Called(opts, producer) + if len(ret) == 0 { + panic("no return value specified for SetProducer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -2098,6 +2422,10 @@ func (_m *VRFCoordinatorInterface) SetProducer(opts *bind.TransactOpts, producer func (_m *VRFCoordinatorInterface) TransferLink(opts *bind.TransactOpts, recipient common.Address, juelsAmount *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, recipient, juelsAmount) + if len(ret) == 0 { + panic("no return value specified for TransferLink") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int) (*types.Transaction, error)); ok { @@ -2124,6 +2452,10 @@ func (_m *VRFCoordinatorInterface) TransferLink(opts *bind.TransactOpts, recipie func (_m *VRFCoordinatorInterface) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, to) + if len(ret) == 0 { + panic("no return value specified for TransferOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -2150,6 +2482,10 @@ func (_m *VRFCoordinatorInterface) TransferOwnership(opts *bind.TransactOpts, to func (_m *VRFCoordinatorInterface) WatchCallbackConfigSet(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorCallbackConfigSet) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchCallbackConfigSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorCallbackConfigSet) (event.Subscription, error)); ok { @@ -2176,6 +2512,10 @@ func (_m *VRFCoordinatorInterface) WatchCallbackConfigSet(opts *bind.WatchOpts, func (_m *VRFCoordinatorInterface) WatchCoordinatorConfigSet(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorCoordinatorConfigSet) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchCoordinatorConfigSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorCoordinatorConfigSet) (event.Subscription, error)); ok { @@ -2202,6 +2542,10 @@ func (_m *VRFCoordinatorInterface) WatchCoordinatorConfigSet(opts *bind.WatchOpt func (_m *VRFCoordinatorInterface) WatchCoordinatorDeregistered(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorCoordinatorDeregistered) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchCoordinatorDeregistered") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorCoordinatorDeregistered) (event.Subscription, error)); ok { @@ -2228,6 +2572,10 @@ func (_m *VRFCoordinatorInterface) WatchCoordinatorDeregistered(opts *bind.Watch func (_m *VRFCoordinatorInterface) WatchCoordinatorRegistered(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorCoordinatorRegistered) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchCoordinatorRegistered") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorCoordinatorRegistered) (event.Subscription, error)); ok { @@ -2254,6 +2602,10 @@ func (_m *VRFCoordinatorInterface) WatchCoordinatorRegistered(opts *bind.WatchOp func (_m *VRFCoordinatorInterface) WatchMigrationCompleted(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorMigrationCompleted, newVersion []uint8, subID []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, newVersion, subID) + if len(ret) == 0 { + panic("no return value specified for WatchMigrationCompleted") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorMigrationCompleted, []uint8, []*big.Int) (event.Subscription, error)); ok { @@ -2280,6 +2632,10 @@ func (_m *VRFCoordinatorInterface) WatchMigrationCompleted(opts *bind.WatchOpts, func (_m *VRFCoordinatorInterface) WatchOutputsServed(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorOutputsServed) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchOutputsServed") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorOutputsServed) (event.Subscription, error)); ok { @@ -2306,6 +2662,10 @@ func (_m *VRFCoordinatorInterface) WatchOutputsServed(opts *bind.WatchOpts, sink func (_m *VRFCoordinatorInterface) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorOwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -2332,6 +2692,10 @@ func (_m *VRFCoordinatorInterface) WatchOwnershipTransferRequested(opts *bind.Wa func (_m *VRFCoordinatorInterface) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorOwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -2358,6 +2722,10 @@ func (_m *VRFCoordinatorInterface) WatchOwnershipTransferred(opts *bind.WatchOpt func (_m *VRFCoordinatorInterface) WatchPauseFlagChanged(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorPauseFlagChanged) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchPauseFlagChanged") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorPauseFlagChanged) (event.Subscription, error)); ok { @@ -2384,6 +2752,10 @@ func (_m *VRFCoordinatorInterface) WatchPauseFlagChanged(opts *bind.WatchOpts, s func (_m *VRFCoordinatorInterface) WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorRandomWordsFulfilled) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchRandomWordsFulfilled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorRandomWordsFulfilled) (event.Subscription, error)); ok { @@ -2410,6 +2782,10 @@ func (_m *VRFCoordinatorInterface) WatchRandomWordsFulfilled(opts *bind.WatchOpt func (_m *VRFCoordinatorInterface) WatchRandomnessFulfillmentRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequested, requestID []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, requestID) + if len(ret) == 0 { + panic("no return value specified for WatchRandomnessFulfillmentRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorRandomnessFulfillmentRequested, []*big.Int) (event.Subscription, error)); ok { @@ -2436,6 +2812,10 @@ func (_m *VRFCoordinatorInterface) WatchRandomnessFulfillmentRequested(opts *bin func (_m *VRFCoordinatorInterface) WatchRandomnessRedeemed(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorRandomnessRedeemed, requestID []*big.Int, requester []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, requestID, requester) + if len(ret) == 0 { + panic("no return value specified for WatchRandomnessRedeemed") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorRandomnessRedeemed, []*big.Int, []common.Address) (event.Subscription, error)); ok { @@ -2462,6 +2842,10 @@ func (_m *VRFCoordinatorInterface) WatchRandomnessRedeemed(opts *bind.WatchOpts, func (_m *VRFCoordinatorInterface) WatchRandomnessRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorRandomnessRequested, requestID []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, requestID) + if len(ret) == 0 { + panic("no return value specified for WatchRandomnessRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorRandomnessRequested, []*big.Int) (event.Subscription, error)); ok { @@ -2488,6 +2872,10 @@ func (_m *VRFCoordinatorInterface) WatchRandomnessRequested(opts *bind.WatchOpts func (_m *VRFCoordinatorInterface) WatchSubscriptionCanceled(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionCanceled, subId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionCanceled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionCanceled, []*big.Int) (event.Subscription, error)); ok { @@ -2514,6 +2902,10 @@ func (_m *VRFCoordinatorInterface) WatchSubscriptionCanceled(opts *bind.WatchOpt func (_m *VRFCoordinatorInterface) WatchSubscriptionConsumerAdded(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionConsumerAdded, subId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionConsumerAdded") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionConsumerAdded, []*big.Int) (event.Subscription, error)); ok { @@ -2540,6 +2932,10 @@ func (_m *VRFCoordinatorInterface) WatchSubscriptionConsumerAdded(opts *bind.Wat func (_m *VRFCoordinatorInterface) WatchSubscriptionConsumerRemoved(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemoved, subId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionConsumerRemoved") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionConsumerRemoved, []*big.Int) (event.Subscription, error)); ok { @@ -2566,6 +2962,10 @@ func (_m *VRFCoordinatorInterface) WatchSubscriptionConsumerRemoved(opts *bind.W func (_m *VRFCoordinatorInterface) WatchSubscriptionCreated(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionCreated, subId []*big.Int, owner []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, subId, owner) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionCreated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionCreated, []*big.Int, []common.Address) (event.Subscription, error)); ok { @@ -2592,6 +2992,10 @@ func (_m *VRFCoordinatorInterface) WatchSubscriptionCreated(opts *bind.WatchOpts func (_m *VRFCoordinatorInterface) WatchSubscriptionFunded(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionFunded, subId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionFunded") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionFunded, []*big.Int) (event.Subscription, error)); ok { @@ -2618,6 +3022,10 @@ func (_m *VRFCoordinatorInterface) WatchSubscriptionFunded(opts *bind.WatchOpts, func (_m *VRFCoordinatorInterface) WatchSubscriptionOwnerTransferRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequested, subId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionOwnerTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferRequested, []*big.Int) (event.Subscription, error)); ok { @@ -2644,6 +3052,10 @@ func (_m *VRFCoordinatorInterface) WatchSubscriptionOwnerTransferRequested(opts func (_m *VRFCoordinatorInterface) WatchSubscriptionOwnerTransferred(opts *bind.WatchOpts, sink chan<- *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferred, subId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionOwnerTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator.VRFCoordinatorSubscriptionOwnerTransferred, []*big.Int) (event.Subscription, error)); ok { diff --git a/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go b/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go index ba97eda30b1..418cb276011 100644 --- a/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go +++ b/core/services/ocr2/plugins/promwrapper/mocks/prometheus_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/core/services/ocr2/plugins/threshold/mocks/decryptor.go b/core/services/ocr2/plugins/threshold/mocks/decryptor.go index 4b91cbbd26f..5496483c9dd 100644 --- a/core/services/ocr2/plugins/threshold/mocks/decryptor.go +++ b/core/services/ocr2/plugins/threshold/mocks/decryptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type Decryptor struct { func (_m *Decryptor) Decrypt(ctx context.Context, ciphertextId decryptionplugin.CiphertextId, ciphertext []byte) ([]byte, error) { ret := _m.Called(ctx, ciphertextId, ciphertext) + if len(ret) == 0 { + panic("no return value specified for Decrypt") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, decryptionplugin.CiphertextId, []byte) ([]byte, error)); ok { diff --git a/core/services/pg/mocks/event_broadcaster.go b/core/services/pg/mocks/event_broadcaster.go index b4d04b8b999..63f06db494b 100644 --- a/core/services/pg/mocks/event_broadcaster.go +++ b/core/services/pg/mocks/event_broadcaster.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type EventBroadcaster struct { func (_m *EventBroadcaster) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *EventBroadcaster) Close() error { func (_m *EventBroadcaster) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -48,6 +56,10 @@ func (_m *EventBroadcaster) HealthReport() map[string]error { func (_m *EventBroadcaster) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -62,6 +74,10 @@ func (_m *EventBroadcaster) Name() string { func (_m *EventBroadcaster) Notify(channel string, payload string) error { ret := _m.Called(channel, payload) + if len(ret) == 0 { + panic("no return value specified for Notify") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(channel, payload) @@ -76,6 +92,10 @@ func (_m *EventBroadcaster) Notify(channel string, payload string) error { func (_m *EventBroadcaster) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -90,6 +110,10 @@ func (_m *EventBroadcaster) Ready() error { func (_m *EventBroadcaster) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -104,6 +128,10 @@ func (_m *EventBroadcaster) Start(_a0 context.Context) error { func (_m *EventBroadcaster) Subscribe(channel string, payloadFilter string) (pg.Subscription, error) { ret := _m.Called(channel, payloadFilter) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 pg.Subscription var r1 error if rf, ok := ret.Get(0).(func(string, string) (pg.Subscription, error)); ok { diff --git a/core/services/pg/mocks/subscription.go b/core/services/pg/mocks/subscription.go index 9cfe4c4f020..fcd194004de 100644 --- a/core/services/pg/mocks/subscription.go +++ b/core/services/pg/mocks/subscription.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type Subscription struct { func (_m *Subscription) ChannelName() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ChannelName") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -35,6 +39,10 @@ func (_m *Subscription) Close() { func (_m *Subscription) Events() <-chan pg.Event { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Events") + } + var r0 <-chan pg.Event if rf, ok := ret.Get(0).(func() <-chan pg.Event); ok { r0 = rf() @@ -51,6 +59,10 @@ func (_m *Subscription) Events() <-chan pg.Event { func (_m *Subscription) InterestedIn(event pg.Event) bool { ret := _m.Called(event) + if len(ret) == 0 { + panic("no return value specified for InterestedIn") + } + var r0 bool if rf, ok := ret.Get(0).(func(pg.Event) bool); ok { r0 = rf(event) diff --git a/core/services/pipeline/mocks/config.go b/core/services/pipeline/mocks/config.go index eb9a411f423..4cd6bc7a724 100644 --- a/core/services/pipeline/mocks/config.go +++ b/core/services/pipeline/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type Config struct { func (_m *Config) DefaultHTTPLimit() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DefaultHTTPLimit") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *Config) DefaultHTTPLimit() int64 { func (_m *Config) DefaultHTTPTimeout() models.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DefaultHTTPTimeout") + } + var r0 models.Duration if rf, ok := ret.Get(0).(func() models.Duration); ok { r0 = rf() @@ -46,6 +54,10 @@ func (_m *Config) DefaultHTTPTimeout() models.Duration { func (_m *Config) MaxRunDuration() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for MaxRunDuration") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -60,6 +72,10 @@ func (_m *Config) MaxRunDuration() time.Duration { func (_m *Config) ReaperInterval() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReaperInterval") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() @@ -74,6 +90,10 @@ func (_m *Config) ReaperInterval() time.Duration { func (_m *Config) ReaperThreshold() time.Duration { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReaperThreshold") + } + var r0 time.Duration if rf, ok := ret.Get(0).(func() time.Duration); ok { r0 = rf() diff --git a/core/services/pipeline/mocks/orm.go b/core/services/pipeline/mocks/orm.go index 88d067c6ffa..759686204d4 100644 --- a/core/services/pipeline/mocks/orm.go +++ b/core/services/pipeline/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -26,6 +26,10 @@ type ORM struct { func (_m *ORM) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -47,6 +51,10 @@ func (_m *ORM) CreateRun(run *pipeline.Run, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateRun") + } + var r0 error if rf, ok := ret.Get(0).(func(*pipeline.Run, ...pg.QOpt) error); ok { r0 = rf(run, qopts...) @@ -68,6 +76,10 @@ func (_m *ORM) CreateSpec(_a0 pipeline.Pipeline, maxTaskTimeout models.Interval, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for CreateSpec") + } + var r0 int32 var r1 error if rf, ok := ret.Get(0).(func(pipeline.Pipeline, models.Interval, ...pg.QOpt) (int32, error)); ok { @@ -92,6 +104,10 @@ func (_m *ORM) CreateSpec(_a0 pipeline.Pipeline, maxTaskTimeout models.Interval, func (_m *ORM) DeleteRun(id int64) error { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for DeleteRun") + } + var r0 error if rf, ok := ret.Get(0).(func(int64) error); ok { r0 = rf(id) @@ -106,6 +122,10 @@ func (_m *ORM) DeleteRun(id int64) error { func (_m *ORM) DeleteRunsOlderThan(_a0 context.Context, _a1 time.Duration) error { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DeleteRunsOlderThan") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Duration) error); ok { r0 = rf(_a0, _a1) @@ -120,6 +140,10 @@ func (_m *ORM) DeleteRunsOlderThan(_a0 context.Context, _a1 time.Duration) error func (_m *ORM) FindRun(id int64) (pipeline.Run, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for FindRun") + } + var r0 pipeline.Run var r1 error if rf, ok := ret.Get(0).(func(int64) (pipeline.Run, error)); ok { @@ -144,6 +168,10 @@ func (_m *ORM) FindRun(id int64) (pipeline.Run, error) { func (_m *ORM) GetAllRuns() ([]pipeline.Run, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAllRuns") + } + var r0 []pipeline.Run var r1 error if rf, ok := ret.Get(0).(func() ([]pipeline.Run, error)); ok { @@ -170,6 +198,10 @@ func (_m *ORM) GetAllRuns() ([]pipeline.Run, error) { func (_m *ORM) GetQ() pg.Q { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetQ") + } + var r0 pg.Q if rf, ok := ret.Get(0).(func() pg.Q); ok { r0 = rf() @@ -184,6 +216,10 @@ func (_m *ORM) GetQ() pg.Q { func (_m *ORM) GetUnfinishedRuns(_a0 context.Context, _a1 time.Time, _a2 func(pipeline.Run) error) error { ret := _m.Called(_a0, _a1, _a2) + if len(ret) == 0 { + panic("no return value specified for GetUnfinishedRuns") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, time.Time, func(pipeline.Run) error) error); ok { r0 = rf(_a0, _a1, _a2) @@ -198,6 +234,10 @@ func (_m *ORM) GetUnfinishedRuns(_a0 context.Context, _a1 time.Time, _a2 func(pi func (_m *ORM) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -221,6 +261,10 @@ func (_m *ORM) InsertFinishedRun(run *pipeline.Run, saveSuccessfulTaskRuns bool, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertFinishedRun") + } + var r0 error if rf, ok := ret.Get(0).(func(*pipeline.Run, bool, ...pg.QOpt) error); ok { r0 = rf(run, saveSuccessfulTaskRuns, qopts...) @@ -242,6 +286,10 @@ func (_m *ORM) InsertFinishedRuns(run []*pipeline.Run, saveSuccessfulTaskRuns bo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertFinishedRuns") + } + var r0 error if rf, ok := ret.Get(0).(func([]*pipeline.Run, bool, ...pg.QOpt) error); ok { r0 = rf(run, saveSuccessfulTaskRuns, qopts...) @@ -263,6 +311,10 @@ func (_m *ORM) InsertRun(run *pipeline.Run, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertRun") + } + var r0 error if rf, ok := ret.Get(0).(func(*pipeline.Run, ...pg.QOpt) error); ok { r0 = rf(run, qopts...) @@ -277,6 +329,10 @@ func (_m *ORM) InsertRun(run *pipeline.Run, qopts ...pg.QOpt) error { func (_m *ORM) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -291,6 +347,10 @@ func (_m *ORM) Name() string { func (_m *ORM) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -305,6 +365,10 @@ func (_m *ORM) Ready() error { func (_m *ORM) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -326,6 +390,10 @@ func (_m *ORM) StoreRun(run *pipeline.Run, qopts ...pg.QOpt) (bool, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for StoreRun") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(*pipeline.Run, ...pg.QOpt) (bool, error)); ok { @@ -350,6 +418,10 @@ func (_m *ORM) StoreRun(run *pipeline.Run, qopts ...pg.QOpt) (bool, error) { func (_m *ORM) UpdateTaskRunResult(taskID uuid.UUID, result pipeline.Result) (pipeline.Run, bool, error) { ret := _m.Called(taskID, result) + if len(ret) == 0 { + panic("no return value specified for UpdateTaskRunResult") + } + var r0 pipeline.Run var r1 bool var r2 error diff --git a/core/services/pipeline/mocks/pipeline_param_unmarshaler.go b/core/services/pipeline/mocks/pipeline_param_unmarshaler.go index 3478ce1322d..40f2ba4dd32 100644 --- a/core/services/pipeline/mocks/pipeline_param_unmarshaler.go +++ b/core/services/pipeline/mocks/pipeline_param_unmarshaler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type PipelineParamUnmarshaler struct { func (_m *PipelineParamUnmarshaler) UnmarshalPipelineParam(val interface{}) error { ret := _m.Called(val) + if len(ret) == 0 { + panic("no return value specified for UnmarshalPipelineParam") + } + var r0 error if rf, ok := ret.Get(0).(func(interface{}) error); ok { r0 = rf(val) diff --git a/core/services/pipeline/mocks/runner.go b/core/services/pipeline/mocks/runner.go index 34b55399752..f6e5033eae9 100644 --- a/core/services/pipeline/mocks/runner.go +++ b/core/services/pipeline/mocks/runner.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type Runner struct { func (_m *Runner) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -38,6 +42,10 @@ func (_m *Runner) Close() error { func (_m *Runner) ExecuteAndInsertFinishedRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger, saveSuccessfulTaskRuns bool) (int64, pipeline.FinalResult, error) { ret := _m.Called(ctx, spec, vars, l, saveSuccessfulTaskRuns) + if len(ret) == 0 { + panic("no return value specified for ExecuteAndInsertFinishedRun") + } + var r0 int64 var r1 pipeline.FinalResult var r2 error @@ -69,6 +77,10 @@ func (_m *Runner) ExecuteAndInsertFinishedRun(ctx context.Context, spec pipeline func (_m *Runner) ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (*pipeline.Run, pipeline.TaskRunResults, error) { ret := _m.Called(ctx, spec, vars, l) + if len(ret) == 0 { + panic("no return value specified for ExecuteRun") + } + var r0 *pipeline.Run var r1 pipeline.TaskRunResults var r2 error @@ -104,6 +116,10 @@ func (_m *Runner) ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipel func (_m *Runner) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -127,6 +143,10 @@ func (_m *Runner) InsertFinishedRun(run *pipeline.Run, saveSuccessfulTaskRuns bo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertFinishedRun") + } + var r0 error if rf, ok := ret.Get(0).(func(*pipeline.Run, bool, ...pg.QOpt) error); ok { r0 = rf(run, saveSuccessfulTaskRuns, qopts...) @@ -148,6 +168,10 @@ func (_m *Runner) InsertFinishedRuns(runs []*pipeline.Run, saveSuccessfulTaskRun _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for InsertFinishedRuns") + } + var r0 error if rf, ok := ret.Get(0).(func([]*pipeline.Run, bool, ...pg.QOpt) error); ok { r0 = rf(runs, saveSuccessfulTaskRuns, qopts...) @@ -162,6 +186,10 @@ func (_m *Runner) InsertFinishedRuns(runs []*pipeline.Run, saveSuccessfulTaskRun func (_m *Runner) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -181,6 +209,10 @@ func (_m *Runner) OnRunFinished(_a0 func(*pipeline.Run)) { func (_m *Runner) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -195,6 +227,10 @@ func (_m *Runner) Ready() error { func (_m *Runner) ResumeRun(taskID uuid.UUID, value interface{}, err error) error { ret := _m.Called(taskID, value, err) + if len(ret) == 0 { + panic("no return value specified for ResumeRun") + } + var r0 error if rf, ok := ret.Get(0).(func(uuid.UUID, interface{}, error) error); ok { r0 = rf(taskID, value, err) @@ -209,6 +245,10 @@ func (_m *Runner) ResumeRun(taskID uuid.UUID, value interface{}, err error) erro func (_m *Runner) Run(ctx context.Context, run *pipeline.Run, l logger.Logger, saveSuccessfulTaskRuns bool, fn func(pg.Queryer) error) (bool, error) { ret := _m.Called(ctx, run, l, saveSuccessfulTaskRuns, fn) + if len(ret) == 0 { + panic("no return value specified for Run") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, *pipeline.Run, logger.Logger, bool, func(pg.Queryer) error) (bool, error)); ok { @@ -233,6 +273,10 @@ func (_m *Runner) Run(ctx context.Context, run *pipeline.Run, l logger.Logger, s func (_m *Runner) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/relay/evm/mercury/mocks/async_deleter.go b/core/services/relay/evm/mercury/mocks/async_deleter.go index c0f583efd18..b706e9c771e 100644 --- a/core/services/relay/evm/mercury/mocks/async_deleter.go +++ b/core/services/relay/evm/mercury/mocks/async_deleter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/core/services/relay/evm/mocks/loop_relay_adapter.go b/core/services/relay/evm/mocks/loop_relay_adapter.go index 0376c9f27a4..5b927f1b8ac 100644 --- a/core/services/relay/evm/mocks/loop_relay_adapter.go +++ b/core/services/relay/evm/mocks/loop_relay_adapter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ type LoopRelayAdapter struct { func (_m *LoopRelayAdapter) Chain() legacyevm.Chain { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Chain") + } + var r0 legacyevm.Chain if rf, ok := ret.Get(0).(func() legacyevm.Chain); ok { r0 = rf() @@ -38,6 +42,10 @@ func (_m *LoopRelayAdapter) Chain() legacyevm.Chain { func (_m *LoopRelayAdapter) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -52,6 +60,10 @@ func (_m *LoopRelayAdapter) Close() error { func (_m *LoopRelayAdapter) GetChainStatus(ctx context.Context) (types.ChainStatus, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetChainStatus") + } + var r0 types.ChainStatus var r1 error if rf, ok := ret.Get(0).(func(context.Context) (types.ChainStatus, error)); ok { @@ -76,6 +88,10 @@ func (_m *LoopRelayAdapter) GetChainStatus(ctx context.Context) (types.ChainStat func (_m *LoopRelayAdapter) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -92,6 +108,10 @@ func (_m *LoopRelayAdapter) HealthReport() map[string]error { func (_m *LoopRelayAdapter) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) ([]types.NodeStatus, string, int, error) { ret := _m.Called(ctx, pageSize, pageToken) + if len(ret) == 0 { + panic("no return value specified for ListNodeStatuses") + } + var r0 []types.NodeStatus var r1 string var r2 int @@ -132,6 +152,10 @@ func (_m *LoopRelayAdapter) ListNodeStatuses(ctx context.Context, pageSize int32 func (_m *LoopRelayAdapter) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -146,6 +170,10 @@ func (_m *LoopRelayAdapter) Name() string { func (_m *LoopRelayAdapter) NewConfigProvider(_a0 context.Context, _a1 types.RelayArgs) (types.ConfigProvider, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for NewConfigProvider") + } + var r0 types.ConfigProvider var r1 error if rf, ok := ret.Get(0).(func(context.Context, types.RelayArgs) (types.ConfigProvider, error)); ok { @@ -172,6 +200,10 @@ func (_m *LoopRelayAdapter) NewConfigProvider(_a0 context.Context, _a1 types.Rel func (_m *LoopRelayAdapter) NewPluginProvider(_a0 context.Context, _a1 types.RelayArgs, _a2 types.PluginArgs) (types.PluginProvider, error) { ret := _m.Called(_a0, _a1, _a2) + if len(ret) == 0 { + panic("no return value specified for NewPluginProvider") + } + var r0 types.PluginProvider var r1 error if rf, ok := ret.Get(0).(func(context.Context, types.RelayArgs, types.PluginArgs) (types.PluginProvider, error)); ok { @@ -198,6 +230,10 @@ func (_m *LoopRelayAdapter) NewPluginProvider(_a0 context.Context, _a1 types.Rel func (_m *LoopRelayAdapter) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -212,6 +248,10 @@ func (_m *LoopRelayAdapter) Ready() error { func (_m *LoopRelayAdapter) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) @@ -226,6 +266,10 @@ func (_m *LoopRelayAdapter) Start(_a0 context.Context) error { func (_m *LoopRelayAdapter) Transact(ctx context.Context, from string, to string, amount *big.Int, balanceCheck bool) error { ret := _m.Called(ctx, from, to, amount, balanceCheck) + if len(ret) == 0 { + panic("no return value specified for Transact") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, *big.Int, bool) error); ok { r0 = rf(ctx, from, to, amount, balanceCheck) diff --git a/core/services/relay/evm/mocks/request_round_db.go b/core/services/relay/evm/mocks/request_round_db.go index 1727e8cc47d..eb27e8bd526 100644 --- a/core/services/relay/evm/mocks/request_round_db.go +++ b/core/services/relay/evm/mocks/request_round_db.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type RequestRoundDB struct { func (_m *RequestRoundDB) LoadLatestRoundRequested() (ocr2aggregator.OCR2AggregatorRoundRequested, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LoadLatestRoundRequested") + } + var r0 ocr2aggregator.OCR2AggregatorRoundRequested var r1 error if rf, ok := ret.Get(0).(func() (ocr2aggregator.OCR2AggregatorRoundRequested, error)); ok { @@ -41,6 +45,10 @@ func (_m *RequestRoundDB) LoadLatestRoundRequested() (ocr2aggregator.OCR2Aggrega func (_m *RequestRoundDB) SaveLatestRoundRequested(tx pg.Queryer, rr ocr2aggregator.OCR2AggregatorRoundRequested) error { ret := _m.Called(tx, rr) + if len(ret) == 0 { + panic("no return value specified for SaveLatestRoundRequested") + } + var r0 error if rf, ok := ret.Get(0).(func(pg.Queryer, ocr2aggregator.OCR2AggregatorRoundRequested) error); ok { r0 = rf(tx, rr) diff --git a/core/services/relay/evm/types/mocks/log_poller_wrapper.go b/core/services/relay/evm/types/mocks/log_poller_wrapper.go index 6812ce5aba3..675cf317b14 100644 --- a/core/services/relay/evm/types/mocks/log_poller_wrapper.go +++ b/core/services/relay/evm/types/mocks/log_poller_wrapper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type LogPollerWrapper struct { func (_m *LogPollerWrapper) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *LogPollerWrapper) Close() error { func (_m *LogPollerWrapper) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -48,6 +56,10 @@ func (_m *LogPollerWrapper) HealthReport() map[string]error { func (_m *LogPollerWrapper) LatestEvents() ([]types.OracleRequest, []types.OracleResponse, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestEvents") + } + var r0 []types.OracleRequest var r1 []types.OracleResponse var r2 error @@ -83,6 +95,10 @@ func (_m *LogPollerWrapper) LatestEvents() ([]types.OracleRequest, []types.Oracl func (_m *LogPollerWrapper) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -97,6 +113,10 @@ func (_m *LogPollerWrapper) Name() string { func (_m *LogPollerWrapper) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -111,6 +131,10 @@ func (_m *LogPollerWrapper) Ready() error { func (_m *LogPollerWrapper) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/s4/mocks/orm.go b/core/services/s4/mocks/orm.go index f053af9ca7b..706c1194293 100644 --- a/core/services/s4/mocks/orm.go +++ b/core/services/s4/mocks/orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ func (_m *ORM) DeleteExpired(limit uint, utcNow time.Time, qopts ...pg.QOpt) (in _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DeleteExpired") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(uint, time.Time, ...pg.QOpt) (int64, error)); ok { @@ -59,6 +63,10 @@ func (_m *ORM) Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*s4.Row, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *s4.Row var r1 error if rf, ok := ret.Get(0).(func(*utils.Big, uint, ...pg.QOpt) (*s4.Row, error)); ok { @@ -92,6 +100,10 @@ func (_m *ORM) GetSnapshot(addressRange *s4.AddressRange, qopts ...pg.QOpt) ([]* _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetSnapshot") + } + var r0 []*s4.SnapshotRow var r1 error if rf, ok := ret.Get(0).(func(*s4.AddressRange, ...pg.QOpt) ([]*s4.SnapshotRow, error)); ok { @@ -125,6 +137,10 @@ func (_m *ORM) GetUnconfirmedRows(limit uint, qopts ...pg.QOpt) ([]*s4.Row, erro _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetUnconfirmedRows") + } + var r0 []*s4.Row var r1 error if rf, ok := ret.Get(0).(func(uint, ...pg.QOpt) ([]*s4.Row, error)); ok { @@ -158,6 +174,10 @@ func (_m *ORM) Update(row *s4.Row, qopts ...pg.QOpt) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(*s4.Row, ...pg.QOpt) error); ok { r0 = rf(row, qopts...) diff --git a/core/services/s4/mocks/storage.go b/core/services/s4/mocks/storage.go index f4174f171b8..06fc153a358 100644 --- a/core/services/s4/mocks/storage.go +++ b/core/services/s4/mocks/storage.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type Storage struct { func (_m *Storage) Constraints() s4.Constraints { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Constraints") + } + var r0 s4.Constraints if rf, ok := ret.Get(0).(func() s4.Constraints); ok { r0 = rf() @@ -35,6 +39,10 @@ func (_m *Storage) Constraints() s4.Constraints { func (_m *Storage) Get(ctx context.Context, key *s4.Key) (*s4.Record, *s4.Metadata, error) { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *s4.Record var r1 *s4.Metadata var r2 error @@ -70,6 +78,10 @@ func (_m *Storage) Get(ctx context.Context, key *s4.Key) (*s4.Record, *s4.Metada func (_m *Storage) List(ctx context.Context, address common.Address) ([]*s4.SnapshotRow, error) { ret := _m.Called(ctx, address) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*s4.SnapshotRow var r1 error if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]*s4.SnapshotRow, error)); ok { @@ -96,6 +108,10 @@ func (_m *Storage) List(ctx context.Context, address common.Address) ([]*s4.Snap func (_m *Storage) Put(ctx context.Context, key *s4.Key, record *s4.Record, signature []byte) error { ret := _m.Called(ctx, key, record, signature) + if len(ret) == 0 { + panic("no return value specified for Put") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s4.Key, *s4.Record, []byte) error); ok { r0 = rf(ctx, key, record, signature) diff --git a/core/services/synchronization/mocks/telemetry_service.go b/core/services/synchronization/mocks/telemetry_service.go index bd822666b97..375b46ad7bb 100644 --- a/core/services/synchronization/mocks/telemetry_service.go +++ b/core/services/synchronization/mocks/telemetry_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type TelemetryService struct { func (_m *TelemetryService) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -32,6 +36,10 @@ func (_m *TelemetryService) Close() error { func (_m *TelemetryService) HealthReport() map[string]error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + var r0 map[string]error if rf, ok := ret.Get(0).(func() map[string]error); ok { r0 = rf() @@ -48,6 +56,10 @@ func (_m *TelemetryService) HealthReport() map[string]error { func (_m *TelemetryService) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -62,6 +74,10 @@ func (_m *TelemetryService) Name() string { func (_m *TelemetryService) Ready() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ready") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -81,6 +97,10 @@ func (_m *TelemetryService) Send(ctx context.Context, telemetry []byte, contract func (_m *TelemetryService) Start(_a0 context.Context) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(_a0) diff --git a/core/services/vrf/mocks/aggregator_v3_interface.go b/core/services/vrf/mocks/aggregator_v3_interface.go index 956e315f297..46ca11aa200 100644 --- a/core/services/vrf/mocks/aggregator_v3_interface.go +++ b/core/services/vrf/mocks/aggregator_v3_interface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -23,6 +23,10 @@ type AggregatorV3Interface struct { func (_m *AggregatorV3Interface) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -39,6 +43,10 @@ func (_m *AggregatorV3Interface) Address() common.Address { func (_m *AggregatorV3Interface) Decimals(opts *bind.CallOpts) (uint8, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Decimals") + } + var r0 uint8 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint8, error)); ok { @@ -63,6 +71,10 @@ func (_m *AggregatorV3Interface) Decimals(opts *bind.CallOpts) (uint8, error) { func (_m *AggregatorV3Interface) Description(opts *bind.CallOpts) (string, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Description") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (string, error)); ok { @@ -87,6 +99,10 @@ func (_m *AggregatorV3Interface) Description(opts *bind.CallOpts) (string, error func (_m *AggregatorV3Interface) GetRoundData(opts *bind.CallOpts, _roundId *big.Int) (aggregator_v3_interface.GetRoundData, error) { ret := _m.Called(opts, _roundId) + if len(ret) == 0 { + panic("no return value specified for GetRoundData") + } + var r0 aggregator_v3_interface.GetRoundData var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) (aggregator_v3_interface.GetRoundData, error)); ok { @@ -111,6 +127,10 @@ func (_m *AggregatorV3Interface) GetRoundData(opts *bind.CallOpts, _roundId *big func (_m *AggregatorV3Interface) LatestRoundData(opts *bind.CallOpts) (aggregator_v3_interface.LatestRoundData, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LatestRoundData") + } + var r0 aggregator_v3_interface.LatestRoundData var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (aggregator_v3_interface.LatestRoundData, error)); ok { @@ -135,6 +155,10 @@ func (_m *AggregatorV3Interface) LatestRoundData(opts *bind.CallOpts) (aggregato func (_m *AggregatorV3Interface) Version(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Version") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { diff --git a/core/services/vrf/mocks/config.go b/core/services/vrf/mocks/config.go index 72d5960f2c9..b46a28ec037 100644 --- a/core/services/vrf/mocks/config.go +++ b/core/services/vrf/mocks/config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type Config struct { func (_m *Config) FinalityDepth() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for FinalityDepth") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -27,6 +31,10 @@ func (_m *Config) FinalityDepth() uint32 { func (_m *Config) MinIncomingConfirmations() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for MinIncomingConfirmations") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() diff --git a/core/services/vrf/mocks/fee_config.go b/core/services/vrf/mocks/fee_config.go index 067ce7e4455..2f33415b338 100644 --- a/core/services/vrf/mocks/fee_config.go +++ b/core/services/vrf/mocks/fee_config.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type FeeConfig struct { func (_m *FeeConfig) LimitDefault() uint32 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitDefault") + } + var r0 uint32 if rf, ok := ret.Get(0).(func() uint32); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *FeeConfig) LimitDefault() uint32 { func (_m *FeeConfig) LimitJobType() config.LimitJobType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LimitJobType") + } + var r0 config.LimitJobType if rf, ok := ret.Get(0).(func() config.LimitJobType); ok { r0 = rf() @@ -50,6 +58,10 @@ func (_m *FeeConfig) LimitJobType() config.LimitJobType { func (_m *FeeConfig) PriceMaxKey(addr common.Address) *assets.Wei { ret := _m.Called(addr) + if len(ret) == 0 { + panic("no return value specified for PriceMaxKey") + } + var r0 *assets.Wei if rf, ok := ret.Get(0).(func(common.Address) *assets.Wei); ok { r0 = rf(addr) diff --git a/core/services/vrf/mocks/vrf_coordinator_v2.go b/core/services/vrf/mocks/vrf_coordinator_v2.go index c39995b38e9..529bc789257 100644 --- a/core/services/vrf/mocks/vrf_coordinator_v2.go +++ b/core/services/vrf/mocks/vrf_coordinator_v2.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ type VRFCoordinatorV2Interface struct { func (_m *VRFCoordinatorV2Interface) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for AcceptOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -54,6 +58,10 @@ func (_m *VRFCoordinatorV2Interface) AcceptOwnership(opts *bind.TransactOpts) (* func (_m *VRFCoordinatorV2Interface) AcceptSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for AcceptSubscriptionOwnerTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64) (*types.Transaction, error)); ok { @@ -80,6 +88,10 @@ func (_m *VRFCoordinatorV2Interface) AcceptSubscriptionOwnerTransfer(opts *bind. func (_m *VRFCoordinatorV2Interface) AddConsumer(opts *bind.TransactOpts, subId uint64, consumer common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, consumer) + if len(ret) == 0 { + panic("no return value specified for AddConsumer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64, common.Address) (*types.Transaction, error)); ok { @@ -106,6 +118,10 @@ func (_m *VRFCoordinatorV2Interface) AddConsumer(opts *bind.TransactOpts, subId func (_m *VRFCoordinatorV2Interface) Address() common.Address { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 common.Address if rf, ok := ret.Get(0).(func() common.Address); ok { r0 = rf() @@ -122,6 +138,10 @@ func (_m *VRFCoordinatorV2Interface) Address() common.Address { func (_m *VRFCoordinatorV2Interface) BLOCKHASHSTORE(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for BLOCKHASHSTORE") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -148,6 +168,10 @@ func (_m *VRFCoordinatorV2Interface) BLOCKHASHSTORE(opts *bind.CallOpts) (common func (_m *VRFCoordinatorV2Interface) CancelSubscription(opts *bind.TransactOpts, subId uint64, to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, to) + if len(ret) == 0 { + panic("no return value specified for CancelSubscription") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64, common.Address) (*types.Transaction, error)); ok { @@ -174,6 +198,10 @@ func (_m *VRFCoordinatorV2Interface) CancelSubscription(opts *bind.TransactOpts, func (_m *VRFCoordinatorV2Interface) CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for CreateSubscription") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { @@ -200,6 +228,10 @@ func (_m *VRFCoordinatorV2Interface) CreateSubscription(opts *bind.TransactOpts) func (_m *VRFCoordinatorV2Interface) DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) { ret := _m.Called(opts, publicProvingKey) + if len(ret) == 0 { + panic("no return value specified for DeregisterProvingKey") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, [2]*big.Int) (*types.Transaction, error)); ok { @@ -226,6 +258,10 @@ func (_m *VRFCoordinatorV2Interface) DeregisterProvingKey(opts *bind.TransactOpt func (_m *VRFCoordinatorV2Interface) FilterConfigSet(opts *bind.FilterOpts) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSetIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterConfigSet") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2ConfigSetIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSetIterator, error)); ok { @@ -252,6 +288,10 @@ func (_m *VRFCoordinatorV2Interface) FilterConfigSet(opts *bind.FilterOpts) (*vr func (_m *VRFCoordinatorV2Interface) FilterFundsRecovered(opts *bind.FilterOpts) (*vrf_coordinator_v2.VRFCoordinatorV2FundsRecoveredIterator, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for FilterFundsRecovered") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2FundsRecoveredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*vrf_coordinator_v2.VRFCoordinatorV2FundsRecoveredIterator, error)); ok { @@ -278,6 +318,10 @@ func (_m *VRFCoordinatorV2Interface) FilterFundsRecovered(opts *bind.FilterOpts) func (_m *VRFCoordinatorV2Interface) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequestedIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferRequested") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequestedIterator, error)); ok { @@ -304,6 +348,10 @@ func (_m *VRFCoordinatorV2Interface) FilterOwnershipTransferRequested(opts *bind func (_m *VRFCoordinatorV2Interface) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferredIterator, error) { ret := _m.Called(opts, from, to) + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferred") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferredIterator, error)); ok { @@ -330,6 +378,10 @@ func (_m *VRFCoordinatorV2Interface) FilterOwnershipTransferred(opts *bind.Filte func (_m *VRFCoordinatorV2Interface) FilterProvingKeyDeregistered(opts *bind.FilterOpts, oracle []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregisteredIterator, error) { ret := _m.Called(opts, oracle) + if len(ret) == 0 { + panic("no return value specified for FilterProvingKeyDeregistered") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregisteredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregisteredIterator, error)); ok { @@ -356,6 +408,10 @@ func (_m *VRFCoordinatorV2Interface) FilterProvingKeyDeregistered(opts *bind.Fil func (_m *VRFCoordinatorV2Interface) FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegisteredIterator, error) { ret := _m.Called(opts, oracle) + if len(ret) == 0 { + panic("no return value specified for FilterProvingKeyRegistered") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegisteredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegisteredIterator, error)); ok { @@ -382,6 +438,10 @@ func (_m *VRFCoordinatorV2Interface) FilterProvingKeyRegistered(opts *bind.Filte func (_m *VRFCoordinatorV2Interface) FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilledIterator, error) { ret := _m.Called(opts, requestId) + if len(ret) == 0 { + panic("no return value specified for FilterRandomWordsFulfilled") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []*big.Int) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilledIterator, error)); ok { @@ -408,6 +468,10 @@ func (_m *VRFCoordinatorV2Interface) FilterRandomWordsFulfilled(opts *bind.Filte func (_m *VRFCoordinatorV2Interface) FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []uint64, sender []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequestedIterator, error) { ret := _m.Called(opts, keyHash, subId, sender) + if len(ret) == 0 { + panic("no return value specified for FilterRandomWordsRequested") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, [][32]byte, []uint64, []common.Address) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequestedIterator, error)); ok { @@ -434,6 +498,10 @@ func (_m *VRFCoordinatorV2Interface) FilterRandomWordsRequested(opts *bind.Filte func (_m *VRFCoordinatorV2Interface) FilterSubscriptionCanceled(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceledIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionCanceled") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceledIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceledIterator, error)); ok { @@ -460,6 +528,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionCanceled(opts *bind.Filte func (_m *VRFCoordinatorV2Interface) FilterSubscriptionConsumerAdded(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAddedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionConsumerAdded") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAddedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAddedIterator, error)); ok { @@ -486,6 +558,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionConsumerAdded(opts *bind. func (_m *VRFCoordinatorV2Interface) FilterSubscriptionConsumerRemoved(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemovedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionConsumerRemoved") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemovedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemovedIterator, error)); ok { @@ -512,6 +588,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionConsumerRemoved(opts *bin func (_m *VRFCoordinatorV2Interface) FilterSubscriptionCreated(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreatedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionCreated") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreatedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreatedIterator, error)); ok { @@ -538,6 +618,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionCreated(opts *bind.Filter func (_m *VRFCoordinatorV2Interface) FilterSubscriptionFunded(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFundedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionFunded") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFundedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFundedIterator, error)); ok { @@ -564,6 +648,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionFunded(opts *bind.FilterO func (_m *VRFCoordinatorV2Interface) FilterSubscriptionOwnerTransferRequested(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequestedIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionOwnerTransferRequested") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequestedIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequestedIterator, error)); ok { @@ -590,6 +678,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionOwnerTransferRequested(op func (_m *VRFCoordinatorV2Interface) FilterSubscriptionOwnerTransferred(opts *bind.FilterOpts, subId []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferredIterator, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for FilterSubscriptionOwnerTransferred") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferredIterator var r1 error if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferredIterator, error)); ok { @@ -616,6 +708,10 @@ func (_m *VRFCoordinatorV2Interface) FilterSubscriptionOwnerTransferred(opts *bi func (_m *VRFCoordinatorV2Interface) FulfillRandomWords(opts *bind.TransactOpts, proof vrf_coordinator_v2.VRFProof, rc vrf_coordinator_v2.VRFCoordinatorV2RequestCommitment) (*types.Transaction, error) { ret := _m.Called(opts, proof, rc) + if len(ret) == 0 { + panic("no return value specified for FulfillRandomWords") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, vrf_coordinator_v2.VRFProof, vrf_coordinator_v2.VRFCoordinatorV2RequestCommitment) (*types.Transaction, error)); ok { @@ -642,6 +738,10 @@ func (_m *VRFCoordinatorV2Interface) FulfillRandomWords(opts *bind.TransactOpts, func (_m *VRFCoordinatorV2Interface) GetCommitment(opts *bind.CallOpts, requestId *big.Int) ([32]byte, error) { ret := _m.Called(opts, requestId) + if len(ret) == 0 { + panic("no return value specified for GetCommitment") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, *big.Int) ([32]byte, error)); ok { @@ -668,6 +768,10 @@ func (_m *VRFCoordinatorV2Interface) GetCommitment(opts *bind.CallOpts, requestI func (_m *VRFCoordinatorV2Interface) GetConfig(opts *bind.CallOpts) (vrf_coordinator_v2.GetConfig, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetConfig") + } + var r0 vrf_coordinator_v2.GetConfig var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_coordinator_v2.GetConfig, error)); ok { @@ -692,6 +796,10 @@ func (_m *VRFCoordinatorV2Interface) GetConfig(opts *bind.CallOpts) (vrf_coordin func (_m *VRFCoordinatorV2Interface) GetCurrentSubId(opts *bind.CallOpts) (uint64, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetCurrentSubId") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint64, error)); ok { @@ -716,6 +824,10 @@ func (_m *VRFCoordinatorV2Interface) GetCurrentSubId(opts *bind.CallOpts) (uint6 func (_m *VRFCoordinatorV2Interface) GetFallbackWeiPerUnitLink(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetFallbackWeiPerUnitLink") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -742,6 +854,10 @@ func (_m *VRFCoordinatorV2Interface) GetFallbackWeiPerUnitLink(opts *bind.CallOp func (_m *VRFCoordinatorV2Interface) GetFeeConfig(opts *bind.CallOpts) (vrf_coordinator_v2.GetFeeConfig, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetFeeConfig") + } + var r0 vrf_coordinator_v2.GetFeeConfig var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (vrf_coordinator_v2.GetFeeConfig, error)); ok { @@ -766,6 +882,10 @@ func (_m *VRFCoordinatorV2Interface) GetFeeConfig(opts *bind.CallOpts) (vrf_coor func (_m *VRFCoordinatorV2Interface) GetFeeTier(opts *bind.CallOpts, reqCount uint64) (uint32, error) { ret := _m.Called(opts, reqCount) + if len(ret) == 0 { + panic("no return value specified for GetFeeTier") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) (uint32, error)); ok { @@ -790,6 +910,10 @@ func (_m *VRFCoordinatorV2Interface) GetFeeTier(opts *bind.CallOpts, reqCount ui func (_m *VRFCoordinatorV2Interface) GetRequestConfig(opts *bind.CallOpts) (uint16, uint32, [][32]byte, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetRequestConfig") + } + var r0 uint16 var r1 uint32 var r2 [][32]byte @@ -830,6 +954,10 @@ func (_m *VRFCoordinatorV2Interface) GetRequestConfig(opts *bind.CallOpts) (uint func (_m *VRFCoordinatorV2Interface) GetSubscription(opts *bind.CallOpts, subId uint64) (vrf_coordinator_v2.GetSubscription, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for GetSubscription") + } + var r0 vrf_coordinator_v2.GetSubscription var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) (vrf_coordinator_v2.GetSubscription, error)); ok { @@ -854,6 +982,10 @@ func (_m *VRFCoordinatorV2Interface) GetSubscription(opts *bind.CallOpts, subId func (_m *VRFCoordinatorV2Interface) GetTotalBalance(opts *bind.CallOpts) (*big.Int, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for GetTotalBalance") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (*big.Int, error)); ok { @@ -880,6 +1012,10 @@ func (_m *VRFCoordinatorV2Interface) GetTotalBalance(opts *bind.CallOpts) (*big. func (_m *VRFCoordinatorV2Interface) HashOfKey(opts *bind.CallOpts, publicKey [2]*big.Int) ([32]byte, error) { ret := _m.Called(opts, publicKey) + if len(ret) == 0 { + panic("no return value specified for HashOfKey") + } + var r0 [32]byte var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, [2]*big.Int) ([32]byte, error)); ok { @@ -906,6 +1042,10 @@ func (_m *VRFCoordinatorV2Interface) HashOfKey(opts *bind.CallOpts, publicKey [2 func (_m *VRFCoordinatorV2Interface) LINK(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LINK") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -932,6 +1072,10 @@ func (_m *VRFCoordinatorV2Interface) LINK(opts *bind.CallOpts) (common.Address, func (_m *VRFCoordinatorV2Interface) LINKETHFEED(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for LINKETHFEED") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -958,6 +1102,10 @@ func (_m *VRFCoordinatorV2Interface) LINKETHFEED(opts *bind.CallOpts) (common.Ad func (_m *VRFCoordinatorV2Interface) MAXCONSUMERS(opts *bind.CallOpts) (uint16, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MAXCONSUMERS") + } + var r0 uint16 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint16, error)); ok { @@ -982,6 +1130,10 @@ func (_m *VRFCoordinatorV2Interface) MAXCONSUMERS(opts *bind.CallOpts) (uint16, func (_m *VRFCoordinatorV2Interface) MAXNUMWORDS(opts *bind.CallOpts) (uint32, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MAXNUMWORDS") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint32, error)); ok { @@ -1006,6 +1158,10 @@ func (_m *VRFCoordinatorV2Interface) MAXNUMWORDS(opts *bind.CallOpts) (uint32, e func (_m *VRFCoordinatorV2Interface) MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts) (uint16, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for MAXREQUESTCONFIRMATIONS") + } + var r0 uint16 var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (uint16, error)); ok { @@ -1030,6 +1186,10 @@ func (_m *VRFCoordinatorV2Interface) MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts func (_m *VRFCoordinatorV2Interface) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { ret := _m.Called(opts, arg0, amount, data) + if len(ret) == 0 { + panic("no return value specified for OnTokenTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int, []byte) (*types.Transaction, error)); ok { @@ -1056,6 +1216,10 @@ func (_m *VRFCoordinatorV2Interface) OnTokenTransfer(opts *bind.TransactOpts, ar func (_m *VRFCoordinatorV2Interface) OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { ret := _m.Called(opts, recipient, amount) + if len(ret) == 0 { + panic("no return value specified for OracleWithdraw") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, *big.Int) (*types.Transaction, error)); ok { @@ -1082,6 +1246,10 @@ func (_m *VRFCoordinatorV2Interface) OracleWithdraw(opts *bind.TransactOpts, rec func (_m *VRFCoordinatorV2Interface) Owner(opts *bind.CallOpts) (common.Address, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for Owner") + } + var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { @@ -1108,6 +1276,10 @@ func (_m *VRFCoordinatorV2Interface) Owner(opts *bind.CallOpts) (common.Address, func (_m *VRFCoordinatorV2Interface) OwnerCancelSubscription(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for OwnerCancelSubscription") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64) (*types.Transaction, error)); ok { @@ -1134,6 +1306,10 @@ func (_m *VRFCoordinatorV2Interface) OwnerCancelSubscription(opts *bind.Transact func (_m *VRFCoordinatorV2Interface) ParseConfigSet(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseConfigSet") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2ConfigSet var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, error)); ok { @@ -1160,6 +1336,10 @@ func (_m *VRFCoordinatorV2Interface) ParseConfigSet(log types.Log) (*vrf_coordin func (_m *VRFCoordinatorV2Interface) ParseFundsRecovered(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2FundsRecovered, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseFundsRecovered") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2FundsRecovered var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2FundsRecovered, error)); ok { @@ -1186,6 +1366,10 @@ func (_m *VRFCoordinatorV2Interface) ParseFundsRecovered(log types.Log) (*vrf_co func (_m *VRFCoordinatorV2Interface) ParseLog(log types.Log) (generated.AbigenLog, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + var r0 generated.AbigenLog var r1 error if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { @@ -1212,6 +1396,10 @@ func (_m *VRFCoordinatorV2Interface) ParseLog(log types.Log) (generated.AbigenLo func (_m *VRFCoordinatorV2Interface) ParseOwnershipTransferRequested(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferRequested") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequested, error)); ok { @@ -1238,6 +1426,10 @@ func (_m *VRFCoordinatorV2Interface) ParseOwnershipTransferRequested(log types.L func (_m *VRFCoordinatorV2Interface) ParseOwnershipTransferred(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferred") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferred, error)); ok { @@ -1264,6 +1456,10 @@ func (_m *VRFCoordinatorV2Interface) ParseOwnershipTransferred(log types.Log) (* func (_m *VRFCoordinatorV2Interface) ParseProvingKeyDeregistered(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregistered, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseProvingKeyDeregistered") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregistered var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregistered, error)); ok { @@ -1290,6 +1486,10 @@ func (_m *VRFCoordinatorV2Interface) ParseProvingKeyDeregistered(log types.Log) func (_m *VRFCoordinatorV2Interface) ParseProvingKeyRegistered(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegistered, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseProvingKeyRegistered") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegistered var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegistered, error)); ok { @@ -1316,6 +1516,10 @@ func (_m *VRFCoordinatorV2Interface) ParseProvingKeyRegistered(log types.Log) (* func (_m *VRFCoordinatorV2Interface) ParseRandomWordsFulfilled(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomWordsFulfilled") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error)); ok { @@ -1342,6 +1546,10 @@ func (_m *VRFCoordinatorV2Interface) ParseRandomWordsFulfilled(log types.Log) (* func (_m *VRFCoordinatorV2Interface) ParseRandomWordsRequested(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseRandomWordsRequested") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error)); ok { @@ -1368,6 +1576,10 @@ func (_m *VRFCoordinatorV2Interface) ParseRandomWordsRequested(log types.Log) (* func (_m *VRFCoordinatorV2Interface) ParseSubscriptionCanceled(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionCanceled") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error)); ok { @@ -1394,6 +1606,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionCanceled(log types.Log) (* func (_m *VRFCoordinatorV2Interface) ParseSubscriptionConsumerAdded(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionConsumerAdded") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded, error)); ok { @@ -1420,6 +1636,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionConsumerAdded(log types.Lo func (_m *VRFCoordinatorV2Interface) ParseSubscriptionConsumerRemoved(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionConsumerRemoved") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved, error)); ok { @@ -1446,6 +1666,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionConsumerRemoved(log types. func (_m *VRFCoordinatorV2Interface) ParseSubscriptionCreated(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionCreated") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated, error)); ok { @@ -1472,6 +1696,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionCreated(log types.Log) (*v func (_m *VRFCoordinatorV2Interface) ParseSubscriptionFunded(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionFunded") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded, error)); ok { @@ -1498,6 +1726,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionFunded(log types.Log) (*vr func (_m *VRFCoordinatorV2Interface) ParseSubscriptionOwnerTransferRequested(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequested, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionOwnerTransferRequested") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequested var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequested, error)); ok { @@ -1524,6 +1756,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionOwnerTransferRequested(log func (_m *VRFCoordinatorV2Interface) ParseSubscriptionOwnerTransferred(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferred, error) { ret := _m.Called(log) + if len(ret) == 0 { + panic("no return value specified for ParseSubscriptionOwnerTransferred") + } + var r0 *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferred var r1 error if rf, ok := ret.Get(0).(func(types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferred, error)); ok { @@ -1550,6 +1786,10 @@ func (_m *VRFCoordinatorV2Interface) ParseSubscriptionOwnerTransferred(log types func (_m *VRFCoordinatorV2Interface) PendingRequestExists(opts *bind.CallOpts, subId uint64) (bool, error) { ret := _m.Called(opts, subId) + if len(ret) == 0 { + panic("no return value specified for PendingRequestExists") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) (bool, error)); ok { @@ -1574,6 +1814,10 @@ func (_m *VRFCoordinatorV2Interface) PendingRequestExists(opts *bind.CallOpts, s func (_m *VRFCoordinatorV2Interface) RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, to) + if len(ret) == 0 { + panic("no return value specified for RecoverFunds") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1600,6 +1844,10 @@ func (_m *VRFCoordinatorV2Interface) RecoverFunds(opts *bind.TransactOpts, to co func (_m *VRFCoordinatorV2Interface) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { ret := _m.Called(opts, oracle, publicProvingKey) + if len(ret) == 0 { + panic("no return value specified for RegisterProvingKey") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address, [2]*big.Int) (*types.Transaction, error)); ok { @@ -1626,6 +1874,10 @@ func (_m *VRFCoordinatorV2Interface) RegisterProvingKey(opts *bind.TransactOpts, func (_m *VRFCoordinatorV2Interface) RemoveConsumer(opts *bind.TransactOpts, subId uint64, consumer common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, consumer) + if len(ret) == 0 { + panic("no return value specified for RemoveConsumer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64, common.Address) (*types.Transaction, error)); ok { @@ -1652,6 +1904,10 @@ func (_m *VRFCoordinatorV2Interface) RemoveConsumer(opts *bind.TransactOpts, sub func (_m *VRFCoordinatorV2Interface) RequestRandomWords(opts *bind.TransactOpts, keyHash [32]byte, subId uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32) (*types.Transaction, error) { ret := _m.Called(opts, keyHash, subId, requestConfirmations, callbackGasLimit, numWords) + if len(ret) == 0 { + panic("no return value specified for RequestRandomWords") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, [32]byte, uint64, uint16, uint32, uint32) (*types.Transaction, error)); ok { @@ -1678,6 +1934,10 @@ func (_m *VRFCoordinatorV2Interface) RequestRandomWords(opts *bind.TransactOpts, func (_m *VRFCoordinatorV2Interface) RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64, newOwner common.Address) (*types.Transaction, error) { ret := _m.Called(opts, subId, newOwner) + if len(ret) == 0 { + panic("no return value specified for RequestSubscriptionOwnerTransfer") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint64, common.Address) (*types.Transaction, error)); ok { @@ -1704,6 +1964,10 @@ func (_m *VRFCoordinatorV2Interface) RequestSubscriptionOwnerTransfer(opts *bind func (_m *VRFCoordinatorV2Interface) SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig vrf_coordinator_v2.VRFCoordinatorV2FeeConfig) (*types.Transaction, error) { ret := _m.Called(opts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) + if len(ret) == 0 { + panic("no return value specified for SetConfig") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, uint16, uint32, uint32, uint32, *big.Int, vrf_coordinator_v2.VRFCoordinatorV2FeeConfig) (*types.Transaction, error)); ok { @@ -1730,6 +1994,10 @@ func (_m *VRFCoordinatorV2Interface) SetConfig(opts *bind.TransactOpts, minimumR func (_m *VRFCoordinatorV2Interface) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { ret := _m.Called(opts, to) + if len(ret) == 0 { + panic("no return value specified for TransferOwnership") + } + var r0 *types.Transaction var r1 error if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { @@ -1756,6 +2024,10 @@ func (_m *VRFCoordinatorV2Interface) TransferOwnership(opts *bind.TransactOpts, func (_m *VRFCoordinatorV2Interface) TypeAndVersion(opts *bind.CallOpts) (string, error) { ret := _m.Called(opts) + if len(ret) == 0 { + panic("no return value specified for TypeAndVersion") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(*bind.CallOpts) (string, error)); ok { @@ -1780,6 +2052,10 @@ func (_m *VRFCoordinatorV2Interface) TypeAndVersion(opts *bind.CallOpts) (string func (_m *VRFCoordinatorV2Interface) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2ConfigSet) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchConfigSet") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2ConfigSet) (event.Subscription, error)); ok { @@ -1806,6 +2082,10 @@ func (_m *VRFCoordinatorV2Interface) WatchConfigSet(opts *bind.WatchOpts, sink c func (_m *VRFCoordinatorV2Interface) WatchFundsRecovered(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2FundsRecovered) (event.Subscription, error) { ret := _m.Called(opts, sink) + if len(ret) == 0 { + panic("no return value specified for WatchFundsRecovered") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2FundsRecovered) (event.Subscription, error)); ok { @@ -1832,6 +2112,10 @@ func (_m *VRFCoordinatorV2Interface) WatchFundsRecovered(opts *bind.WatchOpts, s func (_m *VRFCoordinatorV2Interface) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1858,6 +2142,10 @@ func (_m *VRFCoordinatorV2Interface) WatchOwnershipTransferRequested(opts *bind. func (_m *VRFCoordinatorV2Interface) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, from, to) + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2OwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)); ok { @@ -1884,6 +2172,10 @@ func (_m *VRFCoordinatorV2Interface) WatchOwnershipTransferred(opts *bind.WatchO func (_m *VRFCoordinatorV2Interface) WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregistered, oracle []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, oracle) + if len(ret) == 0 { + panic("no return value specified for WatchProvingKeyDeregistered") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyDeregistered, []common.Address) (event.Subscription, error)); ok { @@ -1910,6 +2202,10 @@ func (_m *VRFCoordinatorV2Interface) WatchProvingKeyDeregistered(opts *bind.Watc func (_m *VRFCoordinatorV2Interface) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, oracle) + if len(ret) == 0 { + panic("no return value specified for WatchProvingKeyRegistered") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2ProvingKeyRegistered, []common.Address) (event.Subscription, error)); ok { @@ -1936,6 +2232,10 @@ func (_m *VRFCoordinatorV2Interface) WatchProvingKeyRegistered(opts *bind.WatchO func (_m *VRFCoordinatorV2Interface) WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, requestId []*big.Int) (event.Subscription, error) { ret := _m.Called(opts, sink, requestId) + if len(ret) == 0 { + panic("no return value specified for WatchRandomWordsFulfilled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, []*big.Int) (event.Subscription, error)); ok { @@ -1962,6 +2262,10 @@ func (_m *VRFCoordinatorV2Interface) WatchRandomWordsFulfilled(opts *bind.WatchO func (_m *VRFCoordinatorV2Interface) WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, keyHash [][32]byte, subId []uint64, sender []common.Address) (event.Subscription, error) { ret := _m.Called(opts, sink, keyHash, subId, sender) + if len(ret) == 0 { + panic("no return value specified for WatchRandomWordsRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, [][32]byte, []uint64, []common.Address) (event.Subscription, error)); ok { @@ -1988,6 +2292,10 @@ func (_m *VRFCoordinatorV2Interface) WatchRandomWordsRequested(opts *bind.WatchO func (_m *VRFCoordinatorV2Interface) WatchSubscriptionCanceled(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionCanceled") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, []uint64) (event.Subscription, error)); ok { @@ -2014,6 +2322,10 @@ func (_m *VRFCoordinatorV2Interface) WatchSubscriptionCanceled(opts *bind.WatchO func (_m *VRFCoordinatorV2Interface) WatchSubscriptionConsumerAdded(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionConsumerAdded") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded, []uint64) (event.Subscription, error)); ok { @@ -2040,6 +2352,10 @@ func (_m *VRFCoordinatorV2Interface) WatchSubscriptionConsumerAdded(opts *bind.W func (_m *VRFCoordinatorV2Interface) WatchSubscriptionConsumerRemoved(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionConsumerRemoved") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved, []uint64) (event.Subscription, error)); ok { @@ -2066,6 +2382,10 @@ func (_m *VRFCoordinatorV2Interface) WatchSubscriptionConsumerRemoved(opts *bind func (_m *VRFCoordinatorV2Interface) WatchSubscriptionCreated(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionCreated") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated, []uint64) (event.Subscription, error)); ok { @@ -2092,6 +2412,10 @@ func (_m *VRFCoordinatorV2Interface) WatchSubscriptionCreated(opts *bind.WatchOp func (_m *VRFCoordinatorV2Interface) WatchSubscriptionFunded(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionFunded") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded, []uint64) (event.Subscription, error)); ok { @@ -2118,6 +2442,10 @@ func (_m *VRFCoordinatorV2Interface) WatchSubscriptionFunded(opts *bind.WatchOpt func (_m *VRFCoordinatorV2Interface) WatchSubscriptionOwnerTransferRequested(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequested, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionOwnerTransferRequested") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferRequested, []uint64) (event.Subscription, error)); ok { @@ -2144,6 +2472,10 @@ func (_m *VRFCoordinatorV2Interface) WatchSubscriptionOwnerTransferRequested(opt func (_m *VRFCoordinatorV2Interface) WatchSubscriptionOwnerTransferred(opts *bind.WatchOpts, sink chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferred, subId []uint64) (event.Subscription, error) { ret := _m.Called(opts, sink, subId) + if len(ret) == 0 { + panic("no return value specified for WatchSubscriptionOwnerTransferred") + } + var r0 event.Subscription var r1 error if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionOwnerTransferred, []uint64) (event.Subscription, error)); ok { diff --git a/core/services/webhook/mocks/external_initiator_manager.go b/core/services/webhook/mocks/external_initiator_manager.go index a94f2ffe97d..6c061f5412d 100644 --- a/core/services/webhook/mocks/external_initiator_manager.go +++ b/core/services/webhook/mocks/external_initiator_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type ExternalInitiatorManager struct { func (_m *ExternalInitiatorManager) DeleteJob(webhookSpecID int32) error { ret := _m.Called(webhookSpecID) + if len(ret) == 0 { + panic("no return value specified for DeleteJob") + } + var r0 error if rf, ok := ret.Get(0).(func(int32) error); ok { r0 = rf(webhookSpecID) @@ -30,6 +34,10 @@ func (_m *ExternalInitiatorManager) DeleteJob(webhookSpecID int32) error { func (_m *ExternalInitiatorManager) FindExternalInitiatorByName(name string) (bridges.ExternalInitiator, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for FindExternalInitiatorByName") + } + var r0 bridges.ExternalInitiator var r1 error if rf, ok := ret.Get(0).(func(string) (bridges.ExternalInitiator, error)); ok { @@ -54,6 +62,10 @@ func (_m *ExternalInitiatorManager) FindExternalInitiatorByName(name string) (br func (_m *ExternalInitiatorManager) Notify(webhookSpecID int32) error { ret := _m.Called(webhookSpecID) + if len(ret) == 0 { + panic("no return value specified for Notify") + } + var r0 error if rf, ok := ret.Get(0).(func(int32) error); ok { r0 = rf(webhookSpecID) diff --git a/core/services/webhook/mocks/http_client.go b/core/services/webhook/mocks/http_client.go index b5b448a56d0..fa4f597dc4f 100644 --- a/core/services/webhook/mocks/http_client.go +++ b/core/services/webhook/mocks/http_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type HTTPClient struct { func (_m *HTTPClient) Do(req *http.Request) (*http.Response, error) { ret := _m.Called(req) + if len(ret) == 0 { + panic("no return value specified for Do") + } + var r0 *http.Response var r1 error if rf, ok := ret.Get(0).(func(*http.Request) (*http.Response, error)); ok { diff --git a/core/sessions/ldapauth/mocks/ldap_client.go b/core/sessions/ldapauth/mocks/ldap_client.go index 7a44778dcaa..63021636018 100644 --- a/core/sessions/ldapauth/mocks/ldap_client.go +++ b/core/sessions/ldapauth/mocks/ldap_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type LDAPClient struct { func (_m *LDAPClient) CreateEphemeralConnection() (ldapauth.LDAPConn, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CreateEphemeralConnection") + } + var r0 ldapauth.LDAPConn var r1 error if rf, ok := ret.Get(0).(func() (ldapauth.LDAPConn, error)); ok { diff --git a/core/sessions/ldapauth/mocks/ldap_conn.go b/core/sessions/ldapauth/mocks/ldap_conn.go index c05fb6c4fa6..8b4fff82047 100644 --- a/core/sessions/ldapauth/mocks/ldap_conn.go +++ b/core/sessions/ldapauth/mocks/ldap_conn.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type LDAPConn struct { func (_m *LDAPConn) Bind(username string, password string) error { ret := _m.Called(username, password) + if len(ret) == 0 { + panic("no return value specified for Bind") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(username, password) @@ -31,6 +35,10 @@ func (_m *LDAPConn) Bind(username string, password string) error { func (_m *LDAPConn) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -45,6 +53,10 @@ func (_m *LDAPConn) Close() error { func (_m *LDAPConn) Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) { ret := _m.Called(searchRequest) + if len(ret) == 0 { + panic("no return value specified for Search") + } + var r0 *ldap.SearchResult var r1 error if rf, ok := ret.Get(0).(func(*ldap.SearchRequest) (*ldap.SearchResult, error)); ok { diff --git a/core/sessions/mocks/authentication_provider.go b/core/sessions/mocks/authentication_provider.go index d6e33d11e45..d1b846c318b 100644 --- a/core/sessions/mocks/authentication_provider.go +++ b/core/sessions/mocks/authentication_provider.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type AuthenticationProvider struct { func (_m *AuthenticationProvider) AuthorizedUserWithSession(sessionID string) (sessions.User, error) { ret := _m.Called(sessionID) + if len(ret) == 0 { + panic("no return value specified for AuthorizedUserWithSession") + } + var r0 sessions.User var r1 error if rf, ok := ret.Get(0).(func(string) (sessions.User, error)); ok { @@ -44,6 +48,10 @@ func (_m *AuthenticationProvider) AuthorizedUserWithSession(sessionID string) (s func (_m *AuthenticationProvider) ClearNonCurrentSessions(sessionID string) error { ret := _m.Called(sessionID) + if len(ret) == 0 { + panic("no return value specified for ClearNonCurrentSessions") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(sessionID) @@ -58,6 +66,10 @@ func (_m *AuthenticationProvider) ClearNonCurrentSessions(sessionID string) erro func (_m *AuthenticationProvider) CreateAndSetAuthToken(user *sessions.User) (*auth.Token, error) { ret := _m.Called(user) + if len(ret) == 0 { + panic("no return value specified for CreateAndSetAuthToken") + } + var r0 *auth.Token var r1 error if rf, ok := ret.Get(0).(func(*sessions.User) (*auth.Token, error)); ok { @@ -84,6 +96,10 @@ func (_m *AuthenticationProvider) CreateAndSetAuthToken(user *sessions.User) (*a func (_m *AuthenticationProvider) CreateSession(sr sessions.SessionRequest) (string, error) { ret := _m.Called(sr) + if len(ret) == 0 { + panic("no return value specified for CreateSession") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(sessions.SessionRequest) (string, error)); ok { @@ -108,6 +124,10 @@ func (_m *AuthenticationProvider) CreateSession(sr sessions.SessionRequest) (str func (_m *AuthenticationProvider) CreateUser(user *sessions.User) error { ret := _m.Called(user) + if len(ret) == 0 { + panic("no return value specified for CreateUser") + } + var r0 error if rf, ok := ret.Get(0).(func(*sessions.User) error); ok { r0 = rf(user) @@ -122,6 +142,10 @@ func (_m *AuthenticationProvider) CreateUser(user *sessions.User) error { func (_m *AuthenticationProvider) DeleteAuthToken(user *sessions.User) error { ret := _m.Called(user) + if len(ret) == 0 { + panic("no return value specified for DeleteAuthToken") + } + var r0 error if rf, ok := ret.Get(0).(func(*sessions.User) error); ok { r0 = rf(user) @@ -136,6 +160,10 @@ func (_m *AuthenticationProvider) DeleteAuthToken(user *sessions.User) error { func (_m *AuthenticationProvider) DeleteUser(email string) error { ret := _m.Called(email) + if len(ret) == 0 { + panic("no return value specified for DeleteUser") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(email) @@ -150,6 +178,10 @@ func (_m *AuthenticationProvider) DeleteUser(email string) error { func (_m *AuthenticationProvider) DeleteUserSession(sessionID string) error { ret := _m.Called(sessionID) + if len(ret) == 0 { + panic("no return value specified for DeleteUserSession") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(sessionID) @@ -164,6 +196,10 @@ func (_m *AuthenticationProvider) DeleteUserSession(sessionID string) error { func (_m *AuthenticationProvider) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiator, error) { ret := _m.Called(eia) + if len(ret) == 0 { + panic("no return value specified for FindExternalInitiator") + } + var r0 *bridges.ExternalInitiator var r1 error if rf, ok := ret.Get(0).(func(*auth.Token) (*bridges.ExternalInitiator, error)); ok { @@ -190,6 +226,10 @@ func (_m *AuthenticationProvider) FindExternalInitiator(eia *auth.Token) (*bridg func (_m *AuthenticationProvider) FindUser(email string) (sessions.User, error) { ret := _m.Called(email) + if len(ret) == 0 { + panic("no return value specified for FindUser") + } + var r0 sessions.User var r1 error if rf, ok := ret.Get(0).(func(string) (sessions.User, error)); ok { @@ -214,6 +254,10 @@ func (_m *AuthenticationProvider) FindUser(email string) (sessions.User, error) func (_m *AuthenticationProvider) FindUserByAPIToken(apiToken string) (sessions.User, error) { ret := _m.Called(apiToken) + if len(ret) == 0 { + panic("no return value specified for FindUserByAPIToken") + } + var r0 sessions.User var r1 error if rf, ok := ret.Get(0).(func(string) (sessions.User, error)); ok { @@ -238,6 +282,10 @@ func (_m *AuthenticationProvider) FindUserByAPIToken(apiToken string) (sessions. func (_m *AuthenticationProvider) GetUserWebAuthn(email string) ([]sessions.WebAuthn, error) { ret := _m.Called(email) + if len(ret) == 0 { + panic("no return value specified for GetUserWebAuthn") + } + var r0 []sessions.WebAuthn var r1 error if rf, ok := ret.Get(0).(func(string) ([]sessions.WebAuthn, error)); ok { @@ -264,6 +312,10 @@ func (_m *AuthenticationProvider) GetUserWebAuthn(email string) ([]sessions.WebA func (_m *AuthenticationProvider) ListUsers() ([]sessions.User, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ListUsers") + } + var r0 []sessions.User var r1 error if rf, ok := ret.Get(0).(func() ([]sessions.User, error)); ok { @@ -290,6 +342,10 @@ func (_m *AuthenticationProvider) ListUsers() ([]sessions.User, error) { func (_m *AuthenticationProvider) SaveWebAuthn(token *sessions.WebAuthn) error { ret := _m.Called(token) + if len(ret) == 0 { + panic("no return value specified for SaveWebAuthn") + } + var r0 error if rf, ok := ret.Get(0).(func(*sessions.WebAuthn) error); ok { r0 = rf(token) @@ -304,6 +360,10 @@ func (_m *AuthenticationProvider) SaveWebAuthn(token *sessions.WebAuthn) error { func (_m *AuthenticationProvider) Sessions(offset int, limit int) ([]sessions.Session, error) { ret := _m.Called(offset, limit) + if len(ret) == 0 { + panic("no return value specified for Sessions") + } + var r0 []sessions.Session var r1 error if rf, ok := ret.Get(0).(func(int, int) ([]sessions.Session, error)); ok { @@ -330,6 +390,10 @@ func (_m *AuthenticationProvider) Sessions(offset int, limit int) ([]sessions.Se func (_m *AuthenticationProvider) SetAuthToken(user *sessions.User, token *auth.Token) error { ret := _m.Called(user, token) + if len(ret) == 0 { + panic("no return value specified for SetAuthToken") + } + var r0 error if rf, ok := ret.Get(0).(func(*sessions.User, *auth.Token) error); ok { r0 = rf(user, token) @@ -344,6 +408,10 @@ func (_m *AuthenticationProvider) SetAuthToken(user *sessions.User, token *auth. func (_m *AuthenticationProvider) SetPassword(user *sessions.User, newPassword string) error { ret := _m.Called(user, newPassword) + if len(ret) == 0 { + panic("no return value specified for SetPassword") + } + var r0 error if rf, ok := ret.Get(0).(func(*sessions.User, string) error); ok { r0 = rf(user, newPassword) @@ -358,6 +426,10 @@ func (_m *AuthenticationProvider) SetPassword(user *sessions.User, newPassword s func (_m *AuthenticationProvider) TestPassword(email string, password string) error { ret := _m.Called(email, password) + if len(ret) == 0 { + panic("no return value specified for TestPassword") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(email, password) @@ -372,6 +444,10 @@ func (_m *AuthenticationProvider) TestPassword(email string, password string) er func (_m *AuthenticationProvider) UpdateRole(email string, newRole string) (sessions.User, error) { ret := _m.Called(email, newRole) + if len(ret) == 0 { + panic("no return value specified for UpdateRole") + } + var r0 sessions.User var r1 error if rf, ok := ret.Get(0).(func(string, string) (sessions.User, error)); ok { diff --git a/core/sessions/mocks/basic_admin_users_orm.go b/core/sessions/mocks/basic_admin_users_orm.go index 845e2d8880e..44ee0b1f705 100644 --- a/core/sessions/mocks/basic_admin_users_orm.go +++ b/core/sessions/mocks/basic_admin_users_orm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type BasicAdminUsersORM struct { func (_m *BasicAdminUsersORM) CreateUser(user *sessions.User) error { ret := _m.Called(user) + if len(ret) == 0 { + panic("no return value specified for CreateUser") + } + var r0 error if rf, ok := ret.Get(0).(func(*sessions.User) error); ok { r0 = rf(user) @@ -30,6 +34,10 @@ func (_m *BasicAdminUsersORM) CreateUser(user *sessions.User) error { func (_m *BasicAdminUsersORM) FindUser(email string) (sessions.User, error) { ret := _m.Called(email) + if len(ret) == 0 { + panic("no return value specified for FindUser") + } + var r0 sessions.User var r1 error if rf, ok := ret.Get(0).(func(string) (sessions.User, error)); ok { @@ -54,6 +62,10 @@ func (_m *BasicAdminUsersORM) FindUser(email string) (sessions.User, error) { func (_m *BasicAdminUsersORM) ListUsers() ([]sessions.User, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ListUsers") + } + var r0 []sessions.User var r1 error if rf, ok := ret.Get(0).(func() ([]sessions.User, error)); ok { From 5a98ac61aaf6e926d4c653543211f5c355f118ed Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:34:17 +0000 Subject: [PATCH 023/234] Test VRF LogPoller functionality- initializeLastProcessedBlock, updateLastProcessedBlock and getUnfulfilled (#11472) * Test VRF LogPoller functionality- initializeLastProcessedBlock, updateLastProcessedBlock and getUnfulfilled * Addressed PR comments * Minor change * Add comments to explain replay * Addressed PR comments * Prettier --- .../scripts/native_solc_compile_all_logpoller | 3 +- contracts/src/v0.8/tests/VRFLogEmitter.sol | 42 + .../vrf_log_emitter/vrf_log_emitter.go | 526 +++++++++ ...rapper-dependency-versions-do-not-edit.txt | 1 + core/gethwrappers/go_generate_logpoller.go | 7 + .../vrf/v2/listener_v2_log_listener_test.go | 1039 +++++++++++++++++ 6 files changed, 1617 insertions(+), 1 deletion(-) create mode 100644 contracts/src/v0.8/tests/VRFLogEmitter.sol create mode 100644 core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go create mode 100644 core/gethwrappers/go_generate_logpoller.go create mode 100644 core/services/vrf/v2/listener_v2_log_listener_test.go diff --git a/contracts/scripts/native_solc_compile_all_logpoller b/contracts/scripts/native_solc_compile_all_logpoller index b6ac51ecedb..e8ea2a2be80 100755 --- a/contracts/scripts/native_solc_compile_all_logpoller +++ b/contracts/scripts/native_solc_compile_all_logpoller @@ -29,4 +29,5 @@ compileContract () { } -compileContract tests/LogEmitter.sol \ No newline at end of file +compileContract tests/LogEmitter.sol +compileContract tests/VRFLogEmitter.sol \ No newline at end of file diff --git a/contracts/src/v0.8/tests/VRFLogEmitter.sol b/contracts/src/v0.8/tests/VRFLogEmitter.sol new file mode 100644 index 00000000000..18b99605ac9 --- /dev/null +++ b/contracts/src/v0.8/tests/VRFLogEmitter.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract VRFLogEmitter { + event RandomWordsRequested( + bytes32 indexed keyHash, + uint256 requestId, + uint256 preSeed, + uint64 indexed subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + address indexed sender + ); + event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); + + function emitRandomWordsRequested( + bytes32 keyHash, + uint256 requestId, + uint256 preSeed, + uint64 subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + address sender + ) public { + emit RandomWordsRequested( + keyHash, + requestId, + preSeed, + subId, + minimumRequestConfirmations, + callbackGasLimit, + numWords, + sender + ); + } + + function emitRandomWordsFulfilled(uint256 requestId, uint256 outputSeed, uint96 payment, bool success) public { + emit RandomWordsFulfilled(requestId, outputSeed, payment, success); + } +} diff --git a/core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go b/core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go new file mode 100644 index 00000000000..2cdeaa6c3a8 --- /dev/null +++ b/core/gethwrappers/generated/vrf_log_emitter/vrf_log_emitter.go @@ -0,0 +1,526 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package vrf_log_emitter + +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 +) + +var VRFLogEmitterMetaData = &bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"emitRandomWordsFulfilled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"emitRandomWordsRequested\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5061027f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063ca920adb1461003b578063fe62d3e914610050575b600080fd5b61004e61004936600461015b565b610063565b005b61004e61005e366004610212565b6100eb565b604080518881526020810188905261ffff86168183015263ffffffff858116606083015284166080820152905173ffffffffffffffffffffffffffffffffffffffff83169167ffffffffffffffff8816918b917f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772919081900360a00190a45050505050505050565b604080518481526bffffffffffffffffffffffff8416602082015282151581830152905185917f7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4919081900360600190a250505050565b803563ffffffff8116811461015657600080fd5b919050565b600080600080600080600080610100898b03121561017857600080fd5b883597506020890135965060408901359550606089013567ffffffffffffffff811681146101a557600080fd5b9450608089013561ffff811681146101bc57600080fd5b93506101ca60a08a01610142565b92506101d860c08a01610142565b915060e089013573ffffffffffffffffffffffffffffffffffffffff8116811461020157600080fd5b809150509295985092959890939650565b6000806000806080858703121561022857600080fd5b843593506020850135925060408501356bffffffffffffffffffffffff8116811461025257600080fd5b91506060850135801515811461026757600080fd5b93969295509093505056fea164736f6c6343000813000a", +} + +var VRFLogEmitterABI = VRFLogEmitterMetaData.ABI + +var VRFLogEmitterBin = VRFLogEmitterMetaData.Bin + +func DeployVRFLogEmitter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *VRFLogEmitter, error) { + parsed, err := VRFLogEmitterMetaData.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(VRFLogEmitterBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &VRFLogEmitter{address: address, abi: *parsed, VRFLogEmitterCaller: VRFLogEmitterCaller{contract: contract}, VRFLogEmitterTransactor: VRFLogEmitterTransactor{contract: contract}, VRFLogEmitterFilterer: VRFLogEmitterFilterer{contract: contract}}, nil +} + +type VRFLogEmitter struct { + address common.Address + abi abi.ABI + VRFLogEmitterCaller + VRFLogEmitterTransactor + VRFLogEmitterFilterer +} + +type VRFLogEmitterCaller struct { + contract *bind.BoundContract +} + +type VRFLogEmitterTransactor struct { + contract *bind.BoundContract +} + +type VRFLogEmitterFilterer struct { + contract *bind.BoundContract +} + +type VRFLogEmitterSession struct { + Contract *VRFLogEmitter + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type VRFLogEmitterCallerSession struct { + Contract *VRFLogEmitterCaller + CallOpts bind.CallOpts +} + +type VRFLogEmitterTransactorSession struct { + Contract *VRFLogEmitterTransactor + TransactOpts bind.TransactOpts +} + +type VRFLogEmitterRaw struct { + Contract *VRFLogEmitter +} + +type VRFLogEmitterCallerRaw struct { + Contract *VRFLogEmitterCaller +} + +type VRFLogEmitterTransactorRaw struct { + Contract *VRFLogEmitterTransactor +} + +func NewVRFLogEmitter(address common.Address, backend bind.ContractBackend) (*VRFLogEmitter, error) { + abi, err := abi.JSON(strings.NewReader(VRFLogEmitterABI)) + if err != nil { + return nil, err + } + contract, err := bindVRFLogEmitter(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &VRFLogEmitter{address: address, abi: abi, VRFLogEmitterCaller: VRFLogEmitterCaller{contract: contract}, VRFLogEmitterTransactor: VRFLogEmitterTransactor{contract: contract}, VRFLogEmitterFilterer: VRFLogEmitterFilterer{contract: contract}}, nil +} + +func NewVRFLogEmitterCaller(address common.Address, caller bind.ContractCaller) (*VRFLogEmitterCaller, error) { + contract, err := bindVRFLogEmitter(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &VRFLogEmitterCaller{contract: contract}, nil +} + +func NewVRFLogEmitterTransactor(address common.Address, transactor bind.ContractTransactor) (*VRFLogEmitterTransactor, error) { + contract, err := bindVRFLogEmitter(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &VRFLogEmitterTransactor{contract: contract}, nil +} + +func NewVRFLogEmitterFilterer(address common.Address, filterer bind.ContractFilterer) (*VRFLogEmitterFilterer, error) { + contract, err := bindVRFLogEmitter(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &VRFLogEmitterFilterer{contract: contract}, nil +} + +func bindVRFLogEmitter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := VRFLogEmitterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_VRFLogEmitter *VRFLogEmitterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFLogEmitter.Contract.VRFLogEmitterCaller.contract.Call(opts, result, method, params...) +} + +func (_VRFLogEmitter *VRFLogEmitterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.VRFLogEmitterTransactor.contract.Transfer(opts) +} + +func (_VRFLogEmitter *VRFLogEmitterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.VRFLogEmitterTransactor.contract.Transact(opts, method, params...) +} + +func (_VRFLogEmitter *VRFLogEmitterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFLogEmitter.Contract.contract.Call(opts, result, method, params...) +} + +func (_VRFLogEmitter *VRFLogEmitterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.contract.Transfer(opts) +} + +func (_VRFLogEmitter *VRFLogEmitterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.contract.Transact(opts, method, params...) +} + +func (_VRFLogEmitter *VRFLogEmitterTransactor) EmitRandomWordsFulfilled(opts *bind.TransactOpts, requestId *big.Int, outputSeed *big.Int, payment *big.Int, success bool) (*types.Transaction, error) { + return _VRFLogEmitter.contract.Transact(opts, "emitRandomWordsFulfilled", requestId, outputSeed, payment, success) +} + +func (_VRFLogEmitter *VRFLogEmitterSession) EmitRandomWordsFulfilled(requestId *big.Int, outputSeed *big.Int, payment *big.Int, success bool) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.EmitRandomWordsFulfilled(&_VRFLogEmitter.TransactOpts, requestId, outputSeed, payment, success) +} + +func (_VRFLogEmitter *VRFLogEmitterTransactorSession) EmitRandomWordsFulfilled(requestId *big.Int, outputSeed *big.Int, payment *big.Int, success bool) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.EmitRandomWordsFulfilled(&_VRFLogEmitter.TransactOpts, requestId, outputSeed, payment, success) +} + +func (_VRFLogEmitter *VRFLogEmitterTransactor) EmitRandomWordsRequested(opts *bind.TransactOpts, keyHash [32]byte, requestId *big.Int, preSeed *big.Int, subId uint64, minimumRequestConfirmations uint16, callbackGasLimit uint32, numWords uint32, sender common.Address) (*types.Transaction, error) { + return _VRFLogEmitter.contract.Transact(opts, "emitRandomWordsRequested", keyHash, requestId, preSeed, subId, minimumRequestConfirmations, callbackGasLimit, numWords, sender) +} + +func (_VRFLogEmitter *VRFLogEmitterSession) EmitRandomWordsRequested(keyHash [32]byte, requestId *big.Int, preSeed *big.Int, subId uint64, minimumRequestConfirmations uint16, callbackGasLimit uint32, numWords uint32, sender common.Address) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.EmitRandomWordsRequested(&_VRFLogEmitter.TransactOpts, keyHash, requestId, preSeed, subId, minimumRequestConfirmations, callbackGasLimit, numWords, sender) +} + +func (_VRFLogEmitter *VRFLogEmitterTransactorSession) EmitRandomWordsRequested(keyHash [32]byte, requestId *big.Int, preSeed *big.Int, subId uint64, minimumRequestConfirmations uint16, callbackGasLimit uint32, numWords uint32, sender common.Address) (*types.Transaction, error) { + return _VRFLogEmitter.Contract.EmitRandomWordsRequested(&_VRFLogEmitter.TransactOpts, keyHash, requestId, preSeed, subId, minimumRequestConfirmations, callbackGasLimit, numWords, sender) +} + +type VRFLogEmitterRandomWordsFulfilledIterator struct { + Event *VRFLogEmitterRandomWordsFulfilled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFLogEmitterRandomWordsFulfilledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFLogEmitterRandomWordsFulfilled) + 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(VRFLogEmitterRandomWordsFulfilled) + 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 *VRFLogEmitterRandomWordsFulfilledIterator) Error() error { + return it.fail +} + +func (it *VRFLogEmitterRandomWordsFulfilledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFLogEmitterRandomWordsFulfilled struct { + RequestId *big.Int + OutputSeed *big.Int + Payment *big.Int + Success bool + Raw types.Log +} + +func (_VRFLogEmitter *VRFLogEmitterFilterer) FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int) (*VRFLogEmitterRandomWordsFulfilledIterator, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _VRFLogEmitter.contract.FilterLogs(opts, "RandomWordsFulfilled", requestIdRule) + if err != nil { + return nil, err + } + return &VRFLogEmitterRandomWordsFulfilledIterator{contract: _VRFLogEmitter.contract, event: "RandomWordsFulfilled", logs: logs, sub: sub}, nil +} + +func (_VRFLogEmitter *VRFLogEmitterFilterer) WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *VRFLogEmitterRandomWordsFulfilled, requestId []*big.Int) (event.Subscription, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _VRFLogEmitter.contract.WatchLogs(opts, "RandomWordsFulfilled", requestIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFLogEmitterRandomWordsFulfilled) + if err := _VRFLogEmitter.contract.UnpackLog(event, "RandomWordsFulfilled", 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 (_VRFLogEmitter *VRFLogEmitterFilterer) ParseRandomWordsFulfilled(log types.Log) (*VRFLogEmitterRandomWordsFulfilled, error) { + event := new(VRFLogEmitterRandomWordsFulfilled) + if err := _VRFLogEmitter.contract.UnpackLog(event, "RandomWordsFulfilled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFLogEmitterRandomWordsRequestedIterator struct { + Event *VRFLogEmitterRandomWordsRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFLogEmitterRandomWordsRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFLogEmitterRandomWordsRequested) + 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(VRFLogEmitterRandomWordsRequested) + 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 *VRFLogEmitterRandomWordsRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFLogEmitterRandomWordsRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFLogEmitterRandomWordsRequested struct { + KeyHash [32]byte + RequestId *big.Int + PreSeed *big.Int + SubId uint64 + MinimumRequestConfirmations uint16 + CallbackGasLimit uint32 + NumWords uint32 + Sender common.Address + Raw types.Log +} + +func (_VRFLogEmitter *VRFLogEmitterFilterer) FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []uint64, sender []common.Address) (*VRFLogEmitterRandomWordsRequestedIterator, error) { + + var keyHashRule []interface{} + for _, keyHashItem := range keyHash { + keyHashRule = append(keyHashRule, keyHashItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _VRFLogEmitter.contract.FilterLogs(opts, "RandomWordsRequested", keyHashRule, subIdRule, senderRule) + if err != nil { + return nil, err + } + return &VRFLogEmitterRandomWordsRequestedIterator{contract: _VRFLogEmitter.contract, event: "RandomWordsRequested", logs: logs, sub: sub}, nil +} + +func (_VRFLogEmitter *VRFLogEmitterFilterer) WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *VRFLogEmitterRandomWordsRequested, keyHash [][32]byte, subId []uint64, sender []common.Address) (event.Subscription, error) { + + var keyHashRule []interface{} + for _, keyHashItem := range keyHash { + keyHashRule = append(keyHashRule, keyHashItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _VRFLogEmitter.contract.WatchLogs(opts, "RandomWordsRequested", keyHashRule, subIdRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFLogEmitterRandomWordsRequested) + if err := _VRFLogEmitter.contract.UnpackLog(event, "RandomWordsRequested", 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 (_VRFLogEmitter *VRFLogEmitterFilterer) ParseRandomWordsRequested(log types.Log) (*VRFLogEmitterRandomWordsRequested, error) { + event := new(VRFLogEmitterRandomWordsRequested) + if err := _VRFLogEmitter.contract.UnpackLog(event, "RandomWordsRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_VRFLogEmitter *VRFLogEmitter) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _VRFLogEmitter.abi.Events["RandomWordsFulfilled"].ID: + return _VRFLogEmitter.ParseRandomWordsFulfilled(log) + case _VRFLogEmitter.abi.Events["RandomWordsRequested"].ID: + return _VRFLogEmitter.ParseRandomWordsRequested(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (VRFLogEmitterRandomWordsFulfilled) Topic() common.Hash { + return common.HexToHash("0x7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4") +} + +func (VRFLogEmitterRandomWordsRequested) Topic() common.Hash { + return common.HexToHash("0x63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772") +} + +func (_VRFLogEmitter *VRFLogEmitter) Address() common.Address { + return _VRFLogEmitter.address +} + +type VRFLogEmitterInterface interface { + EmitRandomWordsFulfilled(opts *bind.TransactOpts, requestId *big.Int, outputSeed *big.Int, payment *big.Int, success bool) (*types.Transaction, error) + + EmitRandomWordsRequested(opts *bind.TransactOpts, keyHash [32]byte, requestId *big.Int, preSeed *big.Int, subId uint64, minimumRequestConfirmations uint16, callbackGasLimit uint32, numWords uint32, sender common.Address) (*types.Transaction, error) + + FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int) (*VRFLogEmitterRandomWordsFulfilledIterator, error) + + WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *VRFLogEmitterRandomWordsFulfilled, requestId []*big.Int) (event.Subscription, error) + + ParseRandomWordsFulfilled(log types.Log) (*VRFLogEmitterRandomWordsFulfilled, error) + + FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []uint64, sender []common.Address) (*VRFLogEmitterRandomWordsRequestedIterator, error) + + WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *VRFLogEmitterRandomWordsRequested, keyHash [][32]byte, subId []uint64, sender []common.Address) (event.Subscription, error) + + ParseRandomWordsRequested(log types.Log) (*VRFLogEmitterRandomWordsRequested, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 1db18d8366d..5e9dcfc3326 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -82,6 +82,7 @@ vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerE vrf_load_test_external_sub_owner: ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.bin 2097faa70265e420036cc8a3efb1f1e0836ad2d7323b295b9a26a125dbbe6c7d vrf_load_test_ownerless_consumer: ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.bin 74f914843cbc70b9c3079c3e1c709382ce415225e8bb40113e7ac018bfcb0f5c vrf_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.bin 8ab9de5816fbdf93a2865e2711b85a39a6fc9c413a4b336578c485be1158d430 +vrf_log_emitter: ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.abi ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.bin 15f491d445ac4d0c712d1cbe4e5054c759b080bf20de7d54bfe2a82cde4dcf06 vrf_malicious_consumer_v2: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.bin 9755fa8ffc7f5f0b337d5d413d77b0c9f6cd6f68c31727d49acdf9d4a51bc522 vrf_malicious_consumer_v2_plus: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.bin e2a72638e11da807b6533d037e7e5aaeed695efd5035777b8e20d2f8973a574c vrf_owner: ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.abi ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.bin eccfae5ee295b5850e22f61240c469f79752b8d9a3bac5d64aec7ac8def2f6cb diff --git a/core/gethwrappers/go_generate_logpoller.go b/core/gethwrappers/go_generate_logpoller.go new file mode 100644 index 00000000000..b28b8205830 --- /dev/null +++ b/core/gethwrappers/go_generate_logpoller.go @@ -0,0 +1,7 @@ +// Package gethwrappers provides tools for wrapping solidity contracts with +// golang packages, using abigen. +package gethwrappers + +// Log tester +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin LogEmitter log_emitter +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.abi ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.bin VRFLogEmitter vrf_log_emitter diff --git a/core/services/vrf/v2/listener_v2_log_listener_test.go b/core/services/vrf/v2/listener_v2_log_listener_test.go new file mode 100644 index 00000000000..11b299abc40 --- /dev/null +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -0,0 +1,1039 @@ +package v2 + +import ( + "context" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "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/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/jmoiron/sqlx" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_log_emitter" + "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/keystore" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" + "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +var ( + emitterABI, _ = abi.JSON(strings.NewReader(log_emitter.LogEmitterABI)) + vrfEmitterABI, _ = abi.JSON(strings.NewReader(vrf_log_emitter.VRFLogEmitterABI)) +) + +type vrfLogPollerListenerTH struct { + Lggr logger.Logger + ChainID *big.Int + ORM *logpoller.DbORM + LogPoller logpoller.LogPollerTest + Client *backends.SimulatedBackend + Emitter *log_emitter.LogEmitter + EmitterAddress common.Address + VRFLogEmitter *vrf_log_emitter.VRFLogEmitter + VRFEmitterAddress common.Address + Owner *bind.TransactOpts + EthDB ethdb.Database + Db *sqlx.DB + Listener *listenerV2 + Ctx context.Context +} + +func setupVRFLogPollerListenerTH(t *testing.T, + useFinalityTag bool, + finalityDepth, backfillBatchSize, + rpcBatchSize, keepFinalizedBlocksDepth int64, + mockChainUpdateFn func(*evmmocks.Chain, *vrfLogPollerListenerTH)) *vrfLogPollerListenerTH { + + lggr := logger.TestLogger(t) + chainID := testutils.NewRandomEVMChainID() + db := pgtest.NewSqlxDB(t) + + o := logpoller.NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) + owner := testutils.MustNewSimTransactor(t) + ethDB := rawdb.NewMemoryDatabase() + ec := backends.NewSimulatedBackendWithDatabase(ethDB, map[common.Address]core.GenesisAccount{ + owner.From: { + Balance: big.NewInt(0).Mul(big.NewInt(10), big.NewInt(1e18)), + }, + }, 10e6) + // VRF Listener relies on block timestamps, but SimulatedBackend uses by default clock starting from 1970-01-01 + // This trick is used to move the clock closer to the current time. We set first block to be X hours ago. + // FirstBlockAge is used to compute first block's timestamp in SimulatedBackend (time.Now() - FirstBlockAge) + const FirstBlockAge = 24 * time.Hour + blockTime := time.UnixMilli(int64(ec.Blockchain().CurrentHeader().Time)) + err := ec.AdjustTime(time.Since(blockTime) - FirstBlockAge) + require.NoError(t, err) + ec.Commit() + + esc := client.NewSimulatedBackendClient(t, ec, chainID) + // Mark genesis block as finalized to avoid any nulls in the tests + head := esc.Backend().Blockchain().CurrentHeader() + esc.Backend().Blockchain().SetFinalized(head) + + // Poll period doesn't matter, we intend to call poll and save logs directly in the test. + // Set it to some insanely high value to not interfere with any tests. + lp := logpoller.NewLogPoller(o, esc, lggr, 1*time.Hour, useFinalityTag, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth) + + emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) + require.NoError(t, err) + vrfLogEmitterAddress, _, vrfLogEmitter, err := vrf_log_emitter.DeployVRFLogEmitter(owner, ec) + require.NoError(t, err) + ec.Commit() + + // Log Poller Listener + cfg := pgtest.NewQConfig(false) + ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg) + require.NoError(t, ks.Unlock("blah")) + j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ + RequestedConfsDelay: 10, + EVMChainID: chainID.String(), + }).Toml()) + require.NoError(t, err) + + coordinatorV2, err := vrf_coordinator_v2.NewVRFCoordinatorV2(vrfLogEmitter.Address(), ec) + require.Nil(t, err) + coordinator := NewCoordinatorV2(coordinatorV2) + + chain := evmmocks.NewChain(t) + listener := &listenerV2{ + respCount: map[string]uint64{}, + job: j, + chain: chain, + l: logger.Sugared(lggr), + coordinator: coordinator, + } + ctx := testutils.Context(t) + + // Filter registration is idempotent, so we can just call it every time + // and retry on errors using the ticker. + err = lp.RegisterFilter(logpoller.Filter{ + Name: fmt.Sprintf("vrf_%s_keyhash_%s_job_%d", "v2", listener.job.VRFSpec.PublicKey.MustHash().String(), listener.job.ID), + EventSigs: evmtypes.HashArray{ + vrf_log_emitter.VRFLogEmitterRandomWordsRequested{}.Topic(), + vrf_log_emitter.VRFLogEmitterRandomWordsFulfilled{}.Topic(), + }, + Addresses: evmtypes.AddressArray{ + vrfLogEmitter.Address(), + // listener.job.VRFSpec.CoordinatorAddress.Address(), + }, + }) + require.Nil(t, err) + require.NoError(t, lp.RegisterFilter(logpoller.Filter{ + Name: "Integration test", + EventSigs: []common.Hash{emitterABI.Events["Log1"].ID}, + Addresses: []common.Address{emitterAddress1}, + Retention: 0})) + require.Nil(t, err) + require.Len(t, lp.Filter(nil, nil, nil).Addresses, 2) + require.Len(t, lp.Filter(nil, nil, nil).Topics, 1) + require.Len(t, lp.Filter(nil, nil, nil).Topics[0], 3) + + th := &vrfLogPollerListenerTH{ + Lggr: lggr, + ChainID: chainID, + ORM: o, + LogPoller: lp, + Emitter: emitter1, + EmitterAddress: emitterAddress1, + VRFLogEmitter: vrfLogEmitter, + VRFEmitterAddress: vrfLogEmitterAddress, + Client: ec, + Owner: owner, + EthDB: ethDB, + Db: db, + Listener: listener, + Ctx: ctx, + } + mockChainUpdateFn(chain, th) + return th +} + +/* Tests for initializeLastProcessedBlock: BEGIN + * TestInitProcessedBlock_NoVRFReqs + * TestInitProcessedBlock_NoUnfulfilledVRFReqs + * TestInitProcessedBlock_OneUnfulfilledVRFReq + * TestInitProcessedBlock_SomeUnfulfilledVRFReqs + * TestInitProcessedBlock_UnfulfilledNFulfilledVRFReqs + */ + +func TestInitProcessedBlock_NoVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, th *vrfLogPollerListenerTH) { + mockChain.On("ID").Return(th.ChainID) + mockChain.On("LogPoller").Return(th.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Emit some logs from block 5 to 9 (Inclusive) + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + } + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 5 (EmitLog blocks) = 9 + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(testutils.Context(t))) + + // The poller starts on a new chain at latest-finality (finalityDepth + 5 in this case), + // Replaying from block 4 should guarantee we have block 4 immediately. (We will also get + // block 3 once the backup poller runs, since it always starts 100 blocks behind.) + require.NoError(t, th.LogPoller.Replay(testutils.Context(t), 4)) + + // Should return logs from block 5 to 7 (inclusive) + logs, err := th.LogPoller.Logs(4, 7, emitterABI.Events["Log1"].ID, th.EmitterAddress, + pg.WithParentCtx(testutils.Context(t))) + require.NoError(t, err) + require.Equal(t, 3, len(logs)) + + lastProcessedBlock, err := th.Listener.initializeLastProcessedBlock(th.Ctx) + require.Nil(t, err) + require.Equal(t, int64(6), lastProcessedBlock) +} + +func TestInitProcessedBlock_NoUnfulfilledVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("ID").Return(curTH.ChainID) + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Create VRF request block and a fulfillment block + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID := big.NewInt(1) + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + _, err2 = th.VRFLogEmitter.EmitRandomWordsFulfilled(th.Owner, reqID, preSeed, big.NewInt(10), true) + require.NoError(t, err2) + th.Client.Commit() + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + } + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 2 (VRF req/resp block) + 5 (EmitLog blocks) = 11 + latestBlock := int64(2 + 2 + 2 + 5) + + // A replay is needed so that log poller has a latest block + // Replay from block 11 (latest) onwards, so that log poller has a latest block + // Then test if log poller is able to replay from finalizedBlockNumber (8 --> onwards) + // since there are no pending VRF requests + // Blocks: 1 2 3 4 [5;Request] [6;Fulfilment] 7 8 9 10 11 + require.NoError(t, th.LogPoller.Replay(th.Ctx, latestBlock)) + + // initializeLastProcessedBlock must return the finalizedBlockNumber (8) instead of + // VRF request block number (5), since all VRF requests are fulfilled + lastProcessedBlock, err := th.Listener.initializeLastProcessedBlock(th.Ctx) + require.Nil(t, err) + require.Equal(t, int64(8), lastProcessedBlock) +} + +func TestInitProcessedBlock_OneUnfulfilledVRFReq(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("ID").Return(curTH.ChainID) + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Make a VRF request without fulfilling it + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID := big.NewInt(1) + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + th.Client.Commit() + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + } + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 1 (VRF req block) + 5 (EmitLog blocks) = 10 + latestBlock := int64(2 + 2 + 1 + 5) + + // A replay is needed so that log poller has a latest block + // Replay from block 10 (latest) onwards, so that log poller has a latest block + // Then test if log poller is able to replay from earliestUnprocessedBlock (5 --> onwards) + // Blocks: 1 2 3 4 [5;Request] 6 7 8 9 10 + require.NoError(t, th.LogPoller.Replay(th.Ctx, latestBlock)) + + // initializeLastProcessedBlock must return the unfulfilled VRF + // request block number (5) instead of finalizedBlockNumber (8) + lastProcessedBlock, err := th.Listener.initializeLastProcessedBlock(th.Ctx) + require.Nil(t, err) + require.Equal(t, int64(5), lastProcessedBlock) +} + +func TestInitProcessedBlock_SomeUnfulfilledVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("ID").Return(curTH.ChainID) + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Emit some logs in blocks with VRF reqs interspersed + // No fulfillment for any VRF requests + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + + // Create 2 blocks with VRF requests in each iteration + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(2 * i)) + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + reqID2 := big.NewInt(int64(2*i + 1)) + _, err2 = th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID2, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + } + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 3*5 (EmitLog + VRF req/resp blocks) = 19 + latestBlock := int64(2 + 2 + 3*5) + + // A replay is needed so that log poller has a latest block + // Replay from block 19 (latest) onwards, so that log poller has a latest block + // Then test if log poller is able to replay from earliestUnprocessedBlock (6 --> onwards) + // Blocks: 1 2 3 4 5 [6;Request] [7;Request] 8 [9;Request] [10;Request] + // 11 [12;Request] [13;Request] 14 [15;Request] [16;Request] + // 17 [18;Request] [19;Request] + require.NoError(t, th.LogPoller.Replay(th.Ctx, latestBlock)) + + // initializeLastProcessedBlock must return the earliest unfulfilled VRF request block + // number instead of finalizedBlockNumber + lastProcessedBlock, err := th.Listener.initializeLastProcessedBlock(th.Ctx) + require.Nil(t, err) + require.Equal(t, int64(6), lastProcessedBlock) +} + +func TestInitProcessedBlock_UnfulfilledNFulfilledVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("ID").Return(curTH.ChainID) + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Emit some logs in blocks with VRF reqs interspersed + // One VRF request in each iteration is fulfilled to imitate mixed workload + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + + // Create 2 blocks with VRF requests in each iteration and fulfill one + // of them. This creates a mixed workload of fulfilled and unfulfilled + // VRF requests for testing the VRF listener + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(2 * i)) + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + reqID2 := big.NewInt(int64(2*i + 1)) + _, err2 = th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID2, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + + _, err2 = th.VRFLogEmitter.EmitRandomWordsFulfilled(th.Owner, reqID1, preSeed, big.NewInt(10), true) + require.NoError(t, err2) + th.Client.Commit() + } + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 3*5 (EmitLog + VRF req/resp blocks) = 19 + latestBlock := int64(2 + 2 + 3*5) + // A replay is needed so that log poller has a latest block + // Replay from block 19 (latest) onwards, so that log poller has a latest block + // Then test if log poller is able to replay from earliestUnprocessedBlock (7 --> onwards) + // Blocks: 1 2 3 4 5 [6;Request] [7;Request;6-Fulfilment] 8 [9;Request] [10;Request;9-Fulfilment] + // 11 [12;Request] [13;Request;12-Fulfilment] 14 [15;Request] [16;Request;15-Fulfilment] + // 17 [18;Request] [19;Request;18-Fulfilment] + require.NoError(t, th.LogPoller.Replay(th.Ctx, latestBlock)) + + // initializeLastProcessedBlock must return the earliest unfulfilled VRF request block + // number instead of finalizedBlockNumber + lastProcessedBlock, err := th.Listener.initializeLastProcessedBlock(th.Ctx) + require.Nil(t, err) + require.Equal(t, int64(7), lastProcessedBlock) +} + +/* Tests for initializeLastProcessedBlock: END */ + +/* Tests for updateLastProcessedBlock: BEGIN + * TestUpdateLastProcessedBlock_NoVRFReqs + * TestUpdateLastProcessedBlock_NoUnfulfilledVRFReqs + * TestUpdateLastProcessedBlock_OneUnfulfilledVRFReq + * TestUpdateLastProcessedBlock_SomeUnfulfilledVRFReqs + * TestUpdateLastProcessedBlock_UnfulfilledNFulfilledVRFReqs + */ + +func TestUpdateLastProcessedBlock_NoVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Create VRF request logs + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(1)) + + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + reqID2 := big.NewInt(int64(2)) + _, err2 = th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID2, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + } + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 2 (VRF req blocks) + 5 (EmitLog blocks) = 11 + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // We've to replay from before VRF request log, since updateLastProcessedBlock + // does not internally call LogPoller.Replay + require.NoError(t, th.LogPoller.Replay(th.Ctx, 4)) + + // updateLastProcessedBlock must return the finalizedBlockNumber as there are + // no VRF requests, after currLastProcessedBlock (block 6). The VRF requests + // made above are before the currLastProcessedBlock (7) passed in below + lastProcessedBlock, err := th.Listener.updateLastProcessedBlock(th.Ctx, 7) + require.Nil(t, err) + require.Equal(t, int64(8), lastProcessedBlock) +} + +func TestUpdateLastProcessedBlock_NoUnfulfilledVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Create VRF request log block with a fulfillment log block + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(1)) + + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + _, err2 = th.VRFLogEmitter.EmitRandomWordsFulfilled(th.Owner, reqID1, preSeed, big.NewInt(10), true) + require.NoError(t, err2) + th.Client.Commit() + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + } + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 2 (VRF req/resp blocks) + 5 (EmitLog blocks) = 11 + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // We've to replay from before VRF request log, since updateLastProcessedBlock + // does not internally call LogPoller.Replay + require.NoError(t, th.LogPoller.Replay(th.Ctx, 4)) + + // updateLastProcessedBlock must return the finalizedBlockNumber (8) though we have + // a VRF req at block (5) after currLastProcessedBlock (4) passed below, because + // the VRF request is fulfilled + lastProcessedBlock, err := th.Listener.updateLastProcessedBlock(th.Ctx, 4) + require.Nil(t, err) + require.Equal(t, int64(8), lastProcessedBlock) +} + +func TestUpdateLastProcessedBlock_OneUnfulfilledVRFReq(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Create VRF request logs without a fulfillment log block + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(1)) + + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + } + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 1 (VRF req block) + 5 (EmitLog blocks) = 10 + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // We've to replay from before VRF request log, since updateLastProcessedBlock + // does not internally call LogPoller.Replay + require.NoError(t, th.LogPoller.Replay(th.Ctx, 4)) + + // updateLastProcessedBlock must return the VRF req at block (5) instead of + // finalizedBlockNumber (8) after currLastProcessedBlock (4) passed below, + // because the VRF request is unfulfilled + lastProcessedBlock, err := th.Listener.updateLastProcessedBlock(th.Ctx, 4) + require.Nil(t, err) + require.Equal(t, int64(5), lastProcessedBlock) +} + +func TestUpdateLastProcessedBlock_SomeUnfulfilledVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + + // Create 2 blocks with VRF requests in each iteration + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(2 * i)) + + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + reqID2 := big.NewInt(int64(2*i + 1)) + _, err2 = th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID2, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + } + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 3*5 (EmitLog + VRF req blocks) = 19 + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // We've to replay from before VRF request log, since updateLastProcessedBlock + // does not internally call LogPoller.Replay + require.NoError(t, th.LogPoller.Replay(th.Ctx, 4)) + + // updateLastProcessedBlock must return the VRF req at block (6) instead of + // finalizedBlockNumber (16) after currLastProcessedBlock (4) passed below, + // as block 6 contains the earliest unfulfilled VRF request + lastProcessedBlock, err := th.Listener.updateLastProcessedBlock(th.Ctx, 4) + require.Nil(t, err) + require.Equal(t, int64(6), lastProcessedBlock) +} + +func TestUpdateLastProcessedBlock_UnfulfilledNFulfilledVRFReqs(t *testing.T) { + t.Parallel() + + finalityDepth := int64(3) + th := setupVRFLogPollerListenerTH(t, false, finalityDepth, 3, 2, 1000, func(mockChain *evmmocks.Chain, curTH *vrfLogPollerListenerTH) { + mockChain.On("LogPoller").Return(curTH.LogPoller) + }) + + // Block 3 to finalityDepth. Ensure we have finality number of blocks + for i := 1; i < int(finalityDepth); i++ { + th.Client.Commit() + } + + // Emit some logs in blocks to make the VRF req and fulfillment older than finalityDepth from latestBlock + n := 5 + for i := 0; i < n; i++ { + _, err1 := th.Emitter.EmitLog1(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + _, err1 = th.Emitter.EmitLog2(th.Owner, []*big.Int{big.NewInt(int64(i))}) + require.NoError(t, err1) + th.Client.Commit() + + // Create 2 blocks with VRF requests in each iteration and fulfill one + // of them. This creates a mixed workload of fulfilled and unfulfilled + // VRF requests for testing the VRF listener + keyHash := [32]byte(th.Listener.job.VRFSpec.PublicKey.MustHash().Bytes()) + preSeed := big.NewInt(105) + subID := uint64(1) + reqID1 := big.NewInt(int64(2 * i)) + + _, err2 := th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID1, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + th.Client.Commit() + + reqID2 := big.NewInt(int64(2*i + 1)) + _, err2 = th.VRFLogEmitter.EmitRandomWordsRequested(th.Owner, + keyHash, reqID2, preSeed, subID, 10, 10000, 2, th.Owner.From) + require.NoError(t, err2) + _, err2 = th.VRFLogEmitter.EmitRandomWordsFulfilled(th.Owner, reqID1, preSeed, big.NewInt(10), true) + require.NoError(t, err2) + th.Client.Commit() + } + + // Blocks till now: 2 (in SetupTH) + 2 (empty blocks) + 3*5 (EmitLog + VRF req blocks) = 19 + + // Calling Start() after RegisterFilter() simulates a node restart after job creation, should reload Filter from db. + require.NoError(t, th.LogPoller.Start(th.Ctx)) + + // We've to replay from before VRF request log, since updateLastProcessedBlock + // does not internally call LogPoller.Replay + require.NoError(t, th.LogPoller.Replay(th.Ctx, 4)) + + // updateLastProcessedBlock must return the VRF req at block (7) instead of + // finalizedBlockNumber (16) after currLastProcessedBlock (4) passed below, + // as block 7 contains the earliest unfulfilled VRF request. VRF request + // in block 6 has been fulfilled in block 7. + lastProcessedBlock, err := th.Listener.updateLastProcessedBlock(th.Ctx, 4) + require.Nil(t, err) + require.Equal(t, int64(7), lastProcessedBlock) +} + +/* Tests for updateLastProcessedBlock: END */ + +/* Tests for getUnfulfilled: BEGIN + * TestGetUnfulfilled_NoVRFReqs + * TestGetUnfulfilled_NoUnfulfilledVRFReqs + * TestGetUnfulfilled_OneUnfulfilledVRFReq + * TestGetUnfulfilled_SomeUnfulfilledVRFReqs + * TestGetUnfulfilled_UnfulfilledNFulfilledVRFReqs + */ + +func SetupGetUnfulfilledTH(t *testing.T) (*listenerV2, *utils.Big) { + chainID := utils.NewBig(big.NewInt(12345)) + lggr := logger.TestLogger(t) + j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ + RequestedConfsDelay: 10, + EVMChainID: chainID.String(), + }).Toml()) + require.NoError(t, err) + chain := evmmocks.NewChain(t) + + // Construct CoordinatorV2_X object for VRF listener + owner := testutils.MustNewSimTransactor(t) + ethDB := rawdb.NewMemoryDatabase() + ec := backends.NewSimulatedBackendWithDatabase(ethDB, map[common.Address]core.GenesisAccount{ + owner.From: { + Balance: big.NewInt(0).Mul(big.NewInt(10), big.NewInt(1e18)), + }, + }, 10e6) + _, _, vrfLogEmitter, err := vrf_log_emitter.DeployVRFLogEmitter(owner, ec) + require.NoError(t, err) + ec.Commit() + coordinatorV2, err := vrf_coordinator_v2.NewVRFCoordinatorV2(vrfLogEmitter.Address(), ec) + require.Nil(t, err) + coordinator := NewCoordinatorV2(coordinatorV2) + + listener := &listenerV2{ + respCount: map[string]uint64{}, + job: j, + chain: chain, + l: logger.Sugared(lggr), + coordinator: coordinator, + } + return listener, chainID +} + +func TestGetUnfulfilled_NoVRFReqs(t *testing.T) { + t.Parallel() + + listener, chainID := SetupGetUnfulfilledTH(t) + + logs := []logpoller.Log{} + for i := 0; i < 10; i++ { + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(i))), + BlockNumber: int64(i), + BlockTimestamp: time.Now(), + Topics: [][]byte{ + []byte("0x46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a8"), + }, + EventSig: emitterABI.Events["Log1"].ID, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(i))), + Data: nil, + CreatedAt: time.Now(), + }) + } + + unfulfilled, _, fulfilled := listener.getUnfulfilled(logs, listener.l) + require.Empty(t, unfulfilled) + require.Empty(t, fulfilled) +} + +func TestGetUnfulfilled_NoUnfulfilledVRFReqs(t *testing.T) { + t.Parallel() + + listener, chainID := SetupGetUnfulfilledTH(t) + + logs := []logpoller.Log{} + for i := 0; i < 10; i++ { + eventSig := emitterABI.Events["Log1"].ID + topics := [][]byte{ + common.FromHex("0x46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a8"), + } + if i%2 == 0 { + eventSig = vrfEmitterABI.Events["RandomWordsRequested"].ID + topics = [][]byte{ + common.FromHex("0x63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772"), + common.FromHex("0xc0a6c424ac7157ae408398df7e5f4552091a69125d5dfcb7b8c2659029395bdf"), + common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000001"), + common.FromHex("0x0000000000000000000000005ee3b50502b5c4c9184dcb281471a0614d4b2ef9"), + } + } + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(2 * i))), + BlockNumber: int64(2 * i), + BlockTimestamp: time.Now(), + Topics: topics, + EventSig: eventSig, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(2 * i))), + Data: common.FromHex("0x000000000000000000000000000000000000000000000000000000000000000" + fmt.Sprintf("%d", i) + "000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000002"), + CreatedAt: time.Now(), + }) + if i%2 == 0 { + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(2*i + 1))), + BlockNumber: int64(2*i + 1), + BlockTimestamp: time.Now(), + Topics: [][]byte{ + common.FromHex("0x7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4"), + common.FromHex("0x000000000000000000000000000000000000000000000000000000000000000" + fmt.Sprintf("%d", i)), + }, + EventSig: vrfEmitterABI.Events["RandomWordsFulfilled"].ID, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(2*i + 1))), + Data: common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001"), + CreatedAt: time.Now(), + }) + } + } + + unfulfilled, _, fulfilled := listener.getUnfulfilled(logs, listener.l) + require.Empty(t, unfulfilled) + require.Len(t, fulfilled, 5) +} + +func TestGetUnfulfilled_OneUnfulfilledVRFReq(t *testing.T) { + t.Parallel() + + listener, chainID := SetupGetUnfulfilledTH(t) + + logs := []logpoller.Log{} + for i := 0; i < 10; i++ { + eventSig := emitterABI.Events["Log1"].ID + topics := [][]byte{ + common.FromHex("0x46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a8"), + } + if i == 4 { + eventSig = vrfEmitterABI.Events["RandomWordsRequested"].ID + topics = [][]byte{ + common.FromHex("0x63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772"), + common.FromHex("0xc0a6c424ac7157ae408398df7e5f4552091a69125d5dfcb7b8c2659029395bdf"), + common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000001"), + common.FromHex("0x0000000000000000000000005ee3b50502b5c4c9184dcb281471a0614d4b2ef9"), + } + } + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(2 * i))), + BlockNumber: int64(2 * i), + BlockTimestamp: time.Now(), + Topics: topics, + EventSig: eventSig, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(2 * i))), + Data: common.FromHex("0x000000000000000000000000000000000000000000000000000000000000000" + fmt.Sprintf("%d", i) + "000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000002"), + CreatedAt: time.Now(), + }) + } + + unfulfilled, _, fulfilled := listener.getUnfulfilled(logs, listener.l) + require.Equal(t, unfulfilled[0].RequestID().Int64(), big.NewInt(4).Int64()) + require.Len(t, unfulfilled, 1) + require.Empty(t, fulfilled) +} + +func TestGetUnfulfilled_SomeUnfulfilledVRFReq(t *testing.T) { + t.Parallel() + + listener, chainID := SetupGetUnfulfilledTH(t) + + logs := []logpoller.Log{} + for i := 0; i < 10; i++ { + eventSig := emitterABI.Events["Log1"].ID + topics := [][]byte{ + common.FromHex("0x46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a8"), + } + if i%2 == 0 { + eventSig = vrfEmitterABI.Events["RandomWordsRequested"].ID + topics = [][]byte{ + common.FromHex("0x63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772"), + common.FromHex("0xc0a6c424ac7157ae408398df7e5f4552091a69125d5dfcb7b8c2659029395bdf"), + common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000001"), + common.FromHex("0x0000000000000000000000005ee3b50502b5c4c9184dcb281471a0614d4b2ef9"), + } + } + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(2 * i))), + BlockNumber: int64(2 * i), + BlockTimestamp: time.Now(), + Topics: topics, + EventSig: eventSig, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(2 * i))), + Data: common.FromHex("0x000000000000000000000000000000000000000000000000000000000000000" + fmt.Sprintf("%d", i) + "000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000002"), + CreatedAt: time.Now(), + }) + } + + unfulfilled, _, fulfilled := listener.getUnfulfilled(logs, listener.l) + require.Len(t, unfulfilled, 5) + require.Len(t, fulfilled, 0) + expected := map[int64]bool{0: true, 2: true, 4: true, 6: true, 8: true} + for _, u := range unfulfilled { + v, ok := expected[u.RequestID().Int64()] + require.Equal(t, ok, true) + require.Equal(t, v, true) + } + require.Equal(t, len(expected), len(unfulfilled)) +} + +func TestGetUnfulfilled_UnfulfilledNFulfilledVRFReqs(t *testing.T) { + t.Parallel() + + listener, chainID := SetupGetUnfulfilledTH(t) + + logs := []logpoller.Log{} + for i := 0; i < 10; i++ { + eventSig := emitterABI.Events["Log1"].ID + topics := [][]byte{ + common.FromHex("0x46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a8"), + } + if i%2 == 0 { + eventSig = vrfEmitterABI.Events["RandomWordsRequested"].ID + topics = [][]byte{ + common.FromHex("0x63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772"), + common.FromHex("0xc0a6c424ac7157ae408398df7e5f4552091a69125d5dfcb7b8c2659029395bdf"), + common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000001"), + common.FromHex("0x0000000000000000000000005ee3b50502b5c4c9184dcb281471a0614d4b2ef9"), + } + } + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(2 * i))), + BlockNumber: int64(2 * i), + BlockTimestamp: time.Now(), + Topics: topics, + EventSig: eventSig, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(2 * i))), + Data: common.FromHex("0x000000000000000000000000000000000000000000000000000000000000000" + fmt.Sprintf("%d", i) + "000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000002"), + CreatedAt: time.Now(), + }) + if i%2 == 0 && i < 6 { + logs = append(logs, logpoller.Log{ + EvmChainId: chainID, + LogIndex: 0, + BlockHash: common.BigToHash(big.NewInt(int64(2*i + 1))), + BlockNumber: int64(2*i + 1), + BlockTimestamp: time.Now(), + Topics: [][]byte{ + common.FromHex("0x7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4"), + common.FromHex("0x000000000000000000000000000000000000000000000000000000000000000" + fmt.Sprintf("%d", i)), + }, + EventSig: vrfEmitterABI.Events["RandomWordsFulfilled"].ID, + Address: common.Address{}, + TxHash: common.BigToHash(big.NewInt(int64(2*i + 1))), + Data: common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001"), + CreatedAt: time.Now(), + }) + } + } + + unfulfilled, _, fulfilled := listener.getUnfulfilled(logs, listener.l) + require.Len(t, unfulfilled, 2) + require.Len(t, fulfilled, 3) + expected := map[int64]bool{6: true, 8: true} + for _, u := range unfulfilled { + v, ok := expected[u.RequestID().Int64()] + require.Equal(t, ok, true) + require.Equal(t, v, true) + } + require.Equal(t, len(expected), len(unfulfilled)) +} + +/* Tests for getUnfulfilled: END */ From df54d26d7f9ebe23f349807850635cb51d4f0616 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Thu, 7 Dec 2023 12:38:24 -0500 Subject: [PATCH 024/234] EVM Extraction core/services/servicetest (#11514) * Extract servicetest * tidy --- core/chains/evm/log/helpers_test.go | 4 ++-- core/chains/evm/log/integration_test.go | 4 ++-- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index ac7eb863e62..49f9266f705 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -21,6 +21,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -40,7 +41,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -89,7 +89,7 @@ func newBroadcasterHelperWithEthClient(t *testing.T, ethClient evmclient.Client, }) config := evmtest.NewChainScopedConfig(t, globalConfig) lggr := logger.Test(t) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) db := pgtest.NewSqlxDB(t) orm := log.NewORM(db, lggr, config.Database(), cltest.FixtureChainID) diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index edc04a4ada4..2d66cdf585c 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -27,7 +28,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -1324,7 +1324,7 @@ func TestBroadcaster_AppendLogChannel(t *testing.T) { ch3 := make(chan types.Log) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) lb := log.NewBroadcaster(nil, ethClient, nil, logger.Test(t), nil, mailMon) chCombined := lb.ExportedAppendLogChannel(ch1, ch2) chCombined = lb.ExportedAppendLogChannel(chCombined, ch3) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index b0b5f801ed5..07d41c9d7e4 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -258,7 +258,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index dd69053b405..48b168459ba 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1214,8 +1214,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad h1:ysPjfbCPJuVxxFZa1Ifv8OPE20pzvnEHjJrPDUo4gT0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/go.mod b/go.mod index 2ac067959ef..f659881d1a0 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index b7305210e05..d700ec724b9 100644 --- a/go.sum +++ b/go.sum @@ -1217,8 +1217,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad h1:ysPjfbCPJuVxxFZa1Ifv8OPE20pzvnEHjJrPDUo4gT0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 32c35c28a9a..90f4bbae267 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad github.com/smartcontractkit/chainlink-testing-framework v1.20.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 21e237923b9..f5640eb8917 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1507,8 +1507,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad h1:ysPjfbCPJuVxxFZa1Ifv8OPE20pzvnEHjJrPDUo4gT0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From 711987f11751d69f0c0d7d0b02d11497539ece90 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Thu, 7 Dec 2023 19:08:18 -0300 Subject: [PATCH 025/234] [TT-590] Logstream with buffered streaming to Loki (#11477) * testing out with local CTF * print location for failed tests * use logwatch with buffer * try logwatch with loki and test targets in CI * fix merge conflict * fix lint issue * adjust env vars in workflow * add logwatch env vars as env and not with * fix env var name * fix go.mod * use newer ctf * fix go.sum * use correct ctf version * fix go.sum * trigger tests * small debug * make the Gh summary print a bit nicer * latest ctf, use var not secret for grafana url * update ctf, change how we print test summary * shutdown logwatch after flushing logs * trigger tests * use latest ctf where logwatch -> logstream * update var name in GH workflow * enable log stream for all smoke tests * use better method for logstream shutdown and log flushing * fix compile error * latest ctf, remove comment * remove replace from go.mod * collect all logs to see what happens * update ctf, better scoped dashboard link * do not save logs if test doesn't fail * print absolute log folder path * cause test failure to make sure file logs are where they should be * fix typo in var declaration in gh workflow * run go mod tidy for integeration tests * always execute test summary step * remove failing vrfv2 test on purpose * use taggeg CTF version * connect mock adapter to logstream * initialise killgrave only when necessary --- .github/workflows/integration-tests.yml | 28 ++++ .gitignore | 3 + integration-tests/docker/test_env/cl_node.go | 33 +++-- integration-tests/docker/test_env/test_env.go | 131 ++++-------------- .../docker/test_env/test_env_builder.go | 46 ++++-- integration-tests/go.mod | 19 ++- integration-tests/go.sum | 43 +++--- integration-tests/load/vrfv2/vrfv2_test.go | 2 +- .../load/vrfv2plus/vrfv2plus_test.go | 2 +- integration-tests/smoke/automation_test.go | 1 + integration-tests/smoke/cron_test.go | 2 + integration-tests/smoke/flux_test.go | 1 + integration-tests/smoke/forwarder_ocr_test.go | 1 + .../smoke/forwarders_ocr2_test.go | 1 + integration-tests/smoke/keeper_test.go | 1 + integration-tests/smoke/ocr2_test.go | 2 + integration-tests/smoke/ocr_test.go | 1 + integration-tests/smoke/runlog_test.go | 1 + integration-tests/smoke/vrf_test.go | 2 + integration-tests/smoke/vrfv2_test.go | 2 + integration-tests/smoke/vrfv2plus_test.go | 3 + .../universal/log_poller/helpers.go | 1 + 22 files changed, 180 insertions(+), 146 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 5e3cf02a7c0..37a2718be46 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -435,6 +435,13 @@ jobs: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} + LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} + GRAFANA_URL: ${{ vars.GRAFANA_URL }} + GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} + RUN_ID: ${{ github.run_id }} with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -487,6 +494,27 @@ jobs: with: name: trace-data path: ./integration-tests/smoke/traces/trace-data.json + - name: Print failed test summary + if: always() + run: | + directory="./integration-tests/smoke/.test_summary" + files=("$directory"/*) + if [ -d "$directory" ]; then + echo "Test summary folder found" + if [ ${#files[@]} -gt 0 ]; then + first_file="${files[0]}" + echo "Name of the first test summary file: $(basename "$first_file")" + echo "### Failed Test Execution Logs Dashboard (over VPN):" >> $GITHUB_STEP_SUMMARY + cat "$first_file" | jq -r '.loki[] | "* [\(.test_name)](\(.value))"' >> $GITHUB_STEP_SUMMARY + if [ ${#files[@]} -gt 1 ]; then + echo "Found more than one test summary file. This is incorrect, there should be only one file" + fi + else + echo "Test summary directory is empty. This should not happen" + fi + else + echo "No test summary folder found. If no test failed or log collection wasn't explicitly requested this is correct. Exiting" + fi ### Used to check the required checks box when the matrix completes eth-smoke-tests: diff --git a/.gitignore b/.gitignore index 3f016503a81..8d127dbb728 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,6 @@ go.work* # This sometimes shows up for some reason tools/flakeytests/coverage.txt + +.test_summary/ +.run.id \ No newline at end of file diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 7dc077ad34b..60394256334 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -1,6 +1,7 @@ package test_env import ( + "context" "fmt" "math/big" "net/url" @@ -22,7 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/docker" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/logwatch" + "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" @@ -47,7 +48,7 @@ type ClNode struct { UserPassword string `json:"userPassword"` t *testing.T l zerolog.Logger - lw *logwatch.LogWatch + ls *logstream.LogStream } type ClNodeOption = func(c *ClNode) @@ -76,9 +77,9 @@ func WithDbContainerName(name string) ClNodeOption { } } -func WithLogWatch(lw *logwatch.LogWatch) ClNodeOption { +func WithLogStream(ls *logstream.LogStream) ClNodeOption { return func(c *ClNode) { - c.lw = lw + c.ls = ls } } @@ -285,11 +286,7 @@ func (n *ClNode) StartContainer() error { if err != nil { return fmt.Errorf("%s err: %w", ErrStartCLNodeContainer, err) } - if n.lw != nil { - if err := n.lw.ConnectContainer(testcontext.Get(n.t), container, "cl-node", true); err != nil { - return err - } - } + clEndpoint, err := test_env.GetEndpoint(testcontext.Get(n.t), container, "http") if err != nil { return err @@ -410,5 +407,23 @@ func (n *ClNode) getContainerRequest(secrets string) ( FileMode: 0644, }, }, + LifecycleHooks: []tc.ContainerLifecycleHooks{ + {PostStarts: []tc.ContainerHook{ + func(ctx context.Context, c tc.Container) error { + if n.ls != nil { + return n.ls.ConnectContainer(ctx, c, "cl-node") + } + return nil + }, + }, + PostStops: []tc.ContainerHook{ + func(ctx context.Context, c tc.Container) error { + if n.ls != nil { + return n.ls.DisconnectContainer(c) + } + return nil + }, + }}, + }, }, nil } diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index b04d24a9593..15bf6641bf4 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -1,14 +1,12 @@ package test_env import ( + "context" "encoding/json" "fmt" - "io" "math/big" "os" - "path/filepath" "runtime/debug" - "strings" "testing" "time" @@ -16,14 +14,12 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" tc "github.com/testcontainers/testcontainers-go" - "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/docker" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/logwatch" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -35,9 +31,9 @@ var ( ) type CLClusterTestEnv struct { - Cfg *TestEnvConfig - Network *tc.DockerNetwork - LogWatch *logwatch.LogWatch + Cfg *TestEnvConfig + Network *tc.DockerNetwork + LogStream *logstream.LogStream /* components */ ClCluster *ClCluster @@ -58,11 +54,9 @@ func NewTestEnv() (*CLClusterTestEnv, error) { if err != nil { return nil, err } - n := []string{network.Name} return &CLClusterTestEnv{ - MockAdapter: test_env.NewKillgrave(n, ""), - Network: network, - l: log.Logger, + Network: network, + l: log.Logger, }, nil } @@ -70,15 +64,19 @@ func NewTestEnv() (*CLClusterTestEnv, error) { // Sets up private ethereum chain and MockAdapter containers with the provided cfg. func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTestEnv { te.Cfg = cfg - n := []string{te.Network.Name} - te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName)) + if cfg.MockAdapter.ContainerName != "" { + n := []string{te.Network.Name} + te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName)) + } return te } func (te *CLClusterTestEnv) WithTestLogger(t *testing.T) *CLClusterTestEnv { te.t = t te.l = logging.GetTestLogger(t) - te.MockAdapter.WithTestLogger(t) + if te.MockAdapter != nil { + te.MockAdapter.WithTestLogger(t) + } return te } @@ -174,6 +172,18 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i } } + if te.LogStream != nil { + for _, node := range te.ClCluster.Nodes { + node.ls = te.LogStream + } + if te.MockAdapter != nil { + err := te.LogStream.ConnectContainer(context.Background(), te.MockAdapter.Container, "mock-adapter") + if err != nil { + return err + } + } + } + // Start/attach node containers return te.ClCluster.Start() } @@ -207,23 +217,6 @@ func (te *CLClusterTestEnv) Cleanup() error { te.logWhetherAllContainersAreRunning() - // Getting the absolute path - wd, err := os.Getwd() - if err != nil { - return err - } - - wd = filepath.Join(wd, "logs") - te.l.Info().Str("Working dir", wd).Msg("Would write test logs here") - - // TODO: This is an imperfect and temporary solution, see TT-590 for a more sustainable solution - // Collect logs if the test fails, or if we just want them - if te.t.Failed() || os.Getenv("TEST_LOG_COLLECT") == "true" { - if err := te.collectTestLogs(); err != nil { - return err - } - } - if te.EVMClient == nil { return fmt.Errorf("evm client is nil, unable to return funds from chainlink nodes during cleanup") } else if te.EVMClient.NetworkSimulated() { @@ -247,6 +240,10 @@ func (te *CLClusterTestEnv) Cleanup() error { func (te *CLClusterTestEnv) logWhetherAllContainersAreRunning() { for _, node := range te.ClCluster.Nodes { + if node.Container == nil { + continue + } + isCLRunning := node.Container.IsRunning() isDBRunning := node.PostgresDb.Container.IsRunning() @@ -260,74 +257,6 @@ func (te *CLClusterTestEnv) logWhetherAllContainersAreRunning() { } } -// collectTestLogs collects the logs from all the Chainlink nodes in the test environment and writes them to local files -func (te *CLClusterTestEnv) collectTestLogs() error { - te.l.Info().Msg("Collecting test logs") - sanitizedNetworkName := strings.ReplaceAll(te.EVMClient.GetNetworkName(), " ", "-") - folder := fmt.Sprintf("./logs/%s-%s-%s", te.t.Name(), sanitizedNetworkName, time.Now().Format("2006-01-02T15-04-05")) - if err := os.MkdirAll(folder, os.ModePerm); err != nil { - return err - } - - eg := &errgroup.Group{} - for _, n := range te.ClCluster.Nodes { - node := n - eg.Go(func() error { - logFileName := filepath.Join(folder, fmt.Sprintf("node-%s.log", node.ContainerName)) - logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer logFile.Close() - logReader, err := node.Container.Logs(testcontext.Get(te.t)) - if err != nil { - return err - } - _, err = io.Copy(logFile, logReader) - if err != nil { - return err - } - te.l.Info().Str("Node", node.ContainerName).Str("File", logFileName).Msg("Wrote Logs") - return nil - }) - } - - if te.MockAdapter != nil { - eg.Go(func() error { - logFileName := filepath.Join(folder, "mock-adapter.log") - logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer logFile.Close() - logReader, err := te.MockAdapter.Container.Logs(testcontext.Get(te.t)) - if err != nil { - return err - } - _, err = io.Copy(logFile, logReader) - if err != nil { - return err - } - te.l.Info().Str("Container", te.MockAdapter.ContainerName).Str("File", logFileName).Msg("Wrote Logs") - return nil - }) - } - - if err := eg.Wait(); err != nil { - return err - } - - // Getting the absolute path - wd, err := os.Getwd() - if err != nil { - return err - } - absolutePath := filepath.Join(wd, folder) - - te.l.Info().Str("Logs absolute Location", absolutePath).Msg("Wrote test logs") - return nil -} - func (te *CLClusterTestEnv) returnFunds() error { te.l.Info().Msg("Attempting to return Chainlink node funds to default network wallets") for _, chainlinkNode := range te.ClCluster.Nodes { diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 685ea5338ae..cc0499ea0ce 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -13,8 +13,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/logwatch" + "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -32,8 +33,7 @@ const ( ) type CLTestEnvBuilder struct { - hasLogWatch bool - // hasGeth bool + hasLogStream bool hasKillgrave bool hasForwarders bool clNodeConfig *chainlink.Config @@ -101,8 +101,8 @@ func (b *CLTestEnvBuilder) WithTestLogger(t *testing.T) *CLTestEnvBuilder { return b } -func (b *CLTestEnvBuilder) WithLogWatcher() *CLTestEnvBuilder { - b.hasLogWatch = true +func (b *CLTestEnvBuilder) WithLogStream() *CLTestEnvBuilder { + b.hasLogStream = true return b } @@ -220,24 +220,30 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } var err error - if b.t != nil { - b.te.WithTestLogger(b.t) - } - - if b.hasLogWatch { - b.te.LogWatch, err = logwatch.NewLogWatch(nil, nil) + if b.hasLogStream { + b.te.LogStream, err = logstream.NewLogStream(b.t, nil) if err != nil { return nil, err } } if b.hasKillgrave { + if b.te.Network == nil { + return nil, fmt.Errorf("test environment builder failed: %w", fmt.Errorf("cannot start mock adapter without a network")) + } + + b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.Network.Name}, "") + err = b.te.StartMockAdapter() if err != nil { return nil, err } } + if b.t != nil { + b.te.WithTestLogger(b.t) + } + switch b.cleanUpType { case CleanUpTypeStandard: b.t.Cleanup(func() { @@ -253,6 +259,24 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { return b.te, fmt.Errorf("test environment builder failed: %w", fmt.Errorf("explicit cleanup type must be set when building test environment")) } + if b.te.LogStream != nil { + b.t.Cleanup(func() { + b.l.Warn().Msg("Shutting down LogStream") + logPath, err := osutil.GetAbsoluteFolderPath("logs") + if err != nil { + b.l.Info().Str("Absolute path", logPath).Msg("LogStream logs folder location") + } + + if b.t.Failed() || os.Getenv("TEST_LOG_COLLECT") == "true" { + // we can't do much if this fails, so we just log the error in logstream + _ = b.te.LogStream.FlushAndShutdown() + b.te.LogStream.PrintLogTargetsLocations() + b.te.LogStream.SaveLogLocationInTestSummary() + } + + }) + } + if b.nonDevGethNetworks != nil { b.te.WithPrivateChain(b.nonDevGethNetworks) err := b.te.StartPrivateChain() diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 90f4bbae267..2f32347bb4b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,12 +25,12 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad - github.com/smartcontractkit/chainlink-testing-framework v1.20.1 + github.com/smartcontractkit/chainlink-testing-framework v1.21.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 - github.com/smartcontractkit/wasp v0.3.6 + github.com/smartcontractkit/wasp v0.3.7 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.23.0 @@ -42,8 +42,12 @@ require ( gopkg.in/guregu/null.v4 v4.0.0 ) +// avoids ambigious imports of indirect dependencies +exclude github.com/hashicorp/consul v1.2.1 + // Pin K8s versions as their updates are highly disruptive and go mod keeps wanting to update them replace ( + github.com/testcontainers/testcontainers-go => github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56 k8s.io/api => k8s.io/api v0.25.11 k8s.io/client-go => k8s.io/client-go v0.25.11 k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d @@ -73,11 +77,13 @@ require ( github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/VictoriaMetrics/fastcache v1.10.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/avast/retry-go v3.0.0+incompatible // indirect github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aws/aws-sdk-go v1.45.25 // indirect github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect @@ -109,7 +115,8 @@ require ( github.com/cometbft/cometbft v0.37.2 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect - github.com/containerd/containerd v1.7.3 // indirect + github.com/containerd/containerd v1.7.7 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect @@ -276,6 +283,7 @@ require ( github.com/libp2p/go-openssl v0.0.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -290,7 +298,7 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/patternmatcher v0.5.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect @@ -316,7 +324,7 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.7 // indirect github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect @@ -349,6 +357,7 @@ require ( github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.23.10 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f5640eb8917..08d02446d1d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -131,16 +131,18 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= +github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.6 h1:U68crOE3y3MPttCMQGywZOLrTeF5HHJ3/vDBCJn9/bA= -github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56 h1:HItfr1XKD/4xnsJE56m3uxnkMQ9lbg8xDnkf9qoZCH0= +github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56/go.mod h1:ICriE9bLX5CLxL9OFQ2N+2N+f+803LNJ1utJb1+Inx0= github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= @@ -177,6 +179,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -304,10 +308,12 @@ github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0 github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= -github.com/containerd/containerd v1.7.3 h1:cKwYKkP1eTj54bP3wCdXXBymmKRQMrWjkLSWZZJDa8o= -github.com/containerd/containerd v1.7.3/go.mod h1:32FOM4/O0RkNg7AjQj3hDzN9cUGtu+HMvaKUNiqCZB8= -github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU= -github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/containerd v1.7.7 h1:QOC2K4A42RQpcrZyptP6z9EJZnlHfHJUfZrAAHe15q4= +github.com/containerd/containerd v1.7.7/go.mod h1:3c4XZv6VeT9qgf9GMTxNTMFxGJrGpI2vz1yk4ye+YY8= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -1135,6 +1141,7 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -1222,8 +1229,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= -github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= @@ -1324,8 +1331,8 @@ github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= @@ -1487,7 +1494,9 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1519,8 +1528,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= -github.com/smartcontractkit/chainlink-testing-framework v1.20.1 h1:0hxLRts4yIum52MaE95RuM2Xi1S/R0r4UFExpp00iK4= -github.com/smartcontractkit/chainlink-testing-framework v1.20.1/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= +github.com/smartcontractkit/chainlink-testing-framework v1.21.0 h1:MrtpGMgPpcRX06FtDEj14Vokoo6Sx8e0/D6AA9LxCgM= +github.com/smartcontractkit/chainlink-testing-framework v1.21.0/go.mod h1:9SCqZ+lcWZNEofpPgasQ+wUF6A6fFgZvWmhqQJwFYV0= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= @@ -1533,8 +1542,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wasp v0.3.6 h1:1TLWfrTzqZwNvyyoKzPZ8FLQat2lNz640eM+mMh2YxM= -github.com/smartcontractkit/wasp v0.3.6/go.mod h1:L/cyUGfpaWxy/2twOVJLRt2mySJEIqGrFj9nyvRLpSo= +github.com/smartcontractkit/wasp v0.3.7 h1:3toT+iMSHJ1EKQXE+jGnxfmtLlT0gwEl1A7xGyw0NZY= +github.com/smartcontractkit/wasp v0.3.7/go.mod h1:L/cyUGfpaWxy/2twOVJLRt2mySJEIqGrFj9nyvRLpSo= github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb6vPbtRQ= github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1606,8 +1615,6 @@ github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= -github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrjiCUizNCxI53bl/BnPiVwXqLzqYTqgU= github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index ae109f75e28..8a0faeabb29 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -178,7 +178,7 @@ func TestVRFV2Performance(t *testing.T) { l.Error().Err(err).Msg("Error cleaning up test environment") } }). - WithLogWatcher(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 4b6728440b3..68c1012987d 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -184,7 +184,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { l.Error().Err(err).Msg("Error cleaning up test environment") } }). - WithLogWatcher(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index f72843e77e8..bc9f1308452 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1097,6 +1097,7 @@ func setupAutomationTestDocker( WithMockAdapter(). WithFunding(big.NewFloat(testConfig.ChainlinkNodeFunding)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "Error deploying test environment for Mercury") env.ParallelTransactions(true) diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index 013e4c631c7..3eebc19f8bf 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -25,6 +25,7 @@ func TestCronBasic(t *testing.T) { WithMockAdapter(). WithCLNodes(1). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) @@ -71,6 +72,7 @@ func TestCronJobReplacement(t *testing.T) { WithMockAdapter(). WithCLNodes(1). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 72dc15e7b11..5b35b0d2484 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -31,6 +31,7 @@ func TestFluxBasic(t *testing.T) { WithMockAdapter(). WithCLNodes(3). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index d4f9b5884a2..923ca046ece 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -26,6 +26,7 @@ func TestForwarderOCRBasic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index c9fe3cb11d9..bdfd65c767e 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -35,6 +35,7 @@ func TestForwarderOCR2Basic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index bcf64a5febd..336223b7a4c 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1113,6 +1113,7 @@ func setupKeeperTest(t *testing.T) ( WithCLNodeConfig(clNodeConfig). WithFunding(big.NewFloat(.5)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "Error deploying test environment") diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 0676ed03004..8f0fbd76053 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -33,6 +33,7 @@ func TestOCRv2Basic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) @@ -107,6 +108,7 @@ func TestOCRv2JobReplacement(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 57afbdc1a4a..25192248c46 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -24,6 +24,7 @@ func TestOCRBasic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.5)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index e2cd28b3320..2fc771fb9bf 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -29,6 +29,7 @@ func TestRunLogBasic(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 9c24d97b13b..04120053745 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -29,6 +29,7 @@ func TestVRFBasic(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) env.ParallelTransactions(true) @@ -118,6 +119,7 @@ func TestVRFJobReplacement(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err) env.ParallelTransactions(true) diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 4edb92e5df6..9976de03954 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -34,6 +34,7 @@ func TestVRFv2Basic(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") @@ -125,6 +126,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index f4f52b6ee01..69e1eb7ebde 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -39,6 +39,7 @@ func TestVRFv2Plus(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") @@ -616,6 +617,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") @@ -706,6 +708,7 @@ func TestVRFv2PlusMigration(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 7bf1657316f..48f34e6827c 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -1059,6 +1059,7 @@ func setupLogPollerTestDocker( WithChainOptions(logPolllerSettingsFn). EVMClientNetworkOptions(evmClientSettingsFn). WithStandardCleanup(). + WithLogStream(). Build() require.NoError(t, err, "Error deploying test environment") From 8b13213bfbbe855fc1ffadf82301b47b4ec1b00b Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 8 Dec 2023 06:53:40 -0600 Subject: [PATCH 026/234] go generate a mermaid flowchart for org modules (#11467) --- GNUmakefile | 4 +++ go.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++ main.go | 1 + tools/bin/modgraph | 46 ++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 go.md create mode 100755 tools/bin/modgraph diff --git a/GNUmakefile b/GNUmakefile index 7d0753911a3..09b1dfe87a3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -147,6 +147,10 @@ goreleaser-dev-build: ## Run goreleaser snapshot build goreleaser-dev-release: ## run goreleaser snapshot release ./tools/bin/goreleaser_wrapper release --snapshot --rm-dist --config ${GORELEASER_CONFIG} +.PHONY: modgraph +modgraph: + ./tools/bin/modgraph > go.md + help: @echo "" @echo " .__ .__ .__ .__ __" diff --git a/go.md b/go.md new file mode 100644 index 00000000000..090221a89fd --- /dev/null +++ b/go.md @@ -0,0 +1,65 @@ +# smartcontractkit Go modules +```mermaid +flowchart LR + subgraph chains + chainlink-cosmos + chainlink-evm + chainlink-solana + chainlink-starknet/relayer + end + + subgraph products + chainlink-automation + chainlink-ccip + chainlink-data-streams + chainlink-feeds + chainlink-functions + chainlink-vrf + end + + classDef outline stroke-dasharray:6,fill:none; + class chains,products outline + + chainlink/v2 --> caigo + click caigo href "https://github.com/smartcontractkit/caigo" + chainlink/v2 --> chainlink-automation + click chainlink-automation href "https://github.com/smartcontractkit/chainlink-automation" + chainlink/v2 --> chainlink-common + click chainlink-common href "https://github.com/smartcontractkit/chainlink-common" + chainlink/v2 --> chainlink-cosmos + click chainlink-cosmos href "https://github.com/smartcontractkit/chainlink-cosmos" + chainlink/v2 --> chainlink-data-streams + 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-solana + click chainlink-solana href "https://github.com/smartcontractkit/chainlink-solana" + chainlink/v2 --> chainlink-starknet/relayer + click chainlink-starknet/relayer href "https://github.com/smartcontractkit/chainlink-starknet" + chainlink/v2 --> chainlink-vrf + click chainlink-vrf href "https://github.com/smartcontractkit/chainlink-vrf" + chainlink/v2 --> libocr + click libocr href "https://github.com/smartcontractkit/libocr" + chainlink/v2 --> tdh2/go/ocr2/decryptionplugin + click tdh2/go/ocr2/decryptionplugin href "https://github.com/smartcontractkit/tdh2" + chainlink/v2 --> tdh2/go/tdh2 + click tdh2/go/tdh2 href "https://github.com/smartcontractkit/tdh2" + chainlink/v2 --> wsrpc + click wsrpc href "https://github.com/smartcontractkit/wsrpc" + chainlink-automation --> libocr + chainlink-common --> libocr + chainlink-cosmos --> chainlink-common + chainlink-cosmos --> libocr + chainlink-data-streams --> chainlink-common + chainlink-data-streams --> libocr + chainlink-feeds --> chainlink-common + chainlink-feeds --> libocr + chainlink-solana --> chainlink-common + chainlink-solana --> libocr + chainlink-starknet/relayer --> caigo + chainlink-starknet/relayer --> chainlink-common + chainlink-starknet/relayer --> libocr + chainlink-vrf --> libocr + tdh2/go/ocr2/decryptionplugin --> libocr + tdh2/go/ocr2/decryptionplugin --> tdh2/go/tdh2 +``` diff --git a/main.go b/main.go index 2a224c96d29..7e262f85f28 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core" ) +//go:generate make modgraph func main() { os.Exit(core.Main()) } diff --git a/tools/bin/modgraph b/tools/bin/modgraph new file mode 100755 index 00000000000..b61264cb021 --- /dev/null +++ b/tools/bin/modgraph @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Generates go.md + +set -e + +echo "# smartcontractkit Go modules +\`\`\`mermaid +flowchart LR + subgraph chains + chainlink-cosmos + chainlink-evm + chainlink-solana + chainlink-starknet/relayer + end + + subgraph products + chainlink-automation + chainlink-ccip + chainlink-data-streams + chainlink-feeds + chainlink-functions + chainlink-vrf + end + + classDef outline stroke-dasharray:6,fill:none; + class chains,products outline +" +go mod graph | \ + # org only + grep smartcontractkit.*smartcontractkit | \ + # drop prefix + sed s/"github\.com\/smartcontractkit\/"/""/g | \ + # insert edges + sed s/" "/" --> "/ | \ + # drop versions + sed s/"@[^ ]*"/""/g | \ + # insert links + sed s/"\([^ ]*\)$"/"\1\nclick \1 href \"https:\/\/github.com\/smartcontractkit\/\1\""/ | \ + # truncate links to repo + sed s/"\"https:\/\/github.com\/smartcontractkit\/\([^\"\/]*\)\/.*\""/"\"https:\/\/github.com\/smartcontractkit\/\1\""/ | \ + # dedupe lines + awk '!x[$0]++' | \ + # indent + sed 's/^/ /' +echo "\`\`\`" \ No newline at end of file From d3f99e408c27eb294873fbbfe9d57d0f9aab91f2 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 8 Dec 2023 08:31:24 -0600 Subject: [PATCH 027/234] use servicetest.Run & RunHealthy (#11354) --- .../chains/evm/gas/arbitrum_estimator_test.go | 19 ++++----- .../gas/rollups/l1_gas_price_oracle_test.go | 10 ++--- .../evm/gas/suggested_price_estimator_test.go | 13 +++---- .../evm/headtracker/head_broadcaster_test.go | 11 +++--- core/chains/evm/log/helpers_test.go | 1 + core/chains/evm/log/integration_test.go | 3 +- .../evm/logpoller/log_poller_internal_test.go | 13 +++---- core/chains/evm/monitor/balance_test.go | 18 +++------ core/chains/evm/txmgr/broadcaster_test.go | 14 +++---- core/chains/evm/txmgr/confirmer_test.go | 7 ++-- core/chains/evm/txmgr/txmgr_test.go | 4 +- core/internal/testutils/evmtest/evmtest.go | 4 +- core/services/directrequest/delegate_test.go | 7 ++-- core/services/feeds/service_test.go | 18 ++++----- .../fluxmonitorv2/flux_monitor_test.go | 25 ++++-------- core/services/functions/listener_test.go | 26 +++++-------- .../gateway/connector/connector_test.go | 5 +-- core/services/gateway/gateway_test.go | 4 +- .../functions/handler.functions_test.go | 4 +- .../gateway_integration_test.go | 8 ++-- .../gateway/network/wsconnection_test.go | 10 ++--- core/services/job/runner_integration_test.go | 31 +++++++-------- core/services/job/spawner_test.go | 10 ++--- .../keeper/registry1_1_synchronizer_test.go | 16 +++----- .../keeper/registry1_2_synchronizer_test.go | 25 +++++------- .../keeper/registry1_3_synchronizer_test.go | 39 +++++++------------ .../registry_synchronizer_helper_test.go | 5 ++- core/services/keeper/upkeep_executer_test.go | 4 +- core/services/nurse_test.go | 1 - core/services/ocr/config_overrider_test.go | 8 ++-- core/services/ocr/contract_tracker_test.go | 8 ++-- .../evmregistry/v21/upkeepstate/store_test.go | 9 ++--- core/services/ocrcommon/peer_wrapper_test.go | 7 ++-- core/services/ocrcommon/run_saver_test.go | 6 +-- core/services/ocrcommon/telemetry.go | 2 +- core/services/ocrcommon/telemetry_test.go | 10 ++--- core/services/pg/event_broadcaster_test.go | 5 +-- .../promreporter/prom_reporter_test.go | 12 +++--- core/services/relay/evm/config_poller_test.go | 8 ++-- .../relay/evm/functions/config_poller_test.go | 9 ++--- .../evm/functions/logpoller_wrapper_test.go | 10 ++--- .../relay/evm/mercury/helpers_test.go | 5 +-- .../evm/mercury/wsrpc/cache/cache_set_test.go | 12 +++--- .../relay/evm/mercury/wsrpc/client_test.go | 7 ++-- core/services/srvctest/servicetest.go | 18 --------- .../telemetry_ingress_batch_client_test.go | 7 +--- .../telemetry_ingress_client_test.go | 5 +-- core/services/vrf/delegate_test.go | 23 ++++++----- .../vrf/v1/listener_v1_test_helpers.go | 4 +- core/web/jobs_controller_test.go | 1 + 50 files changed, 214 insertions(+), 317 deletions(-) delete mode 100644 core/services/srvctest/servicetest.go diff --git a/core/chains/evm/gas/arbitrum_estimator_test.go b/core/chains/evm/gas/arbitrum_estimator_test.go index 53f81617988..9d56e2c10ad 100644 --- a/core/chains/evm/gas/arbitrum_estimator_test.go +++ b/core/chains/evm/gas/arbitrum_estimator_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" @@ -66,8 +67,7 @@ func TestArbitrumEstimator(t *testing.T) { }).Return(zeros.Bytes(), nil) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) require.NoError(t, err) // Expected price for a standard l2_suggested_estimator would be 42, but we add a fixed gasPriceBufferPercentage. @@ -92,8 +92,7 @@ func TestArbitrumEstimator(t *testing.T) { assert.Equal(t, big.NewInt(-1), blockNumber) }).Return(zeros.Bytes(), nil) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, assets.NewWeiI(40)) require.Error(t, err) assert.EqualError(t, err, "estimated gas price: 42 wei is greater than the maximum gas price configured: 40 wei") @@ -118,8 +117,7 @@ func TestArbitrumEstimator(t *testing.T) { assert.Equal(t, big.NewInt(-1), blockNumber) }).Return(zeros.Bytes(), nil) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, assets.NewWeiI(110)) assert.EqualError(t, err, "estimated gas price: 120 wei is greater than the maximum gas price configured: 110 wei") assert.Nil(t, gasPrice) @@ -148,8 +146,7 @@ func TestArbitrumEstimator(t *testing.T) { assert.Equal(t, big.NewInt(-1), blockNumber) }).Return(zeros.Bytes(), nil) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "failed to estimate gas; gas price not set") @@ -181,8 +178,7 @@ func TestArbitrumEstimator(t *testing.T) { }).Return(b.Bytes(), nil) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) require.NoError(t, err) require.NotNil(t, gasPrice) @@ -216,8 +212,7 @@ func TestArbitrumEstimator(t *testing.T) { }).Return(b.Bytes(), nil) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) require.Error(t, err, "expected error but got (%s, %d)", gasPrice, chainSpecificGasLimit) }) diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go index 2defedd6b47..1a1d1ffdeee 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -50,8 +51,7 @@ func TestL1GasPriceOracle(t *testing.T) { }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainArbitrum) - require.NoError(t, oracle.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(testutils.Context(t)) require.NoError(t, err) @@ -72,8 +72,7 @@ func TestL1GasPriceOracle(t *testing.T) { }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainKroma) - require.NoError(t, oracle.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(testutils.Context(t)) require.NoError(t, err) @@ -94,8 +93,7 @@ func TestL1GasPriceOracle(t *testing.T) { }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainOptimismBedrock) - require.NoError(t, oracle.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + servicetest.RunHealthy(t, oracle) gasPrice, err := oracle.GasPrice(testutils.Context(t)) require.NoError(t, err) diff --git a/core/chains/evm/gas/suggested_price_estimator_test.go b/core/chains/evm/gas/suggested_price_estimator_test.go index 304e5359107..8e45f5b759a 100644 --- a/core/chains/evm/gas/suggested_price_estimator_test.go +++ b/core/chains/evm/gas/suggested_price_estimator_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" @@ -40,8 +41,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { }) o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) require.NoError(t, err) assert.Equal(t, assets.NewWeiI(42), gasPrice) @@ -57,8 +57,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { (*big.Int)(res).SetInt64(42) }) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, assets.NewWeiI(40)) require.Error(t, err) assert.EqualError(t, err, "estimated gas price: 42 wei is greater than the maximum gas price configured: 40 wei") @@ -75,8 +74,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { (*big.Int)(res).SetInt64(120) }) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, assets.NewWeiI(110)) assert.EqualError(t, err, "estimated gas price: 120 wei is greater than the maximum gas price configured: 110 wei") assert.Nil(t, gasPrice) @@ -96,8 +94,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) - require.NoError(t, o.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, o.Close()) }) + servicetest.RunHealthy(t, o) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "failed to estimate gas; gas price not set") diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 6fb151bfe6c..ac43c08fe87 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + commonhtrk "github.com/smartcontractkit/chainlink/v2/common/headtracker" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" @@ -21,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -68,14 +69,14 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { checker1 := &cltest.MockHeadTrackable{} checker2 := &cltest.MockHeadTrackable{} - hb := headtracker.NewHeadBroadcaster(logger) orm := headtracker.NewORM(db, logger, cfg.Database(), *ethClient.ConfiguredChainID()) hs := headtracker.NewHeadSaver(logger, orm, evmCfg.EVM(), evmCfg.EVM().HeadTracker()) mailMon := utils.NewMailboxMonitor(t.Name()) + servicetest.Run(t, mailMon) + hb := headtracker.NewHeadBroadcaster(logger) + servicetest.Run(t, hb) ht := headtracker.NewHeadTracker(logger, ethClient, evmCfg.EVM(), evmCfg.EVM().HeadTracker(), hb, hs, mailMon) - var ms services.MultiStart - require.NoError(t, ms.Start(testutils.Context(t), mailMon, hb, ht)) - t.Cleanup(func() { require.NoError(t, services.CloseAll(mailMon, hb, ht)) }) + servicetest.Run(t, ht) latest1, unsubscribe1 := hb.Subscribe(checker1) // "latest head" is nil here because we didn't receive any yet diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index 49f9266f705..e41f08e8d20 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index 2d66cdf585c..e5b6ad3caf5 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -1324,7 +1325,7 @@ func TestBroadcaster_AppendLogChannel(t *testing.T) { ch3 := make(chan types.Log) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.RunHealthy(t, utils.NewMailboxMonitor(t.Name())) lb := log.NewBroadcaster(nil, ethClient, nil, logger.Test(t), nil, mailMon) chCombined := lb.ExportedAppendLogChannel(ch1, ch2) chCombined = lb.ExportedAppendLogChannel(chCombined, ch3) diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index e3ba8b655e8..f840eefc7b8 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -351,8 +352,7 @@ func TestLogPoller_Replay(t *testing.T) { ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil).Maybe() // in case task gets delayed by >= 100ms t.Cleanup(lp.reset) - require.NoError(t, lp.Start(ctx)) - t.Cleanup(func() { assert.NoError(t, lp.Close()) }) + servicetest.Run(t, lp) select { case <-ctx.Done(): @@ -389,8 +389,7 @@ func TestLogPoller_Replay(t *testing.T) { ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil).Maybe() // in case task gets delayed by >= 100ms t.Cleanup(lp.reset) - require.NoError(t, lp.Start(ctx)) - t.Cleanup(func() { assert.NoError(t, lp.Close()) }) + servicetest.Run(t, lp) select { case <-ctx.Done(): @@ -402,8 +401,7 @@ func TestLogPoller_Replay(t *testing.T) { // ReplayAsync should return as soon as replayStart is received t.Run("ReplayAsync success", func(t *testing.T) { t.Cleanup(lp.reset) - require.NoError(t, lp.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, lp.Close()) }) + servicetest.Run(t, lp) lp.ReplayAsync(1) @@ -412,8 +410,7 @@ func TestLogPoller_Replay(t *testing.T) { t.Run("ReplayAsync error", func(t *testing.T) { t.Cleanup(lp.reset) - require.NoError(t, lp.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, lp.Close()) }) + servicetest.Run(t, lp) anyErr := errors.New("async error") observedLogs.TakeAll() diff --git a/core/chains/evm/monitor/balance_test.go b/core/chains/evm/monitor/balance_test.go index c2c976e78da..246d5d0759f 100644 --- a/core/chains/evm/monitor/balance_test.go +++ b/core/chains/evm/monitor/balance_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor" @@ -44,7 +45,6 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) - defer func() { assert.NoError(t, bm.Close()) }() k0bal := big.NewInt(42) k1bal := big.NewInt(43) @@ -54,7 +54,7 @@ func TestBalanceMonitor_Start(t *testing.T) { ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Return(k0bal, nil) ethClient.On("BalanceAt", mock.Anything, k1Addr, nilBigInt).Once().Return(k1bal, nil) - assert.NoError(t, bm.Start(testutils.Context(t))) + servicetest.RunHealthy(t, bm) gomega.NewWithT(t).Eventually(func() *big.Int { return bm.GetEthBalance(k0Addr).ToInt() @@ -72,12 +72,11 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) - defer func() { assert.NoError(t, bm.Close()) }() k0bal := big.NewInt(42) ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Return(k0bal, nil) - assert.NoError(t, bm.Start(testutils.Context(t))) + servicetest.RunHealthy(t, bm) gomega.NewWithT(t).Eventually(func() *big.Int { return bm.GetEthBalance(k0Addr).ToInt() @@ -92,7 +91,6 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) - defer func() { assert.NoError(t, bm.Close()) }() ctxCancelledAwaiter := cltest.NewAwaiter() ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Run(func(args mock.Arguments) { @@ -122,13 +120,12 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) - defer func() { assert.NoError(t, bm.Close()) }() ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt). Once(). Return(nil, errors.New("a little easter egg for the 4chan link marines error")) - assert.NoError(t, bm.Start(testutils.Context(t))) + servicetest.RunHealthy(t, bm) gomega.NewWithT(t).Consistently(func() *big.Int { return bm.GetEthBalance(k0Addr).ToInt() @@ -160,8 +157,7 @@ func TestBalanceMonitor_OnNewLongestChain_UpdatesBalance(t *testing.T) { ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Return(k0bal, nil) ethClient.On("BalanceAt", mock.Anything, k1Addr, nilBigInt).Once().Return(k1bal, nil) - require.NoError(t, bm.Start(testutils.Context(t))) - defer func() { assert.NoError(t, bm.Close()) }() + servicetest.RunHealthy(t, bm) ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Return(k0bal, nil) ethClient.On("BalanceAt", mock.Anything, k1Addr, nilBigInt).Once().Return(k1bal, nil) @@ -205,7 +201,7 @@ func TestBalanceMonitor_FewerRPCCallsWhenBehind(t *testing.T) { ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything). Once(). Return(big.NewInt(1), nil) - require.NoError(t, bm.Start(testutils.Context(t))) + servicetest.RunHealthy(t, bm) head := cltest.Head(0) @@ -234,8 +230,6 @@ func TestBalanceMonitor_FewerRPCCallsWhenBehind(t *testing.T) { mockUnblocker <- time.Time{} }) - bm.Close() - // Make sure the BalanceAt mock wasn't called more than once assert.LessOrEqual(t, callCount.Load(), int32(1)) } diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index c6e05b0954b..21a4bd2865c 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -23,6 +23,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" @@ -59,7 +60,6 @@ func NewTestEthBroadcaster( nonceAutoSync bool, ) *txmgr.Broadcaster { t.Helper() - ctx := testutils.Context(t) lggr := logger.Test(t) ge := config.EVM().GasEstimator() @@ -70,8 +70,7 @@ func NewTestEthBroadcaster( // Mark instance as test ethBroadcaster.XXXTestDisableUnstartedTxAutoProcessing() - require.NoError(t, ethBroadcaster.Start(ctx)) - t.Cleanup(func() { assert.NoError(t, ethBroadcaster.Close()) }) + servicetest.Run(t, ethBroadcaster) return ethBroadcaster } @@ -634,8 +633,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi eb.XXXTestDisableUnstartedTxAutoProcessing() // Start instance of broadcaster - require.NoError(t, eb.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, eb.Close()) }) + servicetest.Run(t, eb) mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) @@ -1794,8 +1792,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, txBuilder, txNonceSyncer, lggr, checkerFactory, true) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(ethNodeNonce), nil).Once() - require.NoError(t, eb.Start(ctx)) - defer func() { assert.NoError(t, eb.Close()) }() + servicetest.Run(t, eb) testutils.WaitForLogMessage(t, observed, "Fast-forward sequence") @@ -1828,8 +1825,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("something exploded")).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(ethNodeNonce, nil) - require.NoError(t, eb.Start(ctx)) - defer func() { assert.NoError(t, eb.Close()) }() + servicetest.Run(t, eb) testutils.WaitForLogMessage(t, observed, "Fast-forward sequence") diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 84c42cd00f1..9d909d58344 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" @@ -1646,7 +1647,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Maybe() // Create confirmer with necessary state ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr) - require.NoError(t, ec.Start(testutils.Context(t))) + servicetest.Run(t, ec) currentHead := int64(30) oldEnough := int64(15) nonce := int64(0) @@ -1691,7 +1692,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Maybe() ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr) - require.NoError(t, ec.Start(testutils.Context(t))) + servicetest.Run(t, ec) currentHead := int64(30) oldEnough := int64(15) nonce := int64(0) @@ -3080,6 +3081,6 @@ func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Cl txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), config.Database(), ks, txBuilder, lggr) ec.SetResumeCallback(fn) - require.NoError(t, ec.Start(testutils.Context(t))) + servicetest.Run(t, ec) return ec } diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 745623ed77e..9833acfd459 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -21,6 +21,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" @@ -552,8 +553,7 @@ func TestTxm_Reset(t *testing.T) { assert.EqualError(t, err, "not started") }) - require.NoError(t, txm.Start(testutils.Context(t))) - defer func() { assert.NoError(t, txm.Close()) }() + servicetest.Run(t, txm) t.Run("returns no error if started", func(t *testing.T) { err := txm.Reset(addr, false) diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 423615145e5..e0a447a3274 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/types" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" @@ -36,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -118,7 +118,7 @@ func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) legacyevm.ChainR } } if opts.MailMon == nil { - opts.MailMon = srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + opts.MailMon = servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) } if testopts.GasEstimator != nil { opts.GenGasEstimator = func(*big.Int) gas.EvmFeeEstimator { diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index 56c28e57458..2d5b9bef03b 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -14,6 +14,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -31,7 +33,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" pipeline_mocks "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -43,7 +44,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) relayerExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: cfg, Client: ethClient, MailMon: mailMon, KeyStore: keyStore.Eth()}) lggr := logger.TestLogger(t) @@ -80,7 +81,7 @@ func NewDirectRequestUniverseWithConfig(t *testing.T, cfg chainlink.GeneralConfi runner := pipeline_mocks.NewRunner(t) broadcaster.On("AddDependents", 1) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index 271321a1169..bd0993825e2 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -9,8 +9,15 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" + "github.com/lib/pq" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -36,13 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" - - "github.com/google/uuid" - "github.com/lib/pq" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" ) const FluxMonitorTestSpecTemplate = ` @@ -3465,9 +3465,7 @@ func Test_Service_StartStop(t *testing.T) { tt.beforeFunc(svc) } - require.NoError(t, svc.Start(testutils.Context(t))) - - svc.Close() + servicetest.Run(t, svc) }) } } diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index 1a14fb8bd0a..b13edcc12d8 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -21,6 +21,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -701,7 +702,7 @@ func TestPollingDeviationChecker_BuffersLogs(t *testing.T) { Once(). Run(func(mock.Arguments) { readyToAssert.ItHappened() }) - require.NoError(t, fm.Start(testutils.Context(t))) + servicetest.Run(t, fm) var logBroadcasts []*logmocks.Broadcast @@ -724,8 +725,6 @@ func TestPollingDeviationChecker_BuffersLogs(t *testing.T) { logsAwaiter.ItHappened() readyToAssert.AwaitOrFail(t) - - fm.Close() } func TestFluxMonitor_TriggerIdleTimeThreshold(t *testing.T) { @@ -1049,8 +1048,7 @@ func TestFluxMonitor_IdleTimerResetsOnNewRound(t *testing.T) { idleDurationOccured := make(chan struct{}, 4) initialPollOccurred := make(chan struct{}, 1) - require.NoError(t, fm.Start(testutils.Context(t))) - t.Cleanup(func() { fm.Close() }) + servicetest.Run(t, fm) // Initial Poll roundState1 := flux_aggregator_wrapper.OracleRoundState{RoundId: 1, EligibleToSubmit: false, LatestSubmission: answerBigInt, StartedAt: now()} @@ -1169,11 +1167,9 @@ func TestFluxMonitor_RoundTimeoutCausesPoll_timesOutAtZero(t *testing.T) { require.NoError(t, fm.SetOracleAddress()) fm.ExportedRoundState(t) - require.NoError(t, fm.Start(testutils.Context(t))) + servicetest.Run(t, fm) g.Eventually(ch).Should(gomega.BeClosed()) - - fm.Close() } func TestFluxMonitor_UsesPreviousRoundStateOnStartup_RoundTimeout(t *testing.T) { @@ -1229,15 +1225,13 @@ func TestFluxMonitor_UsesPreviousRoundStateOnStartup_RoundTimeout(t *testing.T) Run(func(mock.Arguments) { close(chRoundState) }). Maybe() - require.NoError(t, fm.Start(testutils.Context(t))) + servicetest.Run(t, fm) if test.expectedToSubmit { g.Eventually(chRoundState).Should(gomega.BeClosed()) } else { g.Consistently(chRoundState).ShouldNot(gomega.BeClosed()) } - - require.NoError(t, fm.Close()) }) } } @@ -1310,8 +1304,7 @@ func TestFluxMonitor_UsesPreviousRoundStateOnStartup_IdleTimer(t *testing.T) { }). Maybe() - require.NoError(t, fm.Start(testutils.Context(t))) - t.Cleanup(func() { fm.Close() }) + servicetest.Run(t, fm) assert.Eventually(t, func() bool { return len(initialPollOccurred) == 1 }, 3*time.Second, 10*time.Millisecond) @@ -1385,7 +1378,7 @@ func TestFluxMonitor_RoundTimeoutCausesPoll_timesOutNotZero(t *testing.T) { Run(func(mock.Arguments) { close(chRoundState2) }). Once() - require.NoError(t, fm.Start(testutils.Context(t))) + servicetest.Run(t, fm) tm.logBroadcaster.On("WasAlreadyConsumed", mock.Anything, mock.Anything).Return(false, nil) tm.logBroadcaster.On("MarkConsumed", mock.Anything, mock.Anything).Return(nil) @@ -1401,7 +1394,6 @@ func TestFluxMonitor_RoundTimeoutCausesPoll_timesOutNotZero(t *testing.T) { g.Eventually(chRoundState2).Should(gomega.BeClosed()) time.Sleep(time.Duration(2*timeout) * time.Second) - require.NoError(t, fm.Close()) } func TestFluxMonitor_ConsumeLogBroadcast(t *testing.T) { @@ -1927,8 +1919,7 @@ func TestFluxMonitor_DrumbeatTicker(t *testing.T) { Return(flux_aggregator_wrapper.OracleRoundState{RoundId: 4, EligibleToSubmit: false, LatestSubmission: answerBigInt, StartedAt: now()}, nil). Maybe() - require.NoError(t, fm.Start(testutils.Context(t))) - defer func() { assert.NoError(t, fm.Close()) }() + servicetest.Run(t, fm) waitTime := 15 * time.Second interval := 50 * time.Millisecond diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 0fcc9c65599..5020537bf61 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -19,6 +19,8 @@ import ( decryptionPlugin "github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -38,7 +40,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" evmrelay_mocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types/mocks" s4_mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" sync_mocks "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" @@ -81,7 +82,7 @@ func NewFunctionsListenerUniverse(t *testing.T, timeoutSec int, pruneFrequencySe ethClient := evmtest.NewEthClientMockWithDefaultChain(t) broadcaster := log_mocks.NewBroadcaster(t) broadcaster.On("AddDependents", 1) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) db := pgtest.NewSqlxDB(t) kst := cltest.NewKeyStore(t, db, cfg.Database()) @@ -175,9 +176,8 @@ func TestFunctionsListener_HandleOracleRequestV1_Success(t *testing.T) { close(doneCh) }).Return(nil) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) <-doneCh - uni.service.Close() } func TestFunctionsListener_HandleOffchainRequest_Success(t *testing.T) { @@ -270,9 +270,8 @@ func TestFunctionsListener_HandleOracleRequestV1_ComputationError(t *testing.T) close(doneCh) }).Return(nil) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) <-doneCh - uni.service.Close() } func TestFunctionsListener_HandleOracleRequestV1_ThresholdDecryptedSecrets(t *testing.T) { @@ -312,9 +311,8 @@ func TestFunctionsListener_HandleOracleRequestV1_ThresholdDecryptedSecrets(t *te close(doneCh) }).Return(nil) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) <-doneCh - uni.service.Close() } func TestFunctionsListener_HandleOracleRequestV1_CBORTooBig(t *testing.T) { @@ -339,9 +337,8 @@ func TestFunctionsListener_HandleOracleRequestV1_CBORTooBig(t *testing.T) { close(doneCh) }).Return(nil) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) <-doneCh - uni.service.Close() } func TestFunctionsListener_ReportSourceCodeDomains(t *testing.T) { @@ -395,9 +392,8 @@ func TestFunctionsListener_PruneRequests(t *testing.T) { doneCh <- true }) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) <-doneCh - uni.service.Close() } func TestFunctionsListener_TimeoutRequests(t *testing.T) { @@ -411,9 +407,8 @@ func TestFunctionsListener_TimeoutRequests(t *testing.T) { doneCh <- true }) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) <-doneCh - uni.service.Close() } func TestFunctionsListener_ORMDoesNotFreezeHandlersForever(t *testing.T) { @@ -434,7 +429,6 @@ func TestFunctionsListener_ORMDoesNotFreezeHandlersForever(t *testing.T) { ormCallExited.Done() }).Return(errors.New("timeout")) - require.NoError(t, uni.service.Start(testutils.Context(t))) + servicetest.Run(t, uni.service) ormCallExited.Wait() // should not freeze - uni.service.Close() } diff --git a/core/services/gateway/connector/connector_test.go b/core/services/gateway/connector/connector_test.go index 4001914524b..1c2c6d26b10 100644 --- a/core/services/gateway/connector/connector_test.go +++ b/core/services/gateway/connector/connector_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector/mocks" @@ -121,8 +121,7 @@ func TestGatewayConnector_CleanStartAndClose(t *testing.T) { handler.On("Start", mock.Anything).Return(nil) handler.On("Close").Return(nil) signer.On("Sign", mock.Anything).Return(nil, errors.New("cannot sign")) - require.NoError(t, connector.Start(testutils.Context(t))) - require.NoError(t, connector.Close()) + servicetest.Run(t, connector) } func TestGatewayConnector_NewAuthHeader_SignerError(t *testing.T) { diff --git a/core/services/gateway/gateway_test.go b/core/services/gateway/gateway_test.go index 5fad6315a31..74d689fffe1 100644 --- a/core/services/gateway/gateway_test.go +++ b/core/services/gateway/gateway_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway" @@ -130,8 +131,7 @@ func TestGateway_CleanStartAndClose(t *testing.T) { lggr := logger.TestLogger(t) gateway, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, buildConfig("")), gateway.NewHandlerFactory(nil, lggr), lggr) require.NoError(t, err) - require.NoError(t, gateway.Start(testutils.Context(t))) - require.NoError(t, gateway.Close()) + servicetest.Run(t, gateway) } func requireJsonRPCResult(t *testing.T, response []byte, expectedId string, expectedResult string) { diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index f36b64709a2..1d6dd109625 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" @@ -97,8 +98,7 @@ func TestFunctionsHandler_CleanStartAndClose(t *testing.T) { handler, err := functions.NewFunctionsHandlerFromConfig(json.RawMessage("{}"), &config.DONConfig{}, nil, nil, logger.TestLogger(t)) require.NoError(t, err) - require.NoError(t, handler.Start(testutils.Context(t))) - require.NoError(t, handler.Close()) + servicetest.Run(t, handler) } func TestFunctionsHandler_HandleUserMessage_SecretsSet(t *testing.T) { diff --git a/core/services/gateway/integration_tests/gateway_integration_test.go b/core/services/gateway/integration_tests/gateway_integration_test.go index 310047950e6..415a8f67cf8 100644 --- a/core/services/gateway/integration_tests/gateway_integration_test.go +++ b/core/services/gateway/integration_tests/gateway_integration_test.go @@ -14,6 +14,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway" @@ -119,7 +120,7 @@ func TestIntegration_Gateway_NoFullNodes_BasicConnectionAndMessage(t *testing.T) gatewayConfig := fmt.Sprintf(gatewayConfigTemplate, nodeKeys.Address) gateway, err := gateway.NewGatewayFromConfig(parseGatewayConfig(t, gatewayConfig), gateway.NewHandlerFactory(nil, lggr), lggr) require.NoError(t, err) - require.NoError(t, gateway.Start(testutils.Context(t))) + servicetest.Run(t, gateway) userPort, nodePort := gateway.GetUserPort(), gateway.GetNodePort() userUrl := fmt.Sprintf("http://localhost:%d/user", userPort) nodeUrl := fmt.Sprintf("ws://localhost:%d/node", nodePort) @@ -129,7 +130,7 @@ func TestIntegration_Gateway_NoFullNodes_BasicConnectionAndMessage(t *testing.T) connector, err := connector.NewGatewayConnector(parseConnectorConfig(t, nodeConfigTemplate, nodeKeys.Address, nodeUrl), client, client, utils.NewRealClock(), lggr) require.NoError(t, err) client.connector = connector - require.NoError(t, connector.Start(testutils.Context(t))) + servicetest.Run(t, connector) // Send requests until one of them reaches Connector gomega.NewGomegaWithT(t).Eventually(func() bool { @@ -144,7 +145,4 @@ func TestIntegration_Gateway_NoFullNodes_BasicConnectionAndMessage(t *testing.T) _, _ = httpClient.Do(req) // could initially return error if Gateway is not fully initialized yet return client.done.Load() }, testutils.WaitTimeout(t), testutils.TestInterval).Should(gomega.Equal(true)) - - require.NoError(t, connector.Close()) - require.NoError(t, gateway.Close()) } diff --git a/core/services/gateway/network/wsconnection_test.go b/core/services/gateway/network/wsconnection_test.go index 5fd8aa50e33..4ded4f40b10 100644 --- a/core/services/gateway/network/wsconnection_test.go +++ b/core/services/gateway/network/wsconnection_test.go @@ -9,6 +9,7 @@ import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/network" @@ -30,18 +31,17 @@ func (ssl *serverSideLogic) wsHandler(w http.ResponseWriter, r *http.Request) { } func TestWSConnectionWrapper_ClientReconnect(t *testing.T) { - ctx := testutils.Context(t) lggr := logger.TestLogger(t) // server ssl := &serverSideLogic{connWrapper: network.NewWSConnectionWrapper(lggr)} - require.NoError(t, ssl.connWrapper.Start(ctx)) + servicetest.Run(t, ssl.connWrapper) s := httptest.NewServer(http.HandlerFunc(ssl.wsHandler)) serverURL := "ws" + strings.TrimPrefix(s.URL, "http") defer s.Close() // client clientConnWrapper := network.NewWSConnectionWrapper(lggr) - require.NoError(t, clientConnWrapper.Start(ctx)) + servicetest.Run(t, clientConnWrapper) // connect, write a message, disconnect conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) @@ -64,8 +64,4 @@ func TestWSConnectionWrapper_ClientReconnect(t *testing.T) { require.NoError(t, writeErr) <-ssl.connWrapper.ReadChannel() // consumed by server conn.Close() - - ssl.connWrapper.Close() - clientConnWrapper.Close() - clientConnWrapper.Close() // safe to call Close() twice } diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index ef0458312bc..0223f1a10d5 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -23,6 +23,8 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -40,7 +42,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -91,8 +92,7 @@ func TestRunner(t *testing.T) { _, placeHolderAddress := cltest.MustInsertRandomKey(t, keyStore.Eth()) - require.NoError(t, runner.Start(ctx)) - t.Cleanup(func() { assert.NoError(t, runner.Close()) }) + servicetest.Run(t, runner) t.Run("gets the election result winner", func(t *testing.T) { var httpURL string @@ -451,8 +451,7 @@ answer1 [type=median index=0]; _, err = keyStore.P2P().Create() assert.NoError(t, err) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) - require.NoError(t, pw.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, pw.Close()) }) + servicetest.Run(t, pw) sd := ocr.NewDelegate( db, jobORM, @@ -463,7 +462,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -486,8 +485,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) - require.NoError(t, pw.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, pw.Close()) }) + servicetest.Run(t, pw) sd := ocr.NewDelegate( db, jobORM, @@ -498,7 +496,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -515,8 +513,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) - require.NoError(t, pw.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, pw.Close()) }) + servicetest.Run(t, pw) sd := ocr.NewDelegate( db, jobORM, @@ -527,7 +524,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -571,8 +568,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) - require.NoError(t, pw.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, pw.Close()) }) + servicetest.Run(t, pw) sd := ocr.NewDelegate( db, jobORM, @@ -583,7 +579,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), ) jb.OCROracleSpec.CaptureEATelemetry = tc.jbCaptureEATelemetry @@ -616,8 +612,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) - require.NoError(t, pw.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, pw.Close()) }) + servicetest.Run(t, pw) sd := ocr.NewDelegate( db, jobORM, @@ -628,7 +623,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), ) services, err := sd.ServicesForSpec(*jb) require.NoError(t, err) diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 0ad76491438..d639ce859af 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/bridges" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -33,7 +34,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" evmrelayer "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -128,7 +128,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { serviceA2 := mocks.NewServiceCtx(t) serviceA1.On("Start", mock.Anything).Return(nil).Once() serviceA2.On("Start", mock.Anything).Return(nil).Once().Run(func(mock.Arguments) { eventuallyA.ItHappened() }) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) dA := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, make(chan struct{}), dA} @@ -187,7 +187,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { lggr := logger.TestLogger(t) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ @@ -221,7 +221,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { lggr := logger.TestLogger(t) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ @@ -299,7 +299,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { jobOCR2VRF := makeOCR2VRFJobSpec(t, keyStore, config, address, chain.ID(), 2) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) processConfig := plugins.NewRegistrarConfig(loop.GRPCOpts{}, func(name string) (*plugins.RegisteredLoop, error) { return nil, nil }) ocr2DelegateConfig := ocr2.NewDelegateConfig(config.OCR2(), config.Mercury(), config.Threshold(), config.Insecure(), config.JobPipeline(), config.Database(), processConfig) diff --git a/core/services/keeper/registry1_1_synchronizer_test.go b/core/services/keeper/registry1_1_synchronizer_test.go index 031b7a59074..fb0b1866c41 100644 --- a/core/services/keeper/registry1_1_synchronizer_test.go +++ b/core/services/keeper/registry1_1_synchronizer_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -217,8 +218,7 @@ func Test_RegistrySynchronizer1_1_ConfigSetLog(t *testing.T) { upkeepConfig1_1, 0) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) var registry keeper.Registry require.NoError(t, db.Get(®istry, `SELECT * FROM keeper_registries`)) @@ -266,8 +266,7 @@ func Test_RegistrySynchronizer1_1_KeepersUpdatedLog(t *testing.T) { upkeepConfig1_1, 0) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) var registry keeper.Registry require.NoError(t, db.Get(®istry, `SELECT * FROM keeper_registries`)) @@ -313,8 +312,7 @@ func Test_RegistrySynchronizer1_1_UpkeepCanceledLog(t *testing.T) { upkeepConfig1_1, 3) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { require.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 3) @@ -352,8 +350,7 @@ func Test_RegistrySynchronizer1_1_UpkeepRegisteredLog(t *testing.T) { upkeepConfig1_1, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -396,8 +393,7 @@ func Test_RegistrySynchronizer1_1_UpkeepPerformedLog(t *testing.T) { upkeepConfig1_1, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) diff --git a/core/services/keeper/registry1_2_synchronizer_test.go b/core/services/keeper/registry1_2_synchronizer_test.go index e7d8d6a48a2..b7456ad94e4 100644 --- a/core/services/keeper/registry1_2_synchronizer_test.go +++ b/core/services/keeper/registry1_2_synchronizer_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -237,8 +238,7 @@ func Test_RegistrySynchronizer1_2_ConfigSetLog(t *testing.T) { 2, 0) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) var registry keeper.Registry require.NoError(t, db.Get(®istry, `SELECT * FROM keeper_registries`)) @@ -290,8 +290,7 @@ func Test_RegistrySynchronizer1_2_KeepersUpdatedLog(t *testing.T) { 2, 0) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) var registry keeper.Registry require.NoError(t, db.Get(®istry, `SELECT * FROM keeper_registries`)) @@ -342,8 +341,7 @@ func Test_RegistrySynchronizer1_2_UpkeepCanceledLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { require.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 3) @@ -382,8 +380,7 @@ func Test_RegistrySynchronizer1_2_UpkeepRegisteredLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -427,8 +424,7 @@ func Test_RegistrySynchronizer1_2_UpkeepPerformedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -482,8 +478,7 @@ func Test_RegistrySynchronizer1_2_UpkeepGasLimitSetLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -535,8 +530,7 @@ func Test_RegistrySynchronizer1_2_UpkeepReceivedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -578,8 +572,7 @@ func Test_RegistrySynchronizer1_2_UpkeepMigratedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { require.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 3) diff --git a/core/services/keeper/registry1_3_synchronizer_test.go b/core/services/keeper/registry1_3_synchronizer_test.go index a0522fd717e..6e3be4ea785 100644 --- a/core/services/keeper/registry1_3_synchronizer_test.go +++ b/core/services/keeper/registry1_3_synchronizer_test.go @@ -12,19 +12,20 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" - registry1_3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + registry1_3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) var registryConfig1_3 = registry1_3.Config{ @@ -242,8 +243,7 @@ func Test_RegistrySynchronizer1_3_ConfigSetLog(t *testing.T) { 2, 0) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) var registry keeper.Registry require.NoError(t, db.Get(®istry, `SELECT * FROM keeper_registries`)) @@ -295,8 +295,7 @@ func Test_RegistrySynchronizer1_3_KeepersUpdatedLog(t *testing.T) { 2, 0) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) var registry keeper.Registry require.NoError(t, db.Get(®istry, `SELECT * FROM keeper_registries`)) @@ -347,8 +346,7 @@ func Test_RegistrySynchronizer1_3_UpkeepCanceledLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { require.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 3) @@ -387,8 +385,7 @@ func Test_RegistrySynchronizer1_3_UpkeepRegisteredLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -432,8 +429,7 @@ func Test_RegistrySynchronizer1_3_UpkeepPerformedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -487,8 +483,7 @@ func Test_RegistrySynchronizer1_3_UpkeepGasLimitSetLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -540,8 +535,7 @@ func Test_RegistrySynchronizer1_3_UpkeepReceivedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) @@ -583,8 +577,7 @@ func Test_RegistrySynchronizer1_3_UpkeepMigratedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { require.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 3) @@ -625,8 +618,7 @@ func Test_RegistrySynchronizer1_3_UpkeepPausedLog_UpkeepUnpausedLog(t *testing.T 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { require.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 3) @@ -695,8 +687,7 @@ func Test_RegistrySynchronizer1_3_UpkeepCheckDataUpdatedLog(t *testing.T) { 2, 1) - require.NoError(t, synchronizer.Start(testutils.Context(t))) - defer func() { assert.NoError(t, synchronizer.Close()) }() + servicetest.Run(t, synchronizer) cltest.WaitForCount(t, db, "keeper_registries", 1) cltest.WaitForCount(t, db, "upkeep_registrations", 1) diff --git a/core/services/keeper/registry_synchronizer_helper_test.go b/core/services/keeper/registry_synchronizer_helper_test.go index 966366b1069..5ba60db3962 100644 --- a/core/services/keeper/registry_synchronizer_helper_test.go +++ b/core/services/keeper/registry_synchronizer_helper_test.go @@ -10,6 +10,8 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -21,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -72,7 +73,7 @@ func setupRegistrySync(t *testing.T, version keeper.RegistryVersion) ( })).Maybe().Return(func() {}) lbMock.On("IsConnected").Return(true).Maybe() - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) orm := keeper.NewORM(db, logger.TestLogger(t), ch.Config().Database()) synchronizer := keeper.NewRegistrySynchronizer(keeper.RegistrySynchronizerOptions{ diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 7bbecafa22d..123b1dc0de1 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -15,6 +15,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -88,8 +89,7 @@ func setup(t *testing.T, estimator gas.EvmFeeEstimator, overrideFn func(c *chain lggr := logger.TestLogger(t) executer := keeper.NewUpkeepExecuter(job, orm, jpv2.Pr, ethClient, ch.HeadBroadcaster(), ch.GasEstimator(), lggr, ch.Config().Keeper(), job.KeeperSpec.FromAddress.Address()) upkeep := cltest.MustInsertUpkeepForRegistry(t, db, ch.Config().Database(), registry) - require.NoError(t, executer.Start(testutils.Context(t))) - t.Cleanup(func() { executer.Close() }) + servicetest.Run(t, executer) return db, cfg, ethClient, executer, registry, upkeep, job, jpv2, txm, keyStore, ch, orm } diff --git a/core/services/nurse_test.go b/core/services/nurse_test.go index 4e68501b74b..79f57d91235 100644 --- a/core/services/nurse_test.go +++ b/core/services/nurse_test.go @@ -128,7 +128,6 @@ func TestNurse(t *testing.T) { n2, err := nrse.totalProfileBytes() require.NoError(t, err) require.Greater(t, n2, uint64(0)) - } func profileExists(t *testing.T, nrse *Nurse, typ string) bool { diff --git a/core/services/ocr/config_overrider_test.go b/core/services/ocr/config_overrider_test.go index 245d6348765..acd5245e19b 100644 --- a/core/services/ocr/config_overrider_test.go +++ b/core/services/ocr/config_overrider_test.go @@ -14,9 +14,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" @@ -72,7 +72,7 @@ func TestIntegration_OCRConfigOverrider_EntersHibernation(t *testing.T) { Run(checkFlagsAddress(t, uni.contractAddress)). Return([]bool{true, true}, nil) - require.NoError(t, uni.overrider.Start(testutils.Context(t))) + servicetest.Run(t, uni.overrider) // not hibernating initially require.Nil(t, uni.overrider.ConfigOverride()) @@ -102,7 +102,7 @@ func Test_OCRConfigOverrider(t *testing.T) { Run(checkFlagsAddress(t, uni.contractAddress)). Return([]bool{true, true}, nil) - require.NoError(t, uni.overrider.Start(testutils.Context(t))) + servicetest.Run(t, uni.overrider) // not hibernating initially require.Nil(t, uni.overrider.ConfigOverride()) @@ -130,7 +130,7 @@ func Test_OCRConfigOverrider(t *testing.T) { Run(checkFlagsAddress(t, uni.contractAddress)). Return([]bool{true, false}, nil) - require.NoError(t, uni.overrider.Start(testutils.Context(t))) + servicetest.Run(t, uni.overrider) // initially enters hibernation expectedOverride := &ocrtypes.ConfigOverride{AlphaPPB: math.MaxUint64, DeltaC: uni.overrider.DeltaCFromAddress} diff --git a/core/services/ocr/contract_tracker_test.go b/core/services/ocr/contract_tracker_test.go index 5684219cf16..af65f330d66 100644 --- a/core/services/ocr/contract_tracker_test.go +++ b/core/services/ocr/contract_tracker_test.go @@ -15,6 +15,8 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -29,7 +31,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" ocrmocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -83,7 +84,7 @@ func newContractTrackerUni(t *testing.T, opts ...interface{}) (uni contractTrack uni.hb = commonmocks.NewHeadBroadcaster[*evmtypes.Head, common.Hash](t) uni.ec = evmtest.NewEthClientMock(t) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) db := pgtest.NewSqlxDB(t) uni.tracker = ocr.NewOCRContractTracker( contract, @@ -148,13 +149,12 @@ func Test_OCRContractTracker_LatestBlockHeight(t *testing.T) { uni.db.On("LoadLatestRoundRequested").Return(offchainaggregator.OffchainAggregatorRoundRequested{}, nil) uni.lb.On("Register", uni.tracker, mock.Anything).Return(func() {}) - require.NoError(t, uni.tracker.Start(testutils.Context(t))) + servicetest.Run(t, uni.tracker) l, err := uni.tracker.LatestBlockHeight(testutils.Context(t)) require.NoError(t, err) assert.Equal(t, uint64(42), l) - require.NoError(t, uni.tracker.Close()) }) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go index 138b9ffd782..7108e1b64c4 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go @@ -14,6 +14,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -346,7 +347,7 @@ func TestUpkeepStateStore_SetSelectIntegration(t *testing.T) { scanner := &mockScanner{} store := NewUpkeepStateStore(orm, lggr, scanner) - require.NoError(t, store.Start(ctx)) + servicetest.Run(t, store) t.Cleanup(func() { t.Log("cleaning up database") @@ -379,8 +380,6 @@ func TestUpkeepStateStore_SetSelectIntegration(t *testing.T) { observedLogs.TakeAll() require.Equal(t, 0, observedLogs.Len()) - - require.NoError(t, store.Close()) }) } } @@ -467,7 +466,7 @@ func TestUpkeepStateStore_Service(t *testing.T) { store.retention = 500 * time.Millisecond store.cleanCadence = 100 * time.Millisecond - assert.NoError(t, store.Start(ctx), "no error from starting service") + servicetest.Run(t, store) // add a value to set up the test require.NoError(t, store.SetUpkeepState(ctx, ocr2keepers.CheckResult{ @@ -493,8 +492,6 @@ func TestUpkeepStateStore_Service(t *testing.T) { values, err = store.SelectByWorkIDs(ctx, "0x2") require.NoError(t, err, "no error from selecting states") require.Equal(t, []ocr2keepers.UpkeepState{ocr2keepers.UnknownState}, values, "selected values should match expected") - - assert.NoError(t, store.Close(), "no error from closing service") } func createUpkeepIDForTest(v int64) ocr2keepers.UpkeepIdentifier { diff --git a/core/services/ocrcommon/peer_wrapper_test.go b/core/services/ocrcommon/peer_wrapper_test.go index 854ecb153ea..dbe22523728 100644 --- a/core/services/ocrcommon/peer_wrapper_test.go +++ b/core/services/ocrcommon/peer_wrapper_test.go @@ -9,6 +9,7 @@ import ( p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -52,9 +53,8 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { }) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) - require.NoError(t, pw.Start(testutils.Context(t)), "foo") + servicetest.Run(t, pw) require.Equal(t, k.PeerID(), pw.PeerID) - require.NoError(t, pw.Close()) }) t.Run("with one p2p key and mismatching P2P.PeerID returns error", func(t *testing.T) { @@ -89,9 +89,8 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { pw := ocrcommon.NewSingletonPeerWrapper(keyStore, cfg.P2P(), cfg.OCR(), cfg.Database(), db, logger.TestLogger(t)) - require.NoError(t, pw.Start(testutils.Context(t)), "foo") + servicetest.Run(t, pw) require.Equal(t, k2.PeerID(), pw.PeerID) - require.NoError(t, pw.Close()) }) t.Run("with multiple p2p keys and mismatching P2P.PeerID returns error", func(t *testing.T) { diff --git a/core/services/ocrcommon/run_saver_test.go b/core/services/ocrcommon/run_saver_test.go index 73697d181bc..7bfe60f2a06 100644 --- a/core/services/ocrcommon/run_saver_test.go +++ b/core/services/ocrcommon/run_saver_test.go @@ -4,9 +4,8 @@ import ( "testing" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" @@ -20,7 +19,7 @@ func TestRunSaver(t *testing.T) { 1000, 100, ) - require.NoError(t, rs.Start(testutils.Context(t))) + servicetest.Run(t, rs) for i := 0; i < 100; i++ { d := i pipelineRunner.On("InsertFinishedRun", mock.Anything, mock.Anything, mock.Anything, mock.Anything). @@ -31,5 +30,4 @@ func TestRunSaver(t *testing.T) { Once() rs.Save(&pipeline.Run{ID: int64(i)}) } - require.NoError(t, rs.Close()) } diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 18080fe22b6..5fbda456088 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -99,7 +99,7 @@ func (e *EnhancedTelemetryService[T]) Start(context.Context) error { func (e *EnhancedTelemetryService[T]) Close() error { return e.StopOnce("EnhancedTelemetryService", func() error { - e.chDone <- struct{}{} + close(e.chDone) e.lggr.Infof("Stopping enhanced telemetry service for job %d", e.job.ID) return nil }) diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index ae58e89d22e..9c90eea180a 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -14,11 +14,11 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" mercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" mercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -213,7 +213,7 @@ func TestSendEATelemetry(t *testing.T) { lggr, _ := logger.TestLoggerObserved(t, zap.WarnLevel) doneCh := make(chan struct{}) enhancedTelemService := NewEnhancedTelemetryService(&jb, enhancedTelemChan, doneCh, monitoringEndpoint, lggr.Named("Enhanced Telemetry Mercury")) - require.NoError(t, enhancedTelemService.Start(testutils.Context(t))) + servicetest.Run(t, enhancedTelemService) trrs := pipeline.TaskRunResults{ pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ @@ -324,7 +324,7 @@ func TestCollectAndSend(t *testing.T) { doneCh := make(chan struct{}) enhancedTelemService := NewEnhancedTelemetryService(&jb, enhancedTelemChan, doneCh, monitoringEndpoint, lggr.Named("Enhanced Telemetry")) - require.NoError(t, enhancedTelemService.Start(testutils.Context(t))) + servicetest.Run(t, enhancedTelemService) finalResult := &pipeline.FinalResult{ Values: []interface{}{"123456"}, AllErrors: nil, @@ -574,7 +574,7 @@ func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { lggr: lggr, monitoringEndpoint: monitoringEndpoint, } - require.NoError(t, e.Start(testutils.Context(t))) + servicetest.Run(t, &e) wg.Add(1) @@ -690,7 +690,7 @@ func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { lggr: lggr, monitoringEndpoint: monitoringEndpoint, } - require.NoError(t, e.Start(testutils.Context(t))) + servicetest.Run(t, &e) wg.Add(1) diff --git a/core/services/pg/event_broadcaster_test.go b/core/services/pg/event_broadcaster_test.go index 41dcbb0176f..e8a4a1086db 100644 --- a/core/services/pg/event_broadcaster_test.go +++ b/core/services/pg/event_broadcaster_test.go @@ -9,8 +9,8 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -19,8 +19,7 @@ func TestEventBroadcaster(t *testing.T) { config, _ := heavyweight.FullTestDBNoFixturesV2(t, nil) eventBroadcaster := pg.NewEventBroadcaster(config.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) - require.NoError(t, eventBroadcaster.Start(testutils.Context(t))) - t.Cleanup(func() { require.NoError(t, eventBroadcaster.Close()) }) + servicetest.Run(t, eventBroadcaster) t.Run("doesn't broadcast unrelated events (no payload filter)", func(t *testing.T) { sub, err := eventBroadcaster.Subscribe("foo", "") diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 54e3a5d3fab..1cebba2faf9 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -7,10 +7,11 @@ import ( "time" "github.com/jmoiron/sqlx" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" @@ -76,8 +77,7 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { }). Return() - require.NoError(t, reporter.Start(testutils.Context(t))) - defer func() { assert.NoError(t, reporter.Close()) }() + servicetest.Run(t, reporter) head := newHead() reporter.OnNewLongestChain(testutils.Context(t), &head) @@ -107,8 +107,7 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { }). Return() reporter := promreporter.NewPromReporter(db.DB, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) - require.NoError(t, reporter.Start(testutils.Context(t))) - defer func() { assert.NoError(t, reporter.Close()) }() + servicetest.Run(t, reporter) etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress) @@ -143,8 +142,7 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { subscribeCalls.Add(1) }). Return() - require.NoError(t, reporter.Start(testutils.Context(t))) - defer func() { assert.NoError(t, reporter.Close()) }() + servicetest.Run(t, reporter) head := newHead() reporter.OnNewLongestChain(testutils.Context(t), &head) diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index 0a433c3bc54..3409c2f1591 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -7,8 +7,6 @@ import ( "time" "github.com/ethereum/go-ethereum" - "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" @@ -22,12 +20,14 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" + "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -85,11 +85,9 @@ func TestConfigPoller(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := pgtest.NewQConfig(false) ethClient = evmclient.NewSimulatedBackendClient(t, b, testutils.SimulatedChainID) - ctx := testutils.Context(t) lorm := logpoller.NewORM(testutils.SimulatedChainID, db, lggr, cfg) lp = logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000) - require.NoError(t, lp.Start(ctx)) - t.Cleanup(func() { lp.Close() }) + servicetest.Run(t, lp) } t.Run("LatestConfig errors if there is no config in logs and config store is unconfigured", func(t *testing.T) { diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index 085f0c6e317..a5e5c1f8058 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -20,8 +20,7 @@ import ( confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - functionsConfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -29,6 +28,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" + functionsConfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -78,11 +79,9 @@ func runTest(t *testing.T, pluginType functions.FunctionsPluginType, expectedDig ethClient := evmclient.NewSimulatedBackendClient(t, b, big.NewInt(1337)) defer ethClient.Close() lggr := logger.TestLogger(t) - ctx := testutils.Context(t) lorm := logpoller.NewORM(big.NewInt(1337), db, lggr, cfg) lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000) - defer lp.Close() - require.NoError(t, lp.Start(ctx)) + servicetest.Run(t, lp) configPoller, err := functions.NewFunctionsConfigPoller(pluginType, lp, lggr) require.NoError(t, err) require.NoError(t, configPoller.UpdateRoutes(ocrAddress, ocrAddress)) diff --git a/core/services/relay/evm/functions/logpoller_wrapper_test.go b/core/services/relay/evm/functions/logpoller_wrapper_test.go index 2108e822d5e..9df285b4c25 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper_test.go +++ b/core/services/relay/evm/functions/logpoller_wrapper_test.go @@ -12,11 +12,11 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" @@ -95,13 +95,12 @@ func TestLogPollerWrapper_SingleSubscriberEmptyEvents(t *testing.T) { subscriber := newSubscriber(1) lpWrapper.SubscribeToUpdates("mock_subscriber", subscriber) - require.NoError(t, lpWrapper.Start(testutils.Context(t))) + servicetest.Run(t, lpWrapper) subscriber.updates.Wait() reqs, resps, err := lpWrapper.LatestEvents() require.NoError(t, err) require.Equal(t, 0, len(reqs)) require.Equal(t, 0, len(resps)) - lpWrapper.Close() } func TestLogPollerWrapper_ErrorOnZeroAddresses(t *testing.T) { @@ -111,10 +110,9 @@ func TestLogPollerWrapper_ErrorOnZeroAddresses(t *testing.T) { client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(addr(t, "00"), nil) - require.NoError(t, lpWrapper.Start(testutils.Context(t))) + servicetest.Run(t, lpWrapper) _, _, err := lpWrapper.LatestEvents() require.Error(t, err) - lpWrapper.Close() } func TestLogPollerWrapper_LatestEvents_ReorgHandling(t *testing.T) { @@ -135,7 +133,7 @@ func TestLogPollerWrapper_LatestEvents_ReorgHandling(t *testing.T) { // On the 3rd query, the original request log appears again lp.On("Logs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]logpoller.Log{mockedLog}, nil).Once() - require.NoError(t, lpWrapper.Start(testutils.Context(t))) + servicetest.Run(t, lpWrapper) subscriber.updates.Wait() oracleRequests, _, err := lpWrapper.LatestEvents() diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index 3a58a25a557..59e0e587813 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" @@ -167,13 +168,11 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { cfg := pgtest.NewQConfig(false) ethClient := evmclient.NewSimulatedBackendClient(t, b, big.NewInt(1337)) lggr := logger.TestLogger(t) - ctx := testutils.Context(t) lorm := logpoller.NewORM(big.NewInt(1337), db, lggr, cfg) lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000) eventBroadcaster := pgmocks.NewEventBroadcaster(t) subscription := pgmocks.NewSubscription(t) - require.NoError(t, lp.Start(ctx)) - t.Cleanup(func() { lp.Close() }) + servicetest.Run(t, lp) eventBroadcaster.On("Subscribe", "evm.insert_on_logs", "").Return(subscription, nil) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go index 4e19c7b56de..7d754b8326e 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go @@ -3,21 +3,19 @@ package cache import ( "testing" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) func Test_CacheSet(t *testing.T) { lggr := logger.TestLogger(t) cs := newCacheSet(lggr, Config{}) ctx := testutils.Context(t) - require.NoError(t, cs.Start(ctx)) - t.Cleanup(func() { - assert.NoError(t, cs.Close()) - }) + servicetest.Run(t, cs) t.Run("Get", func(t *testing.T) { c := &mockClient{} diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index 9b0100a3cdd..f265d54879c 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -8,11 +8,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" - mocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) @@ -162,7 +163,7 @@ func Test_Client_LatestReport(t *testing.T) { // simulate start without dialling require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) var err error - require.NoError(t, cacheSet.Start(ctx)) + servicetest.Run(t, cacheSet) c.cache, err = cacheSet.Get(ctx, c) require.NoError(t, err) @@ -200,7 +201,7 @@ func Test_Client_LatestReport(t *testing.T) { // simulate start without dialling require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) var err error - require.NoError(t, cacheSet.Start(ctx)) + servicetest.Run(t, cacheSet) c.cache, err = cacheSet.Get(ctx, c) require.NoError(t, err) diff --git a/core/services/srvctest/servicetest.go b/core/services/srvctest/servicetest.go deleted file mode 100644 index ee888a46fa6..00000000000 --- a/core/services/srvctest/servicetest.go +++ /dev/null @@ -1,18 +0,0 @@ -package srvctest - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services" -) - -// Start is test helper to automatically Start/Close a ServiceCtx along with a test. -func Start[S services.ServiceCtx](tb testing.TB, s S) S { - require.NoError(tb, s.Start(testutils.Context(tb))) - tb.Cleanup(func() { assert.NoError(tb, s.Close()) }) - return s -} diff --git a/core/services/synchronization/telemetry_ingress_batch_client_test.go b/core/services/synchronization/telemetry_ingress_batch_client_test.go index 6dd9d401a80..c4f6417131d 100644 --- a/core/services/synchronization/telemetry_ingress_batch_client_test.go +++ b/core/services/synchronization/telemetry_ingress_batch_client_test.go @@ -9,8 +9,8 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" @@ -37,7 +37,7 @@ func TestTelemetryIngressBatchClient_HappyPath(t *testing.T) { serverPubKeyHex := "33333333333" sendInterval := time.Millisecond * 5 telemIngressClient := synchronization.NewTestTelemetryIngressBatchClient(t, url, serverPubKeyHex, csaKeystore, false, telemClient, sendInterval, false) - require.NoError(t, telemIngressClient.Start(testutils.Context(t))) + servicetest.Run(t, telemIngressClient) // Create telemetry payloads for different contracts telemPayload1 := synchronization.TelemPayload{ @@ -100,7 +100,4 @@ func TestTelemetryIngressBatchClient_HappyPath(t *testing.T) { g.Eventually(func() []uint32 { return []uint32{contractCounter1.Load(), contractCounter2.Load(), contractCounter3.Load()} }).Should(gomega.Equal([]uint32{3, 2, 1})) - - // Client should shut down - telemIngressClient.Close() } diff --git a/core/services/synchronization/telemetry_ingress_client_test.go b/core/services/synchronization/telemetry_ingress_client_test.go index 5a0cc23ecd0..55be107b977 100644 --- a/core/services/synchronization/telemetry_ingress_client_test.go +++ b/core/services/synchronization/telemetry_ingress_client_test.go @@ -9,8 +9,8 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" @@ -35,8 +35,7 @@ func TestTelemetryIngressClient_Send_HappyPath(t *testing.T) { url := &url.URL{} serverPubKeyHex := "33333333333" telemIngressClient := synchronization.NewTestTelemetryIngressClient(t, url, serverPubKeyHex, csaKeystore, false, telemClient) - require.NoError(t, telemIngressClient.Start(testutils.Context(t))) - defer func() { assert.NoError(t, telemIngressClient.Close()) }() + servicetest.Run(t, telemIngressClient) // Create the telemetry payload telemetry := []byte("101010") diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 3c297026004..d957e3c721e 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -6,7 +6,15 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/jmoiron/sqlx" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -32,7 +40,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/services/vrf" vrf_mocks "github.com/smartcontractkit/chainlink/v2/core/services/vrf/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/solidity_cross_tests" @@ -40,13 +47,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" ) type vrfUniverse struct { @@ -149,7 +149,7 @@ func setup(t *testing.T) (vrfUniverse, *v1.Listener, job.Job) { cfg := configtest.NewTestGeneralConfig(t) vuni := buildVrfUni(t, db, cfg) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) vd := vrf.NewDelegate( db, @@ -176,8 +176,7 @@ func setup(t *testing.T) (vrfUniverse, *v1.Listener, job.Job) { go func() { listener.RunHeadListener(func() {}) }() - t.Cleanup(func() { listener.Stop(t) }) - require.NoError(t, listener.Start(testutils.Context(t))) + servicetest.Run(t, listener) return vuni, listener, jb } @@ -674,7 +673,7 @@ func Test_VRFV2PlusServiceFailsWhenVRFOwnerProvided(t *testing.T) { cfg := configtest.NewTestGeneralConfig(t) vuni := buildVrfUni(t, db, cfg) - mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) vd := vrf.NewDelegate( db, diff --git a/core/services/vrf/v1/listener_v1_test_helpers.go b/core/services/vrf/v1/listener_v1_test_helpers.go index e9adde35b58..f9532bf83e6 100644 --- a/core/services/vrf/v1/listener_v1_test_helpers.go +++ b/core/services/vrf/v1/listener_v1_test_helpers.go @@ -3,6 +3,8 @@ package v1 import ( "testing" "time" + + "github.com/stretchr/testify/assert" ) func (lsn *Listener) SetReqAdded(fn func()) { @@ -10,7 +12,7 @@ func (lsn *Listener) SetReqAdded(fn func()) { } func (lsn *Listener) Stop(t *testing.T) { - lsn.ChStop <- struct{}{} + assert.NoError(t, lsn.Close()) select { case <-lsn.WaitOnStop: case <-time.After(time.Second): diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 1ce4fd08a36..3beee88c2eb 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -380,6 +380,7 @@ func TestJobController_Create_HappyPath(t *testing.T) { func TestJobsController_Create_WebhookSpec(t *testing.T) { app := cltest.NewApplicationEVMDisabled(t) require.NoError(t, app.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, app.Stop()) }) _, fetchBridge := cltest.MustCreateBridge(t, app.GetSqlxDB(), cltest.BridgeOpts{}, app.GetConfig().Database()) _, submitBridge := cltest.MustCreateBridge(t, app.GetSqlxDB(), cltest.BridgeOpts{}, app.GetConfig().Database()) From f99fd8e8abac4becae9bd125114b404314af2a15 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 8 Dec 2023 08:31:34 -0600 Subject: [PATCH 028/234] bump go to 1.21.5 and misc deps (#11525) --- .tool-versions | 2 +- core/chains/evm/txmgr/confirmer_test.go | 18 +++--- core/scripts/go.mod | 31 ++++----- core/scripts/go.sum | 83 ++++++++++++++----------- go.mod | 33 +++++----- go.sum | 83 ++++++++++++++----------- integration-tests/.tool-versions | 2 +- integration-tests/go.mod | 25 ++++---- integration-tests/go.sum | 79 ++++++++++++----------- 9 files changed, 191 insertions(+), 165 deletions(-) diff --git a/.tool-versions b/.tool-versions index d78ce677cda..d8f0afd901d 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,4 @@ -golang 1.21.4 +golang 1.21.5 mockery 2.38.0 nodejs 16.16.0 postgres 13.3 diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 9d909d58344..3acbfe9800c 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -3006,12 +3006,14 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { pgtest.MustExec(t, db, `DELETE FROM pipeline_runs`) t.Run("processes eth_txes with receipt older than minConfirmations that reverted", func(t *testing.T) { - ch := make(chan interface{}) + type data struct { + value any + error + } + ch := make(chan data) nonce := evmtypes.Nonce(4) - var err error - ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { - err = thisErr - ch <- value + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, err error) error { + ch <- data{value, err} return nil }) @@ -3038,11 +3040,11 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { select { case data := <-ch: - assert.Error(t, err) + assert.Error(t, data.error) - assert.EqualError(t, err, fmt.Sprintf("transaction %s reverted on-chain", etx.TxAttempts[0].Hash.String())) + assert.EqualError(t, data.error, fmt.Sprintf("transaction %s reverted on-chain", etx.TxAttempts[0].Hash.String())) - assert.Nil(t, data) + assert.Nil(t, data.value) case <-testutils.AfterWaitTimeout(t): t.Fatal("no value received") diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 07d41c9d7e4..afaa65b38b7 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -49,7 +49,7 @@ require ( github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect github.com/DataDog/zstd v1.5.2 // indirect - github.com/Depado/ginprom v1.7.11 // indirect + github.com/Depado/ginprom v1.8.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/VictoriaMetrics/fastcache v1.10.0 // indirect @@ -64,12 +64,13 @@ require ( github.com/btcsuite/btcd v0.23.4 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect - github.com/bytedance/sonic v1.9.1 // indirect + github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect @@ -112,32 +113,32 @@ require ( github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/getsentry/sentry-go v0.19.0 // indirect - github.com/gin-contrib/cors v1.4.0 // indirect + github.com/gin-contrib/cors v1.5.0 // indirect github.com/gin-contrib/expvar v0.0.1 // indirect github.com/gin-contrib/sessions v0.0.5 // indirect github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect - github.com/go-ldap/ldap/v3 v3.4.5 // indirect + github.com/go-ldap/ldap/v3 v3.4.6 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/webauthn v0.9.1 // indirect - github.com/go-webauthn/x v0.1.4 // indirect + github.com/go-webauthn/webauthn v0.9.4 // indirect + github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v5 v5.1.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect @@ -193,7 +194,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -255,7 +256,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/shirou/gopsutil/v3 v3.23.10 // indirect + github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad // indirect @@ -270,7 +271,7 @@ require ( github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/status-im/keycard-go v0.2.0 // indirect @@ -288,7 +289,7 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/ulule/limiter/v3 v3.11.2 // indirect github.com/unrolled/secure v1.13.0 // indirect github.com/valyala/fastjson v1.4.1 // indirect @@ -330,7 +331,7 @@ require ( gopkg.in/guregu/null.v2 v2.1.2 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 48b168459ba..2b0504ce5fa 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -81,7 +81,6 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= @@ -99,8 +98,8 @@ github.com/CosmWasm/wasmvm v1.2.4/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Depado/ginprom v1.7.11 h1:qOhxW/NJZkNkkG4TQrzAZklX8SUTjTfLA73zIUNIpww= -github.com/Depado/ginprom v1.7.11/go.mod h1:49mxL3NTQwDrhpDbY4V1mAIB3us9B+b2hP1+ph+Sla8= +github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= +github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= @@ -193,8 +192,9 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -209,8 +209,11 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= @@ -386,8 +389,8 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlK github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -415,8 +418,8 @@ github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TB github.com/getsentry/sentry-go v0.19.0 h1:BcCH3CN5tXt5aML+gwmbFwVptLLQA+eT866fCO9wVOM= github.com/getsentry/sentry-go v0.19.0/go.mod h1:y3+lGEFEFexZtpbG1GUE2WD/f9zGyKYwpEqryTOC/nE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= -github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= +github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= github.com/gin-contrib/expvar v0.0.1 h1:IuU5ArEgihz50vG8Onrwz22kJr7Mcvgv9xSSpfU5g+w= github.com/gin-contrib/expvar v0.0.1/go.mod h1:8o2CznfQi1JjktORdHr2/abg3wSV6OCnXh0yGypvvVw= github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= @@ -430,8 +433,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= -github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= +github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -452,8 +455,8 @@ github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEai github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= -github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= +github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= +github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -477,8 +480,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -486,10 +489,10 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo= -github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI= -github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= -github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= +github.com/go-webauthn/webauthn v0.9.4 h1:YxvHSqgUyc5AK2pZbqkWWR55qKeDPhP8zLDr6lpIc2g= +github.com/go-webauthn/webauthn v0.9.4/go.mod h1:LqupCtzSef38FcxzaklmOn7AykGKhAhr9xlRbdbgnTw= +github.com/go-webauthn/x v0.1.5 h1:V2TCzDU2TGLd0kSZOXdrqDVV5JB9ILnKxA9S53CSBw0= +github.com/go-webauthn/x v0.1.5/go.mod h1:qbzWwcFcv4rTwtCLOZd+icnr6B7oSsAGZJqlt8cukqY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -511,8 +514,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= -github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -611,6 +614,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= @@ -867,8 +871,9 @@ github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -1196,8 +1201,8 @@ github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08O github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= -github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= +github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= +github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1252,8 +1257,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= @@ -1332,8 +1337,8 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= @@ -1492,7 +1497,7 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1592,7 +1597,7 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1698,7 +1703,6 @@ golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1708,7 +1712,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1716,7 +1720,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1729,7 +1734,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1964,8 +1970,8 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -2023,6 +2029,7 @@ modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/go.mod b/go.mod index f659881d1a0..40359ab16f7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/smartcontractkit/chainlink/v2 go 1.21.3 require ( - github.com/Depado/ginprom v1.7.11 + github.com/Depado/ginprom v1.8.0 github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/sprig/v3 v3.2.3 github.com/avast/retry-go/v4 v4.5.1 @@ -17,12 +17,13 @@ require ( github.com/fxamacker/cbor/v2 v2.5.0 github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 github.com/getsentry/sentry-go v0.19.0 - github.com/gin-contrib/cors v1.4.0 + github.com/gin-contrib/cors v1.5.0 github.com/gin-contrib/expvar v0.0.1 github.com/gin-contrib/sessions v0.0.5 github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 - github.com/go-webauthn/webauthn v0.9.1 + github.com/go-ldap/ldap/v3 v3.4.6 + github.com/go-webauthn/webauthn v0.9.4 github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 github.com/google/uuid v1.4.0 github.com/gorilla/securecookie v1.1.2 @@ -37,6 +38,7 @@ require ( github.com/jackc/pgconn v1.14.1 github.com/jackc/pgtype v1.14.0 github.com/jackc/pgx/v4 v4.18.1 + github.com/jmoiron/sqlx v1.3.5 github.com/jpillora/backoff v1.0.0 github.com/kylelemons/godebug v1.1.0 github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a @@ -62,7 +64,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/go-internal v1.11.0 github.com/scylladb/go-reflectx v1.0.1 - github.com/shirou/gopsutil/v3 v3.23.10 + github.com/shirou/gopsutil/v3 v3.23.11 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 @@ -77,11 +79,11 @@ require ( github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wsrpc v0.7.2 - github.com/spf13/cast v1.5.1 + github.com/spf13/cast v1.6.0 github.com/stretchr/testify v1.8.4 github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a github.com/tidwall/gjson v1.17.0 - github.com/ugorji/go/codec v1.2.11 + github.com/ugorji/go/codec v1.2.12 github.com/ulule/limiter/v3 v3.11.2 github.com/umbracle/ethgo v0.1.3 github.com/unrolled/secure v1.13.0 @@ -102,7 +104,7 @@ require ( google.golang.org/protobuf v1.31.0 gopkg.in/guregu/null.v2 v2.1.2 gopkg.in/guregu/null.v4 v4.0.0 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( @@ -131,12 +133,13 @@ require ( github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect - github.com/bytedance/sonic v1.9.1 // indirect + github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect @@ -170,26 +173,25 @@ require ( github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect - github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/x v0.1.4 // indirect + github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v5 v5.1.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -231,10 +233,9 @@ require ( github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/jmoiron/sqlx v1.3.5 github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/go.sum b/go.sum index d700ec724b9..11b377b11f8 100644 --- a/go.sum +++ b/go.sum @@ -81,7 +81,6 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= @@ -99,8 +98,8 @@ github.com/CosmWasm/wasmvm v1.2.4/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Depado/ginprom v1.7.11 h1:qOhxW/NJZkNkkG4TQrzAZklX8SUTjTfLA73zIUNIpww= -github.com/Depado/ginprom v1.7.11/go.mod h1:49mxL3NTQwDrhpDbY4V1mAIB3us9B+b2hP1+ph+Sla8= +github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= +github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= @@ -192,8 +191,9 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -208,8 +208,11 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= @@ -383,8 +386,8 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlK github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -412,8 +415,8 @@ github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TB github.com/getsentry/sentry-go v0.19.0 h1:BcCH3CN5tXt5aML+gwmbFwVptLLQA+eT866fCO9wVOM= github.com/getsentry/sentry-go v0.19.0/go.mod h1:y3+lGEFEFexZtpbG1GUE2WD/f9zGyKYwpEqryTOC/nE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= -github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= +github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= github.com/gin-contrib/expvar v0.0.1 h1:IuU5ArEgihz50vG8Onrwz22kJr7Mcvgv9xSSpfU5g+w= github.com/gin-contrib/expvar v0.0.1/go.mod h1:8o2CznfQi1JjktORdHr2/abg3wSV6OCnXh0yGypvvVw= github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= @@ -427,8 +430,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= -github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= +github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -449,8 +452,8 @@ github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEai github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= -github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= +github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= +github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -474,8 +477,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -485,10 +488,10 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo= -github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI= -github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= -github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= +github.com/go-webauthn/webauthn v0.9.4 h1:YxvHSqgUyc5AK2pZbqkWWR55qKeDPhP8zLDr6lpIc2g= +github.com/go-webauthn/webauthn v0.9.4/go.mod h1:LqupCtzSef38FcxzaklmOn7AykGKhAhr9xlRbdbgnTw= +github.com/go-webauthn/x v0.1.5 h1:V2TCzDU2TGLd0kSZOXdrqDVV5JB9ILnKxA9S53CSBw0= +github.com/go-webauthn/x v0.1.5/go.mod h1:qbzWwcFcv4rTwtCLOZd+icnr6B7oSsAGZJqlt8cukqY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -510,8 +513,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= -github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -610,6 +613,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= @@ -868,8 +872,9 @@ github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -1199,8 +1204,8 @@ github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08O github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= -github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= +github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= +github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1256,8 +1261,8 @@ github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= @@ -1336,8 +1341,8 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= @@ -1497,7 +1502,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1598,7 +1603,7 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1703,7 +1708,6 @@ golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1714,7 +1718,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1723,7 +1727,8 @@ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1738,7 +1743,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1972,8 +1978,8 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -2029,6 +2035,7 @@ modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/integration-tests/.tool-versions b/integration-tests/.tool-versions index 47b73e9de11..ac6300f9797 100644 --- a/integration-tests/.tool-versions +++ b/integration-tests/.tool-versions @@ -1,4 +1,4 @@ -golang 1.21.4 +golang 1.21.5 k3d 5.4.6 kubectl 1.25.5 nodejs 18.13.0 diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 2f32347bb4b..5b44209f9a6 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -95,7 +95,7 @@ require ( github.com/btcsuite/btcd v0.23.4 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect - github.com/bytedance/sonic v1.9.1 // indirect + github.com/bytedance/sonic v1.10.1 // indirect github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect @@ -105,7 +105,8 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cli/safeexec v1.0.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect @@ -165,12 +166,12 @@ require ( github.com/gin-contrib/sessions v0.0.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect - github.com/go-ldap/ldap/v3 v3.4.5 // indirect + github.com/go-ldap/ldap/v3 v3.4.6 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -186,17 +187,17 @@ require ( github.com/go-openapi/validate v0.22.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/webauthn v0.9.1 // indirect - github.com/go-webauthn/x v0.1.4 // indirect + github.com/go-webauthn/webauthn v0.9.4 // indirect + github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect github.com/gogo/status v1.1.1 // indirect - github.com/golang-jwt/jwt/v5 v5.1.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -356,7 +357,7 @@ require ( github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/shirou/gopsutil/v3 v3.23.10 // indirect + github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -372,7 +373,7 @@ require ( github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.15.0 // indirect @@ -394,7 +395,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 // indirect github.com/valyala/fastjson v1.4.1 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -446,7 +447,7 @@ require ( gopkg.in/guregu/null.v2 v2.1.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 08d02446d1d..0e29f6f6715 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -99,8 +99,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzS github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= @@ -113,8 +111,8 @@ github.com/CosmWasm/wasmvm v1.2.4/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Depado/ginprom v1.7.11 h1:qOhxW/NJZkNkkG4TQrzAZklX8SUTjTfLA73zIUNIpww= -github.com/Depado/ginprom v1.7.11/go.mod h1:49mxL3NTQwDrhpDbY4V1mAIB3us9B+b2hP1+ph+Sla8= +github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= +github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= @@ -229,8 +227,9 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= @@ -255,8 +254,11 @@ github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHe github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657 h1:CyuI+igIjadM/GRnE2o0q+WCwipDh0n2cUYFPAvxziM= github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657/go.mod h1:JRiumF+RFsH1mrrP8FUsi9tExPylKkO/oSRWeQEUdLE= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= @@ -471,8 +473,8 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+ github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -502,8 +504,8 @@ github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TB github.com/getsentry/sentry-go v0.19.0 h1:BcCH3CN5tXt5aML+gwmbFwVptLLQA+eT866fCO9wVOM= github.com/getsentry/sentry-go v0.19.0/go.mod h1:y3+lGEFEFexZtpbG1GUE2WD/f9zGyKYwpEqryTOC/nE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= -github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= +github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= github.com/gin-contrib/expvar v0.0.1 h1:IuU5ArEgihz50vG8Onrwz22kJr7Mcvgv9xSSpfU5g+w= github.com/gin-contrib/expvar v0.0.1/go.mod h1:8o2CznfQi1JjktORdHr2/abg3wSV6OCnXh0yGypvvVw= github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= @@ -517,8 +519,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= -github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= +github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -534,8 +536,8 @@ github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= -github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= +github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= +github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= @@ -599,8 +601,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= @@ -613,10 +615,10 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo= -github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI= -github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= -github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= +github.com/go-webauthn/webauthn v0.9.4 h1:YxvHSqgUyc5AK2pZbqkWWR55qKeDPhP8zLDr6lpIc2g= +github.com/go-webauthn/webauthn v0.9.4/go.mod h1:LqupCtzSef38FcxzaklmOn7AykGKhAhr9xlRbdbgnTw= +github.com/go-webauthn/x v0.1.5 h1:V2TCzDU2TGLd0kSZOXdrqDVV5JB9ILnKxA9S53CSBw0= +github.com/go-webauthn/x v0.1.5/go.mod h1:qbzWwcFcv4rTwtCLOZd+icnr6B7oSsAGZJqlt8cukqY= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -672,8 +674,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= -github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -778,6 +780,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= @@ -1084,6 +1087,7 @@ github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -1492,8 +1496,8 @@ github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08O github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= -github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= +github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= +github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -1560,8 +1564,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= @@ -1647,8 +1651,8 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= @@ -1833,7 +1837,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1942,7 +1946,7 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2074,7 +2078,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -2083,7 +2087,8 @@ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2098,7 +2103,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2345,8 +2351,8 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -2402,6 +2408,7 @@ k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 06656fac80999d1539e16951a54b87c6df13a9c7 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 8 Dec 2023 08:43:33 -0600 Subject: [PATCH 029/234] core/scripts/common: rm ava-labs/coreth; lint (#11451) --- .github/workflows/ci-chaincli.yml | 4 - .../command/keeper/verifiable_load.go | 2 +- core/scripts/chaincli/handler/debug.go | 17 -- core/scripts/chaincli/handler/keeper.go | 12 +- .../scripts/chaincli/handler/keeper_launch.go | 2 +- .../handler/keeper_verifiable_load.go | 10 +- .../handler/mercury_lookup_handler.go | 2 + core/scripts/chaincli/handler/report.go | 15 +- .../chaincli/handler/scrape_node_config.go | 2 +- core/scripts/common/avalanche.go | 265 ++++++++++++++++++ core/scripts/common/helpers.go | 20 +- core/scripts/go.mod | 7 +- core/scripts/go.sum | 18 +- 13 files changed, 292 insertions(+), 84 deletions(-) create mode 100644 core/scripts/common/avalanche.go diff --git a/.github/workflows/ci-chaincli.yml b/.github/workflows/ci-chaincli.yml index fd58d08005c..8a9ab03d766 100644 --- a/.github/workflows/ci-chaincli.yml +++ b/.github/workflows/ci-chaincli.yml @@ -2,11 +2,7 @@ name: chaincli CI on: push: - paths: - - "core/scripts/chaincli/**" pull_request: - paths: - - "core/scripts/chaincli/**" jobs: golangci: diff --git a/core/scripts/chaincli/command/keeper/verifiable_load.go b/core/scripts/chaincli/command/keeper/verifiable_load.go index 33acf9bf3b2..ce0acddfbdb 100644 --- a/core/scripts/chaincli/command/keeper/verifiable_load.go +++ b/core/scripts/chaincli/command/keeper/verifiable_load.go @@ -21,7 +21,7 @@ var verifiableLoad = &cobra.Command{ if err != nil { log.Fatal("failed to get verify flag: ", err) } - hdlr.GetVerifiableLoadStats(cmd.Context(), csv) + hdlr.PrintVerifiableLoadStats(cmd.Context(), csv) }, } diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index fec8c6cd414..5947337b187 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -18,7 +18,6 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" @@ -317,22 +316,6 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } -type blockSubscriber struct { - ethClient *ethclient.Client -} - -func (bs *blockSubscriber) LatestBlock() *ocr2keepers.BlockKey { - header, err := bs.ethClient.HeaderByNumber(context.Background(), nil) - if err != nil { - return nil - } - - return &ocr2keepers.BlockKey{ - Number: ocr2keepers.BlockNumber(header.Number.Uint64()), - Hash: header.Hash(), - } -} - func logMatchesTriggerConfig(log *types.Log, config automation_utils_2_1.LogTriggerConfig) bool { if log.Topics[0] != config.Topic0 { return false diff --git a/core/scripts/chaincli/handler/keeper.go b/core/scripts/chaincli/handler/keeper.go index 439532430ab..ad8bd93649c 100644 --- a/core/scripts/chaincli/handler/keeper.go +++ b/core/scripts/chaincli/handler/keeper.go @@ -435,9 +435,8 @@ func (k *Keeper) getRegistry20(ctx context.Context) (common.Address, *registry20 } if k.cfg.RegistryConfigUpdate { panic("KeeperRegistry2.0 could not be updated") - } else { - log.Println("KeeperRegistry2.0 config not updated: KEEPER_CONFIG_UPDATE=false") } + log.Println("KeeperRegistry2.0 config not updated: KEEPER_CONFIG_UPDATE=false") return registryAddr, keeperRegistry20 } @@ -453,9 +452,8 @@ func (k *Keeper) getRegistry21(ctx context.Context) (common.Address, *iregistry2 } if k.cfg.RegistryConfigUpdate { panic("KeeperRegistry2.1 could not be updated") - } else { - log.Println("KeeperRegistry2.1 config not updated: KEEPER_CONFIG_UPDATE=false") } + log.Println("KeeperRegistry2.1 config not updated: KEEPER_CONFIG_UPDATE=false") return registryAddr, keeperRegistry21 } @@ -479,9 +477,8 @@ func (k *Keeper) getRegistry12(ctx context.Context) (common.Address, *registry12 log.Fatalf("KeeperRegistry config update failed on registry address: %s, error is: %s", k.cfg.RegistryAddress, err.Error()) } log.Println("KeeperRegistry config update:", k.cfg.RegistryAddress, "-", helpers.ExplorerLink(k.cfg.ChainID, transaction.Hash())) - } else { - log.Println("KeeperRegistry config not updated: KEEPER_CONFIG_UPDATE=false") } + log.Println("KeeperRegistry config not updated: KEEPER_CONFIG_UPDATE=false") return registryAddr, keeperRegistry12 } @@ -513,9 +510,8 @@ func (k *Keeper) getRegistry11(ctx context.Context) (common.Address, *registry11 log.Fatalf("KeeperRegistry config update failed on registry address: %s, error is %s", k.cfg.RegistryAddress, err.Error()) } log.Println("KeeperRegistry config update:", k.cfg.RegistryAddress, "-", helpers.ExplorerLink(k.cfg.ChainID, transaction.Hash())) - } else { - log.Println("KeeperRegistry config not updated: KEEPER_CONFIG_UPDATE=false") } + log.Println("KeeperRegistry config not updated: KEEPER_CONFIG_UPDATE=false") return registryAddr, keeperRegistry11 } diff --git a/core/scripts/chaincli/handler/keeper_launch.go b/core/scripts/chaincli/handler/keeper_launch.go index 83ee6a77129..22b67763945 100644 --- a/core/scripts/chaincli/handler/keeper_launch.go +++ b/core/scripts/chaincli/handler/keeper_launch.go @@ -371,7 +371,7 @@ func (k *Keeper) createOCR2KeeperJob(client cmd.HTTPClient, contractAddr, nodeAd } // Correctly assign contract version in OCR job spec. - var contractVersion string = "v2.0" + contractVersion := "v2.0" if k.cfg.RegistryVersion == keeper.RegistryVersion_2_1 { contractVersion = "v2.1" } diff --git a/core/scripts/chaincli/handler/keeper_verifiable_load.go b/core/scripts/chaincli/handler/keeper_verifiable_load.go index b71a9af3387..aa62d820101 100644 --- a/core/scripts/chaincli/handler/keeper_verifiable_load.go +++ b/core/scripts/chaincli/handler/keeper_verifiable_load.go @@ -58,7 +58,7 @@ type upkeepStats struct { SortedAllDelays []float64 } -func (k *Keeper) GetVerifiableLoadStats(ctx context.Context, csv bool) { +func (k *Keeper) PrintVerifiableLoadStats(ctx context.Context, csv bool) { var v verifiableLoad var err error addr := common.HexToAddress(k.cfg.VerifiableLoadContractAddress) @@ -99,7 +99,7 @@ func (k *Keeper) GetVerifiableLoadStats(ctx context.Context, csv bool) { // create a number of workers to process the upkeep ids in batch for i := 0; i < workerNum; i++ { wg.Add(1) - go k.getUpkeepInfo(idChan, resultsChan, v, opts, &wg, csv) + go k.fetchUpkeepInfo(idChan, resultsChan, v, opts, &wg, csv) } for _, id := range upkeepIds { @@ -134,7 +134,7 @@ func (k *Keeper) GetVerifiableLoadStats(ctx context.Context, csv bool) { log.Printf("All STATS ABOVE ARE CALCULATED AT BLOCK %d", blockNum) } -func (k *Keeper) getUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInfo, v verifiableLoad, opts *bind.CallOpts, wg *sync.WaitGroup, csv bool) { +func (k *Keeper) fetchUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInfo, v verifiableLoad, opts *bind.CallOpts, wg *sync.WaitGroup, csv bool) { defer wg.Done() for id := range idChan { @@ -161,7 +161,7 @@ func (k *Keeper) getUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInf var wg1 sync.WaitGroup for i := uint16(0); i <= b; i++ { wg1.Add(1) - go k.getBucketData(v, opts, id, i, &wg1, info) + go k.fetchBucketData(v, opts, id, i, &wg1, info) } wg1.Wait() @@ -196,7 +196,7 @@ func (k *Keeper) getUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInf } } -func (k *Keeper) getBucketData(v verifiableLoad, opts *bind.CallOpts, id *big.Int, bucketNum uint16, wg *sync.WaitGroup, info *upkeepInfo) { +func (k *Keeper) fetchBucketData(v verifiableLoad, opts *bind.CallOpts, id *big.Int, bucketNum uint16, wg *sync.WaitGroup, info *upkeepInfo) { defer wg.Done() var bucketDelays []*big.Int diff --git a/core/scripts/chaincli/handler/mercury_lookup_handler.go b/core/scripts/chaincli/handler/mercury_lookup_handler.go index 1bd4b2e183c..1165d83921d 100644 --- a/core/scripts/chaincli/handler/mercury_lookup_handler.go +++ b/core/scripts/chaincli/handler/mercury_lookup_handler.go @@ -218,6 +218,7 @@ func (mlh *MercuryLookupHandler) singleFeedRequest(ctx context.Context, ch chan< defer func(Body io.ReadCloser) { err := Body.Close() if err != nil { + _ = "" // placate linter // mlh.logger.Errorf("Encountered error when closing the body of the response in single feed: %s", err) } }(resp.Body) @@ -326,6 +327,7 @@ func (mlh *MercuryLookupHandler) multiFeedsRequest(ctx context.Context, ch chan< defer func(Body io.ReadCloser) { err := Body.Close() if err != nil { + _ = "" // placate linter // mlh.logger.Errorf("Encountered error when closing the body of the response in the multi feed: %s", err) } }(resp.Body) diff --git a/core/scripts/chaincli/handler/report.go b/core/scripts/chaincli/handler/report.go index 622963f1ac0..10970b5f03b 100644 --- a/core/scripts/chaincli/handler/report.go +++ b/core/scripts/chaincli/handler/report.go @@ -235,15 +235,12 @@ func (t *OCR2Transaction) BlockNumber() (uint64, error) { block, err := hexutil.DecodeUint64(blStr) if err != nil { return 0, fmt.Errorf("failed to parse block number: %s", err) - } else { - return block, nil } - } else { - return 0, fmt.Errorf("not a string") + return block, nil } - } else { - return 0, fmt.Errorf("not found") + return 0, fmt.Errorf("not a string") } + return 0, fmt.Errorf("not found") } func (t *OCR2Transaction) To() *common.Address { @@ -335,17 +332,15 @@ func (t *OCR2TransmitTx) SetStaticValues(elem *OCR2ReportDataElem) { if err != nil { elem.Err = err.Error() return - } else { - elem.From = from.String() } + elem.From = from.String() block, err := t.BlockNumber() if err != nil { elem.Err = err.Error() return - } else { - elem.BlockNumber = fmt.Sprintf("%d", block) } + elem.BlockNumber = fmt.Sprintf("%d", block) upkeeps, err := t.UpkeepsInTransmit() if err != nil { diff --git a/core/scripts/chaincli/handler/scrape_node_config.go b/core/scripts/chaincli/handler/scrape_node_config.go index f00beb4b4fe..aaaf5d26477 100644 --- a/core/scripts/chaincli/handler/scrape_node_config.go +++ b/core/scripts/chaincli/handler/scrape_node_config.go @@ -279,5 +279,5 @@ func writeJSON(data interface{}, path string) error { return err } - return os.WriteFile(path, dataBytes, 0644) + return os.WriteFile(path, dataBytes, 0644) //nolint:gosec } diff --git a/core/scripts/common/avalanche.go b/core/scripts/common/avalanche.go new file mode 100644 index 00000000000..c9c53779905 --- /dev/null +++ b/core/scripts/common/avalanche.go @@ -0,0 +1,265 @@ +package common + +import ( + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + // BloomByteLength represents the number of bytes used in a header log bloom. + BloomByteLength = 256 + + // BloomBitLength represents the number of bits used in a header log bloom. + BloomBitLength = 8 * BloomByteLength +) + +// AvaBloom represents a 2048 bit bloom filter. +type AvaBloom [BloomByteLength]byte + +// SetBytes sets the content of b to the given bytes. +// It panics if d is not of suitable size. +func (b *AvaBloom) SetBytes(d []byte) { + if len(b) < len(d) { + panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) + } + copy(b[BloomByteLength-len(d):], d) +} + +// Add adds d to the filter. Future calls of Test(d) will return true. +func (b *AvaBloom) Add(d []byte) { + b.add(d, make([]byte, 6)) +} + +// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes) +func (b *AvaBloom) add(d []byte, buf []byte) { + i1, v1, i2, v2, i3, v3 := bloomValues(d, buf) + b[i1] |= v1 + b[i2] |= v2 + b[i3] |= v3 +} + +// Big converts b to a big integer. +// Note: Converting a bloom filter to a big.Int and then calling GetBytes +// does not return the same bytes, since big.Int will trim leading zeroes +func (b AvaBloom) Big() *big.Int { + return new(big.Int).SetBytes(b[:]) +} + +// Bytes returns the backing byte slice of the bloom +func (b AvaBloom) Bytes() []byte { + return b[:] +} + +// Test checks if the given topic is present in the bloom filter +func (b AvaBloom) Test(topic []byte) bool { + i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6)) + return v1 == v1&b[i1] && + v2 == v2&b[i2] && + v3 == v3&b[i3] +} + +// MarshalText encodes b as a hex string with 0x prefix. +func (b AvaBloom) MarshalText() ([]byte, error) { + return hexutil.Bytes(b[:]).MarshalText() +} + +// UnmarshalText b as a hex string with 0x prefix. +func (b *AvaBloom) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("Bloom", input, b[:]) +} + +// bloomValues returns the bytes (index-value pairs) to set for the given data +func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) { + sha := crypto.NewKeccakState() + sha.Write(data) + sha.Read(hashbuf) + // The actual bits to flip + v1 := byte(1 << (hashbuf[1] & 0x7)) + v2 := byte(1 << (hashbuf[3] & 0x7)) + v3 := byte(1 << (hashbuf[5] & 0x7)) + // The indices for the bytes to OR in + i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1 + i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1 + i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1 + + return i1, v1, i2, v2, i3, v3 +} + +// A AvaBlockNonce is a 64-bit hash which proves (combined with the +// mix-hash) that a sufficient amount of computation has been carried +// out on a block. +type AvaBlockNonce [8]byte + +// EncodeNonce converts the given integer to a block nonce. +func EncodeNonce(i uint64) AvaBlockNonce { + var n AvaBlockNonce + binary.BigEndian.PutUint64(n[:], i) + return n +} + +// Uint64 returns the integer value of a block nonce. +func (n AvaBlockNonce) Uint64() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + +// MarshalText encodes n as a hex string with 0x prefix. +func (n AvaBlockNonce) MarshalText() ([]byte, error) { + return hexutil.Bytes(n[:]).MarshalText() +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (n *AvaBlockNonce) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("AvaBlockNonce", input, n[:]) +} + +// AvaHeader is a copy of [github.com/ava-labs/coreth/core/types.Header] to avoid importing the whole module. +type AvaHeader struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom AvaBloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce AvaBlockNonce `json:"nonce"` + ExtDataHash common.Hash `json:"extDataHash" gencodec:"required"` + + // BaseFee was added by EIP-1559 and is ignored in legacy headers. + BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + + // ExtDataGasUsed was added by Apricot Phase 4 and is ignored in legacy + // headers. + // + // It is not a uint64 like GasLimit or GasUsed because it is not possible to + // correctly encode this field optionally with uint64. + ExtDataGasUsed *big.Int `json:"extDataGasUsed" rlp:"optional"` + + // BlockGasCost was added by Apricot Phase 4 and is ignored in legacy + // headers. + BlockGasCost *big.Int `json:"blockGasCost" rlp:"optional"` +} + +func (h *AvaHeader) UnmarshalJSON(input []byte) error { + type Header struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *AvaBloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *AvaBlockNonce `json:"nonce"` + ExtDataHash *common.Hash `json:"extDataHash" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + ExtDataGasUsed *hexutil.Big `json:"extDataGasUsed" rlp:"optional"` + BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for Header") + } + h.ParentHash = *dec.ParentHash + if dec.UncleHash == nil { + return errors.New("missing required field 'sha3Uncles' for Header") + } + h.UncleHash = *dec.UncleHash + if dec.Coinbase == nil { + return errors.New("missing required field 'miner' for Header") + } + h.Coinbase = *dec.Coinbase + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for Header") + } + h.Root = *dec.Root + if dec.TxHash == nil { + return errors.New("missing required field 'transactionsRoot' for Header") + } + h.TxHash = *dec.TxHash + if dec.ReceiptHash == nil { + return errors.New("missing required field 'receiptsRoot' for Header") + } + h.ReceiptHash = *dec.ReceiptHash + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Header") + } + h.Bloom = *dec.Bloom + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for Header") + } + h.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Number == nil { + return errors.New("missing required field 'number' for Header") + } + h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for Header") + } + h.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for Header") + } + h.GasUsed = uint64(*dec.GasUsed) + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for Header") + } + h.Time = uint64(*dec.Time) + if dec.Extra == nil { + return errors.New("missing required field 'extraData' for Header") + } + h.Extra = *dec.Extra + if dec.MixDigest != nil { + h.MixDigest = *dec.MixDigest + } + if dec.Nonce != nil { + h.Nonce = *dec.Nonce + } + if dec.ExtDataHash == nil { + return errors.New("missing required field 'extDataHash' for Header") + } + h.ExtDataHash = *dec.ExtDataHash + if dec.BaseFee != nil { + h.BaseFee = (*big.Int)(dec.BaseFee) + } + if dec.ExtDataGasUsed != nil { + h.ExtDataGasUsed = (*big.Int)(dec.ExtDataGasUsed) + } + if dec.BlockGasCost != nil { + h.BlockGasCost = (*big.Int)(dec.BlockGasCost) + } + return nil +} +func (h *AvaHeader) Hash() common.Hash { + return rlpHash(h) +} +func rlpHash(x interface{}) (h common.Hash) { + sha := crypto.NewKeccakState() + sha.Reset() + rlp.Encode(sha, x) + sha.Read(h[:]) + return h +} diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go index c141e8a29c4..b5a3b1ed943 100644 --- a/core/scripts/common/helpers.go +++ b/core/scripts/common/helpers.go @@ -12,7 +12,6 @@ import ( "strings" "time" - avaxclient "github.com/ava-labs/coreth/ethclient" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -34,11 +33,6 @@ type Environment struct { Jc *rpc.Client - // AvaxEc is appropriately set if the environment is configured to interact with an avalanche - // chain. It should be used instead of the regular Ec field because avalanche calculates - // blockhashes differently and the regular Ec will give consistently incorrect results on basic - // queries (like e.g eth_blockByNumber). - AvaxEc avaxclient.Client ChainID int64 } @@ -84,12 +78,6 @@ func SetupEnv(overrideNonce bool) Environment { chainID, err := strconv.ParseInt(chainIDEnv, 10, 64) PanicErr(err) - var avaxClient avaxclient.Client - if IsAvaxNetwork(chainID) { - avaxClient, err = avaxclient.Dial(ethURL) - PanicErr(err) - } - // Owner key. Make sure it has eth b, err := hex.DecodeString(accountKey) PanicErr(err) @@ -136,7 +124,6 @@ func SetupEnv(overrideNonce bool) Environment { Owner: owner, Ec: ec, Jc: jsonRPCClient, - AvaxEc: avaxClient, ChainID: chainID, } } @@ -485,11 +472,10 @@ func GetRlpHeaders(env Environment, blockNumbers []*big.Int, getParentBlocks boo // Avalanche block headers are special, handle them by using the avalanche rpc client // rather than the regular go-ethereum ethclient. if IsAvaxNetwork(env.ChainID) { + var h AvaHeader // Get child block since it's the one that has the parent hash in its header. - h, err := env.AvaxEc.HeaderByNumber( - context.Background(), - new(big.Int).Set(blockNum).Add(blockNum, offset), - ) + nextBlockNum := new(big.Int).Set(blockNum).Add(blockNum, offset) + err := env.Jc.CallContext(context.Background(), &h, "eth_getBlockByNumber", hexutil.EncodeBig(nextBlockNum), false) if err != nil { return nil, hashes, fmt.Errorf("failed to get header: %+v", err) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index afaa65b38b7..b7dd835cb65 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -6,7 +6,6 @@ go 1.21.3 replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( - github.com/ava-labs/coreth v0.12.1 github.com/avast/retry-go v3.0.0+incompatible github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 @@ -55,7 +54,6 @@ require ( github.com/VictoriaMetrics/fastcache v1.10.0 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/ava-labs/avalanchego v1.10.1 // indirect github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -63,6 +61,7 @@ require ( github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd v0.23.4 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect @@ -91,7 +90,6 @@ require ( github.com/danieljoos/wincred v1.1.2 // indirect github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set v1.8.0 // indirect github.com/deckarep/golang-set/v2 v2.3.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect @@ -141,7 +139,6 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect @@ -172,7 +169,6 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect - github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/huandu/skiplist v1.2.0 // indirect @@ -248,7 +244,6 @@ require ( github.com/prometheus/prometheus v0.48.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/rjeczalik/notify v0.9.3 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 2b0504ce5fa..a8cf979a0e6 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -145,10 +145,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/ava-labs/avalanchego v1.10.1 h1:lBeamJ1iNq+p2oKg2nAs+A65m8vhSDjkiTDbwzQW7kY= -github.com/ava-labs/avalanchego v1.10.1/go.mod h1:ZvSXWlbkUKlbk3BsWx29a+8eVHe/WBsOxh55BSGoeRk= -github.com/ava-labs/coreth v0.12.1 h1:EWSkFGHGVUxmu1pnSK/2pdcxaAVHbGspHqO3Ag+i7sA= -github.com/ava-labs/coreth v0.12.1/go.mod h1:/5x54QlIKjlPebkdzTA5ic9wXdejbWOnQosztkv9jxo= github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= @@ -175,10 +171,12 @@ github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHf github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -309,12 +307,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= @@ -718,8 +716,6 @@ github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce h1:7UnVY3T/ZnHUrfv github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -1161,8 +1157,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= -github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -1631,7 +1625,6 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1807,7 +1800,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -1977,8 +1969,6 @@ gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHN gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 1b357f631ad7d576ce60caf27814e163e4db43af Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Fri, 8 Dec 2023 12:02:01 -0500 Subject: [PATCH 030/234] (test): Remove unnecessary fuzzing from Functions OnTokenTransfer tests (#11517) * (test): Remove unnecessary fuzzing from Functions OnTokenTransfer tests * Update gas snapshot --- .../gas-snapshots/functions.gas-snapshot | 12 ++--- .../tests/v1_X/FunctionsSubscriptions.t.sol | 51 ++++++------------- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index 8275d9afafe..d0893969bff 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -142,11 +142,11 @@ FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Reve FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13459) FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59592) FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15010) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43774, ~: 45548) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46286, ~: 48060) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51443, ~: 53040) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata() (gas: 39939) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription() (gas: 42404) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink() (gas: 13441) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused() (gas: 47347) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 81598, ~: 81598) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) @@ -177,7 +177,7 @@ FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscription FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_Success() (gas: 68196) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_SuccessChangeProposedOwner() (gas: 82749) FunctionsSubscriptions_RecoverFunds:test_OwnerCancelSubscription_RevertIfNotOwner() (gas: 15554) -FunctionsSubscriptions_RecoverFunds:test_RecoverFunds_Success(uint64) (runs: 256, μ: 41717, ~: 41721) +FunctionsSubscriptions_RecoverFunds:test_RecoverFunds_Success() (gas: 41111) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfInvalidConsumer() (gas: 30260) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNoSubscription() (gas: 15019) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 57800) diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol index 5a54bcc84ca..907eadacd8e 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol @@ -130,12 +130,8 @@ contract FunctionsSubscriptions_OwnerCancelSubscription is FunctionsSubscription contract FunctionsSubscriptions_RecoverFunds is FunctionsRouterSetup { event FundsRecovered(address to, uint256 amount); - function test_RecoverFunds_Success(uint64 fundsTransferred) public { - //amount must be less than LINK total supply - vm.assume(fundsTransferred < 1_000_000_000 * 1e18); - vm.assume(fundsTransferred > 0); - - // uint256 fundsTransferred = 1 * 1e18; // 1 LINK + function test_RecoverFunds_Success() public { + uint256 fundsTransferred = 1 * 1e18; // 1 LINK s_linkToken.transfer(address(s_functionsRouter), fundsTransferred); // topic0 (function signature, always checked), NOT topic1 (false), NOT topic2 (false), NOT topic3 (false), and data (true). @@ -320,58 +316,43 @@ contract FunctionsSubscriptions_OnTokenTransfer is FunctionsClientSetup { s_functionsRouter.addConsumer(s_subscriptionId, address(s_functionsClient)); } - function test_OnTokenTransfer_RevertIfPaused(uint96 fundingAmount) public { - // Funding amount must be less than LINK total supply + function test_OnTokenTransfer_RevertIfPaused() public { + // Funding amount must be less than or equal to LINK total supply uint256 totalSupplyJuels = 1_000_000_000 * 1e18; - vm.assume(fundingAmount <= totalSupplyJuels); - vm.assume(fundingAmount >= 0); - s_functionsRouter.pause(); vm.expectRevert("Pausable: paused"); - s_linkToken.transferAndCall(address(s_functionsRouter), fundingAmount, abi.encode(s_subscriptionId)); + s_linkToken.transferAndCall(address(s_functionsRouter), totalSupplyJuels, abi.encode(s_subscriptionId)); } - function test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96 fundingAmount) public { - // Funding amount must be less than LINK total supply + function test_OnTokenTransfer_RevertIfCallerIsNotLink() public { + // Funding amount must be less than or equal to LINK total supply uint256 totalSupplyJuels = 1_000_000_000 * 1e18; - vm.assume(fundingAmount <= totalSupplyJuels); - vm.assume(fundingAmount >= 0); - vm.expectRevert(FunctionsSubscriptions.OnlyCallableFromLink.selector); - s_functionsRouter.onTokenTransfer(address(s_functionsRouter), fundingAmount, abi.encode(s_subscriptionId)); + s_functionsRouter.onTokenTransfer(address(s_functionsRouter), totalSupplyJuels, abi.encode(s_subscriptionId)); } - function test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96 fundingAmount) public { - // Funding amount must be less than LINK total supply + function test_OnTokenTransfer_RevertIfCallerIsNoCalldata() public { + // Funding amount must be less than or equal to LINK total supply uint256 totalSupplyJuels = 1_000_000_000 * 1e18; - vm.assume(fundingAmount <= totalSupplyJuels); - vm.assume(fundingAmount >= 0); - vm.expectRevert(FunctionsSubscriptions.InvalidCalldata.selector); - s_linkToken.transferAndCall(address(s_functionsRouter), fundingAmount, new bytes(0)); + s_linkToken.transferAndCall(address(s_functionsRouter), totalSupplyJuels, new bytes(0)); } - function test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96 fundingAmount) public { - // Funding amount must be less than LINK total supply + function test_OnTokenTransfer_RevertIfCallerIsNoSubscription() public { + // Funding amount must be less than or equal to LINK total supply uint256 totalSupplyJuels = 1_000_000_000 * 1e18; - vm.assume(fundingAmount <= totalSupplyJuels); - vm.assume(fundingAmount >= 0); - vm.expectRevert(FunctionsSubscriptions.InvalidSubscription.selector); uint64 invalidSubscriptionId = 123456789; - s_linkToken.transferAndCall(address(s_functionsRouter), fundingAmount, abi.encode(invalidSubscriptionId)); + s_linkToken.transferAndCall(address(s_functionsRouter), totalSupplyJuels, abi.encode(invalidSubscriptionId)); } function test_OnTokenTransfer_Success(uint96 fundingAmount) public { // Funding amount must be less than LINK total supply uint256 totalSupplyJuels = 1_000_000_000 * 1e18; // Some of the total supply is already in the subscription account - vm.assume(fundingAmount <= totalSupplyJuels); - vm.assume(fundingAmount >= 0); - - s_linkToken.transferAndCall(address(s_functionsRouter), fundingAmount, abi.encode(s_subscriptionId)); + s_linkToken.transferAndCall(address(s_functionsRouter), totalSupplyJuels, abi.encode(s_subscriptionId)); uint96 subscriptionBalanceAfter = s_functionsRouter.getSubscription(s_subscriptionId).balance; - assertEq(fundingAmount, subscriptionBalanceAfter); + assertEq(totalSupplyJuels, subscriptionBalanceAfter); } } From 1a45097cbf51751efe6295718629a0e09c7361ce Mon Sep 17 00:00:00 2001 From: Lei Date: Fri, 8 Dec 2023 10:55:35 -0800 Subject: [PATCH 031/234] small improvements based on comments (#11491) * small improvements based on comments * add unit test --- .../evmregistry/v21/mercury/mercury.go | 6 +--- .../v21/mercury/streams/streams.go | 7 +++-- .../v21/mercury/streams/streams_test.go | 28 ++++++++++++++++++- .../evmregistry/v21/mercury/v03/request.go | 2 +- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go index cf6ebeafc6e..f9a3c001c66 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go @@ -105,10 +105,6 @@ type StreamsLookup struct { Block uint64 } -func (l *StreamsLookup) IsMercuryVersionUnkown() bool { - return l.FeedParamKey != FeedIDs -} - func (l *StreamsLookup) IsMercuryV02() bool { return l.FeedParamKey == FeedIdHex && l.TimeParamKey == BlockNumber } @@ -117,6 +113,6 @@ func (l *StreamsLookup) IsMercuryV03() bool { return l.FeedParamKey == FeedIDs } -func (l *StreamsLookup) IsMercuryUsingBatchPathV03() bool { +func (l *StreamsLookup) IsMercuryV03UsingBlockNumber() bool { return l.TimeParamKey == BlockNumber } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index aec23431921..70db40c26bb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -131,6 +131,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper // tried to call mercury lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) streamsLookupErr, err := s.packer.DecodeStreamsLookupRequest(checkResult.PerformData) + if err != nil { lookupLggr.Debugf("at block %d upkeep %s DecodeStreamsLookupRequest failed: %v", block, upkeepId, err) // user contract did not revert with StreamsLookup error @@ -144,7 +145,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper return } - // mercury permission checking for v0.3 is done by mercury server + // mercury permission checking for v0.3 is done by mercury server, so no need to check here if streamsLookupResponse.IsMercuryV02() { // check permission on the registry for mercury v0.2 opts := s.buildCallOpts(ctx, block) @@ -159,8 +160,8 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) return } - } else if streamsLookupResponse.IsMercuryVersionUnkown() { - // if mercury version cannot be determined, set failure reason + } else if !streamsLookupResponse.IsMercuryV03() { + // if mercury version is not v02 or v03, set failure reason lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) return diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index abcc37dca18..32041d0f18b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -11,10 +11,11 @@ import ( "testing" "time" + "github.com/pkg/errors" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -619,6 +620,31 @@ func TestStreams_StreamsLookup(t *testing.T) { }, hasError: true, }, + { + name: "failure - invalid mercury version", + input: []ocr2keepers.CheckResult{ + { + // This Perform data contains invalid FeedParamKey: {feedIdHex:RandomString [ETD-USD BTC-ETH] blockNumber 100 [48 120 48 48]} + PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000166665656449644865783a52616e646f6d537472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000074554442d5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074254432d45544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043078303000000000000000000000000000000000000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), + }, + }, + expectedResults: []ocr2keepers.CheckResult{ + { + PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000166665656449644865783a52616e646f6d537472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000074554442d5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074254432d45544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043078303000000000000000000000000000000000000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput), + }, + }, + hasError: true, + }, } for _, tt := range tests { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go index cb3c9ef3222..ea465ff8582 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go @@ -100,7 +100,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur params := fmt.Sprintf("%s=%s&%s=%s", mercury.FeedIDs, strings.Join(sl.Feeds, ","), mercury.Timestamp, sl.Time.String()) batchPathV03 := mercuryBatchPathV03 - if sl.IsMercuryUsingBatchPathV03() { + if sl.IsMercuryV03UsingBlockNumber() { batchPathV03 = mercuryBatchPathV03BlockNumber } reqUrl := fmt.Sprintf("%s%s%s", c.mercuryConfig.Credentials().URL, batchPathV03, params) From 500a4dba7ef9059fd76e09b84c9acd6022b2c247 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Tue, 12 Dec 2023 00:15:20 +0300 Subject: [PATCH 032/234] wrap devspace commands (#11530) --- .../dashboard/cmd/dashboard_deploy.go | 2 +- charts/chainlink-cluster/devspace.yaml | 24 ++++++++++++++++++- charts/chainlink-cluster/go.mod | 2 +- .../templates/chainlink-pod-monitor.yaml | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go index 93619fe6148..b65be29501d 100644 --- a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go +++ b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go @@ -3,7 +3,7 @@ package main import ( "os" - "github.com/smartcontractkit/chainlink/v2/dashboard/dashboard" + "github.com/smartcontractkit/chainlink/charts/chainlink-cluster/dashboard/dashboard" "github.com/smartcontractkit/wasp" ) diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index f62b87edf69..186e49257a1 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -2,6 +2,7 @@ version: v2beta1 name: chainlink vars: + NS_TTL: 72h DEVSPACE_IMAGE: source: env @@ -20,7 +21,16 @@ pipelines: run_dependencies --all # 1. Deploy any projects this project needs (see "dependencies") ensure_pull_secrets --all # 2. Ensure pull secrets build_images --all -t $(git rev-parse --short HEAD) # 3. Build, tag (git commit hash) and push all images (see "images") - create_deployments --all # 4. Deploy Helm charts and manifests specfied as "deployments" + create_deployments --all # 5. Deploy Helm charts and manifests specfied as "deployments" + kubectl annotate namespace ${DEVSPACE_NAMESPACE} janitor/ttl=${NS_TTL} + echo "Namespace ${DEVSPACE_NAMESPACE} will be deleted in ${NS_TTL}" + purge: + run: |- + kubectl delete ns ${DEVSPACE_NAMESPACE} + +commands: + connect: |- + sudo kubefwd svc -n ${DEVSPACE_NAMESPACE} images: app: @@ -28,9 +38,21 @@ images: dockerfile: ../../core/chainlink.devspace.Dockerfile context: ../.. +hooks: + - wait: + running: true + terminatedWithCode: 0 + container: + labelSelector: + # vars don't work here, = releaseName + release: "app" + events: ["after:deploy:app"] + name: "wait-for-pod-hook" + # This is a list of `deployments` that DevSpace can create for this project deployments: app: + namespace: ${DEVSPACE_NAMESPACE} helm: releaseName: "app" chart: diff --git a/charts/chainlink-cluster/go.mod b/charts/chainlink-cluster/go.mod index 951fb1d2e3c..34ee35887b1 100644 --- a/charts/chainlink-cluster/go.mod +++ b/charts/chainlink-cluster/go.mod @@ -1,4 +1,4 @@ -module github.com/smartcontractkit/chainlink/v2/dashboard +module github.com/smartcontractkit/chainlink/charts/chainlink-cluster/dashboard go 1.21 diff --git a/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml b/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml index 2cd9c3df2b6..05852642a29 100644 --- a/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml +++ b/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml @@ -8,7 +8,7 @@ metadata: spec: namespaceSelector: matchNames: - - "cl-cluster" + - {{ $.Release.Namespace }} podMetricsEndpoints: - port: access selector: From 120bef7aa5259967348e9d8ee46f8aea82d8cc01 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 11 Dec 2023 16:45:19 -0600 Subject: [PATCH 033/234] go.mods: rm libp2p; rm btcd replace (#11502) * go.mods: drop btcd replace; upgrade libp2p * Replace deprecated call to btcd API for secp256k1 constant * Fix comment describing sqrtPower calculation * Change calculation of sqrtPower to standard order of operations * rm libp2p peer & crypto uses --------- Co-authored-by: Alex Coventry --- charts/chainlink-cluster/go.mod | 4 - core/internal/cltest/cltest.go | 4 +- core/internal/cltest/factories.go | 6 +- core/scripts/go.mod | 25 +-- core/scripts/go.sum | 110 ++--------- .../keystore/keys/cosmoskey/export.go | 4 +- core/services/keystore/keys/csakey/export.go | 4 +- .../keystore/keys/dkgencryptkey/export.go | 4 +- .../keystore/keys/dkgsignkey/export.go | 4 +- core/services/keystore/keys/exportutils.go | 9 +- core/services/keystore/keys/ocr2key/export.go | 4 +- core/services/keystore/keys/ocrkey/export.go | 4 +- core/services/keystore/keys/p2pkey/export.go | 14 +- core/services/keystore/keys/p2pkey/key.go | 24 +-- .../services/keystore/keys/p2pkey/key_test.go | 21 +- core/services/keystore/keys/p2pkey/key_v2.go | 46 +++-- .../keystore/keys/p2pkey/key_v2_test.go | 18 +- core/services/keystore/keys/p2pkey/peer_id.go | 23 ++- .../keystore/keys/p2pkey/peer_id_test.go | 8 +- core/services/keystore/keys/solkey/export.go | 4 +- .../services/keystore/keys/starkkey/export.go | 4 +- core/services/keystore/models_test.go | 4 +- core/services/keystore/p2p.go | 2 +- core/services/keystore/p2p_test.go | 32 ++- .../services/ocrcommon/discoverer_database.go | 8 +- .../ocrcommon/discoverer_database_test.go | 19 +- core/services/ocrcommon/peer_wrapper.go | 33 +--- core/services/ocrcommon/peer_wrapper_test.go | 6 +- core/services/ocrcommon/peerstore.go | 182 ------------------ core/services/ocrcommon/peerstore_test.go | 101 ---------- core/services/signatures/secp256k1/curve.go | 2 +- core/services/signatures/secp256k1/field.go | 8 +- core/services/signatures/secp256k1/scalar.go | 2 +- core/utils/utils.go | 12 +- core/web/jobs_controller_test.go | 6 +- core/web/p2p_keys_controller.go | 6 +- core/web/presenters/p2p_key_test.go | 8 +- core/web/resolver/mutation.go | 3 +- go.mod | 27 +-- go.sum | 105 +--------- integration-tests/go.mod | 25 +-- integration-tests/go.sum | 105 +--------- 42 files changed, 204 insertions(+), 836 deletions(-) delete mode 100644 core/services/ocrcommon/peerstore.go delete mode 100644 core/services/ocrcommon/peerstore_test.go diff --git a/charts/chainlink-cluster/go.mod b/charts/chainlink-cluster/go.mod index 34ee35887b1..ae67574aa06 100644 --- a/charts/chainlink-cluster/go.mod +++ b/charts/chainlink-cluster/go.mod @@ -170,10 +170,6 @@ require ( ) replace ( - // Fixes go mod tidy issue for ambiguous imports from go-ethereum - // See https://github.com/ugorji/go/issues/279 - github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1 - github.com/go-kit/log => github.com/go-kit/log v0.2.1 // replicating the replace directive on cosmos SDK diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 8802d9c4b0d..88abc3de5c6 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -26,7 +26,6 @@ import ( "github.com/google/uuid" "github.com/gorilla/securecookie" "github.com/gorilla/sessions" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/manyminds/api2go/jsonapi" "github.com/onsi/gomega" "github.com/stretchr/testify/assert" @@ -141,11 +140,10 @@ func init() { fmt.Printf("[gin] %-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers) } - defaultP2PPeerID, err := p2ppeer.Decode(configtest.DefaultPeerID) + err := DefaultP2PPeerID.UnmarshalString(configtest.DefaultPeerID) if err != nil { panic(err) } - DefaultP2PPeerID = p2pkey.PeerID(defaultP2PPeerID) } func NewRandomPositiveInt64() int64 { diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index c169c5fd228..a76d23d3762 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -16,7 +16,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/google/uuid" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/require" "github.com/urfave/cli" "gopkg.in/guregu/null.v4" @@ -55,8 +55,8 @@ func NewEIP55Address() ethkey.EIP55Address { return e } -func NewPeerID() p2ppeer.ID { - id, err := p2ppeer.Decode("12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw") +func NewPeerID() (id ragep2ptypes.PeerID) { + err := id.UnmarshalText([]byte("12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw")) if err != nil { panic(err) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index b7dd835cb65..2f4d45e746f 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -59,7 +59,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blendle/zapdriver v1.3.1 // indirect - github.com/btcsuite/btcd v0.23.4 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect @@ -145,7 +144,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.2.2 // indirect @@ -174,9 +173,6 @@ require ( github.com/huandu/skiplist v1.2.0 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/go-cid v0.0.7 // indirect - github.com/ipfs/go-log v1.0.4 // indirect - github.com/ipfs/go-log/v2 v2.1.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.1 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -198,9 +194,6 @@ require ( github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-libp2p-core v0.8.5 // indirect - github.com/libp2p/go-libp2p-peerstore v0.2.7 // indirect - github.com/libp2p/go-openssl v0.0.7 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -208,8 +201,6 @@ require ( github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect - github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect - github.com/minio/sha256-simd v0.1.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -218,14 +209,6 @@ require ( github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/multiformats/go-base32 v0.0.3 // indirect - github.com/multiformats/go-base36 v0.1.0 // indirect - github.com/multiformats/go-multiaddr v0.3.3 // indirect - github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multiaddr-net v0.2.0 // indirect - github.com/multiformats/go-multibase v0.0.3 // indirect - github.com/multiformats/go-multihash v0.0.14 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -263,8 +246,6 @@ require ( github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -335,10 +316,6 @@ require ( ) replace ( - // Fix go mod tidy issue for ambiguous imports from go-ethereum - // See https://github.com/ugorji/go/issues/279 - github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1 - // replicating the replace directive on cosmos SDK github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index a8cf979a0e6..a59590fa124 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -169,20 +169,26 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsy github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= -github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= @@ -303,6 +309,7 @@ github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuA github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -322,10 +329,8 @@ github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= @@ -552,7 +557,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -602,14 +606,13 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -703,7 +706,6 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -739,22 +741,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= -github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= -github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= -github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= @@ -816,9 +802,7 @@ github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= @@ -875,7 +859,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -900,22 +883,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-peerstore v0.2.7 h1:83JoLxyR9OYTnNfB5vvFqvMUv/xDNa6NoPHnENhBsGw= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= -github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= @@ -965,13 +934,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -1003,39 +967,10 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= -github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= -github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= -github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= -github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= -github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= -github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= @@ -1057,6 +992,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= @@ -1075,7 +1011,6 @@ github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/ github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -1242,8 +1177,6 @@ github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgq github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1292,7 +1225,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= @@ -1357,8 +1290,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1438,7 +1369,6 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -1455,7 +1385,6 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -1463,6 +1392,7 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1471,14 +1401,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -1545,7 +1472,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1573,6 +1499,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1664,8 +1591,10 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1760,7 +1689,6 @@ golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/core/services/keystore/keys/cosmoskey/export.go b/core/services/keystore/keys/cosmoskey/export.go index a327b60e80a..05061ddb014 100644 --- a/core/services/keystore/keys/cosmoskey/export.go +++ b/core/services/keystore/keys/cosmoskey/export.go @@ -33,12 +33,12 @@ func (key Key) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) password, scryptParams, adulteratedPassword, - func(id string, key Key, cryptoJSON keystore.CryptoJSON) (keys.EncryptedKeyExport, error) { + func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { return keys.EncryptedKeyExport{ KeyType: id, PublicKey: hex.EncodeToString(key.PublicKey().Bytes()), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/csakey/export.go b/core/services/keystore/keys/csakey/export.go index a1ad499e0e4..bd0e0229edf 100644 --- a/core/services/keystore/keys/csakey/export.go +++ b/core/services/keystore/keys/csakey/export.go @@ -29,12 +29,12 @@ func (k KeyV2) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) password, scryptParams, adulteratedPassword, - func(id string, key KeyV2, cryptoJSON keystore.CryptoJSON) (keys.EncryptedKeyExport, error) { + func(id string, key KeyV2, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { return keys.EncryptedKeyExport{ KeyType: id, PublicKey: key.PublicKeyString(), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/dkgencryptkey/export.go b/core/services/keystore/keys/dkgencryptkey/export.go index c689c29f661..3bccf1a07ba 100644 --- a/core/services/keystore/keys/dkgencryptkey/export.go +++ b/core/services/keystore/keys/dkgencryptkey/export.go @@ -30,12 +30,12 @@ func (k Key) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) ( password, scryptParams, adulteratedPassword, - func(id string, key Key, cryptoJSON keystore.CryptoJSON) (keys.EncryptedKeyExport, error) { + func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { return keys.EncryptedKeyExport{ KeyType: id, PublicKey: key.PublicKeyString(), Crypto: cryptoJSON, - }, nil + } }) } diff --git a/core/services/keystore/keys/dkgsignkey/export.go b/core/services/keystore/keys/dkgsignkey/export.go index 7ecada8ddc0..3c421760d63 100644 --- a/core/services/keystore/keys/dkgsignkey/export.go +++ b/core/services/keystore/keys/dkgsignkey/export.go @@ -31,12 +31,12 @@ func (key Key) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) password, scryptParams, adulteratedPassword, - func(id string, key Key, cryptoJSON keystore.CryptoJSON) (keys.EncryptedKeyExport, error) { + func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { return keys.EncryptedKeyExport{ KeyType: id, PublicKey: key.PublicKeyString(), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/exportutils.go b/core/services/keystore/keys/exportutils.go index 0c3e782a9a4..5d75b5076e6 100644 --- a/core/services/keystore/keys/exportutils.go +++ b/core/services/keystore/keys/exportutils.go @@ -3,7 +3,7 @@ package keys import ( "encoding/json" - keystore "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -62,7 +62,7 @@ func ToEncryptedJSON[E Encrypted, K any]( password string, scryptParams utils.ScryptParams, passwordFunc func(string) string, - buildExport func(id string, key K, cryptoJSON keystore.CryptoJSON) (E, error), + buildExport func(id string, key K, cryptoJSON keystore.CryptoJSON) E, ) (export []byte, err error) { // encrypt data using prefixed password @@ -77,10 +77,7 @@ func ToEncryptedJSON[E Encrypted, K any]( } // build [E] export struct using encrypted key, identifier, and original key [K] - encryptedKeyExport, err := buildExport(identifier, key, cryptoJSON) - if err != nil { - return nil, errors.Wrapf(err, "could not build encrypted export for %s key", identifier) - } + encryptedKeyExport := buildExport(identifier, key, cryptoJSON) return json.Marshal(encryptedKeyExport) } diff --git a/core/services/keystore/keys/ocr2key/export.go b/core/services/keystore/keys/ocr2key/export.go index 54e73ecf98f..5487e310462 100644 --- a/core/services/keystore/keys/ocr2key/export.go +++ b/core/services/keystore/keys/ocr2key/export.go @@ -66,7 +66,7 @@ func ToEncryptedJSON(key KeyBundle, password string, scryptParams utils.ScryptPa password, scryptParams, adulteratedPassword, - func(id string, key KeyBundle, cryptoJSON keystore.CryptoJSON) (EncryptedOCRKeyExport, error) { + func(id string, key KeyBundle, cryptoJSON keystore.CryptoJSON) EncryptedOCRKeyExport { pubKeyConfig := key.ConfigEncryptionPublicKey() pubKey := key.OffchainPublicKey() return EncryptedOCRKeyExport{ @@ -77,7 +77,7 @@ func ToEncryptedJSON(key KeyBundle, password string, scryptParams utils.ScryptPa OffChainPublicKey: hex.EncodeToString(pubKey[:]), ConfigPublicKey: hex.EncodeToString(pubKeyConfig[:]), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/ocrkey/export.go b/core/services/keystore/keys/ocrkey/export.go index 9f63a3c2d9c..363f38b20ab 100644 --- a/core/services/keystore/keys/ocrkey/export.go +++ b/core/services/keystore/keys/ocrkey/export.go @@ -42,7 +42,7 @@ func (key KeyV2) ToEncryptedJSON(password string, scryptParams utils.ScryptParam password, scryptParams, adulteratedPassword, - func(id string, key KeyV2, cryptoJSON keystore.CryptoJSON) (EncryptedOCRKeyExport, error) { + func(id string, key KeyV2, cryptoJSON keystore.CryptoJSON) EncryptedOCRKeyExport { return EncryptedOCRKeyExport{ KeyType: id, ID: key.ID(), @@ -50,7 +50,7 @@ func (key KeyV2) ToEncryptedJSON(password string, scryptParams utils.ScryptParam OffChainPublicKey: key.OffChainSigning.PublicKey(), ConfigPublicKey: key.PublicKeyConfig(), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/p2pkey/export.go b/core/services/keystore/keys/p2pkey/export.go index b81cf1d02d2..0db73167c4b 100644 --- a/core/services/keystore/keys/p2pkey/export.go +++ b/core/services/keystore/keys/p2pkey/export.go @@ -1,9 +1,7 @@ package p2pkey import ( - keystore "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -42,17 +40,13 @@ func (key KeyV2) ToEncryptedJSON(password string, scryptParams utils.ScryptParam password, scryptParams, adulteratedPassword, - func(id string, key KeyV2, cryptoJSON keystore.CryptoJSON) (EncryptedP2PKeyExport, error) { - rawPubKey, err := key.GetPublic().Bytes() - if err != nil { - return EncryptedP2PKeyExport{}, errors.Wrapf(err, "could not get raw public key") - } + func(id string, key KeyV2, cryptoJSON keystore.CryptoJSON) EncryptedP2PKeyExport { return EncryptedP2PKeyExport{ KeyType: id, - PublicKey: hexutil.Encode(rawPubKey), + PublicKey: key.PublicKeyHex(), PeerID: key.PeerID(), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/p2pkey/key.go b/core/services/keystore/keys/p2pkey/key.go index 6a96103dacb..abf4f70294c 100644 --- a/core/services/keystore/keys/p2pkey/key.go +++ b/core/services/keystore/keys/p2pkey/key.go @@ -1,6 +1,7 @@ package p2pkey import ( + "crypto/ed25519" "database/sql/driver" "encoding/hex" "encoding/json" @@ -8,14 +9,14 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/keystore" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" "github.com/pkg/errors" + + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" ) -// Key represents a libp2p private key +// Key represents a p2p private key type Key struct { - cryptop2p.PrivKey + PrivKey ed25519.PrivateKey } func (k Key) ToV2() KeyV2 { @@ -25,7 +26,7 @@ func (k Key) ToV2() KeyV2 { } } -// PublicKeyBytes is generated using cryptop2p.PubKey.Raw() +// PublicKeyBytes is a [ed25519.PublicKey] type PublicKeyBytes []byte func (pkb PublicKeyBytes) String() string { @@ -47,7 +48,7 @@ func (pkb *PublicKeyBytes) UnmarshalJSON(input []byte) error { return err } - *pkb = PublicKeyBytes(result) + *pkb = result return nil } @@ -66,19 +67,19 @@ func (pkb PublicKeyBytes) Value() (driver.Value, error) { } func (k Key) GetPeerID() (PeerID, error) { - peerID, err := peer.IDFromPrivateKey(k) + peerID, err := ragep2ptypes.PeerIDFromPrivateKey(k.PrivKey) if err != nil { - return "", errors.WithStack(err) + return PeerID{}, errors.WithStack(err) } return PeerID(peerID), err } func (k Key) PeerID() PeerID { - peerID, err := peer.IDFromPrivateKey(k) + peerID, err := k.GetPeerID() if err != nil { panic(err) } - return PeerID(peerID) + return peerID } type EncryptedP2PKey struct { @@ -113,7 +114,8 @@ func (ep2pk EncryptedP2PKey) Decrypt(auth string) (k Key, err error) { if err != nil { return k, errors.Wrapf(err, "could not decrypt P2P key %s (0x%x)", ep2pk.PeerID.String(), ep2pk.PubKey) } - privK, err := cryptop2p.UnmarshalPrivateKey(marshalledPrivK) + + privK, err := UnmarshalPrivateKey(marshalledPrivK) if err != nil { return k, errors.Wrapf(err, "could not unmarshal P2P private key for %s (0x%x)", ep2pk.PeerID.String(), ep2pk.PubKey) } diff --git a/core/services/keystore/keys/p2pkey/key_test.go b/core/services/keystore/keys/p2pkey/key_test.go index fc111f14219..57490483e86 100644 --- a/core/services/keystore/keys/p2pkey/key_test.go +++ b/core/services/keystore/keys/p2pkey/key_test.go @@ -1,13 +1,13 @@ package p2pkey import ( + "crypto/ed25519" "crypto/rand" "encoding/hex" "encoding/json" "testing" "github.com/ethereum/go-ethereum/accounts/keystore" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -15,7 +15,7 @@ import ( ) func TestP2PKeys_KeyStruct(t *testing.T) { - pk, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) + _, pk, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) k := Key{PrivKey: pk} @@ -37,12 +37,10 @@ func TestP2PKeys_KeyStruct(t *testing.T) { } func TestP2PKeys_PublicKeyBytes(t *testing.T) { - _, pk, err := cryptop2p.GenerateEd25519Key(rand.Reader) - require.NoError(t, err) - r, err := pk.Raw() + pk, _, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) - pkb := PublicKeyBytes(r) + pkb := PublicKeyBytes(pk) assert.Equal(t, hex.EncodeToString(pkb), pkb.String()) b, err := pkb.MarshalJSON() @@ -55,7 +53,7 @@ func TestP2PKeys_PublicKeyBytes(t *testing.T) { err = pkb.UnmarshalJSON([]byte("")) assert.Error(t, err) - err = pkb.Scan(r) + err = pkb.Scan([]byte(pk)) assert.NoError(t, err) err = pkb.Scan("invalid-type") @@ -67,16 +65,15 @@ func TestP2PKeys_PublicKeyBytes(t *testing.T) { } func TestP2PKeys_EncryptedP2PKey(t *testing.T) { - privk, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) + _, privk, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) k := Key{PrivKey: privk} - pubkr, err := k.GetPublic().Raw() - require.NoError(t, err) + pubkr := k.PrivKey.Public().(ed25519.PublicKey) var marshalledPrivK []byte - marshalledPrivK, err = cryptop2p.MarshalPrivateKey(k) + marshalledPrivK, err = MarshalPrivateKey(k.PrivKey) require.NoError(t, err) cryptoJSON, err := keystore.EncryptDataV3(marshalledPrivK, []byte(adulteratedPassword("password")), utils.FastScryptParams.N, utils.FastScryptParams.P) require.NoError(t, err) @@ -86,7 +83,7 @@ func TestP2PKeys_EncryptedP2PKey(t *testing.T) { p2pk := EncryptedP2PKey{ ID: 1, PeerID: k.PeerID(), - PubKey: pubkr, + PubKey: []byte(pubkr), EncryptedPrivKey: encryptedPrivKey, } diff --git a/core/services/keystore/keys/p2pkey/key_v2.go b/core/services/keystore/keys/p2pkey/key_v2.go index d92302cf5f9..6af71d5e2d7 100644 --- a/core/services/keystore/keys/p2pkey/key_v2.go +++ b/core/services/keystore/keys/p2pkey/key_v2.go @@ -1,20 +1,24 @@ package p2pkey import ( + "bytes" "crypto/ed25519" "crypto/rand" "encoding/hex" + "errors" "fmt" "math/big" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" + "github.com/smartcontractkit/libocr/ragep2p/types" ) +var libp2pPBPrefix = []byte{0x08, 0x01, 0x12, 0x40} + +// Raw is an encoded protocol buffer. type Raw []byte func (raw Raw) Key() KeyV2 { - privKey, err := cryptop2p.UnmarshalPrivateKey(raw) + privKey, err := UnmarshalPrivateKey(raw) if err != nil { panic(err) } @@ -25,6 +29,17 @@ func (raw Raw) Key() KeyV2 { return key } +func UnmarshalPrivateKey(raw Raw) (ed25519.PrivateKey, error) { + if !bytes.HasPrefix(raw, libp2pPBPrefix) { + return nil, errors.New("invalid key: missing libp2p protobuf prefix") + } + return ed25519.PrivateKey(raw[len(libp2pPBPrefix):]), nil +} + +func MarshalPrivateKey(key ed25519.PrivateKey) ([]byte, error) { + return bytes.Join([][]byte{libp2pPBPrefix, key}, nil), nil +} + func (raw Raw) String() string { return "" } @@ -36,12 +51,12 @@ func (raw Raw) GoString() string { var _ fmt.GoStringer = &KeyV2{} type KeyV2 struct { - cryptop2p.PrivKey - peerID PeerID + PrivKey ed25519.PrivateKey + peerID PeerID } func NewV2() (KeyV2, error) { - privKey, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) + _, privKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { return KeyV2{}, err } @@ -52,11 +67,7 @@ func MustNewV2XXXTestingOnly(k *big.Int) KeyV2 { seed := make([]byte, ed25519.SeedSize) copy(seed, k.Bytes()) pk := ed25519.NewKeyFromSeed(seed[:]) - p2pPrivKey, err := cryptop2p.UnmarshalEd25519PrivateKey(pk[:]) - if err != nil { - panic(err) - } - key, err := fromPrivkey(p2pPrivKey) + key, err := fromPrivkey(pk) if err != nil { panic(err) } @@ -64,11 +75,11 @@ func MustNewV2XXXTestingOnly(k *big.Int) KeyV2 { } func (key KeyV2) ID() string { - return peer.ID(key.peerID).String() + return types.PeerID(key.peerID).String() } func (key KeyV2) Raw() Raw { - marshalledPrivK, err := cryptop2p.MarshalPrivateKey(key.PrivKey) + marshalledPrivK, err := MarshalPrivateKey(key.PrivKey) if err != nil { panic(err) } @@ -80,10 +91,7 @@ func (key KeyV2) PeerID() PeerID { } func (key KeyV2) PublicKeyHex() string { - pubKeyBytes, err := key.GetPublic().Raw() - if err != nil { - panic(err) - } + pubKeyBytes := key.PrivKey.Public().(ed25519.PublicKey) return hex.EncodeToString(pubKeyBytes) } @@ -95,8 +103,8 @@ func (key KeyV2) GoString() string { return key.String() } -func fromPrivkey(privKey cryptop2p.PrivKey) (KeyV2, error) { - peerID, err := peer.IDFromPrivateKey(privKey) +func fromPrivkey(privKey ed25519.PrivateKey) (KeyV2, error) { + peerID, err := types.PeerIDFromPrivateKey(privKey) if err != nil { return KeyV2{}, err } diff --git a/core/services/keystore/keys/p2pkey/key_v2_test.go b/core/services/keystore/keys/p2pkey/key_v2_test.go index 53c3c302384..d93678b8f2d 100644 --- a/core/services/keystore/keys/p2pkey/key_v2_test.go +++ b/core/services/keystore/keys/p2pkey/key_v2_test.go @@ -1,38 +1,36 @@ package p2pkey import ( + "crypto/ed25519" "crypto/rand" "encoding/hex" "testing" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestP2PKeys_Raw(t *testing.T) { - pk, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) - require.NoError(t, err) - pkr, err := pk.Raw() + _, pk, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) - r := Raw(pkr) + r := Raw(pk) assert.Equal(t, r.String(), r.GoString()) assert.Equal(t, "", r.String()) } func TestP2PKeys_KeyV2(t *testing.T) { - pk, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) + _, pk, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) k := Key{PrivKey: pk} kv2 := k.ToV2() - pkv2, err := kv2.GetPublic().Raw() - require.NoError(t, err) + + pkv2 := kv2.PrivKey.Public().(ed25519.PublicKey) assert.Equal(t, kv2.String(), kv2.GoString()) - assert.Equal(t, peer.ID(k.PeerID()).String(), kv2.ID()) + assert.Equal(t, ragep2ptypes.PeerID(k.PeerID()).String(), kv2.ID()) assert.Equal(t, hex.EncodeToString(pkv2), kv2.PublicKeyHex()) } diff --git a/core/services/keystore/keys/p2pkey/peer_id.go b/core/services/keystore/keys/p2pkey/peer_id.go index 2147fa97744..38fbc471b59 100644 --- a/core/services/keystore/keys/p2pkey/peer_id.go +++ b/core/services/keystore/keys/p2pkey/peer_id.go @@ -6,13 +6,14 @@ import ( "fmt" "strings" - "github.com/libp2p/go-libp2p-core/peer" "github.com/pkg/errors" + + "github.com/smartcontractkit/libocr/ragep2p/types" ) const peerIDPrefix = "p2p_" -type PeerID peer.ID +type PeerID types.PeerID func MakePeerID(s string) (PeerID, error) { var peerID PeerID @@ -22,15 +23,14 @@ func MakePeerID(s string) (PeerID, error) { func (p PeerID) String() string { // Handle a zero peerID more gracefully, i.e. print it as empty string rather // than `p2p_` - raw := p.Raw() - if raw == "" { + if p == (PeerID{}) { return "" } - return fmt.Sprintf("%s%s", peerIDPrefix, raw) + return fmt.Sprintf("%s%s", peerIDPrefix, p.Raw()) } func (p PeerID) Raw() string { - return peer.ID(p).String() + return types.PeerID(p).String() } func (p *PeerID) UnmarshalString(s string) error { @@ -38,6 +38,9 @@ func (p *PeerID) UnmarshalString(s string) error { } func (p *PeerID) MarshalText() ([]byte, error) { + if *p == (PeerID{}) { + return nil, nil + } return []byte(p.Raw()), nil } @@ -51,7 +54,8 @@ func (p *PeerID) UnmarshalText(bs []byte) error { return nil } - peerID, err := peer.Decode(input) + var peerID types.PeerID + err := peerID.UnmarshalText([]byte(input)) if err != nil { return errors.Wrapf(err, `PeerID#UnmarshalText("%v")`, input) } @@ -60,7 +64,7 @@ func (p *PeerID) UnmarshalText(bs []byte) error { } func (p *PeerID) Scan(value interface{}) error { - *p = PeerID("") + *p = PeerID{} switch s := value.(type) { case string: if s != "" { @@ -74,7 +78,8 @@ func (p *PeerID) Scan(value interface{}) error { } func (p PeerID) Value() (driver.Value, error) { - return peer.Encode(peer.ID(p)), nil + b, err := types.PeerID(p).MarshalText() + return string(b), err } func (p PeerID) MarshalJSON() ([]byte, error) { diff --git a/core/services/keystore/keys/p2pkey/peer_id_test.go b/core/services/keystore/keys/p2pkey/peer_id_test.go index 197c54624fb..c648fec8de8 100644 --- a/core/services/keystore/keys/p2pkey/peer_id_test.go +++ b/core/services/keystore/keys/p2pkey/peer_id_test.go @@ -10,16 +10,16 @@ import ( func TestP2PKeys_PeerID(t *testing.T) { t.Run("make peer ID", func(t *testing.T) { - id, err := MakePeerID("11") + id, err := MakePeerID("12D3KooWM1111111111111111111111111111111111111111111") require.NoError(t, err) _, err = MakePeerID("invalid") assert.Error(t, err) - assert.Equal(t, "p2p_11", id.String()) + assert.Equal(t, "p2p_12D3KooWM1111111111111111111111111111111111111111111", id.String()) }) t.Run("unmarshals new ID", func(t *testing.T) { - id, err := MakePeerID("11") + id, err := MakePeerID("12D3KooWM1111111111111111111111111111111111111111111") require.NoError(t, err) fakeKey := MustNewV2XXXTestingOnly(big.NewInt(1)) @@ -30,7 +30,7 @@ func TestP2PKeys_PeerID(t *testing.T) { }) t.Run("scans new ID", func(t *testing.T) { - id, err := MakePeerID("11") + id, err := MakePeerID("12D3KooWM1111111111111111111111111111111111111111111") require.NoError(t, err) fakeKey := MustNewV2XXXTestingOnly(big.NewInt(1)) diff --git a/core/services/keystore/keys/solkey/export.go b/core/services/keystore/keys/solkey/export.go index 27f19fb5ed5..59e84136bc6 100644 --- a/core/services/keystore/keys/solkey/export.go +++ b/core/services/keystore/keys/solkey/export.go @@ -33,12 +33,12 @@ func (key Key) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) password, scryptParams, adulteratedPassword, - func(id string, key Key, cryptoJSON keystore.CryptoJSON) (keys.EncryptedKeyExport, error) { + func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { return keys.EncryptedKeyExport{ KeyType: id, PublicKey: hex.EncodeToString(key.pubKey), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/keys/starkkey/export.go b/core/services/keystore/keys/starkkey/export.go index 366243a8da2..26770028ca9 100644 --- a/core/services/keystore/keys/starkkey/export.go +++ b/core/services/keystore/keys/starkkey/export.go @@ -31,12 +31,12 @@ func ToEncryptedJSON(key Key, password string, scryptParams utils.ScryptParams) password, scryptParams, adulteratedPassword, - func(id string, key Key, cryptoJSON keystore.CryptoJSON) (keys.EncryptedKeyExport, error) { + func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { return keys.EncryptedKeyExport{ KeyType: id, PublicKey: key.StarkKeyStr(), Crypto: cryptoJSON, - }, nil + } }, ) } diff --git a/core/services/keystore/models_test.go b/core/services/keystore/models_test.go index f124f16d9b3..93c0f5fcb25 100644 --- a/core/services/keystore/models_test.go +++ b/core/services/keystore/models_test.go @@ -98,9 +98,9 @@ func TestKeyRing_Encrypt_Decrypt(t *testing.T) { } // compare p2p keys require.Equal(t, 2, len(decryptedKeyRing.P2P)) - require.Equal(t, originalKeyRing.P2P[p2p1.ID()].GetPublic(), decryptedKeyRing.P2P[p2p1.ID()].GetPublic()) + require.Equal(t, originalKeyRing.P2P[p2p1.ID()].PublicKeyHex(), decryptedKeyRing.P2P[p2p1.ID()].PublicKeyHex()) require.Equal(t, originalKeyRing.P2P[p2p1.ID()].PeerID(), decryptedKeyRing.P2P[p2p1.ID()].PeerID()) - require.Equal(t, originalKeyRing.P2P[p2p2.ID()].GetPublic(), decryptedKeyRing.P2P[p2p2.ID()].GetPublic()) + require.Equal(t, originalKeyRing.P2P[p2p2.ID()].PublicKeyHex(), decryptedKeyRing.P2P[p2p2.ID()].PublicKeyHex()) require.Equal(t, originalKeyRing.P2P[p2p2.ID()].PeerID(), decryptedKeyRing.P2P[p2p2.ID()].PeerID()) // compare solana keys require.Equal(t, 2, len(decryptedKeyRing.Solana)) diff --git a/core/services/keystore/p2p.go b/core/services/keystore/p2p.go index 657dfbc8973..ee2c64c021d 100644 --- a/core/services/keystore/p2p.go +++ b/core/services/keystore/p2p.go @@ -160,7 +160,7 @@ func (ks *p2p) GetOrFirst(id p2pkey.PeerID) (p2pkey.KeyV2, error) { if ks.isLocked() { return p2pkey.KeyV2{}, ErrLocked } - if id != "" { + if id != (p2pkey.PeerID{}) { return ks.getByID(id) } else if len(ks.keyRing.P2P) == 1 { ks.logger.Warn("No P2P.PeerID set, defaulting to first key in database") diff --git a/core/services/keystore/p2p_test.go b/core/services/keystore/p2p_test.go index 89cab3e1621..829df9812fd 100644 --- a/core/services/keystore/p2p_test.go +++ b/core/services/keystore/p2p_test.go @@ -1,8 +1,10 @@ package keystore_test import ( + "crypto/rand" "fmt" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -13,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -39,7 +40,10 @@ func Test_P2PKeyStore_E2E(t *testing.T) { t.Run("errors when getting non-existent ID", func(t *testing.T) { defer reset() - _, err := ks.Get("non-existent-id") + var nonExistent p2pkey.PeerID + _, err := rand.Read(nonExistent[:]) + require.NoError(t, err) + _, err = ks.Get(nonExistent) require.Error(t, err) }) @@ -58,7 +62,10 @@ func Test_P2PKeyStore_E2E(t *testing.T) { require.NoError(t, err) exportJSON, err := ks.Export(key.PeerID(), cltest.Password) require.NoError(t, err) - _, err = ks.Export("non-existent", cltest.Password) + var nonExistent p2pkey.PeerID + _, err = rand.Read(nonExistent[:]) + require.NoError(t, err) + _, err = ks.Export(nonExistent, cltest.Password) assert.Error(t, err) _, err = ks.Delete(key.PeerID()) require.NoError(t, err) @@ -117,14 +124,14 @@ func Test_P2PKeyStore_E2E(t *testing.T) { t.Run("GetOrFirst", func(t *testing.T) { defer reset() - _, err := ks.GetOrFirst("") + _, err := ks.GetOrFirst(p2pkey.PeerID{}) require.Contains(t, err.Error(), "no p2p keys exist") - id := p2pkey.PeerID("a0") + id := p2pkey.PeerID{0xa0} _, err = ks.GetOrFirst(id) require.Contains(t, err.Error(), fmt.Sprintf("unable to find P2P key with id %s", id)) k1, err := ks.Create() require.NoError(t, err) - k2, err := ks.GetOrFirst("") + k2, err := ks.GetOrFirst(p2pkey.PeerID{}) require.NoError(t, err) require.Equal(t, k1, k2) k3, err := ks.GetOrFirst(k1.PeerID()) @@ -132,7 +139,7 @@ func Test_P2PKeyStore_E2E(t *testing.T) { require.Equal(t, k1, k3) _, err = ks.Create() require.NoError(t, err) - _, err = ks.GetOrFirst("") + _, err = ks.GetOrFirst(p2pkey.PeerID{}) require.Contains(t, err.Error(), "multiple p2p keys found") //Check for possible keys in error message require.Contains(t, err.Error(), k1.ID()) @@ -147,12 +154,19 @@ func Test_P2PKeyStore_E2E(t *testing.T) { t.Run("clears p2p_peers on delete", func(t *testing.T) { key, err := ks.Create() require.NoError(t, err) - p2pPeer1 := ocrcommon.P2PPeer{ + type P2PPeer struct { + ID string + Addr string + PeerID string + CreatedAt time.Time + UpdatedAt time.Time + } + p2pPeer1 := P2PPeer{ ID: cltest.NewPeerID().String(), Addr: testutils.NewAddress().Hex(), PeerID: cltest.DefaultPeerID, // different p2p key } - p2pPeer2 := ocrcommon.P2PPeer{ + p2pPeer2 := P2PPeer{ ID: cltest.NewPeerID().String(), Addr: testutils.NewAddress().Hex(), PeerID: key.PeerID().Raw(), diff --git a/core/services/ocrcommon/discoverer_database.go b/core/services/ocrcommon/discoverer_database.go index 0a855665575..6468a910a65 100644 --- a/core/services/ocrcommon/discoverer_database.go +++ b/core/services/ocrcommon/discoverer_database.go @@ -5,10 +5,10 @@ import ( "database/sql" "github.com/lib/pq" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/pkg/errors" - ocrnetworking "github.com/smartcontractkit/libocr/networking/types" "go.uber.org/multierr" + + ocrnetworking "github.com/smartcontractkit/libocr/networking/types" ) var _ ocrnetworking.DiscovererDatabase = &DiscovererDatabase{} @@ -18,10 +18,10 @@ type DiscovererDatabase struct { peerID string } -func NewDiscovererDatabase(db *sql.DB, peerID p2ppeer.ID) *DiscovererDatabase { +func NewDiscovererDatabase(db *sql.DB, peerID string) *DiscovererDatabase { return &DiscovererDatabase{ db, - peerID.Pretty(), + peerID, } } diff --git a/core/services/ocrcommon/discoverer_database_test.go b/core/services/ocrcommon/discoverer_database_test.go index ff1a931b017..b7a79e92bce 100644 --- a/core/services/ocrcommon/discoverer_database_test.go +++ b/core/services/ocrcommon/discoverer_database_test.go @@ -1,16 +1,17 @@ package ocrcommon_test import ( + "crypto/ed25519" "crypto/rand" "testing" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" ) @@ -20,8 +21,8 @@ func Test_DiscovererDatabase(t *testing.T) { localPeerID1 := mustRandomP2PPeerID(t) localPeerID2 := mustRandomP2PPeerID(t) - dd1 := ocrcommon.NewDiscovererDatabase(db, localPeerID1) - dd2 := ocrcommon.NewDiscovererDatabase(db, localPeerID2) + dd1 := ocrcommon.NewDiscovererDatabase(db, localPeerID1.Raw()) + dd2 := ocrcommon.NewDiscovererDatabase(db, localPeerID2.Raw()) ctx := testutils.Context(t) @@ -73,7 +74,7 @@ func Test_DiscovererDatabase(t *testing.T) { }) t.Run("persists data across restarts", func(t *testing.T) { - dd3 := ocrcommon.NewDiscovererDatabase(db, localPeerID1) + dd3 := ocrcommon.NewDiscovererDatabase(db, localPeerID1.Raw()) announcements, err := dd3.ReadAnnouncements(ctx, []string{"remote1"}) require.NoError(t, err) @@ -83,10 +84,10 @@ func Test_DiscovererDatabase(t *testing.T) { }) } -func mustRandomP2PPeerID(t *testing.T) p2ppeer.ID { - p2pPrivkey, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) +func mustRandomP2PPeerID(t *testing.T) p2pkey.PeerID { + _, p2pPrivkey, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) - id, err := p2ppeer.IDFromPrivateKey(p2pPrivkey) + id, err := ragep2ptypes.PeerIDFromPrivateKey(p2pPrivkey) require.NoError(t, err) - return id + return p2pkey.PeerID(id) } diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index ff666eefdba..1b8ebfcdb22 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -2,15 +2,10 @@ package ocrcommon import ( "context" - "crypto/ed25519" - "fmt" "io" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" - "github.com/pkg/errors" - "go.uber.org/multierr" - "github.com/jmoiron/sqlx" + "github.com/pkg/errors" ocrnetworking "github.com/smartcontractkit/libocr/networking" ocr1types "github.com/smartcontractkit/libocr/offchainreporting/types" @@ -44,14 +39,13 @@ type ( // SingletonPeerWrapper manages all libocr peers for the application SingletonPeerWrapper struct { services.StateMachine - keyStore keystore.Master - p2pCfg config.P2P - ocrCfg PeerWrapperOCRConfig - dbConfig pg.QConfig - db *sqlx.DB - lggr logger.Logger - PeerID p2pkey.PeerID - pstoreWrapper *Pstorewrapper + keyStore keystore.Master + p2pCfg config.P2P + ocrCfg PeerWrapperOCRConfig + dbConfig pg.QConfig + db *sqlx.DB + lggr logger.Logger + PeerID p2pkey.PeerID // Used at shutdown to stop all of this peer's goroutines peerCloser io.Closer @@ -125,15 +119,11 @@ func (p *SingletonPeerWrapper) peerConfig() (ocrnetworking.PeerConfig, error) { } p.PeerID = key.PeerID() - discovererDB := NewDiscovererDatabase(p.db.DB, p2ppeer.ID(p.PeerID)) + discovererDB := NewDiscovererDatabase(p.db.DB, p.PeerID.Raw()) config := p.p2pCfg - pk, err := key.PrivKey.Raw() - if err != nil { - return ocrnetworking.PeerConfig{}, fmt.Errorf("failed to get raw private key: %w", err) - } peerConfig := ocrnetworking.PeerConfig{ - PrivKey: ed25519.PrivateKey(pk), + PrivKey: key.PrivKey, Logger: commonlogger.NewOCRWrapper(p.lggr, p.ocrCfg.TraceLogging(), func(string) {}), // V2 config @@ -158,9 +148,6 @@ func (p *SingletonPeerWrapper) Close() error { if p.peerCloser != nil { err = p.peerCloser.Close() } - if p.pstoreWrapper != nil { - err = multierr.Combine(err, p.pstoreWrapper.Close()) - } return err }) } diff --git a/core/services/ocrcommon/peer_wrapper_test.go b/core/services/ocrcommon/peer_wrapper_test.go index dbe22523728..278e0684fd2 100644 --- a/core/services/ocrcommon/peer_wrapper_test.go +++ b/core/services/ocrcommon/peer_wrapper_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/hashicorp/consul/sdk/freeport" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" @@ -26,8 +26,8 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { db := pgtest.NewSqlxDB(t) - peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) - require.NoError(t, err) + var peerID ragep2ptypes.PeerID + require.NoError(t, peerID.UnmarshalText([]byte(configtest.DefaultPeerID))) t.Run("with no p2p keys returns error", func(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { diff --git a/core/services/ocrcommon/peerstore.go b/core/services/ocrcommon/peerstore.go deleted file mode 100644 index 1d859184ab5..00000000000 --- a/core/services/ocrcommon/peerstore.go +++ /dev/null @@ -1,182 +0,0 @@ -package ocrcommon - -import ( - "context" - "fmt" - "strings" - "time" - - p2ppeer "github.com/libp2p/go-libp2p-core/peer" - p2ppeerstore "github.com/libp2p/go-libp2p-core/peerstore" - "github.com/libp2p/go-libp2p-peerstore/pstoremem" - ma "github.com/multiformats/go-multiaddr" - "github.com/pkg/errors" - - "github.com/jmoiron/sqlx" - - "github.com/smartcontractkit/chainlink-common/pkg/services" - - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/recovery" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -type ( - P2PPeer struct { - ID string - Addr string - PeerID string - CreatedAt time.Time - UpdatedAt time.Time - } - - Pstorewrapper struct { - services.StateMachine - Peerstore p2ppeerstore.Peerstore - peerID string - q pg.Q - writeInterval time.Duration - ctx context.Context - ctxCancel context.CancelFunc - chDone chan struct{} - lggr logger.SugaredLogger - } -) - -// NewPeerstoreWrapper creates a new database-backed peerstore wrapper scoped to the given jobID -// Multiple peerstore wrappers should not be instantiated with the same jobID -func NewPeerstoreWrapper(db *sqlx.DB, writeInterval time.Duration, peerID p2pkey.PeerID, lggr logger.Logger, cfg pg.QConfig) (*Pstorewrapper, error) { - ctx, cancel := context.WithCancel(context.Background()) - namedLogger := lggr.Named("PeerStore") - q := pg.NewQ(db, namedLogger, cfg) - - return &Pstorewrapper{ - services.StateMachine{}, - pstoremem.NewPeerstore(), - peerID.Raw(), - q, - writeInterval, - ctx, - cancel, - make(chan struct{}), - logger.Sugared(namedLogger), - }, nil -} - -func (p *Pstorewrapper) Start() error { - return p.StartOnce("PeerStore", func() error { - err := p.readFromDB() - if err != nil { - return errors.Wrap(err, "could not start peerstore wrapper") - } - go p.dbLoop() - return nil - }) -} - -func (p *Pstorewrapper) dbLoop() { - defer close(p.chDone) - ticker := time.NewTicker(utils.WithJitter(p.writeInterval)) - defer ticker.Stop() - for { - select { - case <-p.ctx.Done(): - return - case <-ticker.C: - recovery.WrapRecover(p.lggr, func() { - if err := p.WriteToDB(); err != nil { - p.lggr.Errorw("Error writing peerstore to DB", "err", err) - } - }) - } - } -} - -func (p *Pstorewrapper) Close() error { - return p.StopOnce("PeerStore", func() error { - p.ctxCancel() - <-p.chDone - return p.Peerstore.Close() - }) -} - -func (p *Pstorewrapper) readFromDB() error { - peers, err := p.getPeers() - if err != nil { - return err - } - for _, peer := range peers { - peerID, err := p2ppeer.Decode(peer.ID) - if err != nil { - return errors.Wrapf(err, "unexpectedly failed to decode peer ID '%s'", peer.ID) - } - peerAddr, err := ma.NewMultiaddr(peer.Addr) - if err != nil { - return errors.Wrapf(err, "unexpectedly failed to decode peer multiaddr '%s'", peer.Addr) - } - p.Peerstore.AddAddr(peerID, peerAddr, p2ppeerstore.PermanentAddrTTL) - } - return nil -} - -func (p *Pstorewrapper) getPeers() (peers []P2PPeer, err error) { - rows, err := p.q.WithOpts(pg.WithParentCtx(p.ctx)).Query(`SELECT id, addr FROM p2p_peers WHERE peer_id = $1`, p.peerID) - if err != nil { - return nil, errors.Wrap(err, "error querying peers") - } - defer p.lggr.ErrorIfFn(rows.Close, "Error closing p2p_peers rows") - - peers = make([]P2PPeer, 0) - - for rows.Next() { - peer := P2PPeer{} - if err = rows.Scan(&peer.ID, &peer.Addr); err != nil { - return nil, errors.Wrap(err, "unexpected error scanning row") - } - peers = append(peers, peer) - } - if err = rows.Err(); err != nil { - return nil, err - } - - return peers, nil -} - -func (p *Pstorewrapper) WriteToDB() error { - q := p.q.WithOpts(pg.WithParentCtx(p.ctx)) - err := q.Transaction(func(tx pg.Queryer) error { - _, err := tx.Exec(`DELETE FROM p2p_peers WHERE peer_id = $1`, p.peerID) - if err != nil { - return errors.Wrap(err, "delete from p2p_peers failed") - } - peers := make([]P2PPeer, 0) - for _, pid := range p.Peerstore.PeersWithAddrs() { - addrs := p.Peerstore.Addrs(pid) - for _, addr := range addrs { - p := P2PPeer{ - ID: pid.String(), - Addr: addr.String(), - PeerID: p.peerID, - } - peers = append(peers, p) - } - } - valueStrings := []string{} - valueArgs := []interface{}{} - for _, p := range peers { - valueStrings = append(valueStrings, "(?, ?, ?, NOW(), NOW())") - valueArgs = append(valueArgs, p.ID) - valueArgs = append(valueArgs, p.Addr) - valueArgs = append(valueArgs, p.PeerID) - } - - /* #nosec G201 */ - stmt := fmt.Sprintf("INSERT INTO p2p_peers (id, addr, peer_id, created_at, updated_at) VALUES %s", strings.Join(valueStrings, ",")) - stmt = sqlx.Rebind(sqlx.DOLLAR, stmt) - _, err = tx.Exec(stmt, valueArgs...) - return errors.Wrap(err, "insert into p2p_peers failed") - }) - return errors.Wrap(err, "could not write peers to DB") -} diff --git a/core/services/ocrcommon/peerstore_test.go b/core/services/ocrcommon/peerstore_test.go deleted file mode 100644 index 6e692153564..00000000000 --- a/core/services/ocrcommon/peerstore_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package ocrcommon_test - -import ( - "testing" - "time" - - p2ppeer "github.com/libp2p/go-libp2p-core/peer" - p2ppeerstore "github.com/libp2p/go-libp2p-core/peerstore" - ma "github.com/multiformats/go-multiaddr" - - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -func Test_Peerstore_Start(t *testing.T) { - db := pgtest.NewSqlxDB(t) - - peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) - require.NoError(t, err) - - nonExistentP2PPeerID, err := p2ppeer.Decode("12D3KooWAdCzaesXyezatDzgGvCngqsBqoUqnV9PnVc46jsVt2i9") - require.NoError(t, err) - - err = utils.JustError(db.Exec(`INSERT INTO p2p_peers (id, addr, created_at, updated_at, peer_id) VALUES - ( - '12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph', - '/ip4/127.0.0.1/tcp/12000/p2p/12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph', - NOW(), - NOW(), - $1 - ), - ( - '12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph', - '/ip4/127.0.0.2/tcp/12000/p2p/12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph', - NOW(), - NOW(), - $2 - ), - ( - '12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph', - '/ip4/127.0.0.2/tcp/12000/p2p/12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph', - NOW(), - NOW(), - $3 - ) - `, p2pkey.PeerID(peerID), p2pkey.PeerID(peerID), p2pkey.PeerID(nonExistentP2PPeerID))) - require.NoError(t, err) - - cfg := configtest.NewTestGeneralConfig(t) - wrapper, err := ocrcommon.NewPeerstoreWrapper(db, 1*time.Second, p2pkey.PeerID(peerID), logger.TestLogger(t), cfg.Database()) - require.NoError(t, err) - - err = wrapper.Start() - require.NoError(t, err) - - require.Equal(t, 1, wrapper.Peerstore.PeersWithAddrs().Len()) - - peerID, err = p2ppeer.Decode("12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph") - require.NoError(t, err) - - maddrs := wrapper.Peerstore.Addrs(peerID) - - require.Len(t, maddrs, 2) -} - -func Test_Peerstore_WriteToDB(t *testing.T) { - db := pgtest.NewSqlxDB(t) - - peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) - require.NoError(t, err) - - cfg := configtest.NewTestGeneralConfig(t) - wrapper, err := ocrcommon.NewPeerstoreWrapper(db, 1*time.Second, p2pkey.PeerID(peerID), logger.TestLogger(t), cfg.Database()) - require.NoError(t, err) - - maddr, err := ma.NewMultiaddr("/ip4/127.0.0.2/tcp/12000/p2p/12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph") - require.NoError(t, err) - newPeerID, err := p2ppeer.Decode("12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph") - require.NoError(t, err) - - wrapper.Peerstore.AddAddr(newPeerID, maddr, p2ppeerstore.PermanentAddrTTL) - - err = wrapper.WriteToDB() - require.NoError(t, err) - - var peers []ocrcommon.P2PPeer - err = db.Select(&peers, `SELECT * FROM p2p_peers`) - require.NoError(t, err) - require.Equal(t, 1, len(peers)) - - peer := peers[0] - require.Equal(t, "12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph", peer.ID) - require.Equal(t, "/ip4/127.0.0.2/tcp/12000/p2p/12D3KooWL1yndUw9T2oWXjhfjdwSscWA78YCpUdduA3Cnn4dCtph", peer.Addr) - require.Equal(t, p2pkey.PeerID(peerID).Raw(), peer.PeerID) -} diff --git a/core/services/signatures/secp256k1/curve.go b/core/services/signatures/secp256k1/curve.go index 2ed52a87cec..70187e68738 100644 --- a/core/services/signatures/secp256k1/curve.go +++ b/core/services/signatures/secp256k1/curve.go @@ -12,7 +12,7 @@ package secp256k1 import ( "math/big" - secp256k1BTCD "github.com/btcsuite/btcd/btcec" + secp256k1BTCD "github.com/btcsuite/btcd/btcec/v2" "go.dedis.ch/kyber/v3" ) diff --git a/core/services/signatures/secp256k1/field.go b/core/services/signatures/secp256k1/field.go index 7cfa7af2e5c..1ff0d062f3c 100644 --- a/core/services/signatures/secp256k1/field.go +++ b/core/services/signatures/secp256k1/field.go @@ -137,13 +137,11 @@ func fieldSquare(y *fieldElt) *fieldElt { return fieldEltFromBigInt(newFieldZero().int().Exp(y.int(), two, q)) } +func i() *big.Int { return new(big.Int) } + // sqrtPower is s.t. n^sqrtPower≡sqrt(n) mod q, if n has a root at all. See // https://math.stackexchange.com/a/1816280, for instance -// -// What I'm calling sqrtPower is called q on the s256 struct. (See -// btcec.initS256), which is confusing because the "Q" in "QPlus1Div4" refers to -// the field characteristic -var sqrtPower = s256.QPlus1Div4() +var sqrtPower = i().Rsh(i().Add(q, big.NewInt(1)), 2) // (q +1)/4 // maybeSqrtInField returns a square root of v, if it has any, else nil func maybeSqrtInField(v *fieldElt) *fieldElt { diff --git a/core/services/signatures/secp256k1/scalar.go b/core/services/signatures/secp256k1/scalar.go index 9d89188c4c7..d12826a8270 100644 --- a/core/services/signatures/secp256k1/scalar.go +++ b/core/services/signatures/secp256k1/scalar.go @@ -19,7 +19,7 @@ import ( "io" "math/big" - secp256k1BTCD "github.com/btcsuite/btcd/btcec" + secp256k1BTCD "github.com/btcsuite/btcd/btcec/v2" "github.com/ethereum/go-ethereum/common" "go.dedis.ch/kyber/v3" diff --git a/core/utils/utils.go b/core/utils/utils.go index 0df280775b5..b887fe9a800 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "context" + "crypto/ed25519" "crypto/rand" "encoding/base64" "encoding/hex" @@ -18,18 +19,17 @@ import ( "sync/atomic" "time" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" - "golang.org/x/exp/constraints" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/google/uuid" "github.com/jpillora/backoff" - "github.com/libp2p/go-libp2p-core/peer" pkgerrors "github.com/pkg/errors" "github.com/robfig/cron/v3" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/sha3" + "golang.org/x/exp/constraints" + + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/smartcontractkit/chainlink-common/pkg/services" ) @@ -64,11 +64,11 @@ func Bytes32ToSlice(a [32]byte) (r []byte) { } func MustNewPeerID() string { - _, pubKey, err := cryptop2p.GenerateEd25519Key(rand.Reader) + pubKey, _, err := ed25519.GenerateKey(rand.Reader) if err != nil { panic(err) } - peerID, err := peer.IDFromPublicKey(pubKey) + peerID, err := ragep2ptypes.PeerIDFromPublicKey(pubKey) if err != nil { panic(err) } diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 3beee88c2eb..27d4433b63c 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -18,8 +18,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/hashicorp/consul/sdk/freeport" - p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/pelletier/go-toml" + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -48,8 +48,8 @@ func TestJobsController_Create_ValidationFailure_OffchainReportingSpec(t *testin contractAddress = cltest.NewEIP55Address() ) - peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) - require.NoError(t, err) + var peerID ragep2ptypes.PeerID + require.NoError(t, peerID.UnmarshalText([]byte(configtest.DefaultPeerID))) randomBytes := testutils.Random32Byte() var tt = []struct { diff --git a/core/web/p2p_keys_controller.go b/core/web/p2p_keys_controller.go index c81400615b0..bbe9d83f741 100644 --- a/core/web/p2p_keys_controller.go +++ b/core/web/p2p_keys_controller.go @@ -29,6 +29,8 @@ func (p2pkc *P2PKeysController) Index(c *gin.Context) { jsonAPIResponse(c, presenters.NewP2PKeyResources(keys), "p2pKey") } +const keyType = "Ed25519" + // Create and return a P2P key // Example: // "POST /keys/p2p" @@ -44,7 +46,7 @@ func (p2pkc *P2PKeysController) Create(c *gin.Context) { "id": key.ID(), "p2pPublicKey": key.PublicKeyHex(), "p2pPeerID": key.PeerID(), - "p2pType": key.Type(), + "p2pType": keyType, }) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") } @@ -101,7 +103,7 @@ func (p2pkc *P2PKeysController) Import(c *gin.Context) { "id": key.ID(), "p2pPublicKey": key.PublicKeyHex(), "p2pPeerID": key.PeerID(), - "p2pType": key.Type(), + "p2pType": keyType, }) jsonAPIResponse(c, presenters.NewP2PKeyResource(key), "p2pKey") diff --git a/core/web/presenters/p2p_key_test.go b/core/web/presenters/p2p_key_test.go index 2d30f87fe18..4e7b4e954fd 100644 --- a/core/web/presenters/p2p_key_test.go +++ b/core/web/presenters/p2p_key_test.go @@ -1,7 +1,6 @@ package presenters import ( - "encoding/hex" "fmt" "testing" @@ -16,9 +15,6 @@ func TestP2PKeyResource(t *testing.T) { key := keystest.NewP2PKeyV2(t) peerID := key.PeerID() peerIDStr := peerID.String() - pubKey := key.GetPublic() - pubKeyBytes, err := pubKey.Raw() - require.NoError(t, err) r := NewP2PKeyResource(key) b, err := jsonapi.Marshal(r) @@ -34,7 +30,7 @@ func TestP2PKeyResource(t *testing.T) { "publicKey": "%s" } } - }`, key.ID(), peerIDStr, hex.EncodeToString(pubKeyBytes)) + }`, key.ID(), peerIDStr, key.PublicKeyHex()) assert.JSONEq(t, expected, string(b)) @@ -52,7 +48,7 @@ func TestP2PKeyResource(t *testing.T) { "publicKey": "%s" } } - }`, key.ID(), peerIDStr, hex.EncodeToString(pubKeyBytes)) + }`, key.ID(), peerIDStr, key.PublicKeyHex()) assert.JSONEq(t, expected, string(b)) } diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 990a6c08058..996b3859a55 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -640,12 +640,13 @@ func (r *Resolver) CreateP2PKey(ctx context.Context) (*CreateP2PKeyPayloadResolv return nil, err } + const keyType = "Ed25519" r.App.GetAuditLogger().Audit(audit.KeyCreated, map[string]interface{}{ "type": "p2p", "id": key.ID(), "p2pPublicKey": key.PublicKeyHex(), "p2pPeerID": key.PeerID(), - "p2pType": key.Type(), + "p2pType": keyType, }) return NewCreateP2PKeyPayload(key), nil diff --git a/go.mod b/go.mod index 40359ab16f7..d8394633c3f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/sprig/v3 v3.2.3 github.com/avast/retry-go/v4 v4.5.1 - github.com/btcsuite/btcd v0.23.4 + github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/cometbft/cometbft v0.37.2 github.com/cosmos/cosmos-sdk v0.47.4 github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e @@ -24,7 +24,7 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-webauthn/webauthn v0.9.4 - github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b github.com/google/uuid v1.4.0 github.com/gorilla/securecookie v1.1.2 github.com/gorilla/sessions v1.2.2 @@ -43,13 +43,10 @@ require ( github.com/kylelemons/godebug v1.1.0 github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a github.com/lib/pq v1.10.9 - github.com/libp2p/go-libp2p-core v0.8.5 - github.com/libp2p/go-libp2p-peerstore v0.2.7 github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 github.com/mr-tron/base58 v1.2.0 - github.com/multiformats/go-multiaddr v0.3.3 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/gomega v1.30.0 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -131,7 +128,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blendle/zapdriver v1.3.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect @@ -223,9 +219,6 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/go-cid v0.0.7 // indirect - github.com/ipfs/go-log v1.0.4 // indirect - github.com/ipfs/go-log/v2 v2.1.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -240,7 +233,6 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-openssl v0.0.7 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -248,8 +240,6 @@ require ( github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect - github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect - github.com/minio/sha256-simd v0.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -257,13 +247,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/multiformats/go-base32 v0.0.3 // indirect - github.com/multiformats/go-base36 v0.1.0 // indirect - github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multiaddr-net v0.2.0 // indirect - github.com/multiformats/go-multibase v0.0.3 // indirect - github.com/multiformats/go-multihash v0.0.14 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -279,8 +262,6 @@ require ( github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -336,10 +317,6 @@ require ( ) replace ( - // Fix go mod tidy issue for ambiguous imports from go-ethereum - // See https://github.com/ugorji/go/issues/279 - github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1 - // replicating the replace directive on cosmos SDK github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/go.sum b/go.sum index 11b377b11f8..d702fd37e90 100644 --- a/go.sum +++ b/go.sum @@ -123,7 +123,6 @@ github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40wo github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= @@ -173,20 +172,12 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -314,17 +305,14 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= @@ -553,7 +541,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -603,8 +590,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -704,7 +691,6 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -744,22 +730,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= -github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= -github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= -github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= @@ -821,9 +791,6 @@ github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= @@ -843,7 +810,6 @@ github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -862,7 +828,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -880,7 +845,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -905,22 +869,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-peerstore v0.2.7 h1:83JoLxyR9OYTnNfB5vvFqvMUv/xDNa6NoPHnENhBsGw= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= -github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= @@ -970,13 +920,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -1010,39 +955,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= -github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= -github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= -github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= -github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= -github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= -github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= @@ -1061,7 +977,6 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= @@ -1070,8 +985,6 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -1084,7 +997,6 @@ github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/ github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -1251,8 +1163,6 @@ github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgq github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1302,7 +1212,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= @@ -1367,8 +1276,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1448,7 +1355,6 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -1465,7 +1371,6 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -1481,14 +1386,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -1546,7 +1448,6 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1556,7 +1457,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1776,7 +1676,6 @@ golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 5b44209f9a6..72c15a89669 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -92,7 +92,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blendle/zapdriver v1.3.1 // indirect - github.com/btcsuite/btcd v0.23.4 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect @@ -207,7 +206,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -253,9 +252,6 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/go-cid v0.0.7 // indirect - github.com/ipfs/go-log v1.0.4 // indirect - github.com/ipfs/go-log/v2 v2.1.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.1 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -279,9 +275,6 @@ require ( github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-libp2p-core v0.8.5 // indirect - github.com/libp2p/go-libp2p-peerstore v0.2.7 // indirect - github.com/libp2p/go-openssl v0.0.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -293,8 +286,6 @@ require ( github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.56 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect - github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect - github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -310,14 +301,6 @@ require ( github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/multiformats/go-base32 v0.0.3 // indirect - github.com/multiformats/go-base36 v0.1.0 // indirect - github.com/multiformats/go-multiaddr v0.3.3 // indirect - github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multiaddr-net v0.2.0 // indirect - github.com/multiformats/go-multibase v0.0.3 // indirect - github.com/multiformats/go-multihash v0.0.14 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect @@ -370,8 +353,6 @@ require ( github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -472,10 +453,6 @@ require ( ) replace ( - // Fixes go mod tidy issue for ambiguous imports from go-ethereum - // See https://github.com/ugorji/go/issues/279 - github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1 - github.com/go-kit/log => github.com/go-kit/log v0.2.1 // replicating the replace directive on cosmos SDK diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0e29f6f6715..494e5c21ec9 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -145,7 +145,6 @@ github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40wo github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= @@ -207,20 +206,12 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -383,7 +374,6 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= @@ -392,10 +382,8 @@ github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= @@ -714,7 +702,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -770,8 +757,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -905,7 +892,6 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -954,22 +940,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ionos-cloud/sdk-go/v6 v6.1.9 h1:Iq3VIXzeEbc8EbButuACgfLMiY5TPVWUPNrF+Vsddo4= github.com/ionos-cloud/sdk-go/v6 v6.1.9/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= -github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= -github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= -github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= -github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= @@ -1026,9 +996,6 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= @@ -1049,7 +1016,6 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1074,7 +1040,6 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -1121,22 +1086,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-peerstore v0.2.7 h1:83JoLxyR9OYTnNfB5vvFqvMUv/xDNa6NoPHnENhBsGw= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= -github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/linode/linodego v1.23.0 h1:s0ReCZtuN9Z1IoUN9w1RLeYO1dMZUGPwOQ/IBFsBHtU= @@ -1204,14 +1155,8 @@ github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SW github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= -github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1256,9 +1201,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= @@ -1267,32 +1209,6 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= -github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= -github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= -github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= -github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= -github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= -github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1316,7 +1232,6 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= @@ -1325,8 +1240,6 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -1343,7 +1256,6 @@ github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cP github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -1555,8 +1467,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1607,7 +1517,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= @@ -1677,8 +1586,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1780,7 +1687,6 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -1797,7 +1703,6 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -1814,16 +1719,13 @@ golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -1881,7 +1783,6 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1891,7 +1792,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -2141,7 +2041,6 @@ golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= From 0c63446ae535671b67a253ac1c1d43860ebeb653 Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 12 Dec 2023 10:52:16 +0000 Subject: [PATCH 034/234] [BCF-2760] Flakey test detection improvements (#11470) * [BCF-2760] Flakey test detection improvements * [BCF-2760] Dedupe test and subtest entries --- tools/flakeytests/reporter.go | 69 +++++++-- tools/flakeytests/reporter_test.go | 103 ++++++++++--- tools/flakeytests/runner.go | 222 ++++++++++++++++++++++------- tools/flakeytests/runner_test.go | 106 +++++++++++--- 4 files changed, 399 insertions(+), 101 deletions(-) diff --git a/tools/flakeytests/reporter.go b/tools/flakeytests/reporter.go index 6696ec29a40..f17a44ef9f1 100644 --- a/tools/flakeytests/reporter.go +++ b/tools/flakeytests/reporter.go @@ -11,6 +11,12 @@ import ( "time" ) +const ( + messageType_flakeyTest = "flakey_test" + messageType_runReport = "run_report" + messageType_packagePanic = "package_panic" +) + type pushRequest struct { Streams []stream `json:"streams"` } @@ -20,16 +26,28 @@ type stream struct { Values [][]string `json:"values"` } +type BaseMessage struct { + MessageType string `json:"message_type"` + Context +} + type flakeyTest struct { + BaseMessage Package string `json:"package"` TestName string `json:"test_name"` FQTestName string `json:"fq_test_name"` - Context } -type numFlakes struct { - NumFlakes int `json:"num_flakes"` - Context +type packagePanic struct { + BaseMessage + Package string `json:"package"` +} + +type runReport struct { + BaseMessage + NumPackagePanics int `json:"num_package_panics"` + NumFlakes int `json:"num_flakes"` + NumCombined int `json:"num_combined"` } type Context struct { @@ -48,17 +66,21 @@ type LokiReporter struct { ctx Context } -func (l *LokiReporter) createRequest(flakeyTests map[string]map[string]struct{}) (pushRequest, error) { +func (l *LokiReporter) createRequest(report *Report) (pushRequest, error) { vs := [][]string{} now := l.now() nows := fmt.Sprintf("%d", now.UnixNano()) - for pkg, tests := range flakeyTests { + + for pkg, tests := range report.tests { for t := range tests { d, err := json.Marshal(flakeyTest{ + BaseMessage: BaseMessage{ + MessageType: messageType_flakeyTest, + Context: l.ctx, + }, Package: pkg, TestName: t, FQTestName: fmt.Sprintf("%s:%s", pkg, t), - Context: l.ctx, }) if err != nil { return pushRequest{}, err @@ -67,10 +89,35 @@ func (l *LokiReporter) createRequest(flakeyTests map[string]map[string]struct{}) } } - // Flakes are store in a map[string][]string, so to count them, we can't just do len(flakeyTests), + // Flakes are stored in a map[string][]string, so to count them, we can't just do len(flakeyTests), // as that will get us the number of flakey packages, not the number of flakes tests. // However, we do emit one log line per flakey test above, so use that to count our flakes. - f, err := json.Marshal(numFlakes{NumFlakes: len(vs), Context: l.ctx}) + numFlakes := len(vs) + + for pkg := range report.packagePanics { + d, err := json.Marshal(packagePanic{ + BaseMessage: BaseMessage{ + MessageType: messageType_packagePanic, + Context: l.ctx, + }, + Package: pkg, + }) + if err != nil { + return pushRequest{}, err + } + + vs = append(vs, []string{nows, string(d)}) + } + + f, err := json.Marshal(runReport{ + BaseMessage: BaseMessage{ + MessageType: messageType_runReport, + Context: l.ctx, + }, + NumFlakes: numFlakes, + NumPackagePanics: len(report.packagePanics), + NumCombined: numFlakes + len(report.packagePanics), + }) if err != nil { return pushRequest{}, nil } @@ -120,8 +167,8 @@ func (l *LokiReporter) makeRequest(pushReq pushRequest) error { return err } -func (l *LokiReporter) Report(flakeyTests map[string]map[string]struct{}) error { - pushReq, err := l.createRequest(flakeyTests) +func (l *LokiReporter) Report(report *Report) error { + pushReq, err := l.createRequest(report) if err != nil { return err } diff --git a/tools/flakeytests/reporter_test.go b/tools/flakeytests/reporter_test.go index 9cb2c8e9f7d..15650fc7bd1 100644 --- a/tools/flakeytests/reporter_test.go +++ b/tools/flakeytests/reporter_test.go @@ -12,68 +12,131 @@ import ( func TestMakeRequest_SingleTest(t *testing.T) { now := time.Now() ts := fmt.Sprintf("%d", now.UnixNano()) - ft := map[string]map[string]struct{}{ - "core/assets": map[string]struct{}{ - "TestLink": struct{}{}, + r := &Report{ + tests: map[string]map[string]int{ + "core/assets": map[string]int{ + "TestLink": 1, + }, }, } lr := &LokiReporter{auth: "bla", host: "bla", command: "go_core_tests", now: func() time.Time { return now }} - pr, err := lr.createRequest(ft) + pr, err := lr.createRequest(r) require.NoError(t, err) assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, `{"package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink","commit_sha":"","repository":"","event_type":""}`}, - {ts, `{"num_flakes":1,"commit_sha":"","repository":"","event_type":""}`}, + {ts, `{"message_type":"flakey_test","commit_sha":"","repository":"","event_type":"","package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink"}`}, + {ts, `{"message_type":"run_report","commit_sha":"","repository":"","event_type":"","num_package_panics":0,"num_flakes":1,"num_combined":1}`}, }) } func TestMakeRequest_MultipleTests(t *testing.T) { now := time.Now() ts := fmt.Sprintf("%d", now.UnixNano()) - ft := map[string]map[string]struct{}{ - "core/assets": map[string]struct{}{ - "TestLink": struct{}{}, - "TestCore": struct{}{}, + r := &Report{ + tests: map[string]map[string]int{ + "core/assets": map[string]int{ + "TestLink": 1, + "TestCore": 1, + }, }, } lr := &LokiReporter{auth: "bla", host: "bla", command: "go_core_tests", now: func() time.Time { return now }} - pr, err := lr.createRequest(ft) + pr, err := lr.createRequest(r) require.NoError(t, err) assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, `{"package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink","commit_sha":"","repository":"","event_type":""}`}, - {ts, `{"package":"core/assets","test_name":"TestCore","fq_test_name":"core/assets:TestCore","commit_sha":"","repository":"","event_type":""}`}, - {ts, `{"num_flakes":2,"commit_sha":"","repository":"","event_type":""}`}, + {ts, `{"message_type":"flakey_test","commit_sha":"","repository":"","event_type":"","package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink"}`}, + {ts, `{"message_type":"flakey_test","commit_sha":"","repository":"","event_type":"","package":"core/assets","test_name":"TestCore","fq_test_name":"core/assets:TestCore"}`}, + {ts, `{"message_type":"run_report","commit_sha":"","repository":"","event_type":"","num_package_panics":0,"num_flakes":2,"num_combined":2}`}, }) } func TestMakeRequest_NoTests(t *testing.T) { now := time.Now() ts := fmt.Sprintf("%d", now.UnixNano()) - ft := map[string]map[string]struct{}{} + r := NewReport() lr := &LokiReporter{auth: "bla", host: "bla", command: "go_core_tests", now: func() time.Time { return now }} - pr, err := lr.createRequest(ft) + pr, err := lr.createRequest(r) require.NoError(t, err) assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, `{"num_flakes":0,"commit_sha":"","repository":"","event_type":""}`}, + {ts, `{"message_type":"run_report","commit_sha":"","repository":"","event_type":"","num_package_panics":0,"num_flakes":0,"num_combined":0}`}, }) } func TestMakeRequest_WithContext(t *testing.T) { now := time.Now() ts := fmt.Sprintf("%d", now.UnixNano()) - ft := map[string]map[string]struct{}{} + r := NewReport() lr := &LokiReporter{auth: "bla", host: "bla", command: "go_core_tests", now: func() time.Time { return now }, ctx: Context{CommitSHA: "42"}} - pr, err := lr.createRequest(ft) + pr, err := lr.createRequest(r) require.NoError(t, err) assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, `{"num_flakes":0,"commit_sha":"42","repository":"","event_type":""}`}, + {ts, `{"message_type":"run_report","commit_sha":"42","repository":"","event_type":"","num_package_panics":0,"num_flakes":0,"num_combined":0}`}, }) } + +func TestMakeRequest_Panics(t *testing.T) { + now := time.Now() + ts := fmt.Sprintf("%d", now.UnixNano()) + r := &Report{ + tests: map[string]map[string]int{ + "core/assets": map[string]int{ + "TestLink": 1, + }, + }, + packagePanics: map[string]int{ + "core/assets": 1, + }, + } + lr := &LokiReporter{auth: "bla", host: "bla", command: "go_core_tests", now: func() time.Time { return now }} + pr, err := lr.createRequest(r) + require.NoError(t, err) + assert.Len(t, pr.Streams, 1) + assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) + + assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ + {ts, `{"message_type":"flakey_test","commit_sha":"","repository":"","event_type":"","package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink"}`}, + {ts, `{"message_type":"package_panic","commit_sha":"","repository":"","event_type":"","package":"core/assets"}`}, + {ts, `{"message_type":"run_report","commit_sha":"","repository":"","event_type":"","num_package_panics":1,"num_flakes":1,"num_combined":2}`}, + }) +} + +func TestDedupeEntries(t *testing.T) { + r := &Report{ + tests: map[string]map[string]int{ + "core/assets": map[string]int{ + "TestSomethingAboutAssets/test_1": 2, + "TestSomethingAboutAssets": 4, + "TestSomeOtherTest": 1, + "TestSomethingAboutAssets/test_2": 2, + "TestFinalTest/test_1": 1, + }, + "core/services/important_service": map[string]int{ + "TestAnImportantService/a_subtest": 1, + }, + }, + } + + otherReport, err := dedupeEntries(r) + require.NoError(t, err) + + expectedMap := map[string]map[string]int{ + "core/assets": map[string]int{ + "TestSomethingAboutAssets/test_1": 2, + "TestSomeOtherTest": 1, + "TestSomethingAboutAssets/test_2": 2, + "TestFinalTest/test_1": 1, + }, + "core/services/important_service": map[string]int{ + "TestAnImportantService/a_subtest": 1, + }, + } + assert.Equal(t, expectedMap, otherReport.tests) +} diff --git a/tools/flakeytests/runner.go b/tools/flakeytests/runner.go index d935000222f..97402633f38 100644 --- a/tools/flakeytests/runner.go +++ b/tools/flakeytests/runner.go @@ -11,6 +11,7 @@ import ( "os" "os/exec" "regexp" + "sort" "strings" "time" ) @@ -32,10 +33,10 @@ type tester interface { } type reporter interface { - Report(map[string]map[string]struct{}) error + Report(r *Report) error } -type parseFn func(readers ...io.Reader) (map[string]map[string]int, error) +type parseFn func(readers ...io.Reader) (*Report, error) func NewRunner(readers []io.Reader, reporter reporter, numReruns int) *Runner { tc := &testCommand{ @@ -60,9 +61,14 @@ type testCommand struct { func (t *testCommand) test(pkg string, tests []string, w io.Writer) error { replacedPkg := strings.Replace(pkg, t.repo, "", -1) - testFilter := strings.Join(tests, "|") cmd := exec.Command(t.command, fmt.Sprintf(".%s", replacedPkg)) //#nosec - cmd.Env = append(os.Environ(), fmt.Sprintf("TEST_FLAGS=-run %s", testFilter)) + cmd.Env = os.Environ() + + if len(tests) > 0 { + testFilter := strings.Join(tests, "|") + cmd.Env = append(cmd.Env, fmt.Sprintf("TEST_FLAGS=-run %s", testFilter)) + } + cmd.Stdout = io.MultiWriter(os.Stdout, w) cmd.Stderr = io.MultiWriter(os.Stderr, w) t.overrides(cmd) @@ -84,8 +90,8 @@ func newEvent(b []byte) (*TestEvent, error) { return e, err } -func parseOutput(readers ...io.Reader) (map[string]map[string]int, error) { - tests := map[string]map[string]int{} +func parseOutput(readers ...io.Reader) (*Report, error) { + report := NewReport() for _, r := range readers { s := bufio.NewScanner(r) for s.Scan() { @@ -105,24 +111,32 @@ func parseOutput(readers ...io.Reader) (map[string]map[string]int, error) { return nil, err } - // We're only interested in test failures, for which - // both Package and Test would be present. - if e.Package == "" || e.Test == "" { - continue - } - switch e.Action { case "fail": - if tests[e.Package] == nil { - tests[e.Package] = map[string]int{} + // Fail logs come in two forms: + // - with e.Package && e.Test, in which case it indicates a test failure. + // - with e.Package only, which indicates that the package test has failed, + // or possible that there has been a panic in an out-of-process goroutine running + // as part of the tests. + // + // We can ignore the last case because a package failure will be accounted elsewhere, either + // in the form of a failing test entry, or in the form of a panic output log, covered below. + if e.Test == "" { + continue } - tests[e.Package][e.Test]++ + + report.IncTest(e.Package, e.Test) case "output": if panicRe.MatchString(e.Output) { - if tests[e.Package] == nil { - tests[e.Package] = map[string]int{} + // Similar to the above, a panic can come in two forms: + // - attached to a test (i.e. with e.Test != ""), in which case + // we'll treat it like a failing test. + // - package-scoped, in which case we'll treat it as a package panic. + if e.Test != "" { + report.IncTest(e.Package, e.Test) + } else { + report.IncPackagePanic(e.Package) } - tests[e.Package][e.Test]++ } } } @@ -131,75 +145,183 @@ func parseOutput(readers ...io.Reader) (map[string]map[string]int, error) { return nil, err } } - return tests, nil + + return report, nil } type exitCoder interface { ExitCode() int } -func (r *Runner) runTests(failedTests map[string]map[string]int) (map[string]map[string]struct{}, error) { - suspectedFlakes := map[string]map[string]struct{}{} +type Report struct { + tests map[string]map[string]int + packagePanics map[string]int +} + +func NewReport() *Report { + return &Report{ + tests: map[string]map[string]int{}, + packagePanics: map[string]int{}, + } +} + +func (r *Report) HasFlakes() bool { + return len(r.tests) > 0 || len(r.packagePanics) > 0 +} + +func (r *Report) SetTest(pkg, test string, val int) { + if r.tests[pkg] == nil { + r.tests[pkg] = map[string]int{} + } + r.tests[pkg][test] = val +} + +func (r *Report) IncTest(pkg string, test string) { + if r.tests[pkg] == nil { + r.tests[pkg] = map[string]int{} + } + r.tests[pkg][test]++ +} - for pkg, tests := range failedTests { +func (r *Report) IncPackagePanic(pkg string) { + r.packagePanics[pkg]++ +} + +func (r *Runner) runTest(pkg string, tests []string) (*Report, error) { + var out bytes.Buffer + err := r.testCommand.test(pkg, tests, &out) + if err != nil { + log.Printf("Test command errored: %s\n", err) + // There was an error because the command failed with a non-zero + // exit code. This could just mean that the test failed again, so let's + // keep going. + var exErr exitCoder + if errors.As(err, &exErr) && exErr.ExitCode() > 0 { + return r.parse(&out) + } + return nil, err + } + + return r.parse(&out) +} + +func (r *Runner) runTests(rep *Report) (*Report, error) { + report := NewReport() + + // We need to deal with two types of flakes here: + // - flakes where we know the test that failed; in this case, we just rerun the failing test in question + // - flakes where we don't know what test failed. These are flakes where a panic occurred in an out-of-process goroutine, + // thus failing the package as a whole. For these, we'll rerun the whole package again. + for pkg, tests := range rep.tests { ts := []string{} for test := range tests { ts = append(ts, test) } - log.Printf("Executing test command with parameters: pkg=%s, tests=%+v, numReruns=%d\n", pkg, ts, r.numReruns) + log.Printf("[FLAKEY_TEST] Executing test command with parameters: pkg=%s, tests=%+v, numReruns=%d\n", pkg, ts, r.numReruns) for i := 0; i < r.numReruns; i++ { - var out bytes.Buffer - - err := r.testCommand.test(pkg, ts, &out) + pr, err := r.runTest(pkg, ts) if err != nil { - log.Printf("Test command errored: %s\n", err) - // There was an error because the command failed with a non-zero - // exit code. This could just mean that the test failed again, so let's - // keep going. - var exErr exitCoder - if errors.As(err, &exErr) && exErr.ExitCode() > 0 { - continue + return report, err + } + + for t := range tests { + failures := pr.tests[pkg][t] + if failures == 0 { + report.SetTest(pkg, t, 1) } - return suspectedFlakes, err } - fr, err := r.parse(&out) + } + } + + for pkg := range rep.packagePanics { + log.Printf("[PACKAGE_PANIC]: Executing test command with parameters: pkg=%s, numReruns=%d\n", pkg, r.numReruns) + for i := 0; i < r.numReruns; i++ { + pr, err := r.runTest(pkg, []string{}) if err != nil { - return nil, err + return report, err } - for t := range tests { - failures := fr[pkg][t] - if failures == 0 { - if suspectedFlakes[pkg] == nil { - suspectedFlakes[pkg] = map[string]struct{}{} - } - suspectedFlakes[pkg][t] = struct{}{} - } + if pr.packagePanics[pkg] == 0 { + report.IncPackagePanic(pkg) + } + } + } + + return report, nil +} + +func isSubtest(tn string) bool { + return strings.Contains(tn, "/") +} + +func isSubtestOf(st, mt string) bool { + return isSubtest(st) && strings.Contains(st, mt) +} + +func dedupeEntries(report *Report) (*Report, error) { + out := NewReport() + out.packagePanics = report.packagePanics + for pkg, tests := range report.tests { + // Sort the test names + testNames := make([]string, 0, len(tests)) + for t := range tests { + testNames = append(testNames, t) + } + + sort.Strings(testNames) + + for i, tn := range testNames { + // Is this the last element? If it is, then add it to the deduped set. + // This is because a) it's a main test, in which case we add it because + // it has no subtests following it, or b) it's a subtest, which we always add. + if i == len(testNames)-1 { + out.SetTest(pkg, tn, report.tests[pkg][tn]) + continue } + + // Next, let's compare the current item to the next one in the alphabetical order. + // In all cases we want to add the current item, UNLESS the current item is a main test, + // and the following one is a subtest of the current item. + nextItem := testNames[i+1] + if !isSubtest(tn) && isSubtestOf(nextItem, tn) { + continue + } + + out.SetTest(pkg, tn, report.tests[pkg][tn]) } + } - return suspectedFlakes, nil + return out, nil } func (r *Runner) Run() error { - failedTests, err := r.parse(r.readers...) + parseReport, err := r.parse(r.readers...) if err != nil { return err } - suspectedFlakes, err := r.runTests(failedTests) + report, err := r.runTests(parseReport) if err != nil { return err } - if len(suspectedFlakes) > 0 { - log.Printf("ERROR: Suspected flakes found: %+v\n", suspectedFlakes) + if report.HasFlakes() { + log.Printf("ERROR: Suspected flakes found: %+v\n", report) } else { log.Print("SUCCESS: No suspected flakes detected") } - return r.reporter.Report(suspectedFlakes) + // Before reporting the errors, let's dedupe some entries: + // In actuality, a failing subtest will produce two failing test entries, + // namely one for the test as a whole, and one for the subtest. + // This leads to inaccurate metrics since a failing subtest is double-counted. + report, err = dedupeEntries(report) + if err != nil { + return err + } + + return r.reporter.Report(report) } diff --git a/tools/flakeytests/runner_test.go b/tools/flakeytests/runner_test.go index 31f300dcbee..64e2a6c968a 100644 --- a/tools/flakeytests/runner_test.go +++ b/tools/flakeytests/runner_test.go @@ -12,16 +12,16 @@ import ( ) type mockReporter struct { - entries map[string]map[string]struct{} + report *Report } -func (m *mockReporter) Report(entries map[string]map[string]struct{}) error { - m.entries = entries +func (m *mockReporter) Report(report *Report) error { + m.report = report return nil } func newMockReporter() *mockReporter { - return &mockReporter{entries: map[string]map[string]struct{}{}} + return &mockReporter{report: NewReport()} } func TestParser(t *testing.T) { @@ -29,9 +29,10 @@ func TestParser(t *testing.T) { ` r := strings.NewReader(output) - ts, err := parseOutput(r) + pr, err := parseOutput(r) require.NoError(t, err) + ts := pr.tests assert.Len(t, ts, 1) assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"], 1) assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"], 1) @@ -44,9 +45,10 @@ func TestParser_SkipsNonJSON(t *testing.T) { ` r := strings.NewReader(output) - ts, err := parseOutput(r) + pr, err := parseOutput(r) require.NoError(t, err) + ts := pr.tests assert.Len(t, ts, 1) assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"], 1) assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"], 1) @@ -58,9 +60,10 @@ func TestParser_PanicDueToLogging(t *testing.T) { ` r := strings.NewReader(output) - ts, err := parseOutput(r) + pr, err := parseOutput(r) require.NoError(t, err) + ts := pr.tests assert.Len(t, ts, 1) assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"], 1) assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestAssets_LinkScanValue"], 1) @@ -85,7 +88,7 @@ func TestParser_SuccessfulOutput(t *testing.T) { r := strings.NewReader(output) ts, err := parseOutput(r) require.NoError(t, err) - assert.Len(t, ts, 0) + assert.Len(t, ts.tests, 0) } type testAdapter func(string, []string, io.Writer) error @@ -119,8 +122,8 @@ func TestRunner_WithFlake(t *testing.T) { // to only report one failure (not two as expected). err := r.Run() require.NoError(t, err) - assert.Len(t, m.entries, 1) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] + assert.Len(t, m.report.tests, 1) + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } @@ -154,8 +157,8 @@ func TestRunner_WithFailedPackage(t *testing.T) { // to only report one failure (not two as expected). err := r.Run() require.NoError(t, err) - assert.Len(t, m.entries, 1) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] + assert.Len(t, m.report.tests, 1) + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } @@ -180,7 +183,7 @@ func TestRunner_AllFailures(t *testing.T) { err := r.Run() require.NoError(t, err) - assert.Len(t, m.entries, 0) + assert.Len(t, m.report.tests, 0) } func TestRunner_RerunSuccessful(t *testing.T) { @@ -206,7 +209,7 @@ func TestRunner_RerunSuccessful(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } @@ -228,7 +231,7 @@ func TestRunner_RootLevelTest(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/"]["TestConfigDocs"] + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/"]["TestConfigDocs"] assert.True(t, ok) } @@ -255,7 +258,7 @@ func TestRunner_RerunFailsWithNonzeroExitCode(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } @@ -295,10 +298,25 @@ func TestRunner_RerunWithNonZeroExitCodeDoesntStopCommand(t *testing.T) { calls := index assert.Equal(t, 4, calls) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } +// Used for integration tests +func TestSkippedForTests_Subtests(t *testing.T) { + if os.Getenv("FLAKEY_TEST_RUNNER_RUN_FIXTURE_TEST") != "1" { + t.Skip() + } + + t.Run("1: should fail", func(t *testing.T) { + assert.False(t, true) + }) + + t.Run("2: should fail", func(t *testing.T) { + assert.False(t, true) + }) +} + // Used for integration tests func TestSkippedForTests(t *testing.T) { if os.Getenv("FLAKEY_TEST_RUNNER_RUN_FIXTURE_TEST") != "1" { @@ -319,7 +337,51 @@ func TestSkippedForTests_Success(t *testing.T) { assert.True(t, true) } -func TestParsesPanicCorrectly(t *testing.T) { +func TestIntegration_DealsWithSubtests(t *testing.T) { + if testing.Short() { + t.Skip() + } + + output := ` +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/tools/flakeytests/","Test":"TestSkippedForTests_Subtests/1:_should_fail","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/tools/flakeytests/","Test":"TestSkippedForTests_Subtests","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/tools/flakeytests/","Test":"TestSkippedForTests_Subtests/2:_should_fail","Elapsed":0} +` + + m := newMockReporter() + tc := &testCommand{ + repo: "github.com/smartcontractkit/chainlink/v2/tools/flakeytests", + command: "../bin/go_core_tests", + overrides: func(cmd *exec.Cmd) { + cmd.Env = append(cmd.Env, "FLAKEY_TESTRUNNER_RUN_FIXTURE_TEST=1") + cmd.Stdout = io.Discard + cmd.Stderr = io.Discard + }, + } + r := &Runner{ + numReruns: 2, + readers: []io.Reader{strings.NewReader(output)}, + testCommand: tc, + parse: parseOutput, + reporter: m, + } + + err := r.Run() + require.NoError(t, err) + expectedTests := map[string]map[string]int{ + "github.com/smartcontractkit/chainlink/v2/tools/flakeytests/": { + "TestSkippedForTests_Subtests/1:_should_fail": 1, + "TestSkippedForTests_Subtests/2:_should_fail": 1, + }, + } + assert.Equal(t, expectedTests, m.report.tests) +} + +func TestIntegration_ParsesPanics(t *testing.T) { + if testing.Short() { + t.Skip() + } + output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/tools/flakeytests/","Test":"TestSkippedForTests","Elapsed":0}` m := newMockReporter() @@ -342,11 +404,15 @@ func TestParsesPanicCorrectly(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/tools/flakeytests"]["TestSkippedForTests"] + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/tools/flakeytests"]["TestSkippedForTests"] assert.False(t, ok) } func TestIntegration(t *testing.T) { + if testing.Short() { + t.Skip() + } + output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/tools/flakeytests/","Test":"TestSkippedForTests_Success","Elapsed":0}` m := newMockReporter() @@ -369,6 +435,6 @@ func TestIntegration(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/tools/flakeytests"]["TestSkippedForTests_Success"] + _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/tools/flakeytests"]["TestSkippedForTests_Success"] assert.False(t, ok) } From 306eadcf505b54502b7112efc395457d4bd0f3c5 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Tue, 12 Dec 2023 14:35:33 +0200 Subject: [PATCH 035/234] Remove core utils dependencies from common (#11425) * Change difficulty from Big to BigInt * Fix headtracker mock head * Remove EsnureClosed * Fix mock heads * Migrate to common Mailbox * Fix Tracker close on txm * Change to EnsureHexPrefix * Change names to mailbox * Remove core/null dependency from common * Remove core mailbox * Fix dependencies * Tidy * Fix dependencies * Change path to internal utils * Minor fixes * Rename MinKey function * Update MinFunc * Fix utils conflicts --- common/client/node_lifecycle.go | 7 +- common/client/send_only_node_lifecycle.go | 2 +- common/headtracker/head_broadcaster.go | 6 +- common/headtracker/head_listener.go | 2 +- common/headtracker/head_tracker.go | 14 +- common/internal/utils/utils.go | 36 ++++ common/txmgr/broadcaster.go | 2 +- common/txmgr/confirmer.go | 14 +- common/txmgr/reaper.go | 2 +- common/txmgr/resender.go | 2 +- common/txmgr/tracker.go | 8 +- common/txmgr/txmgr.go | 8 +- common/txmgr/types/tx.go | 3 +- .../chains/evm/gas/block_history_estimator.go | 6 +- .../evm/headtracker/head_broadcaster_test.go | 3 +- core/chains/evm/headtracker/head_tracker.go | 5 +- .../evm/headtracker/head_tracker_test.go | 9 +- core/chains/evm/log/broadcaster.go | 13 +- core/chains/evm/log/helpers_internal_test.go | 5 +- core/chains/evm/log/helpers_test.go | 4 +- core/chains/evm/log/integration_test.go | 3 +- core/chains/evm/txmgr/evm_tx_store.go | 3 +- core/chains/legacyevm/chain.go | 3 +- core/chains/legacyevm/chain_test.go | 7 +- core/cmd/shell.go | 3 +- core/cmd/shell_local_test.go | 6 +- core/internal/cltest/cltest.go | 3 +- core/internal/testutils/evmtest/evmtest.go | 5 +- core/services/chainlink/application.go | 3 +- .../relayer_chain_interoperators_test.go | 6 +- core/services/directrequest/delegate.go | 18 +- core/services/directrequest/delegate_test.go | 5 +- core/services/functions/listener_test.go | 4 +- core/services/job/runner_integration_test.go | 12 +- core/services/job/spawner_test.go | 9 +- core/services/keeper/delegate.go | 6 +- .../keeper/registry_synchronizer_core.go | 10 +- .../registry_synchronizer_helper_test.go | 4 +- core/services/keeper/upkeep_executer.go | 7 +- core/services/ocr/contract_tracker.go | 10 +- core/services/ocr/contract_tracker_test.go | 4 +- core/services/ocr/delegate.go | 5 +- core/services/ocr2/delegate.go | 6 +- .../services/ocr2/plugins/functions/plugin.go | 3 +- core/services/pipeline/task.eth_tx.go | 2 +- core/services/pipeline/task.eth_tx_test.go | 2 +- core/services/promreporter/prom_reporter.go | 7 +- core/services/vrf/delegate.go | 8 +- core/services/vrf/delegate_test.go | 5 +- core/services/vrf/v1/listener_v1.go | 5 +- core/services/vrf/v2/listener_v2.go | 1 + core/services/vrf/v2/listener_v2_test.go | 3 +- core/utils/mailbox.go | 126 ------------ core/utils/mailbox_prom.go | 93 --------- core/utils/mailbox_test.go | 181 ------------------ core/utils/utils.go | 21 -- 56 files changed, 200 insertions(+), 550 deletions(-) create mode 100644 common/internal/utils/utils.go delete mode 100644 core/utils/mailbox.go delete mode 100644 core/utils/mailbox_prom.go delete mode 100644 core/utils/mailbox_test.go diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 5ba0bff3238..eda137d5100 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -12,9 +12,10 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" - "github.com/smartcontractkit/chainlink/v2/core/utils" + iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils" ) var ( @@ -360,7 +361,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { lggr := logger.Named(n.lfcLog, "Unreachable") lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) - dialRetryBackoff := utils.NewRedialBackoff() + dialRetryBackoff := iutils.NewRedialBackoff() for { select { @@ -416,7 +417,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { lggr := logger.Named(n.lfcLog, "InvalidChainID") lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) - chainIDRecheckBackoff := utils.NewRedialBackoff() + chainIDRecheckBackoff := iutils.NewRedialBackoff() for { select { diff --git a/common/client/send_only_node_lifecycle.go b/common/client/send_only_node_lifecycle.go index 4d5b102b5bd..c66d267ed42 100644 --- a/common/client/send_only_node_lifecycle.go +++ b/common/client/send_only_node_lifecycle.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/common/internal/utils" ) // verifyLoop may only be triggered once, on Start, if initial chain ID check diff --git a/common/headtracker/head_broadcaster.go b/common/headtracker/head_broadcaster.go index 0e676f864fa..758a7713846 100644 --- a/common/headtracker/head_broadcaster.go +++ b/common/headtracker/head_broadcaster.go @@ -9,9 +9,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const TrackableCallbackTimeout = 2 * time.Second @@ -30,7 +30,7 @@ type HeadBroadcaster[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct services.StateMachine logger logger.Logger callbacks callbackSet[H, BLOCK_HASH] - mailbox *utils.Mailbox[H] + mailbox *mailbox.Mailbox[H] mutex sync.Mutex chClose services.StopChan wgDone sync.WaitGroup @@ -48,7 +48,7 @@ func NewHeadBroadcaster[ return &HeadBroadcaster[H, BLOCK_HASH]{ logger: logger.Named(lggr, "HeadBroadcaster"), callbacks: make(callbackSet[H, BLOCK_HASH]), - mailbox: utils.NewSingleMailbox[H](), + mailbox: mailbox.NewSingle[H](), chClose: make(chan struct{}), } } diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index 0aebf606634..e7ea4fb51ae 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -14,8 +14,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/common/internal/utils" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index c977eb023cc..373aa5a958f 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -12,10 +12,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -43,14 +43,14 @@ type HeadTracker[ log logger.Logger headBroadcaster types.HeadBroadcaster[HTH, BLOCK_HASH] headSaver types.HeadSaver[HTH, BLOCK_HASH] - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor client htrktypes.Client[HTH, S, ID, BLOCK_HASH] chainID ID config htrktypes.Config htConfig htrktypes.HeadTrackerConfig - backfillMB *utils.Mailbox[HTH] - broadcastMB *utils.Mailbox[HTH] + backfillMB *mailbox.Mailbox[HTH] + broadcastMB *mailbox.Mailbox[HTH] headListener types.HeadListener[HTH, BLOCK_HASH] chStop services.StopChan wgDone sync.WaitGroup @@ -70,7 +70,7 @@ func NewHeadTracker[ htConfig htrktypes.HeadTrackerConfig, headBroadcaster types.HeadBroadcaster[HTH, BLOCK_HASH], headSaver types.HeadSaver[HTH, BLOCK_HASH], - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, getNilHead func() HTH, ) types.HeadTracker[HTH, BLOCK_HASH] { chStop := make(chan struct{}) @@ -82,8 +82,8 @@ func NewHeadTracker[ config: config, htConfig: htConfig, log: lggr, - backfillMB: utils.NewSingleMailbox[HTH](), - broadcastMB: utils.NewMailbox[HTH](HeadsBufferSize), + backfillMB: mailbox.NewSingle[HTH](), + broadcastMB: mailbox.New[HTH](HeadsBufferSize), chStop: chStop, headListener: NewHeadListener[HTH, S, ID, BLOCK_HASH](lggr, client, config, chStop), headSaver: headSaver, diff --git a/common/internal/utils/utils.go b/common/internal/utils/utils.go new file mode 100644 index 00000000000..1e285868c53 --- /dev/null +++ b/common/internal/utils/utils.go @@ -0,0 +1,36 @@ +package utils + +import ( + "cmp" + "slices" + "time" + + "github.com/jpillora/backoff" + "golang.org/x/exp/constraints" +) + +// NewRedialBackoff is a standard backoff to use for redialling or reconnecting to +// unreachable network endpoints +func NewRedialBackoff() backoff.Backoff { + return backoff.Backoff{ + Min: 1 * time.Second, + Max: 15 * time.Second, + Jitter: true, + } + +} + +// MinFunc returns the minimum value of the given element array with respect +// to the given key function. In the event U is not a compound type (e.g a +// struct) an identity function can be provided. +func MinFunc[U any, T constraints.Ordered](elems []U, f func(U) T) T { + var min T + if len(elems) == 0 { + return min + } + + e := slices.MinFunc(elems, func(a, b U) int { + return cmp.Compare(f(a), f(b)) + }) + return f(e) +} diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index f10ecafc670..dba2b976c33 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -18,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/chains/label" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 95be9ad23e6..aabdf45ae32 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -17,13 +17,15 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/chains/label" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( @@ -129,7 +131,7 @@ type Confirmer[ ks txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ] enabledAddresses []ADDR - mb *utils.Mailbox[HEAD] + mb *mailbox.Mailbox[HEAD] ctx context.Context ctxCancel context.CancelFunc wg sync.WaitGroup @@ -174,7 +176,7 @@ func NewConfirmer[ dbConfig: dbConfig, chainID: client.ConfiguredChainID(), ks: keystore, - mb: utils.NewSingleMailbox[HEAD](), + mb: mailbox.NewSingle[HEAD](), isReceiptNil: isReceiptNil, } } @@ -223,7 +225,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) clo ec.initSync.Lock() defer ec.initSync.Unlock() if !ec.isStarted { - return fmt.Errorf("Confirmer is not started: %w", utils.ErrAlreadyStopped) + return fmt.Errorf("Confirmer is not started: %w", services.ErrAlreadyStopped) } ec.ctxCancel() ec.wg.Wait() @@ -869,7 +871,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han "err", sendError, "fee", attempt.TxFee, "feeLimit", etx.FeeLimit, - "signedRawTx", utils.AddHexPrefix(hex.EncodeToString(attempt.SignedRawTx)), + "signedRawTx", utils.EnsureHexPrefix(hex.EncodeToString(attempt.SignedRawTx)), "blockHeight", blockHeight, ) ec.SvcErrBuffer.Append(sendError) @@ -1147,7 +1149,7 @@ func observeUntilTxConfirmed[ // Since a tx can have many attempts, we take the number of blocks to confirm as the block number // of the receipt minus the block number of the first ever broadcast for this transaction. - broadcastBefore := utils.MinKey(attempt.Tx.TxAttempts, func(attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) int64 { + broadcastBefore := iutils.MinFunc(attempt.Tx.TxAttempts, func(attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) int64 { if attempt.BroadcastBeforeBlockNum != nil { return *attempt.BroadcastBeforeBlockNum } diff --git a/common/txmgr/reaper.go b/common/txmgr/reaper.go index 385a9a17c3d..3ed05b2caee 100644 --- a/common/txmgr/reaper.go +++ b/common/txmgr/reaper.go @@ -7,10 +7,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Reaper handles periodic database cleanup for Txm diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index 06c466e1730..74cf3d1389c 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -8,12 +8,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/chains/label" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( diff --git a/common/txmgr/tracker.go b/common/txmgr/tracker.go index 1a24dd5b5fe..3ef2fc07208 100644 --- a/common/txmgr/tracker.go +++ b/common/txmgr/tracker.go @@ -8,11 +8,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( @@ -56,7 +56,7 @@ type Tracker[ txCache map[int64]AbandonedTx[ADDR] ttl time.Duration lock sync.Mutex - mb *utils.Mailbox[int64] + mb *mailbox.Mailbox[int64] wg sync.WaitGroup isStarted bool ctx context.Context @@ -85,7 +85,7 @@ func NewTracker[ enabledAddrs: map[ADDR]bool{}, txCache: map[int64]AbandonedTx[ADDR]{}, ttl: defaultTTL, - mb: utils.NewSingleMailbox[int64](), + mb: mailbox.NewSingle[int64](), lock: sync.Mutex{}, wg: sync.WaitGroup{}, } diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 228ab4ec8bf..e43a16b29ef 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -14,10 +14,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // For more information about the Txm architecture, see the design doc: @@ -342,7 +344,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() ctx, cancel := b.chStop.NewCtx() defer cancel() // Retry indefinitely on failure - backoff := utils.NewRedialBackoff() + backoff := iutils.NewRedialBackoff() for { select { case <-time.After(backoff.Duration()): @@ -361,7 +363,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() go func() { defer wg.Done() // Retry indefinitely on failure - backoff := utils.NewRedialBackoff() + backoff := iutils.NewRedialBackoff() for { select { case <-time.After(backoff.Duration()): diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index 3af43b19617..caac763fc0f 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -15,9 +15,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - clnull "github.com/smartcontractkit/chainlink/v2/core/null" ) // TxStrategy controls how txes are queued and sent diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 0ec4721b797..844b9e547f2 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/common/config" @@ -25,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // MaxStartTime is the maximum amount of time we are allowed to spend @@ -109,7 +109,7 @@ type ( blocks []evmtypes.Block blocksMu sync.RWMutex size int64 - mb *utils.Mailbox[*evmtypes.Head] + mb *mailbox.Mailbox[*evmtypes.Head] wg *sync.WaitGroup ctx context.Context ctxCancel context.CancelFunc @@ -139,7 +139,7 @@ func NewBlockHistoryEstimator(lggr logger.Logger, ethClient evmclient.Client, cf blocks: make([]evmtypes.Block, 0), // Must have enough blocks for both estimator and connectivity checker size: int64(mathutil.Max(bhCfg.BlockHistorySize(), bhCfg.CheckInclusionBlocks())), - mb: utils.NewSingleMailbox[*evmtypes.Head](), + mb: mailbox.NewSingle[*evmtypes.Head](), wg: new(sync.WaitGroup), ctx: ctx, ctxCancel: cancel, diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index ac43c08fe87..b9fab9cdd48 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" commonhtrk "github.com/smartcontractkit/chainlink/v2/common/headtracker" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" @@ -71,7 +72,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { orm := headtracker.NewORM(db, logger, cfg.Database(), *ethClient.ConfiguredChainID()) hs := headtracker.NewHeadSaver(logger, orm, evmCfg.EVM(), evmCfg.EVM().HeadTracker()) - mailMon := utils.NewMailboxMonitor(t.Name()) + mailMon := mailbox.NewMonitor(t.Name()) servicetest.Run(t, mailMon) hb := headtracker.NewHeadBroadcaster(logger) servicetest.Run(t, hb) diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index b86a6b5fe22..3cddfb71d09 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -9,12 +9,13 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink/v2/common/headtracker" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type headTracker = headtracker.HeadTracker[*evmtypes.Head, ethereum.Subscription, *big.Int, common.Hash] @@ -28,7 +29,7 @@ func NewHeadTracker( htConfig HeadTrackerConfig, headBroadcaster httypes.HeadBroadcaster, headSaver httypes.HeadSaver, - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, ) httypes.HeadTracker { return headtracker.NewHeadTracker[*evmtypes.Head, ethereum.Subscription, *big.Int, common.Hash]( lggr, diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 4d3cebd24e2..d8abb1328ac 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -992,7 +993,7 @@ func createHeadTracker(t *testing.T, ethClient evmclient.Client, config headtrac lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) - mailMon := utils.NewMailboxMonitor(t.Name()) + mailMon := mailbox.NewMonitor(t.Name()) return &headTrackerUniverse{ mu: new(sync.Mutex), headTracker: headtracker.NewHeadTracker(lggr, ethClient, config, htConfig, hb, hs, mailMon), @@ -1007,7 +1008,7 @@ func createHeadTrackerWithNeverSleeper(t *testing.T, ethClient evmclient.Client, lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, evmcfg.EVM(), evmcfg.EVM().HeadTracker()) - mailMon := utils.NewMailboxMonitor(t.Name()) + mailMon := mailbox.NewMonitor(t.Name()) ht := headtracker.NewHeadTracker(lggr, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), hb, hs, mailMon) _, err := hs.Load(testutils.Context(t)) require.NoError(t, err) @@ -1025,7 +1026,7 @@ func createHeadTrackerWithChecker(t *testing.T, ethClient evmclient.Client, conf hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) hb.Subscribe(checker) - mailMon := utils.NewMailboxMonitor(t.Name()) + mailMon := mailbox.NewMonitor(t.Name()) ht := headtracker.NewHeadTracker(lggr, ethClient, config, htConfig, hb, hs, mailMon) return &headTrackerUniverse{ mu: new(sync.Mutex), @@ -1042,7 +1043,7 @@ type headTrackerUniverse struct { headTracker httypes.HeadTracker headBroadcaster httypes.HeadBroadcaster headSaver httypes.HeadSaver - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor } func (u *headTrackerUniverse) Backfill(ctx context.Context, head *evmtypes.Head, depth uint) error { diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index f4528396093..393d1c1b266 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" @@ -102,11 +103,11 @@ type ( registrations *registrations logPool *logPool - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor // Use the same channel for subs/unsubs so ordering is preserved // (unsubscribe must happen after subscribe) - changeSubscriberStatus *utils.Mailbox[changeSubscriberStatus] - newHeads *utils.Mailbox[*evmtypes.Head] + changeSubscriberStatus *mailbox.Mailbox[changeSubscriberStatus] + newHeads *mailbox.Mailbox[*evmtypes.Head] utils.DependentAwaiter @@ -165,7 +166,7 @@ const ( var _ Broadcaster = (*broadcaster)(nil) // NewBroadcaster creates a new instance of the broadcaster -func NewBroadcaster(orm ORM, ethClient evmclient.Client, config Config, lggr logger.Logger, highestSavedHead *evmtypes.Head, mailMon *utils.MailboxMonitor) *broadcaster { +func NewBroadcaster(orm ORM, ethClient evmclient.Client, config Config, lggr logger.Logger, highestSavedHead *evmtypes.Head, mailMon *mailbox.Monitor) *broadcaster { chStop := make(chan struct{}) lggr = logger.Named(lggr, "LogBroadcaster") chainId := ethClient.ConfiguredChainID() @@ -178,8 +179,8 @@ func NewBroadcaster(orm ORM, ethClient evmclient.Client, config Config, lggr log registrations: newRegistrations(lggr, *chainId), logPool: newLogPool(lggr), mailMon: mailMon, - changeSubscriberStatus: utils.NewHighCapacityMailbox[changeSubscriberStatus](), - newHeads: utils.NewSingleMailbox[*evmtypes.Head](), + changeSubscriberStatus: mailbox.NewHighCapacity[changeSubscriberStatus](), + newHeads: mailbox.NewSingle[*evmtypes.Head](), DependentAwaiter: utils.NewDependentAwaiter(), chStop: chStop, highestSavedHead: highestSavedHead, diff --git a/core/chains/evm/log/helpers_internal_test.go b/core/chains/evm/log/helpers_internal_test.go index 38f40bd329e..4d4318cdf9d 100644 --- a/core/chains/evm/log/helpers_internal_test.go +++ b/core/chains/evm/log/helpers_internal_test.go @@ -4,13 +4,14 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // NewTestBroadcaster creates a broadcaster with Pause/Resume enabled. -func NewTestBroadcaster(orm ORM, ethClient evmclient.Client, config Config, lggr logger.Logger, highestSavedHead *evmtypes.Head, mailMon *utils.MailboxMonitor) *broadcaster { +func NewTestBroadcaster(orm ORM, ethClient evmclient.Client, config Config, lggr logger.Logger, highestSavedHead *evmtypes.Head, mailMon *mailbox.Monitor) *broadcaster { b := NewBroadcaster(orm, ethClient, config, lggr, highestSavedHead, mailMon) b.testPause, b.testResume = make(chan struct{}), make(chan struct{}) return b diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index e41f08e8d20..13dfe1ffab6 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -42,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type broadcasterHelper struct { @@ -90,7 +90,7 @@ func newBroadcasterHelperWithEthClient(t *testing.T, ethClient evmclient.Client, }) config := evmtest.NewChainScopedConfig(t, globalConfig) lggr := logger.Test(t) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) db := pgtest.NewSqlxDB(t) orm := log.NewORM(db, lggr, config.Database(), cltest.FixtureChainID) diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index e5b6ad3caf5..b26e87e668c 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -1325,7 +1326,7 @@ func TestBroadcaster_AppendLogChannel(t *testing.T) { ch3 := make(chan types.Log) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - mailMon := servicetest.RunHealthy(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.RunHealthy(t, mailbox.NewMonitor(t.Name())) lb := log.NewBroadcaster(nil, ethClient, nil, logger.Test(t), nil, mailMon) chCombined := lb.ExportedAppendLogChannel(ch1, ch2) chCombined = lb.ExportedAppendLogChannel(chCombined, ch3) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 730809e8dda..1c9868741fa 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -20,13 +20,14 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + "github.com/smartcontractkit/chainlink-common/pkg/utils/null" + "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 4b4c69f1ab6..18277a55d03 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -16,6 +16,7 @@ import ( common "github.com/smartcontractkit/chainlink-common/pkg/chains" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" @@ -164,7 +165,7 @@ type ChainOpts struct { AppConfig AppConfig EventBroadcaster pg.EventBroadcaster - MailMon *utils.MailboxMonitor + MailMon *mailbox.Monitor GasEstimator gas.EvmFeeEstimator *sqlx.DB diff --git a/core/chains/legacyevm/chain_test.go b/core/chains/legacyevm/chain_test.go index 4fcd51c39d9..93332348aa0 100644 --- a/core/chains/legacyevm/chain_test.go +++ b/core/chains/legacyevm/chain_test.go @@ -8,12 +8,13 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestLegacyChains(t *testing.T) { @@ -35,7 +36,7 @@ func TestChainOpts_Validate(t *testing.T) { type fields struct { AppConfig legacyevm.AppConfig EventBroadcaster pg.EventBroadcaster - MailMon *utils.MailboxMonitor + MailMon *mailbox.Monitor DB *sqlx.DB } tests := []struct { @@ -48,7 +49,7 @@ func TestChainOpts_Validate(t *testing.T) { fields: fields{ AppConfig: configtest.NewTestGeneralConfig(t), EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &utils.MailboxMonitor{}, + MailMon: &mailbox.Monitor{}, DB: pgtest.NewSqlxDB(t), }, }, diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 2e382be4ccf..3810559cf34 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -32,6 +32,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -153,7 +154,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G } keyStore := keystore.New(db, utils.GetScryptParams(cfg), appLggr, cfg.Database()) - mailMon := utils.NewMailboxMonitor(cfg.AppID().String()) + mailMon := mailbox.NewMonitor(cfg.AppID().String()) dbListener := cfg.Database().Listener() eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), dbListener.MinReconnectInterval(), dbListener.MaxReconnectDuration(), appLggr, cfg.AppID()) diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index fac2d7f040b..56da90d811c 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" @@ -89,7 +91,7 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &utils.MailboxMonitor{}, + MailMon: &mailbox.Monitor{}, DB: db, }, } @@ -194,7 +196,7 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &utils.MailboxMonitor{}, + MailMon: &mailbox.Monitor{}, DB: db, }, } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 88abc3de5c6..dc90201890d 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -38,6 +38,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/client" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" @@ -339,7 +340,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn keyStore := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg.Database()) - mailMon := utils.NewMailboxMonitor(cfg.AppID().String()) + mailMon := mailbox.NewMonitor(cfg.AppID().String()) loopRegistry := plugins.NewLoopRegistry(lggr, nil) mercuryPool := wsrpc.NewPool(lggr, cache.Config{ diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index e0a447a3274..095ea1a35c9 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains" @@ -65,7 +66,7 @@ type TestChainOpts struct { DB *sqlx.DB TxManager txmgr.TxManager KeyStore keystore.Eth - MailMon *utils.MailboxMonitor + MailMon *mailbox.Monitor GasEstimator gas.EvmFeeEstimator } @@ -118,7 +119,7 @@ func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) legacyevm.ChainR } } if opts.MailMon == nil { - opts.MailMon = servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + opts.MailMon = servicetest.Run(t, mailbox.NewMonitor(t.Name())) } if testopts.GasEstimator != nil { opts.GenGasEstimator = func(*big.Int) gas.EvmFeeEstimator { diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 5c204d693e9..ed043086586 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -149,7 +150,7 @@ type ApplicationOpts struct { Config GeneralConfig Logger logger.Logger EventBroadcaster pg.EventBroadcaster - MailMon *utils.MailboxMonitor + MailMon *mailbox.Monitor SqlxDB *sqlx.DB KeyStore keystore.Master RelayerChainInteroperators *CoreRelayerChainInteroperators diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 6a5445d9f21..a0754fa0139 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -11,6 +11,8 @@ import ( commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" @@ -206,7 +208,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &utils.MailboxMonitor{}, + MailMon: &mailbox.Monitor{}, DB: db, }, CSAETHKeystore: keyStore, @@ -280,7 +282,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &utils.MailboxMonitor{}, + MailMon: &mailbox.Monitor{}, DB: db, }, CSAETHKeystore: keyStore, diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index a21029ea177..cfdf1eed116 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -21,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ( @@ -31,7 +31,7 @@ type ( pipelineORM pipeline.ORM chHeads chan *evmtypes.Head legacyChains legacyevm.LegacyChainContainer - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor } Config interface { @@ -47,7 +47,7 @@ func NewDelegate( pipelineRunner pipeline.Runner, pipelineORM pipeline.ORM, legacyChains legacyevm.LegacyChainContainer, - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, ) *Delegate { return &Delegate{ logger: logger.Named("DirectRequest"), @@ -101,8 +101,8 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { pipelineORM: d.pipelineORM, mailMon: d.mailMon, job: jb, - mbOracleRequests: utils.NewHighCapacityMailbox[log.Broadcast](), - mbOracleCancelRequests: utils.NewHighCapacityMailbox[log.Broadcast](), + mbOracleRequests: mailbox.NewHighCapacity[log.Broadcast](), + mbOracleCancelRequests: mailbox.NewHighCapacity[log.Broadcast](), minIncomingConfirmations: concreteSpec.MinIncomingConfirmations.Uint32, requesters: concreteSpec.Requesters, minContractPayment: concreteSpec.MinContractPayment, @@ -127,12 +127,12 @@ type listener struct { oracle operator_wrapper.OperatorInterface pipelineRunner pipeline.Runner pipelineORM pipeline.ORM - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor job job.Job runs sync.Map // map[string]services.StopChan shutdownWaitGroup sync.WaitGroup - mbOracleRequests *utils.Mailbox[log.Broadcast] - mbOracleCancelRequests *utils.Mailbox[log.Broadcast] + mbOracleRequests *mailbox.Mailbox[log.Broadcast] + mbOracleCancelRequests *mailbox.Mailbox[log.Broadcast] minIncomingConfirmations uint32 requesters models.AddressCollection minContractPayment *assets.Link @@ -238,7 +238,7 @@ func (l *listener) processCancelOracleRequests() { } } -func (l *listener) handleReceivedLogs(mailbox *utils.Mailbox[log.Broadcast]) { +func (l *listener) handleReceivedLogs(mailbox *mailbox.Mailbox[log.Broadcast]) { for { select { case <-l.chStop: diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index 2d5b9bef03b..865edb1b481 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -44,7 +45,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) relayerExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: cfg, Client: ethClient, MailMon: mailMon, KeyStore: keyStore.Eth()}) lggr := logger.TestLogger(t) @@ -81,7 +82,7 @@ func NewDirectRequestUniverseWithConfig(t *testing.T, cfg chainlink.GeneralConfi runner := pipeline_mocks.NewRunner(t) broadcaster.On("AddDependents", 1) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 5020537bf61..07bd82ed288 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -20,6 +20,7 @@ import ( decryptionPlugin "github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -44,7 +45,6 @@ import ( sync_mocks "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type FunctionsListenerUniverse struct { @@ -82,7 +82,7 @@ func NewFunctionsListenerUniverse(t *testing.T, timeoutSec int, pruneFrequencySe ethClient := evmtest.NewEthClientMockWithDefaultChain(t) broadcaster := log_mocks.NewBroadcaster(t) broadcaster.On("AddDependents", 1) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) db := pgtest.NewSqlxDB(t) kst := cltest.NewKeyStore(t, db, cfg.Database()) diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index 0223f1a10d5..14a5c41b396 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -24,6 +24,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -45,7 +46,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" ) @@ -462,7 +462,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, mailbox.NewMonitor(t.Name())), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -496,7 +496,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, mailbox.NewMonitor(t.Name())), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -524,7 +524,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, mailbox.NewMonitor(t.Name())), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -579,7 +579,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, mailbox.NewMonitor(t.Name())), ) jb.OCROracleSpec.CaptureEATelemetry = tc.jbCaptureEATelemetry @@ -623,7 +623,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, utils.NewMailboxMonitor(t.Name())), + servicetest.Run(t, mailbox.NewMonitor(t.Name())), ) services, err := sd.ServicesForSpec(*jb) require.NoError(t, err) diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index d639ce859af..b82aa73c0b5 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -128,7 +129,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { serviceA2 := mocks.NewServiceCtx(t) serviceA1.On("Start", mock.Anything).Return(nil).Once() serviceA2.On("Start", mock.Anything).Return(nil).Once().Run(func(mock.Arguments) { eventuallyA.ItHappened() }) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) dA := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, make(chan struct{}), dA} @@ -187,7 +188,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { lggr := logger.TestLogger(t) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ @@ -221,7 +222,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { lggr := logger.TestLogger(t) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ @@ -299,7 +300,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { jobOCR2VRF := makeOCR2VRFJobSpec(t, keyStore, config, address, chain.ID(), 2) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) processConfig := plugins.NewRegistrarConfig(loop.GRPCOpts{}, func(name string) (*plugins.RegisteredLoop, error) { return nil, nil }) ocr2DelegateConfig := ocr2.NewDelegateConfig(config.OCR2(), config.Mercury(), config.Threshold(), config.Insecure(), config.JobPipeline(), config.Database(), processConfig) diff --git a/core/services/keeper/delegate.go b/core/services/keeper/delegate.go index 0dbf584c56f..4418bea670a 100644 --- a/core/services/keeper/delegate.go +++ b/core/services/keeper/delegate.go @@ -5,12 +5,12 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // To make sure Delegate struct implements job.Delegate interface @@ -22,7 +22,7 @@ type Delegate struct { jrm job.ORM pr pipeline.Runner legacyChains legacyevm.LegacyChainContainer - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor } // NewDelegate is the constructor of Delegate @@ -32,7 +32,7 @@ func NewDelegate( pr pipeline.Runner, logger logger.Logger, legacyChains legacyevm.LegacyChainContainer, - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, ) *Delegate { return &Delegate{ logger: logger, diff --git a/core/services/keeper/registry_synchronizer_core.go b/core/services/keeper/registry_synchronizer_core.go index db7cca1763f..f26c38fc2e1 100644 --- a/core/services/keeper/registry_synchronizer_core.go +++ b/core/services/keeper/registry_synchronizer_core.go @@ -10,6 +10,8 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -28,7 +30,7 @@ type RegistrySynchronizerOptions struct { ORM ORM JRM job.ORM LogBroadcaster log.Broadcaster - MailMon *utils.MailboxMonitor + MailMon *mailbox.Monitor SyncInterval time.Duration MinIncomingConfirmations uint32 Logger logger.Logger @@ -44,14 +46,14 @@ type RegistrySynchronizer struct { job job.Job jrm job.ORM logBroadcaster log.Broadcaster - mbLogs *utils.Mailbox[log.Broadcast] + mbLogs *mailbox.Mailbox[log.Broadcast] minIncomingConfirmations uint32 effectiveKeeperAddress common.Address orm ORM logger logger.SugaredLogger wgDone sync.WaitGroup syncUpkeepQueueSize uint32 //Represents the max number of upkeeps that can be synced in parallel - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor } // NewRegistrySynchronizer is the constructor of RegistrySynchronizer @@ -63,7 +65,7 @@ func NewRegistrySynchronizer(opts RegistrySynchronizerOptions) *RegistrySynchron job: opts.Job, jrm: opts.JRM, logBroadcaster: opts.LogBroadcaster, - mbLogs: utils.NewMailbox[log.Broadcast](5_000), // Arbitrary limit, better to have excess capacity + mbLogs: mailbox.New[log.Broadcast](5_000), // Arbitrary limit, better to have excess capacity minIncomingConfirmations: opts.MinIncomingConfirmations, orm: opts.ORM, effectiveKeeperAddress: opts.EffectiveKeeperAddress, diff --git a/core/services/keeper/registry_synchronizer_helper_test.go b/core/services/keeper/registry_synchronizer_helper_test.go index 5ba60db3962..dff97202f6c 100644 --- a/core/services/keeper/registry_synchronizer_helper_test.go +++ b/core/services/keeper/registry_synchronizer_helper_test.go @@ -11,6 +11,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -23,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const syncInterval = 1000 * time.Hour // prevents sync timer from triggering during test @@ -73,7 +73,7 @@ func setupRegistrySync(t *testing.T, version keeper.RegistryVersion) ( })).Maybe().Return(func() {}) lbMock.On("IsConnected").Return(true).Maybe() - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) orm := keeper.NewORM(db, logger.TestLogger(t), ch.Config().Database()) synchronizer := keeper.NewRegistrySynchronizer(keeper.RegistrySynchronizerOptions{ diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 84349ba2dca..bab2f73edfc 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -13,6 +13,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -23,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( @@ -62,7 +63,7 @@ type UpkeepExecuter struct { headBroadcaster httypes.HeadBroadcasterRegistry gasEstimator gas.EvmFeeEstimator job job.Job - mailbox *utils.Mailbox[*evmtypes.Head] + mailbox *mailbox.Mailbox[*evmtypes.Head] orm ORM pr pipeline.Runner logger logger.Logger @@ -89,7 +90,7 @@ func NewUpkeepExecuter( headBroadcaster: headBroadcaster, gasEstimator: gasEstimator, job: job, - mailbox: utils.NewSingleMailbox[*evmtypes.Head](), + mailbox: mailbox.NewSingle[*evmtypes.Head](), config: config, orm: orm, pr: pr, diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 4f79bcfc31a..1287e52e9b5 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -21,6 +21,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/config" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -31,7 +32,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // configMailboxSanityLimit is the maximum number of configs that can be held @@ -67,7 +67,7 @@ type ( q pg.Q blockTranslator ocrcommon.BlockTranslator cfg ocrcommon.Config - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor // HeadBroadcaster headBroadcaster httypes.HeadBroadcaster @@ -83,7 +83,7 @@ type ( lrrMu sync.RWMutex // ContractConfig - configsMB *utils.Mailbox[ocrtypes.ContractConfig] + configsMB *mailbox.Mailbox[ocrtypes.ContractConfig] chConfigs chan ocrtypes.ContractConfig // LatestBlockHeight @@ -117,7 +117,7 @@ func NewOCRContractTracker( cfg ocrcommon.Config, q pg.QConfig, headBroadcaster httypes.HeadBroadcaster, - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, ) (o *OCRContractTracker) { logger = logger.Named("OCRContractTracker") return &OCRContractTracker{ @@ -136,7 +136,7 @@ func NewOCRContractTracker( headBroadcaster: headBroadcaster, chStop: make(services.StopChan), latestRoundRequested: offchainaggregator.OffchainAggregatorRoundRequested{}, - configsMB: utils.NewMailbox[ocrtypes.ContractConfig](configMailboxSanityLimit), + configsMB: mailbox.New[ocrtypes.ContractConfig](configMailboxSanityLimit), chConfigs: make(chan ocrtypes.ContractConfig), latestBlockHeight: -1, } diff --git a/core/services/ocr/contract_tracker_test.go b/core/services/ocr/contract_tracker_test.go index af65f330d66..f7ebbe08481 100644 --- a/core/services/ocr/contract_tracker_test.go +++ b/core/services/ocr/contract_tracker_test.go @@ -16,6 +16,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -31,7 +32,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" ocrmocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func mustNewContract(t *testing.T, address gethCommon.Address) *offchain_aggregator_wrapper.OffchainAggregator { @@ -84,7 +84,7 @@ func newContractTrackerUni(t *testing.T, opts ...interface{}) (uni contractTrack uni.hb = commonmocks.NewHeadBroadcaster[*evmtypes.Head, common.Hash](t) uni.ec = evmtest.NewEthClientMock(t) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) db := pgtest.NewSqlxDB(t) uni.tracker = ocr.NewOCRContractTracker( contract, diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index d3d133e7121..0eed680a3d8 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" ocr "github.com/smartcontractkit/libocr/offchainreporting" @@ -43,7 +44,7 @@ type Delegate struct { legacyChains legacyevm.LegacyChainContainer lggr logger.Logger cfg Config - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor } var _ job.Delegate = (*Delegate)(nil) @@ -60,7 +61,7 @@ func NewDelegate( legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, cfg Config, - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, ) *Delegate { return &Delegate{ db: db, diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 16f02282afb..5200866e3af 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -34,6 +34,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -70,7 +71,6 @@ import ( evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -116,7 +116,7 @@ type Delegate struct { ethKs keystore.Eth RelayGetter isNewlyCreatedJob bool // Set to true if this is a new job freshly added, false if job was present already on node boot. - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor legacyChains legacyevm.LegacyChainContainer // legacy: use relayers instead } @@ -226,7 +226,7 @@ func NewDelegate( dkgEncryptKs keystore.DKGEncrypt, ethKs keystore.Eth, relayers RelayGetter, - mailMon *utils.MailboxMonitor, + mailMon *mailbox.Monitor, eventBroadcaster pg.EventBroadcaster, ) *Delegate { return &Delegate{ diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 61fa7f5d825..7e2b15bdccf 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -14,6 +14,7 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -43,7 +44,7 @@ type FunctionsServicesConfig struct { Chain legacyevm.Chain ContractID string Logger logger.Logger - MailMon *utils.MailboxMonitor + MailMon *mailbox.Monitor URLsMonEndpoint commontypes.MonitoringEndpoint EthKeystore keystore.Eth ThresholdKeyShare []byte diff --git a/core/services/pipeline/task.eth_tx.go b/core/services/pipeline/task.eth_tx.go index c421b340c91..58e9f6f2c15 100644 --- a/core/services/pipeline/task.eth_tx.go +++ b/core/services/pipeline/task.eth_tx.go @@ -13,11 +13,11 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" + clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/pipeline/task.eth_tx_test.go b/core/services/pipeline/task.eth_tx_test.go index a0ff54d4448..5f5019d1967 100644 --- a/core/services/pipeline/task.eth_tx_test.go +++ b/core/services/pipeline/task.eth_tx_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" @@ -19,7 +20,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" keystoremocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" diff --git a/core/services/promreporter/prom_reporter.go b/core/services/promreporter/prom_reporter.go index 3e1444a6da1..a302a6fa220 100644 --- a/core/services/promreporter/prom_reporter.go +++ b/core/services/promreporter/prom_reporter.go @@ -17,10 +17,11 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name PrometheusBackend --output ../../internal/mocks/ --case=underscore @@ -31,7 +32,7 @@ type ( chains legacyevm.LegacyChainContainer lggr logger.Logger backend PrometheusBackend - newHeads *utils.Mailbox[*evmtypes.Head] + newHeads *mailbox.Mailbox[*evmtypes.Head] chStop services.StopChan wgDone sync.WaitGroup reportPeriod time.Duration @@ -109,7 +110,7 @@ func NewPromReporter(db *sql.DB, chainContainer legacyevm.LegacyChainContainer, chains: chainContainer, lggr: lggr.Named("PromReporter"), backend: backend, - newHeads: utils.NewSingleMailbox[*evmtypes.Head](), + newHeads: mailbox.NewSingle[*evmtypes.Head](), chStop: chStop, reportPeriod: period, } diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index a13df71d9a3..03e40614a10 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -29,7 +30,6 @@ import ( v1 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v1" v2 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Delegate struct { @@ -39,7 +39,7 @@ type Delegate struct { ks keystore.Master legacyChains legacyevm.LegacyChainContainer lggr logger.Logger - mailMon *utils.MailboxMonitor + mailMon *mailbox.Monitor } func NewDelegate( @@ -50,7 +50,7 @@ func NewDelegate( legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, cfg pg.QConfig, - mailMon *utils.MailboxMonitor) *Delegate { + mailMon *mailbox.Monitor) *Delegate { return &Delegate{ q: pg.NewQ(db, lggr, cfg), ks: ks, @@ -250,7 +250,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { MailMon: d.mailMon, // Note the mailbox size effectively sets a limit on how many logs we can replay // in the event of a VRF outage. - ReqLogs: utils.NewHighCapacityMailbox[log.Broadcast](), + ReqLogs: mailbox.NewHighCapacity[log.Broadcast](), ChStop: make(chan struct{}), WaitOnStop: make(chan struct{}), NewHead: make(chan struct{}, 1), diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index d957e3c721e..3b643d19b0b 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -149,7 +150,7 @@ func setup(t *testing.T) (vrfUniverse, *v1.Listener, job.Job) { cfg := configtest.NewTestGeneralConfig(t) vuni := buildVrfUni(t, db, cfg) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) vd := vrf.NewDelegate( db, @@ -673,7 +674,7 @@ func Test_VRFV2PlusServiceFailsWhenVRFOwnerProvided(t *testing.T) { cfg := configtest.NewTestGeneralConfig(t) vuni := buildVrfUni(t, db, cfg) - mailMon := servicetest.Run(t, utils.NewMailboxMonitor(t.Name())) + mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) vd := vrf.NewDelegate( db, diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 66a8ddcd58c..f4e813d7d61 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -17,6 +17,7 @@ import ( "github.com/theodesp/go-heaps/pairing" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -57,8 +58,8 @@ type Listener struct { Job job.Job Q pg.Q GethKs vrfcommon.GethKeyStore - MailMon *utils.MailboxMonitor - ReqLogs *utils.Mailbox[log.Broadcast] + MailMon *mailbox.Monitor + ReqLogs *mailbox.Mailbox[log.Broadcast] ChStop services.StopChan WaitOnStop chan struct{} NewHead chan struct{} diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 5878bf54763..6556bbd2186 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -14,6 +14,7 @@ import ( "github.com/theodesp/go-heaps/pairing" "github.com/smartcontractkit/chainlink-common/pkg/services" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index 6192db95dfe..d8bc0a6695b 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -17,6 +17,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -28,7 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/utils/mailbox.go b/core/utils/mailbox.go deleted file mode 100644 index 87fe1627f37..00000000000 --- a/core/utils/mailbox.go +++ /dev/null @@ -1,126 +0,0 @@ -package utils - -import ( - "sync" - "sync/atomic" -) - -// Mailbox contains a notify channel, -// a mutual exclusive lock, -// a queue of interfaces, -// and a queue capacity. -type Mailbox[T any] struct { - mu sync.Mutex - chNotify chan struct{} - queue []T - queueLen atomic.Int64 // atomic so monitor can read w/o blocking the queue - - // capacity - number of items the mailbox can buffer - // NOTE: if the capacity is 1, it's possible that an empty Retrieve may occur after a notification. - capacity uint64 - // onCloseFn is a hook used to stop monitoring, if non-nil - onCloseFn func() -} - -// NewHighCapacityMailbox create a new mailbox with a capacity -// that is better able to handle e.g. large log replays. -func NewHighCapacityMailbox[T any]() *Mailbox[T] { - return NewMailbox[T](100_000) -} - -// NewSingleMailbox returns a new Mailbox with capacity one. -func NewSingleMailbox[T any]() *Mailbox[T] { return NewMailbox[T](1) } - -// NewMailbox creates a new mailbox instance. If name is non-empty, it must be unique and calling Start will launch -// prometheus metric monitor that periodically reports mailbox load until Close() is called. -func NewMailbox[T any](capacity uint64) *Mailbox[T] { - queueCap := capacity - if queueCap == 0 { - queueCap = 100 - } - return &Mailbox[T]{ - chNotify: make(chan struct{}, 1), - queue: make([]T, 0, queueCap), - capacity: capacity, - } -} - -// Notify returns the contents of the notify channel -func (m *Mailbox[T]) Notify() <-chan struct{} { - return m.chNotify -} - -func (m *Mailbox[T]) Close() error { - if m.onCloseFn != nil { - m.onCloseFn() - } - return nil -} - -func (m *Mailbox[T]) onClose(fn func()) { m.onCloseFn = fn } - -func (m *Mailbox[T]) load() (capacity uint64, loadPercent float64) { - capacity = m.capacity - loadPercent = 100 * float64(m.queueLen.Load()) / float64(capacity) - return -} - -// Deliver appends to the queue and returns true if the queue was full, causing a message to be dropped. -func (m *Mailbox[T]) Deliver(x T) (wasOverCapacity bool) { - m.mu.Lock() - defer m.mu.Unlock() - - m.queue = append([]T{x}, m.queue...) - if uint64(len(m.queue)) > m.capacity && m.capacity > 0 { - m.queue = m.queue[:len(m.queue)-1] - wasOverCapacity = true - } else { - m.queueLen.Add(1) - } - - select { - case m.chNotify <- struct{}{}: - default: - } - return -} - -// Retrieve fetches one element from the queue. -func (m *Mailbox[T]) Retrieve() (t T, ok bool) { - m.mu.Lock() - defer m.mu.Unlock() - if len(m.queue) == 0 { - return - } - t = m.queue[len(m.queue)-1] - m.queue = m.queue[:len(m.queue)-1] - m.queueLen.Add(-1) - ok = true - return -} - -// RetrieveAll fetches all elements from the queue. -func (m *Mailbox[T]) RetrieveAll() []T { - m.mu.Lock() - defer m.mu.Unlock() - queue := m.queue - m.queue = nil - m.queueLen.Store(0) - for i, j := 0, len(queue)-1; i < j; i, j = i+1, j-1 { - queue[i], queue[j] = queue[j], queue[i] - } - return queue -} - -// RetrieveLatestAndClear fetch the latest value (or nil), and clears the rest of the queue (if any). -func (m *Mailbox[T]) RetrieveLatestAndClear() (t T) { - m.mu.Lock() - defer m.mu.Unlock() - if len(m.queue) == 0 { - return - } - t = m.queue[0] - m.queue = nil - m.queueLen.Store(0) - return -} diff --git a/core/utils/mailbox_prom.go b/core/utils/mailbox_prom.go deleted file mode 100644 index 33cbb2357b1..00000000000 --- a/core/utils/mailbox_prom.go +++ /dev/null @@ -1,93 +0,0 @@ -package utils - -import ( - "context" - "strconv" - "strings" - "sync" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - - "github.com/smartcontractkit/chainlink-common/pkg/services" -) - -var mailboxLoad = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "mailbox_load_percent", - Help: "Percent of mailbox capacity used", -}, - []string{"appID", "name", "capacity"}, -) - -const mailboxPromInterval = 5 * time.Second - -type MailboxMonitor struct { - services.StateMachine - appID string - - mailboxes sync.Map - stop func() - done chan struct{} -} - -func NewMailboxMonitor(appID string) *MailboxMonitor { - return &MailboxMonitor{appID: appID} -} - -func (m *MailboxMonitor) Name() string { return "MailboxMonitor" } - -func (m *MailboxMonitor) Start(context.Context) error { - return m.StartOnce("MailboxMonitor", func() error { - t := time.NewTicker(WithJitter(mailboxPromInterval)) - ctx, cancel := context.WithCancel(context.Background()) - m.stop = func() { - t.Stop() - cancel() - } - m.done = make(chan struct{}) - go m.monitorLoop(ctx, t.C) - return nil - }) -} - -func (m *MailboxMonitor) Close() error { - return m.StopOnce("MailboxMonitor", func() error { - m.stop() - <-m.done - return nil - }) -} - -func (m *MailboxMonitor) HealthReport() map[string]error { - return map[string]error{m.Name(): m.Healthy()} -} - -func (m *MailboxMonitor) monitorLoop(ctx context.Context, c <-chan time.Time) { - defer close(m.done) - for { - select { - case <-ctx.Done(): - return - case <-c: - m.mailboxes.Range(func(k, v any) bool { - name, mb := k.(string), v.(mailbox) - c, p := mb.load() - capacity := strconv.FormatUint(c, 10) - mailboxLoad.WithLabelValues(m.appID, name, capacity).Set(p) - return true - }) - } - } -} - -type mailbox interface { - load() (capacity uint64, percent float64) - onClose(func()) -} - -func (m *MailboxMonitor) Monitor(mb mailbox, name ...string) { - n := strings.Join(name, ".") - m.mailboxes.Store(n, mb) - mb.onClose(func() { m.mailboxes.Delete(n) }) -} diff --git a/core/utils/mailbox_test.go b/core/utils/mailbox_test.go deleted file mode 100644 index c83d0035baa..00000000000 --- a/core/utils/mailbox_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package utils - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestMailbox(t *testing.T) { - var ( - expected = []int{2, 3, 4, 5, 6, 7, 8, 9, 10, 11} - toDeliver = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} - ) - - const capacity = 10 - m := NewMailbox[int](capacity) - - // Queue deliveries - for i, d := range toDeliver { - atCapacity := m.Deliver(d) - if atCapacity && i < capacity { - t.Errorf("mailbox at capacity %d", i) - } else if !atCapacity && i >= capacity { - t.Errorf("mailbox below capacity %d", i) - } - } - - // Retrieve them - var recvd []int - chDone := make(chan struct{}) - go func() { - defer close(chDone) - for range m.Notify() { - for { - x, exists := m.Retrieve() - if !exists { - break - } - recvd = append(recvd, x) - } - } - }() - - close(m.chNotify) - <-chDone - - require.Equal(t, expected, recvd) -} - -func TestMailbox_RetrieveAll(t *testing.T) { - var ( - expected = []int{2, 3, 4, 5, 6, 7, 8, 9, 10, 11} - toDeliver = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} - ) - - const capacity = 10 - m := NewMailbox[int](capacity) - - // Queue deliveries - for i, d := range toDeliver { - atCapacity := m.Deliver(d) - if atCapacity && i < capacity { - t.Errorf("mailbox at capacity %d", i) - } else if !atCapacity && i >= capacity { - t.Errorf("mailbox below capacity %d", i) - } - } - - require.Equal(t, expected, m.RetrieveAll()) -} - -func TestMailbox_RetrieveLatestAndClear(t *testing.T) { - var ( - expected = 11 - toDeliver = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} - ) - - const capacity = 10 - m := NewMailbox[int](capacity) - - // Queue deliveries - for i, d := range toDeliver { - atCapacity := m.Deliver(d) - if atCapacity && i < capacity { - t.Errorf("mailbox at capacity %d", i) - } else if !atCapacity && i >= capacity { - t.Errorf("mailbox below capacity %d", i) - } - } - - require.Equal(t, expected, m.RetrieveLatestAndClear()) - require.Len(t, m.RetrieveAll(), 0) -} - -func TestMailbox_NoEmptyReceivesWhenCapacityIsTwo(t *testing.T) { - m := NewMailbox[int](2) - - var ( - recvd []int - emptyReceives []int - ) - - chDone := make(chan struct{}) - go func() { - defer close(chDone) - for range m.Notify() { - x, exists := m.Retrieve() - if !exists { - emptyReceives = append(emptyReceives, recvd[len(recvd)-1]) - } else { - recvd = append(recvd, x) - } - } - }() - - for i := 0; i < 100000; i++ { - m.Deliver(i) - } - close(m.chNotify) - - <-chDone - require.Len(t, emptyReceives, 0) -} - -func TestMailbox_load(t *testing.T) { - for _, tt := range []struct { - name string - capacity uint64 - deliver []int - exp float64 - - retrieve int - exp2 float64 - - all bool - }{ - {"single-all", 1, []int{1}, 100, 0, 100, true}, - {"single-latest", 1, []int{1}, 100, 0, 100, false}, - {"ten-low", 10, []int{1}, 10, 1, 0.0, false}, - {"ten-full-all", 10, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 100, 5, 50, true}, - {"ten-full-latest", 10, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 100, 5, 50, false}, - {"ten-overflow", 10, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 100, 5, 50, false}, - {"nine", 9, []int{1, 2, 3}, 100.0 / 3.0, 2, 100.0 / 9.0, true}, - } { - t.Run(tt.name, func(t *testing.T) { - m := NewMailbox[int](tt.capacity) - - // Queue deliveries - for i, d := range tt.deliver { - atCapacity := m.Deliver(d) - if atCapacity && i < int(tt.capacity) { - t.Errorf("mailbox at capacity %d", i) - } else if !atCapacity && i >= int(tt.capacity) { - t.Errorf("mailbox below capacity %d", i) - } - } - gotCap, gotLoad := m.load() - require.Equal(t, gotCap, tt.capacity) - require.Equal(t, gotLoad, tt.exp) - - // Retrieve some - for i := 0; i < tt.retrieve; i++ { - _, ok := m.Retrieve() - require.True(t, ok) - } - gotCap, gotLoad = m.load() - require.Equal(t, gotCap, tt.capacity) - require.Equal(t, gotLoad, tt.exp2) - - // Drain it - if tt.all { - m.RetrieveAll() - } else { - m.RetrieveLatestAndClear() - } - gotCap, gotLoad = m.load() - require.Equal(t, gotCap, tt.capacity) - require.Equal(t, gotLoad, 0.0) - }) - } -} diff --git a/core/utils/utils.go b/core/utils/utils.go index b887fe9a800..4f3f9212337 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -27,7 +27,6 @@ import ( "github.com/robfig/cron/v3" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/sha3" - "golang.org/x/exp/constraints" ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" @@ -879,26 +878,6 @@ func TryParseHex(s string) (b []byte, err error) { return } -// MinKey returns the minimum value of the given element array with respect -// to the given key function. In the event U is not a compound type (e.g a -// struct) an identity function can be provided. -func MinKey[U any, T constraints.Ordered](elems []U, key func(U) T) T { - var min T - if len(elems) == 0 { - return min - } - - min = key(elems[0]) - for i := 1; i < len(elems); i++ { - v := key(elems[i]) - if v < min { - min = v - } - } - - return min -} - // ErrorBuffer uses joinedErrors interface to join multiple errors into a single error. // This is useful to track the most recent N errors in a service and flush them as a single error. type ErrorBuffer struct { From cf74cd0c9e9601c213b03c9d076339464a634291 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Tue, 12 Dec 2023 11:50:40 -0300 Subject: [PATCH 036/234] Use multiple EL clients with ocrv2 median smoke test (#11399) * use multiple EL clients with ocrv2 median test * update ethereum network component to latest * try with latest implementation of eth clients * change WithTestLogger() to WithTestInstance() * fix geth backward compatibility * run ocrv2 eth2 tests in parallel * run also ocr1 and 2 vrf tests iwth multiple clients * try running multipl el client tests in parallel * fix go.sum * fix lint * 1. add matrix for running 4 smoke tests on different execution clients, 2. if for these tests no client is set in env var use geth * comment out failing nethermind & besu vrf tests in the CI * fix go.sum * do not use negative lookahead with go test * use faster eth2 network config * add comment to integration tests workflow * run only OCR and OCRv2 jobs with multiple clients, add on-demand jobs for VRF * use latest ctf with increased network timeout * slow down private eth networks a bit * update CTF dep to tagged version * disable nethermind ocr smoke tests in CI * fix compilation errors --- .github/workflows/integration-tests.yml | 67 +++++++++++++++++- .../on-demand-vrfv2-eth2-clients-test.yml | 70 +++++++++++++++++++ .../on-demand-vrfv2plus-eth2-clients-test.yml | 70 +++++++++++++++++++ .gitignore | 2 +- integration-tests/actions/private_network.go | 33 +++++++++ integration-tests/docker/test_env/cl_node.go | 2 +- integration-tests/docker/test_env/test_env.go | 6 +- .../docker/test_env/test_env_builder.go | 10 ++- integration-tests/go.mod | 2 +- integration-tests/go.sum | 5 +- integration-tests/load/vrfv2/vrfv2_test.go | 4 +- .../load/vrfv2plus/vrfv2plus_test.go | 4 +- .../migration/upgrade_version_test.go | 2 +- integration-tests/smoke/automation_test.go | 4 +- integration-tests/smoke/cron_test.go | 4 +- integration-tests/smoke/flux_test.go | 2 +- integration-tests/smoke/forwarder_ocr_test.go | 2 +- .../smoke/forwarders_ocr2_test.go | 2 +- integration-tests/smoke/keeper_test.go | 2 +- integration-tests/smoke/ocr2_test.go | 10 ++- integration-tests/smoke/ocr_test.go | 9 ++- integration-tests/smoke/runlog_test.go | 2 +- integration-tests/smoke/vrf_test.go | 4 +- integration-tests/smoke/vrfv2_test.go | 11 +-- integration-tests/smoke/vrfv2plus_test.go | 14 ++-- .../universal/log_poller/helpers.go | 5 +- 26 files changed, 303 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/on-demand-vrfv2-eth2-clients-test.yml create mode 100644 .github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml create mode 100644 integration-tests/actions/private_network.go diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 37a2718be46..6ffd1a14ff2 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -325,11 +325,73 @@ jobs: - name: ocr nodes: 1 os: ubuntu20.04-8cores-32GB + run: -run TestOCRJobReplacement + file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated + - name: ocr-geth + nodes: 1 + os: ubuntu20.04-8cores-32GB + run: -run TestOCRBasic + file: ocr + client: geth + pyroscope_env: ci-smoke-ocr-evm-simulated + # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE + # - name: ocr-nethermind + # nodes: 1 + # os: ubuntu20.04-8cores-32GB + # run: -run TestOCRBasic + # file: ocr + # client: nethermind + # pyroscope_env: ci-smoke-ocr-evm-simulated + - name: ocr-besu + nodes: 1 + os: ubuntu20.04-8cores-32GB + run: -run TestOCRBasic + file: ocr + client: besu + pyroscope_env: ci-smoke-ocr-evm-simulated + - name: ocr-erigon + nodes: 1 + os: ubuntu20.04-8cores-32GB + run: -run TestOCRBasic + file: ocr + client: erigon + pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 nodes: 1 os: ubuntu20.04-8cores-32GB + run: -run TestOCRv2JobReplacement + file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated + - name: ocr2-geth + nodes: 1 + os: ubuntu20.04-8cores-32GB + run: -run TestOCRv2Basic + file: ocr2 + client: geth + pyroscope_env: ci-smoke-ocr2-evm-simulated + # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE + # - name: ocr2-nethermind + # nodes: 1 + # os: ubuntu20.04-8cores-32GB + # run: -run TestOCRv2Basic + # file: ocr2 + # client: nethermind + # pyroscope_env: ci-smoke-ocr2-evm-simulated + - name: ocr2-besu + nodes: 1 + os: ubuntu20.04-8cores-32GB + run: -run TestOCRv2Basic + file: ocr2 + client: besu + pyroscope_env: ci-smoke-ocr2-evm-simulated + - name: ocr2-erigon + nodes: 1 + os: ubuntu20.04-8cores-32GB + run: -run TestOCRv2Basic + file: ocr2 + client: erigon + pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 nodes: 1 os: ubuntu20.04-8cores-32GB @@ -345,12 +407,14 @@ jobs: pyroscope_env: ci-smoke-vrf-evm-simulated - name: vrfv2 nodes: 1 + run: -run TestVRFv2MultipleSendingKeys + file: vrfv2 os: ubuntu20.04-8cores-32GB pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus nodes: 1 os: ubuntu20.04-8cores-32GB - pyroscope_env: ci-smoke-vrf2plus-evm-simulated + pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr nodes: 1 os: ubuntu20.04-8cores-32GB @@ -435,6 +499,7 @@ jobs: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + ETH2_EL_CLIENT: ${{matrix.product.client}} LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} LOKI_URL: ${{ secrets.LOKI_URL }} LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} diff --git a/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml b/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml new file mode 100644 index 00000000000..3b27e4519c8 --- /dev/null +++ b/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml @@ -0,0 +1,70 @@ +name: On Demand VRFV2 Smoke Test (Ethereum clients) +on: + workflow_dispatch: + inputs: + client: + description: Execution client to use + type: choice + options: + - "geth" + - "nethermind" + - "besu" + - "erigon" + chainlinkImage: + description: Container image location for the Chainlink nodes + required: true + default: public.ecr.aws/chainlink/chainlink + chainlinkVersion: + description: Container image version for the Chainlink nodes + required: true + default: "2.6.0" + configBase64: + description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) + required: false +jobs: + vrfv2_smoke_test: + name: VRFV2 Smoke Test with ${{ inputs.client }} client + environment: integration + runs-on: ubuntu20.04-8cores-32GB + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + env: + SELECTED_NETWORKS: "SIMULATED" + CONFIG: ${{ inputs.configBase64 }} + TEST_LOG_LEVEL: debug + REF_NAME: ${{ github.head_ref || github.ref_name }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + steps: + - name: Setup Push Tag + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -run TestVRFv2Basic ./smoke/vrfv2_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ inputs.chainlinkImage }} + cl_image_tag: ${{ inputs.chainlinkVersion }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + artifacts_name: vrf-test-logs + artifacts_location: ./integration-tests/smoke/logs/ + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + should_cleanup: false + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: "" + env: + ETH2_EL_CLIENT: ${{ inputs.client }} diff --git a/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml b/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml new file mode 100644 index 00000000000..331c626ba9c --- /dev/null +++ b/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml @@ -0,0 +1,70 @@ +name: On Demand VRFV2Plus Smoke Test (Ethereum clients) +on: + workflow_dispatch: + inputs: + client: + description: Execution client to use + type: choice + options: + - "geth" + - "nethermind" + - "besu" + - "erigon" + chainlinkImage: + description: Container image location for the Chainlink nodes + required: true + default: public.ecr.aws/chainlink/chainlink + chainlinkVersion: + description: Container image version for the Chainlink nodes + required: true + default: "2.6.0" + configBase64: + description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) + required: false +jobs: + vrfv2plus_smoke_test: + name: VRFV2Plus Smoke Test with ${{ inputs.client }} client + environment: integration + runs-on: ubuntu20.04-8cores-32GB + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + env: + SELECTED_NETWORKS: "SIMULATED" + CONFIG: ${{ inputs.configBase64 }} + TEST_LOG_LEVEL: debug + REF_NAME: ${{ github.head_ref || github.ref_name }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + steps: + - name: Setup Push Tag + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -run ^TestVRFv2Plus$/^Link_Billing$ ./smoke/vrfv2plus_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ inputs.chainlinkImage }} + cl_image_tag: ${{ inputs.chainlinkVersion }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + artifacts_name: vrfplus-test-logs + artifacts_location: ./integration-tests/smoke/logs/ + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + should_cleanup: false + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: "" + env: + ETH2_EL_CLIENT: ${{ inputs.client }} diff --git a/.gitignore b/.gitignore index 8d127dbb728..2f68ec2c945 100644 --- a/.gitignore +++ b/.gitignore @@ -84,4 +84,4 @@ go.work* tools/flakeytests/coverage.txt .test_summary/ -.run.id \ No newline at end of file +.run.id diff --git a/integration-tests/actions/private_network.go b/integration-tests/actions/private_network.go new file mode 100644 index 00000000000..c7556f8061b --- /dev/null +++ b/integration-tests/actions/private_network.go @@ -0,0 +1,33 @@ +package actions + +import ( + "errors" + + "github.com/rs/zerolog" + + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" +) + +func EthereumNetworkConfigFromEnvOrDefault(l zerolog.Logger) (network ctf_test_env.EthereumNetwork, err error) { + chainConfig := ctf_test_env.EthereumChainConfig{ + SecondsPerSlot: 8, + SlotsPerEpoch: 4, + } + + ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() + network, err = ethBuilder. + WithExecClientFromEnvVar(). + WithEthereumChainConfig(chainConfig). + Build() + + if errors.Is(err, ctf_test_env.ErrMissingExecClientEnvVar) { + l.Warn().Msg("No exec client env var set, will use old geth") + ethBuilder = ctf_test_env.NewEthereumNetworkBuilder() + network, err = ethBuilder. + WithConsensusType(ctf_test_env.ConsensusType_PoW). + WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). + Build() + } + + return +} diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 60394256334..b79bb91c70e 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -112,7 +112,7 @@ func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *ch func (n *ClNode) SetTestLogger(t *testing.T) { n.l = logging.GetTestLogger(t) n.t = t - n.PostgresDb.WithTestLogger(t) + n.PostgresDb.WithTestInstance(t) } // Restart restarts only CL node, DB container is reused diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 15bf6641bf4..20e231e5985 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -71,11 +71,11 @@ func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTest return te } -func (te *CLClusterTestEnv) WithTestLogger(t *testing.T) *CLClusterTestEnv { +func (te *CLClusterTestEnv) WithTestInstance(t *testing.T) *CLClusterTestEnv { te.t = t te.l = logging.GetTestLogger(t) if te.MockAdapter != nil { - te.MockAdapter.WithTestLogger(t) + te.MockAdapter.WithTestInstance(t) } return te } @@ -90,7 +90,7 @@ func (te *CLClusterTestEnv) WithPrivateChain(evmNetworks []blockchain.EVMNetwork n := evmNetwork pgc := test_env.NewPrivateGethChain(&n, []string{te.Network.Name}) if te.t != nil { - pgc.GetPrimaryNode().WithTestLogger(te.t) + pgc.GetPrimaryNode().WithTestInstance(te.t) } chains = append(chains, pgc) var privateChain test_env.PrivateChain diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index cc0499ea0ce..7358d76f2af 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -95,7 +95,7 @@ func (b *CLTestEnvBuilder) WithTestEnv(te *CLClusterTestEnv) (*CLTestEnvBuilder, // WithTestLogger sets the test logger to use for the test. // Useful for parallel tests so the logging will be separated correctly in the results views. -func (b *CLTestEnvBuilder) WithTestLogger(t *testing.T) *CLTestEnvBuilder { +func (b *CLTestEnvBuilder) WithTestInstance(t *testing.T) *CLTestEnvBuilder { b.t = t b.l = logging.GetTestLogger(t) return b @@ -220,8 +220,12 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } var err error + if b.t != nil { + b.te.WithTestInstance(b.t) + } + if b.hasLogStream { - b.te.LogStream, err = logstream.NewLogStream(b.t, nil) + b.te.LogStream, err = logstream.NewLogStream(b.te.t, nil) if err != nil { return nil, err } @@ -241,7 +245,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } if b.t != nil { - b.te.WithTestLogger(b.t) + b.te.WithTestInstance(b.t) } switch b.cleanUpType { diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 72c15a89669..c080f617552 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad - github.com/smartcontractkit/chainlink-testing-framework v1.21.0 + github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 494e5c21ec9..e8cefd67d6d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1411,6 +1411,7 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= @@ -1444,8 +1445,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= -github.com/smartcontractkit/chainlink-testing-framework v1.21.0 h1:MrtpGMgPpcRX06FtDEj14Vokoo6Sx8e0/D6AA9LxCgM= -github.com/smartcontractkit/chainlink-testing-framework v1.21.0/go.mod h1:9SCqZ+lcWZNEofpPgasQ+wUF6A6fFgZvWmhqQJwFYV0= +github.com/smartcontractkit/chainlink-testing-framework v1.22.0 h1:Lur628wkrceWgcLmxGZe7Mauwxht4YO71hX9Jj5YslE= +github.com/smartcontractkit/chainlink-testing-framework v1.22.0/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 8a0faeabb29..86f9b8d61f6 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -87,7 +87,7 @@ func TestVRFV2Performance(t *testing.T) { vrfv2Config.KeyHash = cfg.ExistingEnvConfig.KeyHash env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithCustomCleanup( func() { teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2Config) @@ -156,7 +156,7 @@ func TestVRFV2Performance(t *testing.T) { vrfv2Config.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding vrfv2Config.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 68c1012987d..6fe0d06e7da 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -89,7 +89,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusConfig.KeyHash = cfg.ExistingEnvConfig.KeyHash env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithCustomCleanup( func() { teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) @@ -162,7 +162,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.NewEnvConfig.Funding.SubFundsNative env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index d1f07a52b74..97db2374bf3 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -13,7 +13,7 @@ import ( func TestVersionUpgrade(t *testing.T) { t.Parallel() env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). Build() diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index bc9f1308452..1dbfc78ec87 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1092,7 +1092,7 @@ func setupAutomationTestDocker( clNodesCount := 5 if isMercuryV02 || isMercuryV03 { env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithFunding(big.NewFloat(testConfig.ChainlinkNodeFunding)). @@ -1129,7 +1129,7 @@ func setupAutomationTestDocker( } else { env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodes(clNodesCount). diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index 3eebc19f8bf..751c2867676 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -20,7 +20,7 @@ func TestCronBasic(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodes(1). @@ -67,7 +67,7 @@ func TestCronJobReplacement(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodes(1). diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 5b35b0d2484..828350a9422 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -26,7 +26,7 @@ func TestFluxBasic(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodes(3). diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 923ca046ece..c71c6e31516 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -19,7 +19,7 @@ func TestForwarderOCRBasic(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithForwarders(). diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index bdfd65c767e..c2e2b12e51d 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -24,7 +24,7 @@ func TestForwarderOCR2Basic(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 336223b7a4c..1bc416f0456 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1107,7 +1107,7 @@ func setupKeeperTest(t *testing.T) ( clNodeConfig.Keeper.Registry.PerformGasOverhead = &performGasOverhead env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(5). WithCLNodeConfig(clNodeConfig). diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 8f0fbd76053..a1a37c3b18e 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -19,11 +19,15 @@ import ( // Tests a basic OCRv2 median feed func TestOCRv2Basic(t *testing.T) { + t.Parallel() l := logging.GetTestLogger(t) + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). - WithGeth(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), node.WithOCR2(), @@ -97,7 +101,7 @@ func TestOCRv2JobReplacement(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 25192248c46..ba158923812 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -17,9 +17,12 @@ func TestOCRBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). - WithGeth(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodes(6). WithFunding(big.NewFloat(.5)). @@ -66,7 +69,7 @@ func TestOCRJobReplacement(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodes(6). diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index 2fc771fb9bf..5cc21a28c2c 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -23,7 +23,7 @@ func TestRunLogBasic(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithMockAdapter(). WithCLNodes(1). diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 04120053745..0554bd34760 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -24,7 +24,7 @@ func TestVRFBasic(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(.1)). @@ -114,7 +114,7 @@ func TestVRFJobReplacement(t *testing.T) { l := logging.GetTestLogger(t) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(.1)). diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 9976de03954..a8edb2c51d9 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -24,13 +24,16 @@ func TestVRFv2Basic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + var vrfv2Config vrfv2_config.VRFV2Config - err := envconfig.Process("VRFV2", &vrfv2Config) + err = envconfig.Process("VRFV2", &vrfv2Config) require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). - WithGeth(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). @@ -121,7 +124,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 69e1eb7ebde..a850cb4fe15 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -29,13 +29,16 @@ func TestVRFv2Plus(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err := envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) + err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). - WithGeth(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). @@ -118,6 +121,7 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) + t.Run("Native Billing", func(t *testing.T) { testConfig := vrfv2PlusConfig var isNativeBilling = true @@ -612,7 +616,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). @@ -703,7 +707,7 @@ func TestVRFv2PlusMigration(t *testing.T) { require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 48f34e6827c..1abd98632f1 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -1043,7 +1043,8 @@ func setupLogPollerTestDocker( WithConsensusType(ctf_test_env.ConsensusType_PoS). WithConsensusLayer(ctf_test_env.ConsensusLayer_Prysm). WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). - WithBeaconChainConfig(ctf_test_env.BeaconChainConfig{ + WithWaitingForFinalization(). + WithEthereumChainConfig(ctf_test_env.EthereumChainConfig{ SecondsPerSlot: 8, SlotsPerEpoch: 2, }). @@ -1051,7 +1052,7 @@ func setupLogPollerTestDocker( require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). + WithTestInstance(t). WithPrivateEthereumNetwork(cfg). WithCLNodes(clNodesCount). WithCLNodeConfig(clNodeConfig). From dfc62cc51035f1dd7a555a6797fe305cd1b09bc6 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Wed, 13 Dec 2023 00:01:58 +0300 Subject: [PATCH 037/234] disable kaniko fallback, increase deploy wait timeout (#11548) --- charts/chainlink-cluster/devspace.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index 186e49257a1..bd50a469ded 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -37,11 +37,14 @@ images: image: ${DEVSPACE_IMAGE} dockerfile: ../../core/chainlink.devspace.Dockerfile context: ../.. + docker: + disableFallback: true hooks: - wait: running: true terminatedWithCode: 0 + timeout: 600 container: labelSelector: # vars don't work here, = releaseName From 82faf5d4ca91706fa8fdf9fdc9893f97a4449548 Mon Sep 17 00:00:00 2001 From: chris-de-leon-cll <147140544+chris-de-leon-cll@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:07:37 -0800 Subject: [PATCH 038/234] [DEPLOY-178]: Adds Scroll L2EP Contracts (#11405) * Adds scroll L2EP contracts and tests * fixes comments, adds fixed solidity compiler version, and adds scroll tech contracts as a dev dependency * renames SCROLL_CROSS_DOMAIN_MESSENGER to i_SCROLL_CROSS_DOMAIN_MESSENGER for solhint * removes unnecessary solhint disable rule * moves scroll mocks from tests folder to l2ep folder * resolve solhint errors for scroll * proposed restructure to reduce inheritance * removes extraneous comments from scroll contracts and refactors typeAndVersion * use named parameters in mappings for scroll contracts * removes extraneous empty comments from scroll contracts (again) * removes unnecessary comment from scroll cross domain governor * adds minor formatting updates to scroll mocks * adds onlyL1Owner modifier back to ScrollCrossDomainGovernor * adds style and formatting improvements * adds formatting updates * refactors scroll sequencer uptime feed to reduce gas and updates test suites --------- Co-authored-by: Rens Rooimans --- contracts/foundry.toml | 2 +- contracts/package.json | 1 + contracts/pnpm-lock.yaml | 17 +- .../ScrollSequencerUptimeFeedInterface.sol | 6 + .../dev/scroll/ScrollCrossDomainForwarder.sol | 65 +++ .../dev/scroll/ScrollCrossDomainGovernor.sol | 92 ++++ .../dev/scroll/ScrollSequencerUptimeFeed.sol | 231 +++++++++ .../v0.8/l2ep/dev/scroll/ScrollValidator.sol | 77 +++ .../MockScrollL1CrossDomainMessenger.sol | 60 +++ .../MockScrollL2CrossDomainMessenger.sol | 50 ++ .../vendor/MockScrollCrossDomainMessenger.sol | 101 ++++ .../dev/ScrollCrossDomainForwarder.test.ts | 259 ++++++++++ .../dev/ScrollCrossDomainGovernor.test.ts | 459 ++++++++++++++++++ .../dev/ScrollSequencerUptimeFeed.test.ts | 426 ++++++++++++++++ .../test/v0.8/dev/ScrollValidator.test.ts | 118 +++++ 15 files changed, 1961 insertions(+), 3 deletions(-) create mode 100644 contracts/src/v0.8/l2ep/dev/interfaces/ScrollSequencerUptimeFeedInterface.sol create mode 100644 contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainForwarder.sol create mode 100644 contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainGovernor.sol create mode 100644 contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol create mode 100644 contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol create mode 100644 contracts/src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol create mode 100644 contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol create mode 100644 contracts/src/v0.8/vendor/MockScrollCrossDomainMessenger.sol create mode 100644 contracts/test/v0.8/dev/ScrollCrossDomainForwarder.test.ts create mode 100644 contracts/test/v0.8/dev/ScrollCrossDomainGovernor.test.ts create mode 100644 contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts create mode 100644 contracts/test/v0.8/dev/ScrollValidator.test.ts diff --git a/contracts/foundry.toml b/contracts/foundry.toml index cf27c0f2a8b..003c836b1f3 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -37,7 +37,7 @@ test = 'src/v0.8/automation/test' optimizer_runs = 1000000 src = 'src/v0.8/l2ep' test = 'src/v0.8/l2ep/test' - +solc_version = '0.8.19' [profile.llo-feeds] optimizer_runs = 1000000 diff --git a/contracts/package.json b/contracts/package.json index 2604a12bea3..abbf722140c 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -43,6 +43,7 @@ "@nomiclabs/hardhat-etherscan": "^3.1.7", "@nomiclabs/hardhat-waffle": "2.0.6", "@openzeppelin/hardhat-upgrades": "1.28.0", + "@scroll-tech/contracts": "0.1.0", "@openzeppelin/test-helpers": "^0.5.16", "@typechain/ethers-v5": "^7.2.0", "@typechain/hardhat": "^7.0.0", diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index fbae71fb3d4..dffcb0a7c7c 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -55,6 +55,9 @@ devDependencies: '@openzeppelin/test-helpers': specifier: ^0.5.16 version: 0.5.16(bn.js@4.12.0) + '@scroll-tech/contracts': + specifier: 0.1.0 + version: 0.1.0 '@typechain/ethers-v5': specifier: ^7.2.0 version: 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2) @@ -1387,6 +1390,10 @@ packages: - supports-color dev: true + /@scroll-tech/contracts@0.1.0: + resolution: {integrity: sha512-aBbDOc3WB/WveZdpJYcrfvMYMz7ZTEiW8M9XMJLba8p9FAR5KGYB/cV+8+EUsq3MKt7C1BfR+WnXoTVdvwIY6w==} + dev: true + /@scure/base@1.1.1: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} dev: true @@ -5861,7 +5868,7 @@ packages: heap: 0.2.6 level-sublevel: 6.6.4 levelup: 3.1.1 - lodash: 4.17.21 + lodash: 4.17.20 lru-cache: 5.1.1 merkle-patricia-tree: 3.0.0 patch-package: 6.2.2 @@ -7919,6 +7926,12 @@ packages: /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + /minimatch@3.0.4: + resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} + dependencies: + brace-expansion: 1.1.11 + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -9232,7 +9245,7 @@ packages: resolution: {integrity: sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==} engines: {node: '>=0.10.0'} dependencies: - minimatch: 3.1.2 + minimatch: 3.0.4 dev: true /reduce-flatten@2.0.0: diff --git a/contracts/src/v0.8/l2ep/dev/interfaces/ScrollSequencerUptimeFeedInterface.sol b/contracts/src/v0.8/l2ep/dev/interfaces/ScrollSequencerUptimeFeedInterface.sol new file mode 100644 index 00000000000..f0f716d6f02 --- /dev/null +++ b/contracts/src/v0.8/l2ep/dev/interfaces/ScrollSequencerUptimeFeedInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface ScrollSequencerUptimeFeedInterface { + function updateStatus(bool status, uint64 timestamp) external; +} diff --git a/contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainForwarder.sol b/contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainForwarder.sol new file mode 100644 index 00000000000..f18f7c3270b --- /dev/null +++ b/contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainForwarder.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; +import {ForwarderInterface} from "../interfaces/ForwarderInterface.sol"; + +import {CrossDomainForwarder} from "../CrossDomainForwarder.sol"; +import {CrossDomainOwnable} from "../CrossDomainOwnable.sol"; + +import {IScrollMessenger} from "@scroll-tech/contracts/libraries/IScrollMessenger.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; + +/// @title ScrollCrossDomainForwarder - L1 xDomain account representation +/// @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. +/// @dev Any other L2 contract which uses this contract's address as a privileged position, +/// can be considered to be owned by the `l1Owner` +contract ScrollCrossDomainForwarder is TypeAndVersionInterface, CrossDomainForwarder { + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "ScrollCrossDomainForwarder 1.0.0"; + + address internal immutable i_scrollCrossDomainMessenger; + + /// @param crossDomainMessengerAddr the xDomain bridge messenger (Scroll bridge L2) contract address + /// @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn + constructor(IScrollMessenger crossDomainMessengerAddr, address l1OwnerAddr) CrossDomainOwnable(l1OwnerAddr) { + // solhint-disable-next-line custom-errors + require(address(crossDomainMessengerAddr) != address(0), "Invalid xDomain Messenger address"); + i_scrollCrossDomainMessenger = address(crossDomainMessengerAddr); + } + + /// @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` being the L1 owner address + /// @inheritdoc ForwarderInterface + function forward(address target, bytes memory data) external override onlyL1Owner { + Address.functionCall(target, data, "Forwarder call reverted"); + } + + /// @notice This is always the address of the Scroll Cross Domain Messenger contract + function crossDomainMessenger() external view returns (address) { + return address(i_scrollCrossDomainMessenger); + } + + /// @notice The call MUST come from the L1 owner (via cross-chain message.) Reverts otherwise. + modifier onlyL1Owner() override { + // solhint-disable-next-line custom-errors + require(msg.sender == i_scrollCrossDomainMessenger, "Sender is not the L2 messenger"); + // solhint-disable-next-line custom-errors + require( + IScrollMessenger(i_scrollCrossDomainMessenger).xDomainMessageSender() == l1Owner(), + "xDomain sender is not the L1 owner" + ); + _; + } + + /// @notice The call MUST come from the proposed L1 owner (via cross-chain message.) Reverts otherwise. + modifier onlyProposedL1Owner() override { + // solhint-disable-next-line custom-errors + require(msg.sender == i_scrollCrossDomainMessenger, "Sender is not the L2 messenger"); + // solhint-disable-next-line custom-errors + require( + IScrollMessenger(i_scrollCrossDomainMessenger).xDomainMessageSender() == s_l1PendingOwner, + "Must be proposed L1 owner" + ); + _; + } +} diff --git a/contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainGovernor.sol b/contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainGovernor.sol new file mode 100644 index 00000000000..00ef9219b26 --- /dev/null +++ b/contracts/src/v0.8/l2ep/dev/scroll/ScrollCrossDomainGovernor.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; +import {DelegateForwarderInterface} from "../interfaces/DelegateForwarderInterface.sol"; +// solhint-disable-next-line no-unused-import +import {ForwarderInterface} from "../interfaces/ForwarderInterface.sol"; + +import {CrossDomainForwarder} from "../CrossDomainForwarder.sol"; +import {CrossDomainOwnable} from "../CrossDomainOwnable.sol"; + +import {IScrollMessenger} from "@scroll-tech/contracts/libraries/IScrollMessenger.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; + +/// @title ScrollCrossDomainGovernor - L1 xDomain account representation (with delegatecall support) for Scroll +/// @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. +/// @dev Any other L2 contract which uses this contract's address as a privileged position, +/// can be considered to be simultaneously owned by the `l1Owner` and L2 `owner` +contract ScrollCrossDomainGovernor is DelegateForwarderInterface, TypeAndVersionInterface, CrossDomainForwarder { + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "ScrollCrossDomainGovernor 1.0.0"; + + address internal immutable i_scrollCrossDomainMessenger; + + /// @param crossDomainMessengerAddr the xDomain bridge messenger (Scroll bridge L2) contract address + /// @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn + constructor(IScrollMessenger crossDomainMessengerAddr, address l1OwnerAddr) CrossDomainOwnable(l1OwnerAddr) { + // solhint-disable-next-line custom-errors + require(address(crossDomainMessengerAddr) != address(0), "Invalid xDomain Messenger address"); + i_scrollCrossDomainMessenger = address(crossDomainMessengerAddr); + } + + /// @inheritdoc ForwarderInterface + /// @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner + function forward(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { + Address.functionCall(target, data, "Governor call reverted"); + } + + /// @inheritdoc DelegateForwarderInterface + /// @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner + function forwardDelegate(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { + Address.functionDelegateCall(target, data, "Governor delegatecall reverted"); + } + + /// @notice The address of the Scroll Cross Domain Messenger contract + function crossDomainMessenger() external view returns (address) { + return address(i_scrollCrossDomainMessenger); + } + + /// @notice The call MUST come from the L1 owner (via cross-chain message.) Reverts otherwise. + modifier onlyL1Owner() override { + // solhint-disable-next-line custom-errors + require(msg.sender == i_scrollCrossDomainMessenger, "Sender is not the L2 messenger"); + // solhint-disable-next-line custom-errors + require( + IScrollMessenger(i_scrollCrossDomainMessenger).xDomainMessageSender() == l1Owner(), + "xDomain sender is not the L1 owner" + ); + _; + } + + /// @notice The call MUST come from either the L1 owner (via cross-chain message) or the L2 owner. Reverts otherwise. + modifier onlyLocalOrCrossDomainOwner() { + // 1. The delegatecall MUST come from either the L1 owner (via cross-chain message) or the L2 owner + // solhint-disable-next-line custom-errors + require( + msg.sender == i_scrollCrossDomainMessenger || msg.sender == owner(), + "Sender is not the L2 messenger or owner" + ); + // 2. The L2 Messenger's caller MUST be the L1 Owner + if (msg.sender == i_scrollCrossDomainMessenger) { + // solhint-disable-next-line custom-errors + require( + IScrollMessenger(i_scrollCrossDomainMessenger).xDomainMessageSender() == l1Owner(), + "xDomain sender is not the L1 owner" + ); + } + _; + } + + /// @notice The call MUST come from the proposed L1 owner (via cross-chain message.) Reverts otherwise. + modifier onlyProposedL1Owner() override { + // solhint-disable-next-line custom-errors + require(msg.sender == i_scrollCrossDomainMessenger, "Sender is not the L2 messenger"); + // solhint-disable-next-line custom-errors + require( + IScrollMessenger(i_scrollCrossDomainMessenger).xDomainMessageSender() == s_l1PendingOwner, + "Must be proposed L1 owner" + ); + _; + } +} diff --git a/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol b/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol new file mode 100644 index 00000000000..22b5ed1e2b6 --- /dev/null +++ b/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ScrollSequencerUptimeFeedInterface} from "../interfaces/ScrollSequencerUptimeFeedInterface.sol"; +import {AggregatorInterface} from "../../../shared/interfaces/AggregatorInterface.sol"; +import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; +import {AggregatorV2V3Interface} from "../../../shared/interfaces/AggregatorV2V3Interface.sol"; +import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; + +import {SimpleReadAccessController} from "../../../shared/access/SimpleReadAccessController.sol"; + +import {IL2ScrollMessenger} from "@scroll-tech/contracts/L2/IL2ScrollMessenger.sol"; + +/// @title ScrollSequencerUptimeFeed - L2 sequencer uptime status aggregator +/// @notice L2 contract that receives status updates, and records a new answer if the status changed +contract ScrollSequencerUptimeFeed is + AggregatorV2V3Interface, + ScrollSequencerUptimeFeedInterface, + TypeAndVersionInterface, + SimpleReadAccessController +{ + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "ScrollSequencerUptimeFeed 1.0.0"; + + /// @dev Round info (for uptime history) + struct Round { + bool status; + uint64 startedAt; + uint64 updatedAt; + } + + /// @dev Packed state struct to save sloads + struct FeedState { + uint80 latestRoundId; + bool latestStatus; + uint64 startedAt; + uint64 updatedAt; + } + + /// @notice Sender is not the L2 messenger + error InvalidSender(); + /// @notice Replacement for AggregatorV3Interface "No data present" + error NoDataPresent(); + + event L1SenderTransferred(address indexed from, address indexed to); + /// @dev Emitted when an `updateStatus` call is ignored due to unchanged status or stale timestamp + event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); + /// @dev Emitted when a updateStatus is called without the status changing + event RoundUpdated(int256 status, uint64 updatedAt); + + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + uint8 public constant override decimals = 0; + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override description = "L2 Sequencer Uptime Status Feed"; + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + uint256 public constant override version = 1; + + /// @dev L1 address + address private s_l1Sender; + /// @dev s_latestRoundId == 0 means this contract is uninitialized. + FeedState private s_feedState = FeedState({latestRoundId: 0, latestStatus: false, startedAt: 0, updatedAt: 0}); + mapping(uint80 roundId => Round round) private s_rounds; + + // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i + IL2ScrollMessenger private immutable s_l2CrossDomainMessenger; + + /// @param l1SenderAddress Address of the L1 contract that is permissioned to call this contract + /// @param l2CrossDomainMessengerAddr Address of the L2CrossDomainMessenger contract + /// @param initialStatus The initial status of the feed + constructor(address l1SenderAddress, address l2CrossDomainMessengerAddr, bool initialStatus) { + _setL1Sender(l1SenderAddress); + s_l2CrossDomainMessenger = IL2ScrollMessenger(l2CrossDomainMessengerAddr); + + // Initialise roundId == 1 as the first round + _recordRound(1, initialStatus, uint64(block.timestamp)); + } + + /// @notice Check if a roundId is valid in this current contract state + /// @dev Mainly used for AggregatorV2V3Interface functions + /// @param roundId Round ID to check + function _isValidRound(uint256 roundId) private view returns (bool) { + return roundId > 0 && roundId <= type(uint80).max && s_feedState.latestRoundId >= roundId; + } + + /// @return L1 sender address + function l1Sender() public view virtual returns (address) { + return s_l1Sender; + } + + /// @notice Set the allowed L1 sender for this contract to a new L1 sender + /// @dev Can be disabled by setting the L1 sender as `address(0)`. Accessible only by owner. + /// @param to new L1 sender that will be allowed to call `updateStatus` on this contract + function transferL1Sender(address to) external virtual onlyOwner { + _setL1Sender(to); + } + + /// @notice internal method that stores the L1 sender + function _setL1Sender(address to) private { + address from = s_l1Sender; + if (from != to) { + s_l1Sender = to; + emit L1SenderTransferred(from, to); + } + } + + /// @dev Returns an AggregatorV2V3Interface compatible answer from status flag + /// @param status The status flag to convert to an aggregator-compatible answer + function _getStatusAnswer(bool status) private pure returns (int256) { + return status ? int256(1) : int256(0); + } + + /// @notice Helper function to record a round and set the latest feed state. + /// @param roundId The round ID to record + /// @param status Sequencer status + /// @param timestamp The L1 block timestamp of status update + function _recordRound(uint80 roundId, bool status, uint64 timestamp) private { + s_feedState = FeedState(roundId, status, timestamp, uint64(block.timestamp)); + s_rounds[roundId] = Round(status, timestamp, uint64(block.timestamp)); + + emit NewRound(roundId, msg.sender, timestamp); + emit AnswerUpdated(_getStatusAnswer(status), roundId, timestamp); + } + + /// @notice Helper function to update when a round was last updated + /// @param roundId The round ID to update + /// @param status Sequencer status + function _updateRound(uint80 roundId, bool status) private { + s_feedState.updatedAt = uint64(block.timestamp); + s_rounds[roundId].updatedAt = uint64(block.timestamp); + emit RoundUpdated(_getStatusAnswer(status), uint64(block.timestamp)); + } + + /// @notice Record a new status and timestamp if it has changed since the last round. + /// @dev This function will revert if not called from `l1Sender` via the L1->L2 messenger. + /// + /// @param status Sequencer status + /// @param timestamp Block timestamp of status update + function updateStatus(bool status, uint64 timestamp) external override { + FeedState memory feedState = s_feedState; + + if ( + msg.sender != address(s_l2CrossDomainMessenger) || s_l2CrossDomainMessenger.xDomainMessageSender() != s_l1Sender + ) { + revert InvalidSender(); + } + + // Ignore if latest recorded timestamp is newer + if (feedState.startedAt > timestamp) { + emit UpdateIgnored(feedState.latestStatus, feedState.startedAt, status, timestamp); + return; + } + + if (feedState.latestStatus == status) { + _updateRound(feedState.latestRoundId, status); + } else { + feedState.latestRoundId += 1; + _recordRound(feedState.latestRoundId, status, timestamp); + } + } + + /// @inheritdoc AggregatorInterface + function latestAnswer() external view override checkAccess returns (int256) { + return _getStatusAnswer(s_feedState.latestStatus); + } + + /// @inheritdoc AggregatorInterface + function latestTimestamp() external view override checkAccess returns (uint256) { + return s_feedState.startedAt; + } + + /// @inheritdoc AggregatorInterface + function latestRound() external view override checkAccess returns (uint256) { + return s_feedState.latestRoundId; + } + + /// @inheritdoc AggregatorInterface + function getAnswer(uint256 roundId) external view override checkAccess returns (int256) { + if (!_isValidRound(roundId)) { + revert NoDataPresent(); + } + + return _getStatusAnswer(s_rounds[uint80(roundId)].status); + } + + /// @inheritdoc AggregatorInterface + function getTimestamp(uint256 roundId) external view override checkAccess returns (uint256) { + if (!_isValidRound(roundId)) { + revert NoDataPresent(); + } + + return s_rounds[uint80(roundId)].startedAt; + } + + /// @inheritdoc AggregatorV3Interface + function getRoundData( + uint80 _roundId + ) + public + view + override + checkAccess + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + if (!_isValidRound(_roundId)) { + revert NoDataPresent(); + } + + Round memory round = s_rounds[_roundId]; + + return (_roundId, _getStatusAnswer(round.status), uint256(round.startedAt), uint256(round.updatedAt), _roundId); + } + + /// @inheritdoc AggregatorV3Interface + function latestRoundData() + external + view + override + checkAccess + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + FeedState memory feedState = s_feedState; + + return ( + feedState.latestRoundId, + _getStatusAnswer(feedState.latestStatus), + feedState.startedAt, + feedState.updatedAt, + feedState.latestRoundId + ); + } +} diff --git a/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol b/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol new file mode 100644 index 00000000000..31a5f0764ef --- /dev/null +++ b/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {AggregatorValidatorInterface} from "../../../shared/interfaces/AggregatorValidatorInterface.sol"; +import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; +import {ScrollSequencerUptimeFeedInterface} from "../interfaces/ScrollSequencerUptimeFeedInterface.sol"; + +import {SimpleWriteAccessController} from "../../../shared/access/SimpleWriteAccessController.sol"; + +import {IL1ScrollMessenger} from "@scroll-tech/contracts/L1/IL1ScrollMessenger.sol"; + +/// @title ScrollValidator - makes cross chain call to update the Sequencer Uptime Feed on L2 +contract ScrollValidator is TypeAndVersionInterface, AggregatorValidatorInterface, SimpleWriteAccessController { + // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i + address public immutable L1_CROSS_DOMAIN_MESSENGER_ADDRESS; + // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i + address public immutable L2_UPTIME_FEED_ADDR; + + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "ScrollValidator 1.0.0"; + int256 private constant ANSWER_SEQ_OFFLINE = 1; + uint32 private s_gasLimit; + + /// @notice emitted when gas cost to spend on L2 is updated + /// @param gasLimit updated gas cost + event GasLimitUpdated(uint32 gasLimit); + + /// @param l1CrossDomainMessengerAddress address the L1CrossDomainMessenger contract address + /// @param l2UptimeFeedAddr the address of the ScrollSequencerUptimeFeed contract address + /// @param gasLimit the gasLimit to use for sending a message from L1 to L2 + constructor(address l1CrossDomainMessengerAddress, address l2UptimeFeedAddr, uint32 gasLimit) { + // solhint-disable-next-line custom-errors + require(l1CrossDomainMessengerAddress != address(0), "Invalid xDomain Messenger address"); + // solhint-disable-next-line custom-errors + require(l2UptimeFeedAddr != address(0), "Invalid ScrollSequencerUptimeFeed contract address"); + L1_CROSS_DOMAIN_MESSENGER_ADDRESS = l1CrossDomainMessengerAddress; + L2_UPTIME_FEED_ADDR = l2UptimeFeedAddr; + s_gasLimit = gasLimit; + } + + /// @notice sets the new gas cost to spend when sending cross chain message + /// @param gasLimit the updated gas cost + function setGasLimit(uint32 gasLimit) external onlyOwner { + s_gasLimit = gasLimit; + emit GasLimitUpdated(gasLimit); + } + + /// @notice fetches the gas cost of sending a cross chain message + function getGasLimit() external view returns (uint32) { + return s_gasLimit; + } + + /// @notice validate method sends an xDomain L2 tx to update Uptime Feed contract on L2. + /// @dev A message is sent using the L1CrossDomainMessenger. This method is accessed controlled. + /// @param currentAnswer new aggregator answer - value of 1 considers the sequencer offline. + function validate( + uint256 /* previousRoundId */, + int256 /* previousAnswer */, + uint256 /* currentRoundId */, + int256 currentAnswer + ) external override checkAccess returns (bool) { + // Make the xDomain call + IL1ScrollMessenger(L1_CROSS_DOMAIN_MESSENGER_ADDRESS).sendMessage( + L2_UPTIME_FEED_ADDR, + 0, + abi.encodeWithSelector( + ScrollSequencerUptimeFeedInterface.updateStatus.selector, + currentAnswer == ANSWER_SEQ_OFFLINE, + uint64(block.timestamp) + ), + s_gasLimit + ); + + // return success + return true; + } +} diff --git a/contracts/src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol new file mode 100644 index 00000000000..e63847d6557 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {IL1ScrollMessenger} from "@scroll-tech/contracts/L1/IL1ScrollMessenger.sol"; + +contract MockScrollL1CrossDomainMessenger is IL1ScrollMessenger { + uint256 private s_nonce; + + function xDomainMessageSender() public pure returns (address) { + return address(0); + } + + function sendMessage( + address _target, + uint256 _value, + bytes calldata _message, + uint256 _gasLimit + ) external payable override { + emit SentMessage(msg.sender, _target, _value, s_nonce, _gasLimit, _message); + s_nonce++; + } + + function sendMessage( + address _target, + uint256 _value, + bytes calldata _message, + uint256 _gasLimit, + address + ) external payable override { + emit SentMessage(msg.sender, _target, _value, s_nonce, _gasLimit, _message); + s_nonce++; + } + + function relayMessageWithProof( + address from, + address to, + uint256 value, + uint256 nonce, + bytes memory message, + L2MessageProof memory proof + ) external override {} + + function replayMessage( + address from, + address to, + uint256 value, + uint256 messageNonce, + bytes memory message, + uint32 newGasLimit, + address refundAddress + ) external payable override {} + + function dropMessage( + address from, + address to, + uint256 value, + uint256 messageNonce, + bytes memory message + ) external override {} +} diff --git a/contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol new file mode 100644 index 00000000000..f63faa35179 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {IL2ScrollMessenger} from "@scroll-tech/contracts/L2/IL2ScrollMessenger.sol"; + +contract MockScrollL2CrossDomainMessenger is IL2ScrollMessenger { + uint256 private s_nonce; + address private s_sender; + + function xDomainMessageSender() public view returns (address) { + return s_sender; + } + + function sendMessage( + address _target, + uint256 _value, + bytes calldata _message, + uint256 _gasLimit + ) external payable override { + emit SentMessage(msg.sender, _target, _value, s_nonce, _gasLimit, _message); + s_nonce++; + } + + function sendMessage( + address _target, + uint256 _value, + bytes calldata _message, + uint256 _gasLimit, + address + ) external payable override { + emit SentMessage(msg.sender, _target, _value, s_nonce, _gasLimit, _message); + s_nonce++; + } + + function relayMessage( + address from, + address to, + uint256 value, + uint256 nonce, + bytes calldata message + ) external override {} + + /// Needed for testing + function setSender(address newSender) external { + s_sender = newSender; + } + + /// Needed for testing + receive() external payable {} +} diff --git a/contracts/src/v0.8/vendor/MockScrollCrossDomainMessenger.sol b/contracts/src/v0.8/vendor/MockScrollCrossDomainMessenger.sol new file mode 100644 index 00000000000..bb5390b945d --- /dev/null +++ b/contracts/src/v0.8/vendor/MockScrollCrossDomainMessenger.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.16; + +import "./openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; + +/// sourced from: https://github.com/scroll-tech/scroll/blob/develop/contracts/src/libraries/IScrollMessenger.sol +interface IScrollMessenger { + /// ********** + /// * Events * + /// ********** + + /// @notice Emitted when a cross domain message is sent. + /// @param sender The address of the sender who initiates the message. + /// @param target The address of target contract to call. + /// @param value The amount of value passed to the target contract. + /// @param messageNonce The nonce of the message. + /// @param gasLimit The optional gas limit passed to L1 or L2. + /// @param message The calldata passed to the target contract. + event SentMessage( + address indexed sender, + address indexed target, + uint256 value, + uint256 messageNonce, + uint256 gasLimit, + bytes message + ); + + /// @notice Emitted when a cross domain message is relayed successfully. + /// @param messageHash The hash of the message. + event RelayedMessage(bytes32 indexed messageHash); + + /// @notice Emitted when a cross domain message is failed to relay. + /// @param messageHash The hash of the message. + event FailedRelayedMessage(bytes32 indexed messageHash); + + /// ************************* + /// * Public View Functions * + /// ************************* + + /// @notice Return the sender of a cross domain message. + function xDomainMessageSender() external view returns (address); + + /// ***************************** + /// * Public Mutating Functions * + /// ***************************** + + /// @notice Send cross chain message from L1 to L2 or L2 to L1. + /// @param target The address of account who receive the message. + /// @param value The amount of ether passed when call target contract. + /// @param message The content of the message. + /// @param gasLimit Gas limit required to complete the message relay on corresponding chain. + function sendMessage(address target, uint256 value, bytes calldata message, uint256 gasLimit) external payable; + + /// @notice Send cross chain message from L1 to L2 or L2 to L1. + /// @param target The address of account who receive the message. + /// @param value The amount of ether passed when call target contract. + /// @param message The content of the message. + /// @param gasLimit Gas limit required to complete the message relay on corresponding chain. + /// @param refundAddress The address of account who will receive the refunded fee. + function sendMessage( + address target, + uint256 value, + bytes calldata message, + uint256 gasLimit, + address refundAddress + ) external payable; +} + +contract MockScrollCrossDomainMessenger is IScrollMessenger { + address internal mockMessageSender; + + constructor(address sender) { + mockMessageSender = sender; + } + + function xDomainMessageSender() external view override returns (address) { + return mockMessageSender; + } + + function _setMockMessageSender(address sender) external { + mockMessageSender = sender; + } + + /// ***************************** + /// * Public Mutating Functions * + /// ***************************** + + /// @notice Send cross chain message from L1 to L2 or L2 to L1. + /// @param _target The address of account who receive the message. + /// @param _message The content of the message. + function sendMessage(address _target, uint256, bytes calldata _message, uint256) external payable override { + Address.functionCall(_target, _message, "sendMessage reverted"); + } + + /// @notice Send cross chain message from L1 to L2 or L2 to L1. + /// @param _target The address of account who receive the message. + /// @param _message The content of the message. + function sendMessage(address _target, uint256, bytes calldata _message, uint256, address) external payable override { + Address.functionCall(_target, _message, "sendMessage reverted"); + } +} diff --git a/contracts/test/v0.8/dev/ScrollCrossDomainForwarder.test.ts b/contracts/test/v0.8/dev/ScrollCrossDomainForwarder.test.ts new file mode 100644 index 00000000000..923d41326ae --- /dev/null +++ b/contracts/test/v0.8/dev/ScrollCrossDomainForwarder.test.ts @@ -0,0 +1,259 @@ +import { ethers } from 'hardhat' +import { assert, expect } from 'chai' +import { Contract, ContractFactory } from 'ethers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { publicAbi } from '../../test-helpers/helpers' + +let owner: SignerWithAddress +let stranger: SignerWithAddress +let l1OwnerAddress: string +let newL1OwnerAddress: string +let forwarderFactory: ContractFactory +let greeterFactory: ContractFactory +let crossDomainMessengerFactory: ContractFactory +let crossDomainMessenger: Contract +let forwarder: Contract +let greeter: Contract + +before(async () => { + const accounts = await ethers.getSigners() + owner = accounts[0] + stranger = accounts[1] + + // forwarder config + l1OwnerAddress = owner.address + newL1OwnerAddress = stranger.address + + // Contract factories + forwarderFactory = await ethers.getContractFactory( + 'src/v0.8/l2ep/dev/scroll/ScrollCrossDomainForwarder.sol:ScrollCrossDomainForwarder', + owner, + ) + greeterFactory = await ethers.getContractFactory( + 'src/v0.8/tests/Greeter.sol:Greeter', + owner, + ) + crossDomainMessengerFactory = await ethers.getContractFactory( + 'src/v0.8/vendor/MockScrollCrossDomainMessenger.sol:MockScrollCrossDomainMessenger', + ) +}) + +describe('ScrollCrossDomainForwarder', () => { + beforeEach(async () => { + crossDomainMessenger = + await crossDomainMessengerFactory.deploy(l1OwnerAddress) + forwarder = await forwarderFactory.deploy( + crossDomainMessenger.address, + l1OwnerAddress, + ) + greeter = await greeterFactory.deploy(forwarder.address) + }) + + it('has a limited public interface [ @skip-coverage ]', async () => { + publicAbi(forwarder, [ + 'typeAndVersion', + 'crossDomainMessenger', + 'forward', + 'l1Owner', + 'transferL1Ownership', + 'acceptL1Ownership', + // ConfirmedOwner methods: + 'owner', + 'transferOwnership', + 'acceptOwnership', + ]) + }) + + describe('#constructor', () => { + it('should set the owner correctly', async () => { + const response = await forwarder.owner() + assert.equal(response, owner.address) + }) + + it('should set the l1Owner correctly', async () => { + const response = await forwarder.l1Owner() + assert.equal(response, l1OwnerAddress) + }) + + it('should set the crossdomain messenger correctly', async () => { + const response = await forwarder.crossDomainMessenger() + assert.equal(response, crossDomainMessenger.address) + }) + + it('should set the typeAndVersion correctly', async () => { + const response = await forwarder.typeAndVersion() + assert.equal(response, 'ScrollCrossDomainForwarder 1.0.0') + }) + }) + + describe('#forward', () => { + it('should not be callable by unknown address', async () => { + await expect( + forwarder.connect(stranger).forward(greeter.address, '0x'), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should be callable by crossdomain messenger address / L1 owner', async () => { + const newGreeting = 'hello' + const setGreetingData = greeterFactory.interface.encodeFunctionData( + 'setGreeting', + [newGreeting], + ) + const forwardData = forwarderFactory.interface.encodeFunctionData( + 'forward', + [greeter.address, setGreetingData], + ) + await crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ) + + const updatedGreeting = await greeter.greeting() + assert.equal(updatedGreeting, newGreeting) + }) + + it('should revert when contract call reverts', async () => { + const setGreetingData = greeterFactory.interface.encodeFunctionData( + 'setGreeting', + [''], + ) + const forwardData = forwarderFactory.interface.encodeFunctionData( + 'forward', + [greeter.address, setGreetingData], + ) + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ).to.be.revertedWith('Invalid greeting length') + }) + }) + + describe('#transferL1Ownership', () => { + it('should not be callable by non-owners', async () => { + await expect( + forwarder.connect(stranger).transferL1Ownership(stranger.address), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should not be callable by L2 owner', async () => { + const forwarderOwner = await forwarder.owner() + assert.equal(forwarderOwner, owner.address) + + await expect( + forwarder.connect(owner).transferL1Ownership(stranger.address), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should be callable by current L1 owner', async () => { + const currentL1Owner = await forwarder.l1Owner() + const forwardData = forwarderFactory.interface.encodeFunctionData( + 'transferL1Ownership', + [newL1OwnerAddress], + ) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ) + .to.emit(forwarder, 'L1OwnershipTransferRequested') + .withArgs(currentL1Owner, newL1OwnerAddress) + }) + + it('should be callable by current L1 owner to zero address', async () => { + const currentL1Owner = await forwarder.l1Owner() + const forwardData = forwarderFactory.interface.encodeFunctionData( + 'transferL1Ownership', + [ethers.constants.AddressZero], + ) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ) + .to.emit(forwarder, 'L1OwnershipTransferRequested') + .withArgs(currentL1Owner, ethers.constants.AddressZero) + }) + }) + + describe('#acceptL1Ownership', () => { + it('should not be callable by non pending-owners', async () => { + const forwardData = forwarderFactory.interface.encodeFunctionData( + 'acceptL1Ownership', + [], + ) + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ).to.be.revertedWith('Must be proposed L1 owner') + }) + + it('should be callable by pending L1 owner', async () => { + const currentL1Owner = await forwarder.l1Owner() + + // Transfer ownership + const forwardTransferData = forwarderFactory.interface.encodeFunctionData( + 'transferL1Ownership', + [newL1OwnerAddress], + ) + await crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardTransferData, // message + 0, // gasLimit + ) + + const forwardAcceptData = forwarderFactory.interface.encodeFunctionData( + 'acceptL1Ownership', + [], + ) + // Simulate cross-chain message from another sender + await crossDomainMessenger._setMockMessageSender(newL1OwnerAddress) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + forwarder.address, // target + 0, // value + forwardAcceptData, // message + 0, // gasLimit + ), + ) + .to.emit(forwarder, 'L1OwnershipTransferred') + .withArgs(currentL1Owner, newL1OwnerAddress) + + const updatedL1Owner = await forwarder.l1Owner() + assert.equal(updatedL1Owner, newL1OwnerAddress) + }) + }) +}) diff --git a/contracts/test/v0.8/dev/ScrollCrossDomainGovernor.test.ts b/contracts/test/v0.8/dev/ScrollCrossDomainGovernor.test.ts new file mode 100644 index 00000000000..adb78c26248 --- /dev/null +++ b/contracts/test/v0.8/dev/ScrollCrossDomainGovernor.test.ts @@ -0,0 +1,459 @@ +import { ethers } from 'hardhat' +import { assert, expect } from 'chai' +import etherslib, { Contract, ContractFactory } from 'ethers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { publicAbi, stripHexPrefix } from '../../test-helpers/helpers' + +let owner: SignerWithAddress +let stranger: SignerWithAddress +let l1OwnerAddress: string +let newL1OwnerAddress: string +let governorFactory: ContractFactory +let greeterFactory: ContractFactory +let multisendFactory: ContractFactory +let crossDomainMessengerFactory: ContractFactory +let crossDomainMessenger: Contract +let governor: Contract +let greeter: Contract +let multisend: Contract + +before(async () => { + const accounts = await ethers.getSigners() + owner = accounts[0] + stranger = accounts[1] + + // governor config + l1OwnerAddress = owner.address + newL1OwnerAddress = stranger.address + + // Contract factories + governorFactory = await ethers.getContractFactory( + 'src/v0.8/l2ep/dev/scroll/ScrollCrossDomainGovernor.sol:ScrollCrossDomainGovernor', + owner, + ) + greeterFactory = await ethers.getContractFactory( + 'src/v0.8/tests/Greeter.sol:Greeter', + owner, + ) + multisendFactory = await ethers.getContractFactory( + 'src/v0.8/vendor/MultiSend.sol:MultiSend', + owner, + ) + crossDomainMessengerFactory = await ethers.getContractFactory( + 'src/v0.8/vendor/MockScrollCrossDomainMessenger.sol:MockScrollCrossDomainMessenger', + ) +}) + +describe('ScrollCrossDomainGovernor', () => { + beforeEach(async () => { + crossDomainMessenger = + await crossDomainMessengerFactory.deploy(l1OwnerAddress) + governor = await governorFactory.deploy( + crossDomainMessenger.address, + l1OwnerAddress, + ) + greeter = await greeterFactory.deploy(governor.address) + multisend = await multisendFactory.deploy() + }) + + it('has a limited public interface [ @skip-coverage ]', async () => { + publicAbi(governor, [ + 'typeAndVersion', + 'crossDomainMessenger', + 'forward', + 'forwardDelegate', + 'l1Owner', + 'transferL1Ownership', + 'acceptL1Ownership', + // ConfirmedOwner methods: + 'owner', + 'transferOwnership', + 'acceptOwnership', + ]) + }) + + describe('#constructor', () => { + it('should set the owner correctly', async () => { + const response = await governor.owner() + assert.equal(response, owner.address) + }) + + it('should set the l1Owner correctly', async () => { + const response = await governor.l1Owner() + assert.equal(response, l1OwnerAddress) + }) + + it('should set the crossdomain messenger correctly', async () => { + const response = await governor.crossDomainMessenger() + assert.equal(response, crossDomainMessenger.address) + }) + + it('should set the typeAndVersion correctly', async () => { + const response = await governor.typeAndVersion() + assert.equal(response, 'ScrollCrossDomainGovernor 1.0.0') + }) + }) + + describe('#forward', () => { + it('should not be callable by unknown address', async () => { + await expect( + governor.connect(stranger).forward(greeter.address, '0x'), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should be callable by crossdomain messenger address / L1 owner', async () => { + const newGreeting = 'hello' + const setGreetingData = greeterFactory.interface.encodeFunctionData( + 'setGreeting', + [newGreeting], + ) + const forwardData = governorFactory.interface.encodeFunctionData( + 'forward', + [greeter.address, setGreetingData], + ) + await crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ) + + const updatedGreeting = await greeter.greeting() + assert.equal(updatedGreeting, newGreeting) + }) + + it('should be callable by L2 owner', async () => { + const newGreeting = 'hello' + const setGreetingData = greeterFactory.interface.encodeFunctionData( + 'setGreeting', + [newGreeting], + ) + await governor.connect(owner).forward(greeter.address, setGreetingData) + + const updatedGreeting = await greeter.greeting() + assert.equal(updatedGreeting, newGreeting) + }) + + it('should revert when contract call reverts', async () => { + const setGreetingData = greeterFactory.interface.encodeFunctionData( + 'setGreeting', + [''], + ) + const forwardData = governorFactory.interface.encodeFunctionData( + 'forward', + [greeter.address, setGreetingData], + ) + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ).to.be.revertedWith('Invalid greeting length') + }) + }) + + describe('#forwardDelegate', () => { + it('should not be callable by unknown address', async () => { + await expect( + governor.connect(stranger).forwardDelegate(multisend.address, '0x'), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should be callable by crossdomain messenger address / L1 owner', async () => { + const calls = [ + { + to: greeter.address, + data: greeterFactory.interface.encodeFunctionData('setGreeting', [ + 'foo', + ]), + value: 0, + }, + { + to: greeter.address, + data: greeterFactory.interface.encodeFunctionData('setGreeting', [ + 'bar', + ]), + value: 0, + }, + ] + const multisendData = encodeMultisendData(multisend.interface, calls) + const forwardData = governorFactory.interface.encodeFunctionData( + 'forwardDelegate', + [multisend.address, multisendData], + ) + + await crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ) + + const updatedGreeting = await greeter.greeting() + assert.equal(updatedGreeting, 'bar') + }) + + it('should be callable by L2 owner', async () => { + const calls = [ + { + to: greeter.address, + data: greeterFactory.interface.encodeFunctionData('setGreeting', [ + 'foo', + ]), + value: 0, + }, + { + to: greeter.address, + data: greeterFactory.interface.encodeFunctionData('setGreeting', [ + 'bar', + ]), + value: 0, + }, + ] + const multisendData = encodeMultisendData(multisend.interface, calls) + await governor + .connect(owner) + .forwardDelegate(multisend.address, multisendData) + + const updatedGreeting = await greeter.greeting() + assert.equal(updatedGreeting, 'bar') + }) + + it('should revert batch when one call fails', async () => { + const calls = [ + { + to: greeter.address, + data: greeterFactory.interface.encodeFunctionData('setGreeting', [ + 'foo', + ]), + value: 0, + }, + { + to: greeter.address, + data: greeterFactory.interface.encodeFunctionData('setGreeting', [ + '', // should revert + ]), + value: 0, + }, + ] + const multisendData = encodeMultisendData(multisend.interface, calls) + const forwardData = governorFactory.interface.encodeFunctionData( + 'forwardDelegate', + [multisend.address, multisendData], + ) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ).to.be.revertedWith('Governor delegatecall reverted') + + const greeting = await greeter.greeting() + assert.equal(greeting, '') // Unchanged + }) + + it('should bubble up revert when contract call reverts', async () => { + const triggerRevertData = + greeterFactory.interface.encodeFunctionData('triggerRevert') + const forwardData = governorFactory.interface.encodeFunctionData( + 'forwardDelegate', + [greeter.address, triggerRevertData], + ) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ).to.be.revertedWith('Greeter: revert triggered') + }) + }) + + describe('#transferL1Ownership', () => { + it('should not be callable by non-owners', async () => { + await expect( + governor.connect(stranger).transferL1Ownership(stranger.address), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should not be callable by L2 owner', async () => { + const governorOwner = await governor.owner() + assert.equal(governorOwner, owner.address) + + await expect( + governor.connect(owner).transferL1Ownership(stranger.address), + ).to.be.revertedWith('Sender is not the L2 messenger') + }) + + it('should be callable by current L1 owner', async () => { + const currentL1Owner = await governor.l1Owner() + const forwardData = governorFactory.interface.encodeFunctionData( + 'transferL1Ownership', + [newL1OwnerAddress], + ) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ) + .to.emit(governor, 'L1OwnershipTransferRequested') + .withArgs(currentL1Owner, newL1OwnerAddress) + }) + + it('should be callable by current L1 owner to zero address', async () => { + const currentL1Owner = await governor.l1Owner() + const forwardData = governorFactory.interface.encodeFunctionData( + 'transferL1Ownership', + [ethers.constants.AddressZero], + ) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ) + .to.emit(governor, 'L1OwnershipTransferRequested') + .withArgs(currentL1Owner, ethers.constants.AddressZero) + }) + }) + + describe('#acceptL1Ownership', () => { + it('should not be callable by non pending-owners', async () => { + const forwardData = governorFactory.interface.encodeFunctionData( + 'acceptL1Ownership', + [], + ) + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardData, // message + 0, // gasLimit + ), + ).to.be.revertedWith('Must be proposed L1 owner') + }) + + it('should be callable by pending L1 owner', async () => { + const currentL1Owner = await governor.l1Owner() + + // Transfer ownership + const forwardTransferData = governorFactory.interface.encodeFunctionData( + 'transferL1Ownership', + [newL1OwnerAddress], + ) + await crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardTransferData, // message + 0, // gasLimit + ) + + const forwardAcceptData = governorFactory.interface.encodeFunctionData( + 'acceptL1Ownership', + [], + ) + // Simulate cross-chain message from another sender + await crossDomainMessenger._setMockMessageSender(newL1OwnerAddress) + + await expect( + crossDomainMessenger // Simulate cross-chain message + .connect(stranger) + ['sendMessage(address,uint256,bytes,uint256)']( + governor.address, // target + 0, // value + forwardAcceptData, // message + 0, // gasLimit + ), + ) + .to.emit(governor, 'L1OwnershipTransferred') + .withArgs(currentL1Owner, newL1OwnerAddress) + + const updatedL1Owner = await governor.l1Owner() + assert.equal(updatedL1Owner, newL1OwnerAddress) + }) + }) +}) + +// Multisend contract helpers + +/** + * Encodes an underlying transaction for the Multisend contract + * + * @param operation 0 for CALL, 1 for DELEGATECALL + * @param to tx target address + * @param value tx value + * @param data tx data + */ +export function encodeTxData( + operation: number, + to: string, + value: number, + data: string, +): string { + const dataBuffer = Buffer.from(stripHexPrefix(data), 'hex') + const types = ['uint8', 'address', 'uint256', 'uint256', 'bytes'] + const values = [operation, to, value, dataBuffer.length, dataBuffer] + const encoded = ethers.utils.solidityPack(types, values) + return stripHexPrefix(encoded) +} + +/** + * Encodes a Multisend call + * + * @param MultisendInterface Ethers Interface object of the Multisend contract + * @param transactions one or more transactions to include in the Multisend call + * @param to tx target address + * @param value tx value + * @param data tx data + */ +export function encodeMultisendData( + MultisendInterface: etherslib.utils.Interface, + transactions: { to: string; value: number; data: string }[], +): string { + let nestedTransactionData = '0x' + for (const transaction of transactions) { + nestedTransactionData += encodeTxData( + 0, + transaction.to, + transaction.value, + transaction.data, + ) + } + const encodedMultisendFnData = MultisendInterface.encodeFunctionData( + 'multiSend', + [nestedTransactionData], + ) + return encodedMultisendFnData +} diff --git a/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts b/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts new file mode 100644 index 00000000000..b294032e73d --- /dev/null +++ b/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts @@ -0,0 +1,426 @@ +import { ethers, network } from 'hardhat' +import { BigNumber, Contract } from 'ethers' +import { expect } from 'chai' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' + +describe('ScrollSequencerUptimeFeed', () => { + let l2CrossDomainMessenger: Contract + let scrollUptimeFeed: Contract + let uptimeFeedConsumer: Contract + let deployer: SignerWithAddress + let l1Owner: SignerWithAddress + let l2Messenger: SignerWithAddress + let dummy: SignerWithAddress + const gasUsedDeviation = 100 + const initialStatus = 0 + + before(async () => { + const accounts = await ethers.getSigners() + deployer = accounts[0] + l1Owner = accounts[1] + dummy = accounts[3] + + const l2CrossDomainMessengerFactory = await ethers.getContractFactory( + 'src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol:MockScrollL2CrossDomainMessenger', + deployer, + ) + + l2CrossDomainMessenger = await l2CrossDomainMessengerFactory.deploy() + + // Pretend we're on L2 + await network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [l2CrossDomainMessenger.address], + }) + l2Messenger = await ethers.getSigner(l2CrossDomainMessenger.address) + // Credit the L2 messenger with some ETH + await dummy.sendTransaction({ + to: l2Messenger.address, + value: ethers.utils.parseEther('10'), + }) + }) + + beforeEach(async () => { + const scrollSequencerStatusRecorderFactory = + await ethers.getContractFactory( + 'src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol:ScrollSequencerUptimeFeed', + deployer, + ) + scrollUptimeFeed = await scrollSequencerStatusRecorderFactory.deploy( + l1Owner.address, + l2CrossDomainMessenger.address, + initialStatus, + ) + + // Set mock sender in mock L2 messenger contract + await l2CrossDomainMessenger.setSender(l1Owner.address) + + // Mock consumer + const statusFeedConsumerFactory = await ethers.getContractFactory( + 'src/v0.8/tests/FeedConsumer.sol:FeedConsumer', + deployer, + ) + uptimeFeedConsumer = await statusFeedConsumerFactory.deploy( + scrollUptimeFeed.address, + ) + }) + + describe('constructor', () => { + it('should have been deployed with the correct initial state', async () => { + const l1Sender = await scrollUptimeFeed.l1Sender() + expect(l1Sender).to.equal(l1Owner.address) + const { roundId, answer } = await scrollUptimeFeed.latestRoundData() + expect(roundId).to.equal(1) + expect(answer).to.equal(initialStatus) + }) + }) + + describe('#updateStatus', () => { + it('should revert if called by an address that is not the L2 Cross Domain Messenger', async () => { + const timestamp = await scrollUptimeFeed.latestTimestamp() + expect( + scrollUptimeFeed.connect(dummy).updateStatus(true, timestamp), + ).to.be.revertedWith('InvalidSender') + }) + + it('should revert if called by an address that is not the L2 Cross Domain Messenger and is not the L1 sender', async () => { + const timestamp = await scrollUptimeFeed.latestTimestamp() + await l2CrossDomainMessenger.setSender(dummy.address) + expect( + scrollUptimeFeed.connect(dummy).updateStatus(true, timestamp), + ).to.be.revertedWith('InvalidSender') + }) + + it(`should update status when status has not changed and incoming timestamp is the same as latest`, async () => { + const timestamp = await scrollUptimeFeed.latestTimestamp() + let tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp) + await expect(tx) + .to.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(1, 2 /** roundId */, timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(1) + + const latestRoundBeforeUpdate = await scrollUptimeFeed.latestRoundData() + + tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp.add(200)) + + // Submit another status update with the same status + const latestBlock = await ethers.provider.getBlock('latest') + + await expect(tx) + .to.emit(scrollUptimeFeed, 'RoundUpdated') + .withArgs(1, latestBlock.timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(1) + expect(await scrollUptimeFeed.latestTimestamp()).to.equal(timestamp) + + // Verify that latest round has been properly updated + const latestRoundDataAfterUpdate = + await scrollUptimeFeed.latestRoundData() + expect(latestRoundDataAfterUpdate.roundId).to.equal( + latestRoundBeforeUpdate.roundId, + ) + expect(latestRoundDataAfterUpdate.answer).to.equal( + latestRoundBeforeUpdate.answer, + ) + expect(latestRoundDataAfterUpdate.startedAt).to.equal( + latestRoundBeforeUpdate.startedAt, + ) + expect(latestRoundDataAfterUpdate.answeredInRound).to.equal( + latestRoundBeforeUpdate.answeredInRound, + ) + expect(latestRoundDataAfterUpdate.updatedAt).to.equal( + latestBlock.timestamp, + ) + }) + + it(`should update status when status has changed and incoming timestamp is newer than the latest`, async () => { + let timestamp = await scrollUptimeFeed.latestTimestamp() + let tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp) + await expect(tx) + .to.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(1, 2 /** roundId */, timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(1) + + // Submit another status update, different status, newer timestamp should update + timestamp = timestamp.add(2000) + tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(false, timestamp) + await expect(tx) + .to.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(0, 3 /** roundId */, timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(0) + expect(await scrollUptimeFeed.latestTimestamp()).to.equal(timestamp) + }) + + it(`should update status when status has changed and incoming timestamp is the same as latest`, async () => { + const timestamp = await scrollUptimeFeed.latestTimestamp() + let tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp) + await expect(tx) + .to.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(1, 2 /** roundId */, timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(1) + + // Submit another status update, different status, same timestamp should update + tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(false, timestamp) + await expect(tx) + .to.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(0, 3 /** roundId */, timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(0) + expect(await scrollUptimeFeed.latestTimestamp()).to.equal(timestamp) + }) + + it('should ignore out-of-order updates', async () => { + const timestamp = (await scrollUptimeFeed.latestTimestamp()).add(10_000) + // Update status + let tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp) + await expect(tx) + .to.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(1, 2 /** roundId */, timestamp) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(1) + + // Update with different status, but stale timestamp, should be ignored + const staleTimestamp = timestamp.sub(1000) + tx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(false, staleTimestamp) + await expect(tx) + .to.not.emit(scrollUptimeFeed, 'AnswerUpdated') + .withArgs(1, 2 /** roundId */, timestamp) + await expect(tx).to.emit(scrollUptimeFeed, 'UpdateIgnored') + }) + }) + + describe('AggregatorV3Interface', () => { + it('should return valid answer from getRoundData and latestRoundData', async () => { + let [roundId, answer, startedAt, updatedAt, answeredInRound] = + await scrollUptimeFeed.latestRoundData() + expect(roundId).to.equal(1) + expect(answer).to.equal(0) + expect(answeredInRound).to.equal(roundId) + expect(startedAt).to.equal(updatedAt) + + // Submit status update with different status and newer timestamp, should update + const timestamp = (startedAt as BigNumber).add(1000) + await scrollUptimeFeed.connect(l2Messenger).updateStatus(true, timestamp) + ;[roundId, answer, startedAt, updatedAt, answeredInRound] = + await scrollUptimeFeed.getRoundData(2) + expect(roundId).to.equal(2) + expect(answer).to.equal(1) + expect(answeredInRound).to.equal(roundId) + expect(startedAt).to.equal(timestamp) + expect(updatedAt.lte(startedAt)).to.be.true + + // Check that last round is still returning the correct data + ;[roundId, answer, startedAt, updatedAt, answeredInRound] = + await scrollUptimeFeed.getRoundData(1) + expect(roundId).to.equal(1) + expect(answer).to.equal(0) + expect(answeredInRound).to.equal(roundId) + expect(startedAt).to.equal(updatedAt) + + // Assert latestRoundData corresponds to latest round id + expect(await scrollUptimeFeed.getRoundData(2)).to.deep.equal( + await scrollUptimeFeed.latestRoundData(), + ) + }) + + it('should revert from #getRoundData when round does not yet exist (future roundId)', async () => { + expect(scrollUptimeFeed.getRoundData(2)).to.be.revertedWith( + 'NoDataPresent()', + ) + }) + + it('should revert from #getAnswer when round does not yet exist (future roundId)', async () => { + expect(scrollUptimeFeed.getAnswer(2)).to.be.revertedWith( + 'NoDataPresent()', + ) + }) + + it('should revert from #getTimestamp when round does not yet exist (future roundId)', async () => { + expect(scrollUptimeFeed.getTimestamp(2)).to.be.revertedWith( + 'NoDataPresent()', + ) + }) + }) + + describe('Protect reads on AggregatorV2V3Interface functions', () => { + it('should disallow reads on AggregatorV2V3Interface functions when consuming contract is not whitelisted', async () => { + // Sanity - consumer is not whitelisted + expect(await scrollUptimeFeed.checkEnabled()).to.be.true + expect( + await scrollUptimeFeed.hasAccess(uptimeFeedConsumer.address, '0x00'), + ).to.be.false + + // Assert reads are not possible from consuming contract + await expect(uptimeFeedConsumer.latestAnswer()).to.be.revertedWith( + 'No access', + ) + await expect(uptimeFeedConsumer.latestRoundData()).to.be.revertedWith( + 'No access', + ) + }) + + it('should allow reads on AggregatorV2V3Interface functions when consuming contract is whitelisted', async () => { + // Whitelist consumer + await scrollUptimeFeed.addAccess(uptimeFeedConsumer.address) + // Sanity - consumer is whitelisted + expect(await scrollUptimeFeed.checkEnabled()).to.be.true + expect( + await scrollUptimeFeed.hasAccess(uptimeFeedConsumer.address, '0x00'), + ).to.be.true + + // Assert reads are possible from consuming contract + expect(await uptimeFeedConsumer.latestAnswer()).to.be.equal('0') + const [roundId, answer] = await uptimeFeedConsumer.latestRoundData() + expect(roundId).to.equal(1) + expect(answer).to.equal(0) + }) + }) + + describe('Gas costs', () => { + it('should consume a known amount of gas for updates @skip-coverage', async () => { + // Sanity - start at flag = 0 (`false`) + expect(await scrollUptimeFeed.latestAnswer()).to.equal(0) + let timestamp = await scrollUptimeFeed.latestTimestamp() + + // Gas for no update + timestamp = timestamp.add(1000) + const _noUpdateTx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(false, timestamp) + const noUpdateTx = await _noUpdateTx.wait(1) + // Assert no update + expect(await scrollUptimeFeed.latestAnswer()).to.equal(0) + expect(noUpdateTx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 38594, + gasUsedDeviation, + ) + + // Gas for update + timestamp = timestamp.add(1000) + const _updateTx = await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp) + const updateTx = await _updateTx.wait(1) + // Assert update + expect(await scrollUptimeFeed.latestAnswer()).to.equal(1) + expect(updateTx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 58458, + gasUsedDeviation, + ) + }) + + describe('Aggregator interface', () => { + beforeEach(async () => { + const timestamp = (await scrollUptimeFeed.latestTimestamp()).add(1000) + // Initialise a round + await scrollUptimeFeed + .connect(l2Messenger) + .updateStatus(true, timestamp) + }) + + it('should consume a known amount of gas for getRoundData(uint80) @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.getRoundData(1), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 30952, + gasUsedDeviation, + ) + }) + + it('should consume a known amount of gas for latestRoundData() @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.latestRoundData(), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 28523, + gasUsedDeviation, + ) + }) + + it('should consume a known amount of gas for latestAnswer() @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.latestAnswer(), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 28229, + gasUsedDeviation, + ) + }) + + it('should consume a known amount of gas for latestTimestamp() @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.latestTimestamp(), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 28129, + gasUsedDeviation, + ) + }) + + it('should consume a known amount of gas for latestRound() @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.latestRound(), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 28145, + gasUsedDeviation, + ) + }) + + it('should consume a known amount of gas for getAnswer(roundId) @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.getAnswer(1), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 30682, + gasUsedDeviation, + ) + }) + + it('should consume a known amount of gas for getTimestamp(roundId) @skip-coverage', async () => { + const _tx = await l2Messenger.sendTransaction( + await scrollUptimeFeed + .connect(l2Messenger) + .populateTransaction.getTimestamp(1), + ) + const tx = await _tx.wait(1) + expect(tx.cumulativeGasUsed.toNumber()).to.be.closeTo( + 30570, + gasUsedDeviation, + ) + }) + }) + }) +}) diff --git a/contracts/test/v0.8/dev/ScrollValidator.test.ts b/contracts/test/v0.8/dev/ScrollValidator.test.ts new file mode 100644 index 00000000000..866d52b202f --- /dev/null +++ b/contracts/test/v0.8/dev/ScrollValidator.test.ts @@ -0,0 +1,118 @@ +import { ethers } from 'hardhat' +import { BigNumber, Contract, ContractFactory } from 'ethers' +import { expect } from 'chai' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' + +describe('ScrollValidator', () => { + const GAS_LIMIT = BigNumber.from(1_900_000) + /** Fake L2 target */ + const L2_SEQ_STATUS_RECORDER_ADDRESS = + '0x491B1dDA0A8fa069bbC1125133A975BF4e85a91b' + let scrollValidator: Contract + let scrollUptimeFeedFactory: ContractFactory + let mockScrollL1CrossDomainMessenger: Contract + let deployer: SignerWithAddress + let eoaValidator: SignerWithAddress + + before(async () => { + const accounts = await ethers.getSigners() + deployer = accounts[0] + eoaValidator = accounts[1] + }) + + beforeEach(async () => { + // Required for building the calldata + scrollUptimeFeedFactory = await ethers.getContractFactory( + 'src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol:ScrollSequencerUptimeFeed', + deployer, + ) + + // Scroll Messenger contract on L1 + const mockScrollL1CrossDomainMessengerFactory = + await ethers.getContractFactory( + 'src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol:MockScrollL1CrossDomainMessenger', + ) + mockScrollL1CrossDomainMessenger = + await mockScrollL1CrossDomainMessengerFactory.deploy() + + // Contract under test + const scrollValidatorFactory = await ethers.getContractFactory( + 'src/v0.8/l2ep/dev/scroll/ScrollValidator.sol:ScrollValidator', + deployer, + ) + + scrollValidator = await scrollValidatorFactory.deploy( + mockScrollL1CrossDomainMessenger.address, + L2_SEQ_STATUS_RECORDER_ADDRESS, + GAS_LIMIT, + ) + }) + + describe('#setGasLimit', () => { + it('correctly updates the gas limit', async () => { + const newGasLimit = BigNumber.from(2_000_000) + const tx = await scrollValidator.setGasLimit(newGasLimit) + await tx.wait() + const currentGasLimit = await scrollValidator.getGasLimit() + expect(currentGasLimit).to.equal(newGasLimit) + }) + }) + + describe('#validate', () => { + it('reverts if called by account with no access', async () => { + await expect( + scrollValidator.connect(eoaValidator).validate(0, 0, 1, 1), + ).to.be.revertedWith('No access') + }) + + it('posts sequencer status when there is not status change', async () => { + await scrollValidator.addAccess(eoaValidator.address) + + const currentBlock = await ethers.provider.getBlock('latest') + const futureTimestamp = currentBlock.timestamp + 5000 + + await ethers.provider.send('evm_setNextBlockTimestamp', [futureTimestamp]) + const sequencerStatusRecorderCallData = + scrollUptimeFeedFactory.interface.encodeFunctionData('updateStatus', [ + false, + futureTimestamp, + ]) + + await expect(scrollValidator.connect(eoaValidator).validate(0, 0, 0, 0)) + .to.emit(mockScrollL1CrossDomainMessenger, 'SentMessage') + .withArgs( + scrollValidator.address, // sender + L2_SEQ_STATUS_RECORDER_ADDRESS, // target + 0, // value + 0, // nonce + GAS_LIMIT, // gas limit + sequencerStatusRecorderCallData, // message + ) + }) + + it('post sequencer offline', async () => { + await scrollValidator.addAccess(eoaValidator.address) + + const currentBlock = await ethers.provider.getBlock('latest') + const futureTimestamp = currentBlock.timestamp + 10000 + + await ethers.provider.send('evm_setNextBlockTimestamp', [futureTimestamp]) + const sequencerStatusRecorderCallData = + scrollUptimeFeedFactory.interface.encodeFunctionData('updateStatus', [ + true, + futureTimestamp, + ]) + + await expect(scrollValidator.connect(eoaValidator).validate(0, 0, 1, 1)) + .to.emit(mockScrollL1CrossDomainMessenger, 'SentMessage') + .withArgs( + scrollValidator.address, // sender + L2_SEQ_STATUS_RECORDER_ADDRESS, // target + 0, // value + 0, // nonce + GAS_LIMIT, // gas limit + sequencerStatusRecorderCallData, // message + ) + }) + }) +}) From 35ad7d164a29409e60a9896434df5685bc8f2a59 Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:11:14 -0800 Subject: [PATCH 039/234] Handle edge case involving blocks not being found in the db (#11298) If there is a backfill followed by an error, logs get written to the db but no blocks. This will make the logpoller (or backup log poller) rely on the chain next time instead of the db to determine lastFinalizedBlock, which could result in a gap in logs processed. Fixing this by writing the last block in the backfil to the db along with the logs. Its lastFinalizedBlock field will be set to its own block number + 1, so if the db crashes it should start by pulling the logs from that one. --- core/chains/evm/logpoller/log_poller.go | 2 +- core/chains/evm/logpoller/log_poller_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index fb380f84b2c..1cf1cf42beb 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -679,7 +679,7 @@ func (lp *logPoller) backfill(ctx context.Context, start, end int64) error { } lp.lggr.Debugw("Backfill found logs", "from", from, "to", to, "logs", len(gethLogs), "blocks", blocks) - err = lp.orm.InsertLogs(convertLogs(gethLogs, blocks, lp.lggr, lp.ec.ConfiguredChainID()), pg.WithParentCtx(ctx)) + err = lp.orm.InsertLogsWithBlock(convertLogs(gethLogs, blocks, lp.lggr, lp.ec.ConfiguredChainID()), blocks[len(blocks)-1], pg.WithParentCtx(ctx)) if err != nil { lp.lggr.Warnw("Unable to insert logs, retrying", "err", err, "from", from, "to", to) return err diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 82447bdb5f4..6c4dd381b54 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -965,8 +965,8 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) { lgs, err = th.ORM.SelectLogsByBlockRange(11, 17) require.NoError(t, err) assert.Equal(t, 7, len(lgs)) - th.assertHaveCanonical(t, 15, 16) - th.assertDontHave(t, 11, 14) // Do not expect to save backfilled blocks. + th.assertHaveCanonical(t, 14, 16) // Should have last finalized block plus unfinalized blocks + th.assertDontHave(t, 11, 13) // Should not have older finalized blocks // Verify that a custom block timestamp will get written to db correctly also b, err = th.Client.BlockByNumber(testutils.Context(t), nil) @@ -1057,8 +1057,8 @@ func TestLogPoller_PollAndSaveLogsDeepReorg(t *testing.T) { require.NoError(t, err) assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000002`), lgs[0].Data) th.assertHaveCanonical(t, 1, 1) - th.assertDontHave(t, 2, 5) // These blocks are backfilled - th.assertHaveCanonical(t, 6, 10) + th.assertDontHave(t, 2, 3) // These blocks are backfilled + th.assertHaveCanonical(t, 5, 10) }) } } From a8d096cc69b49cd2f8fc0672b819ccd8b5b38048 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Tue, 12 Dec 2023 23:18:34 +0200 Subject: [PATCH 040/234] Remove big from core utils (#11511) * Change difficulty from Big to BigInt * Fix headtracker mock head * Remove EsnureClosed * Fix mock heads * Migrate to common Mailbox * Fix Tracker close on txm * Change to EnsureHexPrefix * Change names to mailbox * Remove core/null dependency from common * Remove core mailbox * Fix dependencies * Tidy * Fix dependencies * Change path to internal utils * Minor fixes * Implement skeleton interfaces, structs, & methods for ChainReader EVM POC - Read ChainReader config in from RelayConfig - Add some initialization and validation relay skeletons - Use medianProviderWrapper instead of passing medianContract separately This avoids us having to modify the signature of NewMedianFactory, which would require further modifications to all non-evm repos and chainlink-relay - Add chain_reader_test.go with some basic relay tests Co-authored-by: Jordan Krage - Add chain reader config validation - Add chain reader config validation tests - Add config for chain reader median contract to cr validation testcases - Add unimplemented Encode(), Decode(), GetMaxEncodingSize(), GetMaxDecodingSize() - Add ChainReader() method to mock provider for plugin test - Rename relaymercury.ChainReader to MercuryChainReader, resolve name collisions - Add tests for errors during ChainReader construction - Propagate InvalidConfig & any other errors back to client We should ignore Unimplemented until node ops have been given ample time to migrate to the new job spec (including a section for ChainReader config) so that we can remove the old product-specific MedianContract component from MedianProvider. All other errors we can immediately start passing back to the client, letting the core node decide how to handle them (eg. displaying an "invalid job spec" message to the UI if the RelayConfig was invalid or the ContractID missing) * Update relay versions * Big migration * Simplify chain reader config validation * Rename MinKey function * Remove big from utils * Fix dependencies * Minor fixes * Fix merge conflicts * Minor fixes --------- Co-authored-by: Domino Valdano <2644901+reductionista@users.noreply.github.com> Co-authored-by: ilija --- core/chains/evm/assets/assets.go | 6 +- core/chains/evm/assets/wei.go | 10 +-- core/chains/evm/client/chain_id_sub.go | 4 +- core/chains/evm/client/chain_id_sub_test.go | 4 +- core/chains/evm/client/client.go | 5 +- core/chains/evm/client/node_lifecycle.go | 5 +- core/chains/evm/client/pool.go | 2 +- core/chains/evm/client/rpc_client.go | 5 +- .../evm/client/send_only_node_lifecycle.go | 2 +- .../evm/client/simulated_backend_client.go | 8 +-- core/chains/evm/config/config_test.go | 16 ++--- core/chains/evm/config/toml/config.go | 8 +-- core/chains/evm/config/toml/defaults.go | 14 ++-- core/chains/evm/forwarders/forwarder.go | 4 +- .../evm/forwarders/forwarder_manager.go | 7 +- .../evm/forwarders/forwarder_manager_test.go | 12 ++-- core/chains/evm/forwarders/mocks/orm.go | 29 ++++---- core/chains/evm/forwarders/orm.go | 14 ++-- core/chains/evm/forwarders/orm_test.go | 4 +- .../evm/gas/block_history_estimator_test.go | 15 ++-- .../evm/headtracker/head_broadcaster_test.go | 5 +- .../evm/headtracker/head_tracker_test.go | 9 +-- core/chains/evm/headtracker/heads_test.go | 5 +- core/chains/evm/headtracker/orm.go | 6 +- core/chains/evm/log/orm.go | 7 +- core/chains/evm/logpoller/log_poller.go | 3 +- core/chains/evm/logpoller/log_poller_test.go | 5 +- core/chains/evm/logpoller/models.go | 6 +- .../evm/logpoller/observability_test.go | 3 +- core/chains/evm/logpoller/orm.go | 16 ++--- core/chains/evm/logpoller/orm_test.go | 69 ++++++++++--------- core/chains/evm/logpoller/query.go | 4 +- core/chains/evm/logpoller/query_test.go | 3 +- core/chains/evm/txmgr/broadcaster_test.go | 3 +- core/chains/evm/txmgr/evm_tx_store.go | 6 +- core/chains/evm/txmgr/resender_test.go | 4 +- core/chains/evm/txmgr/tracker_test.go | 6 +- core/chains/evm/txmgr/txmgr_test.go | 5 +- core/chains/evm/types/models.go | 5 +- core/chains/evm/types/types.go | 3 +- core/{utils => chains/evm/utils/big}/big.go | 21 +++--- .../evm/utils/big}/big_test.go | 14 ++-- core/chains/evm/utils/utils.go | 37 ++++++++++ core/chains/legacyevm/chain.go | 4 +- core/cmd/blocks_commands_test.go | 4 +- core/cmd/eth_keys_commands_test.go | 3 +- core/cmd/evm_chains_commands_test.go | 6 +- core/cmd/evm_transaction_commands.go | 3 +- core/cmd/forwarders_commands.go | 4 +- core/cmd/forwarders_commands_test.go | 3 +- core/cmd/ocr2vrf_configure_commands.go | 3 +- core/cmd/shell_local.go | 3 +- core/internal/cltest/cltest.go | 21 +++--- core/internal/cltest/factories.go | 17 ++--- core/internal/cltest/simulated_backend.go | 6 +- core/internal/features/features_test.go | 5 +- .../features/ocr2/features_ocr2_test.go | 4 +- .../testutils/configtest/general_config.go | 8 +-- core/internal/testutils/evmtest/evmtest.go | 6 +- core/internal/testutils/evmtest/v2/evmtest.go | 4 +- core/scripts/functions/src/fetching.go | 3 +- core/services/blockhashstore/delegate_test.go | 10 +-- core/services/blockhashstore/validate_test.go | 4 +- .../blockheaderfeeder/validate_test.go | 6 +- core/services/chainlink/config.go | 4 +- core/services/chainlink/config_test.go | 11 +-- .../relayer_chain_interoperators_test.go | 6 +- core/services/directrequest/delegate_test.go | 4 +- core/services/directrequest/validate.go | 4 +- core/services/feeds/service.go | 4 +- core/services/feeds/service_test.go | 4 +- core/services/fluxmonitorv2/orm_test.go | 4 +- core/services/job/job_orm_test.go | 14 ++-- core/services/job/mocks/orm.go | 15 ++-- core/services/job/models.go | 29 ++++---- core/services/job/orm.go | 6 +- core/services/keeper/integration_test.go | 8 +-- core/services/keeper/models.go | 9 +-- core/services/keeper/models_test.go | 4 +- core/services/keeper/orm.go | 10 +-- core/services/keeper/orm_test.go | 13 ++-- .../keeper/registry1_1_synchronizer_test.go | 4 +- .../keeper/registry1_3_synchronizer_test.go | 6 +- .../registry_synchronizer_process_logs.go | 20 +++--- .../keeper/registry_synchronizer_sync.go | 17 ++--- .../keeper/registry_synchronizer_sync_test.go | 4 +- core/services/keeper/upkeep_executer_test.go | 15 ++-- .../keeper/upkeep_executer_unit_test.go | 4 +- core/services/keystore/eth_test.go | 3 +- core/services/keystore/keys/ethkey/models.go | 4 +- core/services/ocr/database.go | 6 +- core/services/ocr2/delegate_test.go | 4 +- .../evmregistry/v20/registry_test.go | 6 +- .../v21/logprovider/recoverer_test.go | 4 +- .../evmregistry/v21/registry_test.go | 6 +- .../evmregistry/v21/upkeepstate/orm.go | 12 ++-- .../evmregistry/v21/upkeepstate/orm_test.go | 4 +- .../evmregistry/v21/upkeepstate/store.go | 3 +- .../plugins/ocr2keeper/integration_test.go | 4 +- .../internal/ocr2vrf_integration_test.go | 3 +- .../ocr2/plugins/s4/integration_test.go | 6 +- core/services/ocr2/plugins/s4/messages.go | 6 +- core/services/ocr2/plugins/s4/plugin.go | 4 +- core/services/ocr2/plugins/s4/plugin_test.go | 8 +-- core/services/ocrcommon/telemetry_test.go | 5 +- core/services/pipeline/orm_test.go | 5 +- .../promreporter/prom_reporter_test.go | 3 +- core/services/relay/evm/median_test.go | 4 +- .../relay/evm/relayer_extender_test.go | 4 +- core/services/relay/evm/types/types.go | 4 +- core/services/relay/evm/types/types_test.go | 2 +- core/services/s4/address_range.go | 28 ++++---- core/services/s4/address_range_test.go | 8 +-- core/services/s4/in_memory_orm.go | 6 +- core/services/s4/in_memory_orm_test.go | 16 ++--- core/services/s4/mocks/orm.go | 15 ++-- core/services/s4/orm.go | 10 +-- core/services/s4/postgres_orm.go | 4 +- core/services/s4/postgres_orm_test.go | 4 +- core/services/s4/storage.go | 7 +- core/services/s4/storage_test.go | 9 +-- core/services/vrf/delegate_test.go | 2 +- core/services/vrf/v1/integration_test.go | 6 +- .../vrf/v2/integration_helpers_test.go | 5 +- core/services/vrf/v2/integration_v2_test.go | 3 +- .../vrf/v2/listener_v2_log_listener_test.go | 5 +- core/store/migrate/migrate_test.go | 8 +-- core/store/models/common.go | 4 +- core/web/eth_keys_controller.go | 4 +- core/web/evm_chains_controller_test.go | 10 +-- core/web/evm_forwarders_controller.go | 4 +- core/web/evm_forwarders_controller_test.go | 5 +- core/web/evm_transfer_controller_test.go | 18 ++--- core/web/loader/loader_test.go | 5 +- core/web/presenters/eth_key.go | 8 +-- core/web/presenters/eth_key_test.go | 8 +-- core/web/presenters/eth_tx.go | 8 +-- core/web/presenters/evm_forwarder.go | 4 +- core/web/presenters/job.go | 16 ++--- core/web/presenters/job_test.go | 8 +-- core/web/replay_controller.go | 8 +-- core/web/resolver/chain_test.go | 6 +- core/web/resolver/eth_key_test.go | 22 +++--- core/web/resolver/eth_transaction_test.go | 4 +- core/web/resolver/node_test.go | 4 +- core/web/resolver/spec_test.go | 18 ++--- integration-tests/client/chainlink_models.go | 6 +- integration-tests/types/config/node/core.go | 5 +- 148 files changed, 636 insertions(+), 550 deletions(-) rename core/{utils => chains/evm/utils/big}/big.go (91%) rename core/{utils => chains/evm/utils/big}/big_test.go (95%) create mode 100644 core/chains/evm/utils/utils.go diff --git a/core/chains/evm/assets/assets.go b/core/chains/evm/assets/assets.go index 377e92a855a..738ba5c817b 100644 --- a/core/chains/evm/assets/assets.go +++ b/core/chains/evm/assets/assets.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/shopspring/decimal" ) @@ -108,10 +108,10 @@ func (e *Eth) ToInt() *big.Int { // Scan reads the database value and returns an instance. func (e *Eth) Scan(value interface{}) error { - return (*utils.Big)(e).Scan(value) + return (*ubig.Big)(e).Scan(value) } // Value returns the Eth value for serialization to database. func (e Eth) Value() (driver.Value, error) { - return (utils.Big)(e).Value() + return (ubig.Big)(e).Value() } diff --git a/core/chains/evm/assets/wei.go b/core/chains/evm/assets/wei.go index be0143b3a86..8bacabfdb4a 100644 --- a/core/chains/evm/assets/wei.go +++ b/core/chains/evm/assets/wei.go @@ -11,7 +11,7 @@ import ( "golang.org/x/exp/constraints" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) const ( @@ -58,10 +58,10 @@ func suffixExp(suf string) int32 { } } -// Wei extends utils.Big to implement encoding.TextMarshaler and +// Wei extends ubig.Big to implement encoding.TextMarshaler and // encoding.TextUnmarshaler with support for unit suffixes, as well as // additional functions -type Wei utils.Big +type Wei ubig.Big func MaxWei(w, x *Wei) *Wei { return NewWei(bigmath.Max(w.ToInt(), x.ToInt())) @@ -271,10 +271,10 @@ func (w *Wei) AddPercentage(percentage uint16) *Wei { // Scan reads the database value and returns an instance. func (w *Wei) Scan(value interface{}) error { - return (*utils.Big)(w).Scan(value) + return (*ubig.Big)(w).Scan(value) } // Value returns this instance serialized for database storage. func (w Wei) Value() (driver.Value, error) { - return (utils.Big)(w).Value() + return (ubig.Big)(w).Value() } diff --git a/core/chains/evm/client/chain_id_sub.go b/core/chains/evm/client/chain_id_sub.go index 8ea4e207970..c3162b300c7 100644 --- a/core/chains/evm/client/chain_id_sub.go +++ b/core/chains/evm/client/chain_id_sub.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) var _ ethereum.Subscription = &chainIDSubForwarder{} @@ -64,7 +64,7 @@ func (c *chainIDSubForwarder) forwardLoop() { return case h := <-c.srcCh: - h.EVMChainID = utils.NewBig(c.chainID) + h.EVMChainID = ubig.New(c.chainID) select { case c.destCh <- h: case <-c.unSub: diff --git a/core/chains/evm/client/chain_id_sub_test.go b/core/chains/evm/client/chain_id_sub_test.go index 211ba812fbe..c71b45c489e 100644 --- a/core/chains/evm/client/chain_id_sub_test.go +++ b/core/chains/evm/client/chain_id_sub_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) type mockSubscription struct { @@ -111,7 +111,7 @@ func TestChainIDSubForwarder(t *testing.T) { forwarder.srcCh <- head receivedHead := <-ch assert.Equal(t, head, receivedHead) - assert.Equal(t, utils.NewBig(chainID), receivedHead.EVMChainID) + assert.Equal(t, ubig.New(chainID), receivedHead.EVMChainID) expectedErr := errors.New("error") sub.Errors <- expectedErr diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index 688cc3c9bea..b85331a62a1 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/config" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum" @@ -286,7 +287,7 @@ func (client *client) HeadByNumber(ctx context.Context, number *big.Int) (head * err = ethereum.NotFound return } - head.EVMChainID = utils.NewBig(client.ConfiguredChainID()) + head.EVMChainID = ubig.New(client.ConfiguredChainID()) return } @@ -299,7 +300,7 @@ func (client *client) HeadByHash(ctx context.Context, hash common.Hash) (head *e err = ethereum.NotFound return } - head.EVMChainID = utils.NewBig(client.ConfiguredChainID()) + head.EVMChainID = ubig.New(client.ConfiguredChainID()) return } diff --git a/core/chains/evm/client/node_lifecycle.go b/core/chains/evm/client/node_lifecycle.go index f838325a646..4e984de00f0 100644 --- a/core/chains/evm/client/node_lifecycle.go +++ b/core/chains/evm/client/node_lifecycle.go @@ -12,10 +12,11 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-common/pkg/logger" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) var ( @@ -50,7 +51,7 @@ func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration { if interval <= 0 || interval > queryTimeout { interval = queryTimeout } - return utils.WithJitter(interval) + return cutils.WithJitter(interval) } func (n *node) setLatestReceived(blockNumber int64, totalDifficulty *big.Int) { diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 6c36cc3e987..afe592533c3 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -17,10 +17,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/common/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 01851c4ae90..3cc90c4d8d9 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -24,6 +24,7 @@ import ( commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -491,7 +492,7 @@ func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *e err = ethereum.NotFound return } - head.EVMChainID = utils.NewBig(r.chainID) + head.EVMChainID = ubig.New(r.chainID) return } @@ -504,7 +505,7 @@ func (r *rpcClient) BlockByHash(ctx context.Context, hash common.Hash) (head *ev err = ethereum.NotFound return } - head.EVMChainID = utils.NewBig(r.chainID) + head.EVMChainID = ubig.New(r.chainID) return } diff --git a/core/chains/evm/client/send_only_node_lifecycle.go b/core/chains/evm/client/send_only_node_lifecycle.go index 9d704e49389..127a5c6678c 100644 --- a/core/chains/evm/client/send_only_node_lifecycle.go +++ b/core/chains/evm/client/send_only_node_lifecycle.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) // verifyLoop may only be triggered once, on Start, if initial chain ID check diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 293bf64badc..bd2e959d9bc 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -23,7 +23,7 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) func init() { @@ -197,7 +197,7 @@ func (c *SimulatedBackendClient) HeadByNumber(ctx context.Context, n *big.Int) ( return nil, ethereum.NotFound } return &evmtypes.Head{ - EVMChainID: utils.NewBigI(c.chainId.Int64()), + EVMChainID: ubig.NewI(c.chainId.Int64()), Hash: header.Hash(), Number: header.Number.Int64(), ParentHash: header.ParentHash, @@ -214,7 +214,7 @@ func (c *SimulatedBackendClient) HeadByHash(ctx context.Context, h common.Hash) return nil, ethereum.NotFound } return &evmtypes.Head{ - EVMChainID: utils.NewBigI(c.chainId.Int64()), + EVMChainID: ubig.NewI(c.chainId.Int64()), Hash: header.Hash(), Number: header.Number.Int64(), ParentHash: header.ParentHash, @@ -302,7 +302,7 @@ func (c *SimulatedBackendClient) SubscribeNewHead( case h := <-ch: var head *evmtypes.Head if h != nil { - head = &evmtypes.Head{Difficulty: h.Difficulty, Timestamp: time.Unix(int64(h.Time), 0), Number: h.Number.Int64(), Hash: h.Hash(), ParentHash: h.ParentHash, Parent: lastHead, EVMChainID: utils.NewBig(c.chainId)} + head = &evmtypes.Head{Difficulty: h.Difficulty, Timestamp: time.Unix(int64(h.Time), 0), Number: h.Number.Int64(), Hash: h.Hash(), ParentHash: h.ParentHash, Parent: lastHead, EVMChainID: ubig.New(c.chainId)} lastHead = head } select { diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index d34d1eae63e..0127328239a 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -14,6 +14,7 @@ import ( commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -21,13 +22,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestChainScopedConfig(t *testing.T) { t.Parallel() gcfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - id := utils.NewBig(big.NewInt(rand.Int63())) + id := ubig.New(big.NewInt(rand.Int63())) c.EVM[0] = &toml.EVMConfig{ ChainID: id, Chain: toml.Defaults(id, &toml.Chain{ @@ -38,7 +38,7 @@ func TestChainScopedConfig(t *testing.T) { cfg := evmtest.NewChainScopedConfig(t, gcfg) overrides := func(c *chainlink.Config, s *chainlink.Secrets) { - id := utils.NewBig(big.NewInt(rand.Int63())) + id := ubig.New(big.NewInt(rand.Int63())) c.EVM[0] = &toml.EVMConfig{ ChainID: id, Chain: toml.Defaults(id, &toml.Chain{ @@ -65,7 +65,7 @@ func TestChainScopedConfig(t *testing.T) { t.Run("uses customer configured value when set", func(t *testing.T) { var override uint32 = 10 gasBumpOverrides := func(c *chainlink.Config, s *chainlink.Secrets) { - id := utils.NewBig(big.NewInt(rand.Int63())) + id := ubig.New(big.NewInt(rand.Int63())) c.EVM[0] = &toml.EVMConfig{ ChainID: id, Chain: toml.Defaults(id, &toml.Chain{ @@ -292,7 +292,7 @@ func TestChainScopedConfig_GasEstimator(t *testing.T) { func TestChainScopedConfig_BSCDefaults(t *testing.T) { chainID := big.NewInt(56) gcfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, secrets *chainlink.Secrets) { - id := utils.NewBig(chainID) + id := ubig.New(chainID) cfg := toml.Defaults(id) c.EVM[0] = &toml.EVMConfig{ ChainID: id, @@ -344,7 +344,7 @@ func TestChainScopedConfig_Profiles(t *testing.T) { t.Parallel() gcfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, secrets *chainlink.Secrets) { - id := utils.NewBigI(tt.chainID) + id := ubig.NewI(tt.chainID) cfg := toml.Defaults(id) c.EVM[0] = &toml.EVMConfig{ ChainID: id, @@ -379,7 +379,7 @@ func TestChainScopedConfig_HeadTracker(t *testing.T) { func Test_chainScopedConfig_Validate(t *testing.T) { configWithChains := func(t *testing.T, id int64, chains ...*toml.Chain) config.AppConfig { return configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - chainID := utils.NewBigI(id) + chainID := ubig.NewI(id) c.EVM[0] = &toml.EVMConfig{ChainID: chainID, Enabled: ptr(true), Chain: toml.Defaults(chainID, chains...), Nodes: toml.EVMNodes{{ Name: ptr("fake"), @@ -462,7 +462,7 @@ func Test_chainScopedConfig_Validate(t *testing.T) { func TestNodePoolConfig(t *testing.T) { gcfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - id := utils.NewBig(big.NewInt(rand.Int63())) + id := ubig.New(big.NewInt(rand.Int63())) c.EVM[0] = &toml.EVMConfig{ ChainID: id, Chain: toml.Defaults(id, &toml.Chain{}), diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 9e51d5be790..16ad74dbca2 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -19,10 +19,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "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/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) @@ -161,7 +161,7 @@ func (cs EVMConfigs) NodeStatus(name string) (commontypes.NodeStatus, error) { return commontypes.NodeStatus{}, fmt.Errorf("node %s: %w", name, chains.ErrNotFound) } -func legacyNode(n *Node, chainID *utils.Big) (v2 types.Node) { +func legacyNode(n *Node, chainID *big.Big) (v2 types.Node) { v2.Name = *n.Name v2.EVMChainID = *chainID if n.HTTPURL != nil { @@ -215,7 +215,7 @@ func (cs EVMConfigs) Nodes(chainID relay.ChainID) (ns []types.Node, err error) { continue } - ns = append(ns, legacyNode(n, utils.NewBigI(evmID))) + ns = append(ns, legacyNode(n, big.NewI(evmID))) } return } @@ -268,7 +268,7 @@ func (ns *EVMNodes) SetFrom(fs *EVMNodes) { } type EVMConfig struct { - ChainID *utils.Big + ChainID *big.Big Enabled *bool Chain Nodes EVMNodes diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index d362e9ac3dc..27127993d8a 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/smartcontractkit/chainlink/v2/common/config" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) @@ -21,7 +21,7 @@ var ( defaultNames = map[string]string{} // DefaultIDs is the set of chain ids which have defaults. - DefaultIDs []*utils.Big + DefaultIDs []*big.Big ) func init() { @@ -36,7 +36,7 @@ func init() { log.Fatalf("failed to read %q: %v", path, err) } var config = struct { - ChainID *utils.Big + ChainID *big.Big Chain }{} @@ -61,13 +61,13 @@ func init() { defaults[id] = config.Chain defaultNames[id] = strings.ReplaceAll(strings.TrimSuffix(fe.Name(), ".toml"), "_", " ") } - slices.SortFunc(DefaultIDs, func(a, b *utils.Big) int { + slices.SortFunc(DefaultIDs, func(a, b *big.Big) int { return a.Cmp(b) }) } // DefaultsNamed returns the default Chain values, optionally for the given chainID, as well as a name if the chainID is known. -func DefaultsNamed(chainID *utils.Big) (c Chain, name string) { +func DefaultsNamed(chainID *big.Big) (c Chain, name string) { c.SetFrom(&fallback) if chainID == nil { return @@ -82,7 +82,7 @@ func DefaultsNamed(chainID *utils.Big) (c Chain, name string) { // Defaults returns a Chain based on the defaults for chainID and fields from with, applied in order so later Chains // override earlier ones. -func Defaults(chainID *utils.Big, with ...*Chain) Chain { +func Defaults(chainID *big.Big, with ...*Chain) Chain { c, _ := DefaultsNamed(chainID) for _, w := range with { c.SetFrom(w) @@ -90,7 +90,7 @@ func Defaults(chainID *utils.Big, with ...*Chain) Chain { return c } -func ChainTypeForID(chainID *utils.Big) (config.ChainType, bool) { +func ChainTypeForID(chainID *big.Big) (config.ChainType, bool) { s := chainID.String() if d, ok := defaults[s]; ok { if d.ChainType == nil { diff --git a/core/chains/evm/forwarders/forwarder.go b/core/chains/evm/forwarders/forwarder.go index 0221c39e0a6..a0f6334a2c7 100644 --- a/core/chains/evm/forwarders/forwarder.go +++ b/core/chains/evm/forwarders/forwarder.go @@ -5,14 +5,14 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) // Forwarder is the struct for Forwarder Addresses type Forwarder struct { ID int64 Address common.Address - EVMChainID utils.Big + EVMChainID big.Big CreatedAt time.Time UpdatedAt time.Time } diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index eaf0c32afe3..03792966fef 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -14,15 +14,16 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmlogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "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/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/offchain_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var forwardABI = evmtypes.MustGetABI(authorized_forwarder.AuthorizedForwarderABI).Methods["forward"] @@ -79,7 +80,7 @@ func (f *FwdMgr) Start(ctx context.Context) error { f.logger.Debug("Initializing EVM forwarder manager") chainId := f.evmClient.ConfiguredChainID() - fwdrs, err := f.ORM.FindForwardersByChain(utils.Big(*chainId)) + fwdrs, err := f.ORM.FindForwardersByChain(big.Big(*chainId)) if err != nil { return errors.Wrapf(err, "Failed to retrieve forwarders for chain %d", chainId) } @@ -112,7 +113,7 @@ func FilterName(addr common.Address) string { func (f *FwdMgr) ForwarderFor(addr common.Address) (forwarder common.Address, err error) { // Gets forwarders for current chain. - fwdrs, err := f.ORM.FindForwardersByChain(utils.Big(*f.evmClient.ConfiguredChainID())) + fwdrs, err := f.ORM.FindForwardersByChain(big.Big(*f.evmClient.ConfiguredChainID())) if err != nil { return common.Address{}, err } diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 1da638e743d..5ef150aa5c3 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -12,10 +12,13 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" @@ -24,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var GetAuthorisedSendersABI = evmtypes.MustGetABI(authorized_receiver.AuthorizedReceiverABI).Methods["getAuthorizedSenders"] @@ -62,9 +64,9 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) - fwd, err := fwdMgr.ORM.CreateForwarder(forwarderAddr, utils.Big(*testutils.FixtureChainID)) + fwd, err := fwdMgr.ORM.CreateForwarder(forwarderAddr, ubig.Big(*testutils.FixtureChainID)) require.NoError(t, err) - lst, err := fwdMgr.ORM.FindForwardersByChain(utils.Big(*testutils.FixtureChainID)) + lst, err := fwdMgr.ORM.FindForwardersByChain(ubig.Big(*testutils.FixtureChainID)) require.NoError(t, err) require.Equal(t, len(lst), 1) require.Equal(t, lst[0].Address, forwarderAddr) @@ -115,9 +117,9 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) - _, err = fwdMgr.ORM.CreateForwarder(forwarderAddr, utils.Big(*testutils.FixtureChainID)) + _, err = fwdMgr.ORM.CreateForwarder(forwarderAddr, ubig.Big(*testutils.FixtureChainID)) require.NoError(t, err) - lst, err := fwdMgr.ORM.FindForwardersByChain(utils.Big(*testutils.FixtureChainID)) + lst, err := fwdMgr.ORM.FindForwardersByChain(ubig.Big(*testutils.FixtureChainID)) require.NoError(t, err) require.Equal(t, len(lst), 1) require.Equal(t, lst[0].Address, forwarderAddr) diff --git a/core/chains/evm/forwarders/mocks/orm.go b/core/chains/evm/forwarders/mocks/orm.go index c06a04b8eb1..691fbce8e9c 100644 --- a/core/chains/evm/forwarders/mocks/orm.go +++ b/core/chains/evm/forwarders/mocks/orm.go @@ -4,12 +4,13 @@ package mocks import ( common "github.com/ethereum/go-ethereum/common" + big "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + forwarders "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + mock "github.com/stretchr/testify/mock" pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" - - utils "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ORM is an autogenerated mock type for the ORM type @@ -18,7 +19,7 @@ type ORM struct { } // CreateForwarder provides a mock function with given fields: addr, evmChainId -func (_m *ORM) CreateForwarder(addr common.Address, evmChainId utils.Big) (forwarders.Forwarder, error) { +func (_m *ORM) CreateForwarder(addr common.Address, evmChainId big.Big) (forwarders.Forwarder, error) { ret := _m.Called(addr, evmChainId) if len(ret) == 0 { @@ -27,16 +28,16 @@ func (_m *ORM) CreateForwarder(addr common.Address, evmChainId utils.Big) (forwa var r0 forwarders.Forwarder var r1 error - if rf, ok := ret.Get(0).(func(common.Address, utils.Big) (forwarders.Forwarder, error)); ok { + if rf, ok := ret.Get(0).(func(common.Address, big.Big) (forwarders.Forwarder, error)); ok { return rf(addr, evmChainId) } - if rf, ok := ret.Get(0).(func(common.Address, utils.Big) forwarders.Forwarder); ok { + if rf, ok := ret.Get(0).(func(common.Address, big.Big) forwarders.Forwarder); ok { r0 = rf(addr, evmChainId) } else { r0 = ret.Get(0).(forwarders.Forwarder) } - if rf, ok := ret.Get(1).(func(common.Address, utils.Big) error); ok { + if rf, ok := ret.Get(1).(func(common.Address, big.Big) error); ok { r1 = rf(addr, evmChainId) } else { r1 = ret.Error(1) @@ -101,7 +102,7 @@ func (_m *ORM) FindForwarders(offset int, limit int) ([]forwarders.Forwarder, in } // FindForwardersByChain provides a mock function with given fields: evmChainId -func (_m *ORM) FindForwardersByChain(evmChainId utils.Big) ([]forwarders.Forwarder, error) { +func (_m *ORM) FindForwardersByChain(evmChainId big.Big) ([]forwarders.Forwarder, error) { ret := _m.Called(evmChainId) if len(ret) == 0 { @@ -110,10 +111,10 @@ func (_m *ORM) FindForwardersByChain(evmChainId utils.Big) ([]forwarders.Forward var r0 []forwarders.Forwarder var r1 error - if rf, ok := ret.Get(0).(func(utils.Big) ([]forwarders.Forwarder, error)); ok { + if rf, ok := ret.Get(0).(func(big.Big) ([]forwarders.Forwarder, error)); ok { return rf(evmChainId) } - if rf, ok := ret.Get(0).(func(utils.Big) []forwarders.Forwarder); ok { + if rf, ok := ret.Get(0).(func(big.Big) []forwarders.Forwarder); ok { r0 = rf(evmChainId) } else { if ret.Get(0) != nil { @@ -121,7 +122,7 @@ func (_m *ORM) FindForwardersByChain(evmChainId utils.Big) ([]forwarders.Forward } } - if rf, ok := ret.Get(1).(func(utils.Big) error); ok { + if rf, ok := ret.Get(1).(func(big.Big) error); ok { r1 = rf(evmChainId) } else { r1 = ret.Error(1) @@ -131,7 +132,7 @@ func (_m *ORM) FindForwardersByChain(evmChainId utils.Big) ([]forwarders.Forward } // FindForwardersInListByChain provides a mock function with given fields: evmChainId, addrs -func (_m *ORM) FindForwardersInListByChain(evmChainId utils.Big, addrs []common.Address) ([]forwarders.Forwarder, error) { +func (_m *ORM) FindForwardersInListByChain(evmChainId big.Big, addrs []common.Address) ([]forwarders.Forwarder, error) { ret := _m.Called(evmChainId, addrs) if len(ret) == 0 { @@ -140,10 +141,10 @@ func (_m *ORM) FindForwardersInListByChain(evmChainId utils.Big, addrs []common. var r0 []forwarders.Forwarder var r1 error - if rf, ok := ret.Get(0).(func(utils.Big, []common.Address) ([]forwarders.Forwarder, error)); ok { + if rf, ok := ret.Get(0).(func(big.Big, []common.Address) ([]forwarders.Forwarder, error)); ok { return rf(evmChainId, addrs) } - if rf, ok := ret.Get(0).(func(utils.Big, []common.Address) []forwarders.Forwarder); ok { + if rf, ok := ret.Get(0).(func(big.Big, []common.Address) []forwarders.Forwarder); ok { r0 = rf(evmChainId, addrs) } else { if ret.Get(0) != nil { @@ -151,7 +152,7 @@ func (_m *ORM) FindForwardersInListByChain(evmChainId utils.Big, addrs []common. } } - if rf, ok := ret.Get(1).(func(utils.Big, []common.Address) error); ok { + if rf, ok := ret.Get(1).(func(big.Big, []common.Address) error); ok { r1 = rf(evmChainId, addrs) } else { r1 = ret.Error(1) diff --git a/core/chains/evm/forwarders/orm.go b/core/chains/evm/forwarders/orm.go index 104e2574252..2a455360190 100644 --- a/core/chains/evm/forwarders/orm.go +++ b/core/chains/evm/forwarders/orm.go @@ -8,18 +8,18 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore type ORM interface { - CreateForwarder(addr common.Address, evmChainId utils.Big) (fwd Forwarder, err error) + CreateForwarder(addr common.Address, evmChainId big.Big) (fwd Forwarder, err error) FindForwarders(offset, limit int) ([]Forwarder, int, error) - FindForwardersByChain(evmChainId utils.Big) ([]Forwarder, error) + FindForwardersByChain(evmChainId big.Big) ([]Forwarder, error) DeleteForwarder(id int64, cleanup func(tx pg.Queryer, evmChainId int64, addr common.Address) error) error - FindForwardersInListByChain(evmChainId utils.Big, addrs []common.Address) ([]Forwarder, error) + FindForwardersInListByChain(evmChainId big.Big, addrs []common.Address) ([]Forwarder, error) } type orm struct { @@ -33,7 +33,7 @@ func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) *orm { } // CreateForwarder creates the Forwarder address associated with the current EVM chain id. -func (o *orm) CreateForwarder(addr common.Address, evmChainId utils.Big) (fwd Forwarder, err error) { +func (o *orm) CreateForwarder(addr common.Address, evmChainId big.Big) (fwd Forwarder, err error) { sql := `INSERT INTO evm.forwarders (address, evm_chain_id, created_at, updated_at) VALUES ($1, $2, now(), now()) RETURNING *` err = o.q.Get(&fwd, sql, addr, evmChainId) return fwd, err @@ -93,13 +93,13 @@ func (o *orm) FindForwarders(offset, limit int) (fwds []Forwarder, count int, er } // FindForwardersByChain returns all forwarder addresses for a chain. -func (o *orm) FindForwardersByChain(evmChainId utils.Big) (fwds []Forwarder, err error) { +func (o *orm) FindForwardersByChain(evmChainId big.Big) (fwds []Forwarder, err error) { sql := `SELECT * FROM evm.forwarders where evm_chain_id = $1 ORDER BY created_at DESC, id DESC` err = o.q.Select(&fwds, sql, evmChainId) return } -func (o *orm) FindForwardersInListByChain(evmChainId utils.Big, addrs []common.Address) ([]Forwarder, error) { +func (o *orm) FindForwardersInListByChain(evmChainId big.Big, addrs []common.Address) ([]Forwarder, error) { var fwdrs []Forwarder arg := map[string]interface{}{ diff --git a/core/chains/evm/forwarders/orm_test.go b/core/chains/evm/forwarders/orm_test.go index ba9664c196a..e95ac3778c6 100644 --- a/core/chains/evm/forwarders/orm_test.go +++ b/core/chains/evm/forwarders/orm_test.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/jmoiron/sqlx" ) @@ -42,7 +42,7 @@ func Test_DeleteForwarder(t *testing.T) { addr := testutils.NewAddress() chainID := testutils.FixtureChainID - fwd, err := orm.CreateForwarder(addr, *utils.NewBig(chainID)) + fwd, err := orm.CreateForwarder(addr, *big.New(chainID)) require.NoError(t, err) assert.Equal(t, addr, fwd.Address) diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index d3edf212b6a..43ea3e0bb79 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -25,6 +25,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" @@ -429,7 +430,7 @@ func TestBlockHistoryEstimator_FetchBlocks(t *testing.T) { elems[1].Result = &b42 }) - head := evmtypes.NewHead(big.NewInt(44), b44.Hash, b43.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) + head := evmtypes.NewHead(big.NewInt(44), b44.Hash, b43.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) err = bhe.FetchBlocks(testutils.Context(t), &head) require.NoError(t, err) @@ -493,8 +494,8 @@ func TestBlockHistoryEstimator_FetchBlocks(t *testing.T) { elems[1].Result = &b2 }) - head2 := evmtypes.NewHead(big.NewInt(2), b2.Hash, b1.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) - head3 := evmtypes.NewHead(big.NewInt(3), b3.Hash, b2.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) + head2 := evmtypes.NewHead(big.NewInt(2), b2.Hash, b1.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) + head3 := evmtypes.NewHead(big.NewInt(3), b3.Hash, b2.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) head3.Parent = &head2 err := bhe.FetchBlocks(testutils.Context(t), &head3) require.NoError(t, err) @@ -546,8 +547,8 @@ func TestBlockHistoryEstimator_FetchBlocks(t *testing.T) { gas.SetRollingBlockHistory(bhe, blocks) // RE-ORG, head2 and head3 have different hash than saved b2 and b3 - head2 := evmtypes.NewHead(big.NewInt(2), utils.NewHash(), b1.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) - head3 := evmtypes.NewHead(big.NewInt(3), utils.NewHash(), head2.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) + head2 := evmtypes.NewHead(big.NewInt(2), utils.NewHash(), b1.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) + head3 := evmtypes.NewHead(big.NewInt(3), utils.NewHash(), head2.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) head3.Parent = &head2 ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { @@ -617,8 +618,8 @@ func TestBlockHistoryEstimator_FetchBlocks(t *testing.T) { gas.SetRollingBlockHistory(bhe, blocks) // head2 and head3 have identical hash to saved blocks - head2 := evmtypes.NewHead(big.NewInt(2), b2.Hash, b1.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) - head3 := evmtypes.NewHead(big.NewInt(3), b3.Hash, head2.Hash, uint64(time.Now().Unix()), utils.NewBig(&cltest.FixtureChainID)) + head2 := evmtypes.NewHead(big.NewInt(2), b2.Hash, b1.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) + head3 := evmtypes.NewHead(big.NewInt(3), b3.Hash, head2.Hash, uint64(time.Now().Unix()), ubig.New(&cltest.FixtureChainID)) head3.Parent = &head2 err := bhe.FetchBlocks(testutils.Context(t), &head3) diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index b9fab9cdd48..21c864eda69 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "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/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -84,7 +85,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { assert.Equal(t, (*evmtypes.Head)(nil), latest1) headers := <-chchHeaders - h := evmtypes.Head{Number: 1, Hash: utils.NewHash(), ParentHash: utils.NewHash(), EVMChainID: utils.NewBig(&cltest.FixtureChainID)} + h := evmtypes.Head{Number: 1, Hash: utils.NewHash(), ParentHash: utils.NewHash(), EVMChainID: big.New(&cltest.FixtureChainID)} headers <- &h g.Eventually(checker1.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) @@ -95,7 +96,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { unsubscribe1() - headers <- &evmtypes.Head{Number: 2, Hash: utils.NewHash(), ParentHash: h.Hash, EVMChainID: utils.NewBig(&cltest.FixtureChainID)} + headers <- &evmtypes.Head{Number: 2, Hash: utils.NewHash(), ParentHash: h.Hash, EVMChainID: big.New(&cltest.FixtureChainID)} g.Eventually(checker2.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) } diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index d8abb1328ac..67e76aee52b 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -29,6 +29,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -258,7 +259,7 @@ func TestHeadTracker_CallsHeadTrackableCallbacks(t *testing.T) { assert.Equal(t, int32(0), checker.OnNewLongestChainCount()) headers := <-chchHeaders - headers.TrySend(&evmtypes.Head{Number: 1, Hash: utils.NewHash(), EVMChainID: utils.NewBig(&cltest.FixtureChainID)}) + headers.TrySend(&evmtypes.Head{Number: 1, Hash: utils.NewHash(), EVMChainID: ubig.New(&cltest.FixtureChainID)}) g.Eventually(checker.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) ht.Stop(t) @@ -719,7 +720,7 @@ func TestHeadTracker_Backfill(t *testing.T) { ParentHash: gethCommon.BigToHash(big.NewInt(0)), Time: now, } - head0 := evmtypes.NewHead(gethHead0.Number, utils.NewHash(), gethHead0.ParentHash, gethHead0.Time, utils.NewBig(&cltest.FixtureChainID)) + head0 := evmtypes.NewHead(gethHead0.Number, utils.NewHash(), gethHead0.ParentHash, gethHead0.Time, ubig.New(&cltest.FixtureChainID)) h1 := *cltest.Head(1) h1.ParentHash = head0.Hash @@ -729,7 +730,7 @@ func TestHeadTracker_Backfill(t *testing.T) { ParentHash: utils.NewHash(), Time: now, } - head8 := evmtypes.NewHead(gethHead8.Number, utils.NewHash(), gethHead8.ParentHash, gethHead8.Time, utils.NewBig(&cltest.FixtureChainID)) + head8 := evmtypes.NewHead(gethHead8.Number, utils.NewHash(), gethHead8.ParentHash, gethHead8.Time, ubig.New(&cltest.FixtureChainID)) h9 := *cltest.Head(9) h9.ParentHash = head8.Hash @@ -739,7 +740,7 @@ func TestHeadTracker_Backfill(t *testing.T) { ParentHash: h9.Hash, Time: now, } - head10 := evmtypes.NewHead(gethHead10.Number, utils.NewHash(), gethHead10.ParentHash, gethHead10.Time, utils.NewBig(&cltest.FixtureChainID)) + head10 := evmtypes.NewHead(gethHead10.Number, utils.NewHash(), gethHead10.ParentHash, gethHead10.Time, ubig.New(&cltest.FixtureChainID)) h11 := *cltest.Head(11) h11.ParentHash = head10.Hash diff --git a/core/chains/evm/headtracker/heads_test.go b/core/chains/evm/headtracker/heads_test.go index 7da6b1cc9ba..11c1bfd4f7a 100644 --- a/core/chains/evm/headtracker/heads_test.go +++ b/core/chains/evm/headtracker/heads_test.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -77,11 +78,11 @@ func TestHeads_AddHeads(t *testing.T) { var parentHash common.Hash for i := 0; i < 5; i++ { hash := utils.NewHash() - h := evmtypes.NewHead(big.NewInt(int64(i)), hash, parentHash, uint64(time.Now().Unix()), utils.NewBigI(0)) + h := evmtypes.NewHead(big.NewInt(int64(i)), hash, parentHash, uint64(time.Now().Unix()), ubig.NewI(0)) testHeads = append(testHeads, &h) if i == 2 { // uncled block - h := evmtypes.NewHead(big.NewInt(int64(i)), uncleHash, parentHash, uint64(time.Now().Unix()), utils.NewBigI(0)) + h := evmtypes.NewHead(big.NewInt(int64(i)), uncleHash, parentHash, uint64(time.Now().Unix()), ubig.NewI(0)) testHeads = append(testHeads, &h) } parentHash = hash diff --git a/core/chains/evm/headtracker/orm.go b/core/chains/evm/headtracker/orm.go index 88d569b9a21..859f6764b63 100644 --- a/core/chains/evm/headtracker/orm.go +++ b/core/chains/evm/headtracker/orm.go @@ -12,8 +12,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ORM interface { @@ -32,11 +32,11 @@ type ORM interface { type orm struct { q pg.Q - chainID utils.Big + chainID ubig.Big } func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, chainID big.Int) ORM { - return &orm{pg.NewQ(db, logger.Named(lggr, "HeadTrackerORM"), cfg), utils.Big(chainID)} + return &orm{pg.NewQ(db, logger.Named(lggr, "HeadTrackerORM"), cfg), ubig.Big(chainID)} } func (orm *orm) IdempotentInsertHead(ctx context.Context, head *evmtypes.Head) error { diff --git a/core/chains/evm/log/orm.go b/core/chains/evm/log/orm.go index a2bcab6e785..3feb4bb836b 100644 --- a/core/chains/evm/log/orm.go +++ b/core/chains/evm/log/orm.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/logger" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -48,13 +49,13 @@ type ORM interface { type orm struct { q pg.Q - evmChainID utils.Big + evmChainID ubig.Big } var _ ORM = (*orm)(nil) func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, evmChainID big.Int) *orm { - return &orm{pg.NewQ(db, lggr, cfg), *utils.NewBig(&evmChainID)} + return &orm{pg.NewQ(db, lggr, cfg), *ubig.New(&evmChainID)} } func (o *orm) WasBroadcastConsumed(blockHash common.Hash, logIndex uint, jobID int32, qopts ...pg.QOpt) (consumed bool, err error) { @@ -128,7 +129,7 @@ func (o *orm) MarkBroadcastsConsumed(blockHashes []common.Hash, blockNumbers []u BlockNumber uint64 `db:"blockNumber"` LogIndex uint `db:"logIndex"` JobID int32 `db:"jobID"` - ChainID utils.Big `db:"chainID"` + ChainID ubig.Big `db:"chainID"` } inputs := make([]input, len(blockHashes)) query := ` diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 1cf1cf42beb..77b80253591 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -26,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -608,7 +609,7 @@ func convertLogs(logs []types.Log, blocks []LogPollerBlock, lggr logger.Logger, blockTimestamp = blocks[i].BlockTimestamp } lgs = append(lgs, Log{ - EvmChainId: utils.NewBig(chainID), + EvmChainId: ubig.New(chainID), LogIndex: int64(l.Index), BlockHash: l.BlockHash, // We assume block numbers fit in int64 diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 6c4dd381b54..ed2d617065b 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -62,7 +63,7 @@ func populateDatabase(t testing.TB, o *logpoller.DbORM, chainID *big.Int) (commo blockTimestamp := startDate.Add(time.Duration(j*1000) * time.Hour) logs = append(logs, logpoller.Log{ - EvmChainId: utils.NewBig(chainID), + EvmChainId: ubig.New(chainID), LogIndex: 1, BlockHash: common.HexToHash(fmt.Sprintf("0x%d", i+(1000*j))), BlockNumber: blockNumber, @@ -1341,7 +1342,7 @@ func TestNotifyAfterInsert(t *testing.T) { require.NoError(t, err) log := logpoller.Log{ - EvmChainId: utils.NewBig(chainID), + EvmChainId: ubig.New(chainID), LogIndex: 10, BlockHash: testutils.Random32Byte(), BlockNumber: 100, diff --git a/core/chains/evm/logpoller/models.go b/core/chains/evm/logpoller/models.go index 87ddd079a5b..c5d6f5eab1c 100644 --- a/core/chains/evm/logpoller/models.go +++ b/core/chains/evm/logpoller/models.go @@ -7,13 +7,13 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/lib/pq" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) // LogPollerBlock represents an unfinalized block // used for reorg detection when polling. type LogPollerBlock struct { - EvmChainId *utils.Big + EvmChainId *big.Big BlockHash common.Hash // Note geth uses int64 internally https://github.com/ethereum/go-ethereum/blob/f66f1a16b3c480d3a43ac7e8a09ab3e362e96ae4/eth/filters/api.go#L340 BlockNumber int64 @@ -24,7 +24,7 @@ type LogPollerBlock struct { // Log represents an EVM log. type Log struct { - EvmChainId *utils.Big + EvmChainId *big.Big LogIndex int64 BlockHash common.Hash BlockNumber int64 diff --git a/core/chains/evm/logpoller/observability_test.go b/core/chains/evm/logpoller/observability_test.go index 76575f46ca4..749934dc4b5 100644 --- a/core/chains/evm/logpoller/observability_test.go +++ b/core/chains/evm/logpoller/observability_test.go @@ -16,6 +16,7 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/smartcontractkit/chainlink-common/pkg/logger" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -126,7 +127,7 @@ func generateRandomLogs(chainId, count int) []Log { logs := make([]Log, count) for i := range logs { logs[i] = Log{ - EvmChainId: utils.NewBigI(int64(chainId)), + EvmChainId: ubig.NewI(int64(chainId)), LogIndex: int64(i + 1), BlockHash: utils.RandomBytes32(), BlockNumber: int64(i + 1), diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index c24d423b253..663c56d10ed 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -12,8 +12,8 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ORM represents the persistent data access layer used by the log poller. At this moment, it's a bit leaky abstraction, because @@ -119,7 +119,7 @@ func (o *DbORM) InsertFilter(filter Filter, qopts ...pg.QOpt) (err error) { // DeleteFilter removes all events,address pairs associated with the Filter func (o *DbORM) DeleteFilter(name string, qopts ...pg.QOpt) error { q := o.q.WithOpts(qopts...) - return q.ExecQ(`DELETE FROM evm.log_poller_filters WHERE name = $1 AND evm_chain_id = $2`, name, utils.NewBig(o.chainID)) + return q.ExecQ(`DELETE FROM evm.log_poller_filters WHERE name = $1 AND evm_chain_id = $2`, name, ubig.New(o.chainID)) } // LoadFiltersForChain returns all filters for this chain @@ -131,7 +131,7 @@ func (o *DbORM) LoadFilters(qopts ...pg.QOpt) (map[string]Filter, error) { ARRAY_AGG(DISTINCT event)::BYTEA[] AS event_sigs, MAX(retention) AS retention FROM evm.log_poller_filters WHERE evm_chain_id = $1 - GROUP BY name`, utils.NewBig(o.chainID)) + GROUP BY name`, ubig.New(o.chainID)) filters := make(map[string]Filter) for _, filter := range rows { filters[filter.Name] = filter @@ -143,7 +143,7 @@ func (o *DbORM) LoadFilters(qopts ...pg.QOpt) (map[string]Filter, error) { func (o *DbORM) SelectBlockByHash(hash common.Hash, qopts ...pg.QOpt) (*LogPollerBlock, error) { q := o.q.WithOpts(qopts...) var b LogPollerBlock - if err := q.Get(&b, `SELECT * FROM evm.log_poller_blocks WHERE block_hash = $1 AND evm_chain_id = $2`, hash, utils.NewBig(o.chainID)); err != nil { + if err := q.Get(&b, `SELECT * FROM evm.log_poller_blocks WHERE block_hash = $1 AND evm_chain_id = $2`, hash, ubig.New(o.chainID)); err != nil { return nil, err } return &b, nil @@ -152,7 +152,7 @@ func (o *DbORM) SelectBlockByHash(hash common.Hash, qopts ...pg.QOpt) (*LogPolle func (o *DbORM) SelectBlockByNumber(n int64, qopts ...pg.QOpt) (*LogPollerBlock, error) { q := o.q.WithOpts(qopts...) var b LogPollerBlock - if err := q.Get(&b, `SELECT * FROM evm.log_poller_blocks WHERE block_number = $1 AND evm_chain_id = $2`, n, utils.NewBig(o.chainID)); err != nil { + if err := q.Get(&b, `SELECT * FROM evm.log_poller_blocks WHERE block_number = $1 AND evm_chain_id = $2`, n, ubig.New(o.chainID)); err != nil { return nil, err } return &b, nil @@ -161,7 +161,7 @@ func (o *DbORM) SelectBlockByNumber(n int64, qopts ...pg.QOpt) (*LogPollerBlock, func (o *DbORM) SelectLatestBlock(qopts ...pg.QOpt) (*LogPollerBlock, error) { q := o.q.WithOpts(qopts...) var b LogPollerBlock - if err := q.Get(&b, `SELECT * FROM evm.log_poller_blocks WHERE evm_chain_id = $1 ORDER BY block_number DESC LIMIT 1`, utils.NewBig(o.chainID)); err != nil { + if err := q.Get(&b, `SELECT * FROM evm.log_poller_blocks WHERE evm_chain_id = $1 ORDER BY block_number DESC LIMIT 1`, ubig.New(o.chainID)); err != nil { return nil, err } return &b, nil @@ -191,7 +191,7 @@ func (o *DbORM) SelectLatestLogByEventSigWithConfs(eventSig common.Hash, address // DeleteBlocksBefore delete all blocks before and including end. func (o *DbORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { q := o.q.WithOpts(qopts...) - _, err := q.Exec(`DELETE FROM evm.log_poller_blocks WHERE block_number <= $1 AND evm_chain_id = $2`, end, utils.NewBig(o.chainID)) + _, err := q.Exec(`DELETE FROM evm.log_poller_blocks WHERE block_number <= $1 AND evm_chain_id = $2`, end, ubig.New(o.chainID)) return err } @@ -241,7 +241,7 @@ func (o *DbORM) DeleteExpiredLogs(qopts ...pg.QOpt) error { ) DELETE FROM evm.logs l USING r WHERE l.evm_chain_id = $1 AND l.address=r.address AND l.event_sig=r.event AND l.created_at <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')`, // retention is in nanoseconds (time.Duration aka BIGINT) - utils.NewBig(o.chainID)) + ubig.New(o.chainID)) } // InsertLogs is idempotent to support replays. diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 2a6ebb2c04e..3c54b3cf654 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -37,7 +38,7 @@ func GenLog(chainID *big.Int, logIndex int64, blockNum int64, blockHash string, func GenLogWithTimestamp(chainID *big.Int, logIndex int64, blockNum int64, blockHash string, topic1 []byte, address common.Address, blockTimestamp time.Time) logpoller.Log { return logpoller.Log{ - EvmChainId: utils.NewBig(chainID), + EvmChainId: ubig.New(chainID), LogIndex: logIndex, BlockHash: common.HexToHash(blockHash), BlockNumber: blockNum, @@ -52,7 +53,7 @@ func GenLogWithTimestamp(chainID *big.Int, logIndex int64, blockNum int64, block func GenLogWithData(chainID *big.Int, address common.Address, eventSig common.Hash, logIndex int64, blockNum int64, data []byte) logpoller.Log { return logpoller.Log{ - EvmChainId: utils.NewBig(chainID), + EvmChainId: ubig.New(chainID), LogIndex: logIndex, BlockHash: utils.RandomBytes32(), BlockNumber: blockNum, @@ -219,7 +220,7 @@ func TestORM(t *testing.T) { topic2 := common.HexToHash("0x1600") require.NoError(t, o1.InsertLogs([]logpoller.Log{ { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 1, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(10), @@ -230,7 +231,7 @@ func TestORM(t *testing.T) { Data: []byte("hello"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 2, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(11), @@ -241,7 +242,7 @@ func TestORM(t *testing.T) { Data: []byte("hello"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 3, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(12), @@ -252,7 +253,7 @@ func TestORM(t *testing.T) { Data: []byte("hello"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 4, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(13), @@ -263,7 +264,7 @@ func TestORM(t *testing.T) { Data: []byte("hello"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 5, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(14), @@ -274,7 +275,7 @@ func TestORM(t *testing.T) { Data: []byte("hello2"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 6, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(15), @@ -285,7 +286,7 @@ func TestORM(t *testing.T) { Data: []byte("hello2"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 7, BlockHash: common.HexToHash("0x1237"), BlockNumber: int64(16), @@ -296,7 +297,7 @@ func TestORM(t *testing.T) { Data: []byte("hello short retention"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 8, BlockHash: common.HexToHash("0x1238"), BlockNumber: int64(17), @@ -451,7 +452,7 @@ func insertLogsTopicValueRange(t *testing.T, chainID *big.Int, o *logpoller.DbOR var lgs []logpoller.Log for i := start; i <= stop; i++ { lgs = append(lgs, logpoller.Log{ - EvmChainId: utils.NewBig(chainID), + EvmChainId: ubig.New(chainID), LogIndex: int64(i), BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(blockNumber), @@ -536,7 +537,7 @@ func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { require.NoError(t, o1.InsertBlock(common.HexToHash("0x1"), 1, time.Now(), 0)) logs := []logpoller.Log{ { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(0), BlockHash: common.HexToHash("0x1"), BlockNumber: int64(1), @@ -547,7 +548,7 @@ func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { Data: logpoller.EvmWord(1).Bytes(), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(1), BlockHash: common.HexToHash("0x1"), BlockNumber: int64(1), @@ -559,7 +560,7 @@ func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { }, // Different txHash { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(2), BlockHash: common.HexToHash("0x1"), BlockNumber: int64(1), @@ -571,7 +572,7 @@ func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { }, // Different eventSig { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(3), BlockHash: common.HexToHash("0x1"), BlockNumber: int64(1), @@ -600,7 +601,7 @@ func TestORM_DataWords(t *testing.T) { require.NoError(t, o1.InsertBlock(common.HexToHash("0x1"), 1, time.Now(), 0)) require.NoError(t, o1.InsertLogs([]logpoller.Log{ { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(0), BlockHash: common.HexToHash("0x1"), BlockNumber: int64(1), @@ -612,7 +613,7 @@ func TestORM_DataWords(t *testing.T) { }, { // In block 2, unconfirmed to start - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(1), BlockHash: common.HexToHash("0x2"), BlockNumber: int64(2), @@ -667,7 +668,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { sourceAddr := common.HexToAddress("0x12345") inputLogs := []logpoller.Log{ { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 1, BlockHash: common.HexToHash("0x1234"), BlockNumber: int64(10), @@ -678,7 +679,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { Data: []byte("hello1"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 2, BlockHash: common.HexToHash("0x1235"), BlockNumber: int64(11), @@ -689,7 +690,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { Data: []byte("hello2"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 3, BlockHash: common.HexToHash("0x1236"), BlockNumber: int64(12), @@ -700,7 +701,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { Data: []byte("hello3"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 4, BlockHash: common.HexToHash("0x1237"), BlockNumber: int64(13), @@ -711,7 +712,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { Data: []byte("hello4"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 5, BlockHash: common.HexToHash("0x1238"), BlockNumber: int64(14), @@ -722,7 +723,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { Data: []byte("hello5"), }, { - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: 6, BlockHash: common.HexToHash("0x1239"), BlockNumber: int64(15), @@ -827,7 +828,7 @@ func BenchmarkLogs(b *testing.B) { addr := common.HexToAddress("0x1234") for i := 0; i < 10_000; i++ { lgs = append(lgs, logpoller.Log{ - EvmChainId: utils.NewBig(th.ChainID), + EvmChainId: ubig.New(th.ChainID), LogIndex: int64(i), BlockHash: common.HexToHash("0x1"), BlockNumber: 1, @@ -866,7 +867,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { //Insert two logs that mimics an oracle request from 2 different addresses (matching will be on topic index 1) require.NoError(t, orm.InsertLogs([]logpoller.Log{ { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 1, BlockHash: common.HexToHash("0x1"), BlockNumber: 1, @@ -878,7 +879,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { Data: []byte("requestID-A1"), }, { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 2, BlockHash: common.HexToHash("0x1"), BlockNumber: 1, @@ -907,7 +908,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { //Insert a log that mimics response for requestID-A1 require.NoError(t, orm.InsertLogs([]logpoller.Log{ { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 3, BlockHash: common.HexToHash("0x2"), BlockNumber: 2, @@ -935,7 +936,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { //Insert 3 request from addressC (matching will be on topic index 3) require.NoError(t, orm.InsertLogs([]logpoller.Log{ { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 5, BlockHash: common.HexToHash("0x2"), BlockNumber: 3, @@ -947,7 +948,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { Data: []byte("requestID-C1"), }, { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 6, BlockHash: common.HexToHash("0x2"), BlockNumber: 3, @@ -958,7 +959,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { TxHash: common.HexToHash("0x0002"), Data: []byte("requestID-C2"), }, { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 7, BlockHash: common.HexToHash("0x2"), BlockNumber: 3, @@ -983,7 +984,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { //Fulfill requestID-C2 require.NoError(t, orm.InsertLogs([]logpoller.Log{ { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 8, BlockHash: common.HexToHash("0x3"), BlockNumber: 3, @@ -1006,7 +1007,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { //Fulfill requestID-C3 require.NoError(t, orm.InsertLogs([]logpoller.Log{ { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 9, BlockHash: common.HexToHash("0x3"), BlockNumber: 3, @@ -1041,7 +1042,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { //Fulfill requestID-C3 require.NoError(t, orm.InsertLogs([]logpoller.Log{ { - EvmChainId: (*utils.Big)(th.ChainID), + EvmChainId: (*ubig.Big)(th.ChainID), LogIndex: 10, BlockHash: common.HexToHash("0x2"), BlockNumber: 10, @@ -1533,7 +1534,7 @@ func Benchmark_LogsDataWordBetween(b *testing.B) { data = append(data, logpoller.EvmWord(uint64(numberOfMessagesPerReport*(i+1))).Bytes()...) dbLogs = append(dbLogs, logpoller.Log{ - EvmChainId: utils.NewBig(chainId), + EvmChainId: ubig.New(chainId), LogIndex: int64(i + 1), BlockHash: utils.RandomBytes32(), BlockNumber: int64(i + 1), diff --git a/core/chains/evm/logpoller/query.go b/core/chains/evm/logpoller/query.go index b7fbfade680..a37b15b2b2d 100644 --- a/core/chains/evm/logpoller/query.go +++ b/core/chains/evm/logpoller/query.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) type bytesProducer interface { @@ -34,7 +34,7 @@ type queryArgs struct { func newQueryArgs(chainId *big.Int) *queryArgs { return &queryArgs{ args: map[string]interface{}{ - "evm_chain_id": utils.NewBig(chainId), + "evm_chain_id": ubig.New(chainId), }, err: []error{}, } diff --git a/core/chains/evm/logpoller/query_test.go b/core/chains/evm/logpoller/query_test.go index e38aeb05819..bc2b20c85c2 100644 --- a/core/chains/evm/logpoller/query_test.go +++ b/core/chains/evm/logpoller/query_test.go @@ -8,6 +8,7 @@ import ( "github.com/lib/pq" "github.com/stretchr/testify/require" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -22,7 +23,7 @@ func Test_QueryArgs(t *testing.T) { name: "valid arguments", queryArgs: newQueryArgs(big.NewInt(20)).withAddress(utils.ZeroAddress), want: map[string]interface{}{ - "evm_chain_id": utils.NewBigI(20), + "evm_chain_id": ubig.NewI(20), "address": utils.ZeroAddress, }, }, diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 21a4bd2865c..b9e8fe99e31 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -37,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -1897,7 +1898,7 @@ func Test_NextNonce(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, addr1).Return(uint64(randNonce), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) ctx := testutils.Context(t) - cltest.MustInsertRandomKey(t, ks, *utils.NewBig(testutils.FixtureChainID)) + cltest.MustInsertRandomKey(t, ks, *ubig.New(testutils.FixtureChainID)) nonce, err := eb.GetNextSequence(ctx, addr1) require.NoError(t, err) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 1c9868741fa..bb2a30e51dd 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -28,8 +28,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -177,7 +177,7 @@ type DbEthTx struct { Subject uuid.NullUUID PipelineTaskRunID uuid.NullUUID MinConfirmations null.Uint32 - EVMChainID utils.Big + EVMChainID ubig.Big // TransmitChecker defines the check that should be performed before a transaction is submitted on // chain. TransmitChecker *sqlutil.JSON @@ -210,7 +210,7 @@ func (db *DbEthTx) FromTx(tx *Tx) { db.CallbackCompleted = tx.CallbackCompleted if tx.ChainID != nil { - db.EVMChainID = *utils.NewBig(tx.ChainID) + db.EVMChainID = *ubig.New(tx.ChainID) } if tx.Sequence != nil { n := tx.Sequence.Int64() diff --git a/core/chains/evm/txmgr/resender_test.go b/core/chains/evm/txmgr/resender_test.go index 0e86c0d4f8c..f6b0e3e29c7 100644 --- a/core/chains/evm/txmgr/resender_test.go +++ b/core/chains/evm/txmgr/resender_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -23,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_EthResender_resendUnconfirmed(t *testing.T) { @@ -109,7 +109,7 @@ func Test_EthResender_alertUnconfirmed(t *testing.T) { delay := models.MustNewDuration(1 * time.Nanosecond) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0] = &toml.EVMConfig{ - Chain: toml.Defaults(utils.NewBig(big.NewInt(0)), &toml.Chain{ + Chain: toml.Defaults(ubig.New(big.NewInt(0)), &toml.Chain{ Transactions: toml.Transactions{ResendAfterThreshold: delay}, }), } diff --git a/core/chains/evm/txmgr/tracker_test.go b/core/chains/evm/txmgr/tracker_test.go index a31187f04e8..3e0bef51568 100644 --- a/core/chains/evm/txmgr/tracker_test.go +++ b/core/chains/evm/txmgr/tracker_test.go @@ -7,12 +7,12 @@ import ( "time" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -33,8 +33,8 @@ func newTestEvmTrackerSetup(t *testing.T) (*txmgr.Tracker, txmgr.TestEvmTxStore, func generateEnabledAddresses(t *testing.T, keyStore keystore.Eth, chainID *big.Int) []common.Address { var enabledAddresses []common.Address - _, addr1 := cltest.MustInsertRandomKey(t, keyStore, *utils.NewBigI(chainID.Int64())) - _, addr2 := cltest.MustInsertRandomKey(t, keyStore, *utils.NewBigI(chainID.Int64())) + _, addr1 := cltest.MustInsertRandomKey(t, keyStore, *ubig.NewI(chainID.Int64())) + _, addr2 := cltest.MustInsertRandomKey(t, keyStore, *ubig.NewI(chainID.Int64())) enabledAddresses = append(enabledAddresses, addr1, addr2) return enabledAddresses } diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 9833acfd459..df913a41905 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -33,6 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -206,7 +207,7 @@ func TestTxm_CreateTransaction(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), fmt.Sprintf("no eth key exists with address %s", rndAddr.String())) - _, otherAddress := cltest.MustInsertRandomKey(t, kst.Eth(), *utils.NewBigI(1337)) + _, otherAddress := cltest.MustInsertRandomKey(t, kst.Eth(), *ubig.NewI(1337)) _, err = txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ FromAddress: otherAddress, @@ -301,7 +302,7 @@ func TestTxm_CreateTransaction(t *testing.T) { // Create mock forwarder, mock authorizedsenders call. form := forwarders.NewORM(db, logger.Test(t), cfg.Database()) fwdrAddr := testutils.NewAddress() - fwdr, err := form.CreateForwarder(fwdrAddr, utils.Big(cltest.FixtureChainID)) + fwdr, err := form.CreateForwarder(fwdrAddr, ubig.Big(cltest.FixtureChainID)) require.NoError(t, err) require.Equal(t, fwdr.Address, fwdrAddr) diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 314180a7e98..b7c1e5b56d5 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -20,6 +20,7 @@ import ( 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/types/internal/blocks" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -32,7 +33,7 @@ type Head struct { L1BlockNumber null.Int64 ParentHash common.Hash Parent *Head - EVMChainID *utils.Big + EVMChainID *ubig.Big Timestamp time.Time CreatedAt time.Time BaseFeePerGas *assets.Wei @@ -47,7 +48,7 @@ var _ commontypes.Head[common.Hash] = &Head{} var _ htrktypes.Head[common.Hash, *big.Int] = &Head{} // NewHead returns a Head instance. -func NewHead(number *big.Int, blockHash common.Hash, parentHash common.Hash, timestamp uint64, chainID *utils.Big) Head { +func NewHead(number *big.Int, blockHash common.Hash, parentHash common.Hash, timestamp uint64, chainID *ubig.Big) Head { return Head{ Number: number.Int64(), Hash: blockHash, diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index d0e7292b204..aa43806da1f 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -13,6 +13,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -26,7 +27,7 @@ type Configs interface { type Node struct { Name string - EVMChainID utils.Big + EVMChainID ubig.Big WSURL null.String HTTPURL null.String SendOnly bool diff --git a/core/utils/big.go b/core/chains/evm/utils/big/big.go similarity index 91% rename from core/utils/big.go rename to core/chains/evm/utils/big/big.go index 69fab223de7..f0ff475a7e7 100644 --- a/core/utils/big.go +++ b/core/chains/evm/utils/big/big.go @@ -1,4 +1,4 @@ -package utils +package big import ( "database/sql/driver" @@ -10,6 +10,7 @@ import ( bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) const base10 = 10 @@ -50,8 +51,8 @@ func (b *BigFloat) Value() *big.Float { // Big stores large integers and can deserialize a variety of inputs. type Big big.Int -// NewBig constructs a Big from *big.Int. -func NewBig(i *big.Int) *Big { +// New constructs a Big from *big.Int. +func New(i *big.Int) *Big { if i != nil { var b big.Int b.Set(i) @@ -60,9 +61,9 @@ func NewBig(i *big.Int) *Big { return nil } -// NewBigI constructs a Big from int64. -func NewBigI(i int64) *Big { - return NewBig(big.NewInt(i)) +// NewI constructs a Big from int64. +func NewI(i int64) *Big { + return New(big.NewInt(i)) } // MarshalText marshals this instance to base 10 number as string. @@ -83,7 +84,7 @@ func (b Big) MarshalJSON() ([]byte, error) { func (b *Big) UnmarshalText(input []byte) error { input = bytes.TrimQuotes(input) str := string(input) - if HasHexPrefix(str) { + if utils.HasHexPrefix(str) { decoded, err := hexutil.DecodeBig(str) if err != nil { return err @@ -174,15 +175,15 @@ func (b *Big) Int64() int64 { // Add returns the sum of b and c func (b *Big) Add(c *Big) *Big { - return NewBig(bigmath.Add(b.ToInt(), c.ToInt())) + return New(bigmath.Add(b.ToInt(), c.ToInt())) } // Sub returns the differencs between b and c func (b *Big) Sub(c *Big) *Big { - return NewBig(bigmath.Sub(b.ToInt(), c.ToInt())) + return New(bigmath.Sub(b.ToInt(), c.ToInt())) } // Sub returns b % c func (b *Big) Mod(c *Big) *Big { - return NewBig(bigmath.Mod(b.ToInt(), c.ToInt())) + return New(bigmath.Mod(b.ToInt(), c.ToInt())) } diff --git a/core/utils/big_test.go b/core/chains/evm/utils/big/big_test.go similarity index 95% rename from core/utils/big_test.go rename to core/chains/evm/utils/big/big_test.go index e46d46a0651..c4774cf1988 100644 --- a/core/utils/big_test.go +++ b/core/chains/evm/utils/big/big_test.go @@ -1,4 +1,4 @@ -package utils +package big import ( "encoding/json" @@ -198,15 +198,15 @@ func TestBig_Scan(t *testing.T) { input interface{} want *Big }{ - {"zero string", "0", NewBig(big.NewInt(0))}, - {"one string", "1", NewBig(big.NewInt(1))}, + {"zero string", "0", New(big.NewInt(0))}, + {"one string", "1", New(big.NewInt(1))}, { "large string", "115792089237316195423570985008687907853269984665640564039457584007913129639935", - NewBig(uint256Max), + New(uint256Max), }, - {"zero as bytes", []uint8{48}, NewBig(big.NewInt(0))}, - {"small number as bytes", []uint8{49, 52}, NewBig(big.NewInt(14))}, + {"zero as bytes", []uint8{48}, New(big.NewInt(0))}, + {"small number as bytes", []uint8{49, 52}, New(big.NewInt(14))}, { "max number as bytes", []uint8{ @@ -216,7 +216,7 @@ func TestBig_Scan(t *testing.T) { 48, 51, 57, 52, 53, 55, 53, 56, 52, 48, 48, 55, 57, 49, 51, 49, 50, 57, 54, 51, 57, 57, 51, 53, }, - NewBig(uint256Max), + New(uint256Max), }, } for _, test := range tests { diff --git a/core/chains/evm/utils/utils.go b/core/chains/evm/utils/utils.go new file mode 100644 index 00000000000..abcea77124a --- /dev/null +++ b/core/chains/evm/utils/utils.go @@ -0,0 +1,37 @@ +package utils + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/jpillora/backoff" + "golang.org/x/crypto/sha3" +) + +// MustHash returns the keccak256 hash, or panics on failure. +func MustHash(in string) common.Hash { + out, err := Keccak256([]byte(in)) + if err != nil { + panic(err) + } + return common.BytesToHash(out) +} + +// Keccak256 is a simplified interface for the legacy SHA3 implementation that +// Ethereum uses. +func Keccak256(in []byte) ([]byte, error) { + hash := sha3.NewLegacyKeccak256() + _, err := hash.Write(in) + return hash.Sum(nil), err +} + +// NewRedialBackoff is a standard backoff to use for redialling or reconnecting to +// unreachable network endpoints +func NewRedialBackoff() backoff.Backoff { + return backoff.Backoff{ + Min: 1 * time.Second, + Max: 15 * time.Second, + Jitter: true, + } + +} diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 18277a55d03..ef84573cd09 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -33,11 +33,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name Chain --output ./mocks/ --case=underscore @@ -127,7 +127,7 @@ type chain struct { } type errChainDisabled struct { - ChainID *utils.Big + ChainID *ubig.Big } func (e errChainDisabled) Error() string { diff --git a/core/cmd/blocks_commands_test.go b/core/cmd/blocks_commands_test.go index d0c0e118f9d..30540748cb1 100644 --- a/core/cmd/blocks_commands_test.go +++ b/core/cmd/blocks_commands_test.go @@ -8,15 +8,15 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_ReplayFromBlock(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].ChainID = (*utils.Big)(big.NewInt(5)) + c.EVM[0].ChainID = (*ubig.Big)(big.NewInt(5)) c.EVM[0].Enabled = ptr(true) }) diff --git a/core/cmd/eth_keys_commands_test.go b/core/cmd/eth_keys_commands_test.go index 3eb45e27bd0..7c85b779ecc 100644 --- a/core/cmd/eth_keys_commands_test.go +++ b/core/cmd/eth_keys_commands_test.go @@ -14,6 +14,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -39,7 +40,7 @@ func TestEthKeysPresenter_RenderTable(t *testing.T) { isDisabled = true createdAt = time.Now() updatedAt = time.Now().Add(time.Second) - maxGasPriceWei = utils.NewBigI(12345) + maxGasPriceWei = ubig.NewI(12345) bundleID = cltest.DefaultOCRKeyBundleID buffer = bytes.NewBufferString("") r = cmd.RendererTable{Writer: buffer} diff --git a/core/cmd/evm_chains_commands_test.go b/core/cmd/evm_chains_commands_test.go index b4891271210..fa6d7bb519c 100644 --- a/core/cmd/evm_chains_commands_test.go +++ b/core/cmd/evm_chains_commands_test.go @@ -8,15 +8,15 @@ import ( "github.com/stretchr/testify/require" client2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) -func newRandChainID() *utils.Big { - return utils.NewBig(testutils.NewRandomEVMChainID()) +func newRandChainID() *big.Big { + return big.New(testutils.NewRandomEVMChainID()) } func TestShell_IndexEVMChains(t *testing.T) { diff --git a/core/cmd/evm_transaction_commands.go b/core/cmd/evm_transaction_commands.go index d702bc3b799..a1a11ab9b37 100644 --- a/core/cmd/evm_transaction_commands.go +++ b/core/cmd/evm_transaction_commands.go @@ -11,6 +11,7 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" @@ -186,7 +187,7 @@ func (s *Shell) SendEther(c *cli.Context) (err error) { DestinationAddress: destinationAddress, FromAddress: fromAddress, Amount: amount, - EVMChainID: (*utils.Big)(evmChainID), + EVMChainID: (*ubig.Big)(evmChainID), AllowHigherAmounts: c.IsSet("force"), } diff --git a/core/cmd/forwarders_commands.go b/core/cmd/forwarders_commands.go index 51e90a4390c..a870d4714c7 100644 --- a/core/cmd/forwarders_commands.go +++ b/core/cmd/forwarders_commands.go @@ -14,7 +14,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -136,7 +136,7 @@ func (s *Shell) TrackForwarder(c *cli.Context) (err error) { } request, err := json.Marshal(web.TrackEVMForwarderRequest{ - EVMChainID: (*utils.Big)(chainID), + EVMChainID: (*ubig.Big)(chainID), Address: address, }) if err != nil { diff --git a/core/cmd/forwarders_commands_test.go b/core/cmd/forwarders_commands_test.go index 179216b8e41..5946d31dc3d 100644 --- a/core/cmd/forwarders_commands_test.go +++ b/core/cmd/forwarders_commands_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -23,7 +24,7 @@ func TestEVMForwarderPresenter_RenderTable(t *testing.T) { var ( id = "1" address = utils.RandomAddress() - evmChainID = utils.NewBigI(4) + evmChainID = big.NewI(4) createdAt = time.Now() updatedAt = time.Now().Add(time.Second) buffer = bytes.NewBufferString("") diff --git a/core/cmd/ocr2vrf_configure_commands.go b/core/cmd/ocr2vrf_configure_commands.go index cf014d5e5dc..06f26ddb6a4 100644 --- a/core/cmd/ocr2vrf_configure_commands.go +++ b/core/cmd/ocr2vrf_configure_commands.go @@ -17,6 +17,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -342,7 +343,7 @@ func (s *Shell) authorizeForwarder(c *cli.Context, db *sqlx.DB, lggr logger.Logg // Create forwarder for management in forwarder_manager.go. orm := forwarders.NewORM(db, lggr, s.Config.Database()) - _, err = orm.CreateForwarder(common.HexToAddress(forwarderAddress), *utils.NewBigI(chainID)) + _, err = orm.CreateForwarder(common.HexToAddress(forwarderAddress), *ubig.NewI(chainID)) if err != nil { return err } diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index a1f7fdb857c..e4c29a0e5c2 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -38,6 +38,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -855,7 +856,7 @@ func randomizeTestDBSequences(db *sqlx.DB) error { } var randNum *big.Int - randNum, err = crand.Int(crand.Reader, utils.NewBigI(10000).ToInt()) + randNum, err = crand.Int(crand.Reader, ubig.NewI(10000).ToInt()) if err != nil { return fmt.Errorf("%s: failed to generate random number", failedToRandomizeTestDBSequencesError{}) } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index dc90201890d..c5e1fc5e4d0 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -51,6 +51,7 @@ import ( httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -232,10 +233,10 @@ func NewApplicationWithKey(t *testing.T, flagsAndDeps ...interface{}) *TestAppli func NewApplicationWithConfigAndKey(t testing.TB, c chainlink.GeneralConfig, flagsAndDeps ...interface{}) *TestApplication { app := NewApplicationWithConfig(t, c, flagsAndDeps...) - chainID := *utils.NewBig(&FixtureChainID) + chainID := *ubig.New(&FixtureChainID) for _, dep := range flagsAndDeps { switch v := dep.(type) { - case *utils.Big: + case *ubig.Big: chainID = *v } } @@ -255,7 +256,7 @@ func NewApplicationWithConfigAndKey(t testing.TB, c chainlink.GeneralConfig, fla return app } -func setKeys(t testing.TB, app *TestApplication, flagsAndDeps ...interface{}) (chainID utils.Big) { +func setKeys(t testing.TB, app *TestApplication, flagsAndDeps ...interface{}) (chainID ubig.Big) { require.NoError(t, app.KeyStore.Unlock(Password)) for _, dep := range flagsAndDeps { @@ -993,13 +994,13 @@ func Head(val interface{}) *evmtypes.Head { time := uint64(0) switch t := val.(type) { case int: - h = evmtypes.NewHead(big.NewInt(int64(t)), utils.NewHash(), utils.NewHash(), time, utils.NewBig(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(int64(t)), utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) case uint64: - h = evmtypes.NewHead(big.NewInt(int64(t)), utils.NewHash(), utils.NewHash(), time, utils.NewBig(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(int64(t)), utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) case int64: - h = evmtypes.NewHead(big.NewInt(t), utils.NewHash(), utils.NewHash(), time, utils.NewBig(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(t), utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) case *big.Int: - h = evmtypes.NewHead(t, utils.NewHash(), utils.NewHash(), time, utils.NewBig(&FixtureChainID)) + h = evmtypes.NewHead(t, utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) default: panic(fmt.Sprintf("Could not convert %v of type %T to Head", val, val)) } @@ -1009,7 +1010,7 @@ func Head(val interface{}) *evmtypes.Head { func HeadWithHash(n int64, hash common.Hash) *evmtypes.Head { var h evmtypes.Head time := uint64(0) - h = evmtypes.NewHead(big.NewInt(n), hash, utils.NewHash(), time, utils.NewBig(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(n), hash, utils.NewHash(), time, ubig.New(&FixtureChainID)) return &h } @@ -1390,7 +1391,7 @@ func (b *Blocks) NewHead(number uint64) *evmtypes.Head { ParentHash: parent.Hash, Parent: parent, Timestamp: time.Unix(parent.Number+1, 0), - EVMChainID: utils.NewBig(&FixtureChainID), + EVMChainID: ubig.New(&FixtureChainID), } return head } @@ -1429,7 +1430,7 @@ func NewBlocks(t *testing.T, numHashes int) *Blocks { hash := utils.NewHash() hashes = append(hashes, hash) - heads[i] = &evmtypes.Head{Hash: hash, Number: i, Timestamp: time.Unix(i, 0), EVMChainID: utils.NewBig(&FixtureChainID)} + heads[i] = &evmtypes.Head{Hash: hash, Number: i, Timestamp: time.Unix(i, 0), EVMChainID: ubig.New(&FixtureChainID)} if i > 0 { parent := heads[i-1] heads[i].Parent = parent diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index a76d23d3762..bece916ecc5 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -32,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -240,12 +241,12 @@ type RandomKey struct { Nonce int64 Disabled bool - chainIDs []utils.Big // nil: Fixture, set empty for none + chainIDs []ubig.Big // nil: Fixture, set empty for none } func (r RandomKey) MustInsert(t testing.TB, keystore keystore.Eth) (ethkey.KeyV2, common.Address) { if r.chainIDs == nil { - r.chainIDs = []utils.Big{*utils.NewBig(&FixtureChainID)} + r.chainIDs = []ubig.Big{*ubig.New(&FixtureChainID)} } key := MustGenerateRandomKey(t) @@ -272,7 +273,7 @@ func (r RandomKey) MustInsertWithState(t testing.TB, keystore keystore.Eth) (eth // MustInsertRandomKey inserts a randomly generated (not cryptographically secure) key for testing. // By default, it is enabled for the fixture chain. Pass chainIDs to override. // Use MustInsertRandomKeyNoChains for a key associate with no chains. -func MustInsertRandomKey(t testing.TB, keystore keystore.Eth, chainIDs ...utils.Big) (ethkey.KeyV2, common.Address) { +func MustInsertRandomKey(t testing.TB, keystore keystore.Eth, chainIDs ...ubig.Big) (ethkey.KeyV2, common.Address) { r := RandomKey{} if len(chainIDs) > 0 { r.chainIDs = chainIDs @@ -281,7 +282,7 @@ func MustInsertRandomKey(t testing.TB, keystore keystore.Eth, chainIDs ...utils. } func MustInsertRandomKeyNoChains(t testing.TB, keystore keystore.Eth) (ethkey.KeyV2, common.Address) { - return RandomKey{chainIDs: []utils.Big{}}.MustInsert(t, keystore) + return RandomKey{chainIDs: []ubig.Big{}}.MustInsert(t, keystore) } func MustInsertRandomKeyReturningState(t testing.TB, keystore keystore.Eth) (ethkey.State, common.Address) { @@ -299,7 +300,7 @@ func MustGenerateRandomKeyState(_ testing.TB) ethkey.State { } func MustInsertHead(t *testing.T, db *sqlx.DB, cfg pg.QConfig, number int64) evmtypes.Head { - h := evmtypes.NewHead(big.NewInt(number), utils.NewHash(), utils.NewHash(), 0, utils.NewBig(&FixtureChainID)) + h := evmtypes.NewHead(big.NewInt(number), utils.NewHash(), utils.NewHash(), 0, ubig.New(&FixtureChainID)) horm := headtracker.NewORM(db, logger.TestLogger(t), cfg, FixtureChainID) err := horm.IdempotentInsertHead(testutils.Context(t), &h) @@ -347,7 +348,7 @@ NOW(),NOW(),$1,'{}',false,$2,$3,0,0,0,0,0,0,0,0,0 func MakeDirectRequestJobSpec(t *testing.T) *job.Job { t.Helper() - drs := &job.DirectRequestSpec{EVMChainID: (*utils.Big)(testutils.FixtureChainID)} + drs := &job.DirectRequestSpec{EVMChainID: (*ubig.Big)(testutils.FixtureChainID)} spec := &job.Job{ Type: job.DirectRequest, SchemaVersion: 1, @@ -391,7 +392,7 @@ func MustInsertKeeperJob(t *testing.T, db *sqlx.DB, korm keeper.ORM, from ethkey } func MustInsertKeeperRegistry(t *testing.T, db *sqlx.DB, korm keeper.ORM, ethKeyStore keystore.Eth, keeperIndex, numKeepers, blockCountPerTurn int32) (keeper.Registry, job.Job) { - key, _ := MustInsertRandomKey(t, ethKeyStore, *utils.NewBig(testutils.SimulatedChainID)) + key, _ := MustInsertRandomKey(t, ethKeyStore, *ubig.New(testutils.SimulatedChainID)) from := key.EIP55Address t.Helper() contractAddress := NewEIP55Address() @@ -415,7 +416,7 @@ func MustInsertKeeperRegistry(t *testing.T, db *sqlx.DB, korm keeper.ORM, ethKey func MustInsertUpkeepForRegistry(t *testing.T, db *sqlx.DB, cfg pg.QConfig, registry keeper.Registry) keeper.UpkeepRegistration { korm := keeper.NewORM(db, logger.TestLogger(t), cfg) - upkeepID := utils.NewBigI(int64(mathrand.Uint32())) + upkeepID := ubig.NewI(int64(mathrand.Uint32())) upkeep := keeper.UpkeepRegistration{ UpkeepID: upkeepID, ExecuteGas: uint32(150_000), diff --git a/core/internal/cltest/simulated_backend.go b/core/internal/cltest/simulated_backend.go index 010478837da..0aecc7f8324 100644 --- a/core/internal/cltest/simulated_backend.go +++ b/core/internal/cltest/simulated_backend.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func NewSimulatedBackend(t *testing.T, alloc core.GenesisAlloc, gasLimit uint32) *backends.SimulatedBackend { @@ -39,7 +39,7 @@ func NewApplicationWithConfigV2OnSimulatedBlockchain( } require.Zero(t, evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()).Cmp(testutils.SimulatedChainID)) - chainID := utils.NewBig(testutils.SimulatedChainID) + chainID := big.New(testutils.SimulatedChainID) client := client.NewSimulatedBackendClient(t, backend, testutils.SimulatedChainID) eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) @@ -64,7 +64,7 @@ func NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain( } require.Zero(t, evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()).Cmp(testutils.SimulatedChainID)) - chainID := utils.NewBig(testutils.SimulatedChainID) + chainID := big.New(testutils.SimulatedChainID) client := client.NewSimulatedBackendClient(t, backend, testutils.SimulatedChainID) eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 7b29fdae4fa..f115a3f8858 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -46,6 +46,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" @@ -771,7 +772,7 @@ func setupForwarderEnabledNode(t *testing.T, owner *bind.TransactOpts, portV2 in // add forwarder address to be tracked in db forwarderORM := forwarders.NewORM(app.GetSqlxDB(), logger.TestLogger(t), config.Database()) - chainID := utils.Big(*b.Blockchain().Config().ChainID) + chainID := ubig.Big(*b.Blockchain().Config().ChainID) _, err = forwarderORM.CreateForwarder(forwarder, chainID) require.NoError(t, err) @@ -1277,7 +1278,7 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { Transactions: cltest.LegacyTransactionsFromGasPrices(48_000_000_000, 49_000_000_000, 31_000_000_000), } - evmChainID := utils.NewBig(evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs())) + evmChainID := ubig.New(evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs())) h40 := evmtypes.Head{Hash: utils.NewHash(), Number: 40, EVMChainID: evmChainID} h41 := evmtypes.Head{Hash: b41.Hash, ParentHash: h40.Hash, Number: 41, EVMChainID: evmChainID} h42 := evmtypes.Head{Hash: b42.Hash, ParentHash: h41.Hash, Number: 42, EVMChainID: evmChainID} diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 295bb7fb14a..70d4b0d79fd 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -37,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -51,7 +52,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ocr2Node struct { @@ -170,7 +170,7 @@ func setupNodeOCR2( // add forwarder address to be tracked in db forwarderORM := forwarders.NewORM(app.GetSqlxDB(), logger.TestLogger(t), config.Database()) - chainID := utils.Big(*b.Blockchain().Config().ChainID) + chainID := ubig.Big(*b.Blockchain().Config().ChainID) _, err2 = forwarderORM.CreateForwarder(faddr, chainID) require.NoError(t, err2) diff --git a/core/internal/testutils/configtest/general_config.go b/core/internal/testutils/configtest/general_config.go index f076521b71c..c414c973160 100644 --- a/core/internal/testutils/configtest/general_config.go +++ b/core/internal/testutils/configtest/general_config.go @@ -10,11 +10,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const DefaultPeerID = "12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X" @@ -64,7 +64,7 @@ func overrides(c *chainlink.Config, s *chainlink.Secrets) { c.WebServer.ListenIP = &testIP c.WebServer.TLS.ListenIP = &testIP - chainID := utils.NewBigI(evmclient.NullClientChainID) + chainID := big.NewI(evmclient.NullClientChainID) c.EVM = append(c.EVM, &evmcfg.EVMConfig{ ChainID: chainID, Chain: evmcfg.Defaults(chainID), @@ -95,7 +95,7 @@ func NewGeneralConfigSimulated(t testing.TB, overrideFn func(*chainlink.Config, // simulated is a config override func that appends the simulated EVM chain (testutils.SimulatedChainID), // or replaces the null chain (client.NullClientChainID) if that is the only entry. func simulated(c *chainlink.Config, s *chainlink.Secrets) { - chainID := utils.NewBig(testutils.SimulatedChainID) + chainID := big.New(testutils.SimulatedChainID) enabled := true cfg := evmcfg.EVMConfig{ ChainID: chainID, @@ -103,7 +103,7 @@ func simulated(c *chainlink.Config, s *chainlink.Secrets) { Enabled: &enabled, Nodes: evmcfg.EVMNodes{&validTestNode}, } - if len(c.EVM) == 1 && c.EVM[0].ChainID.Cmp(utils.NewBigI(client.NullClientChainID)) == 0 { + if len(c.EVM) == 1 && c.EVM[0].ChainID.Cmp(big.NewI(client.NullClientChainID)) == 0 { c.EVM[0] = &cfg // replace null, if only entry } else { c.EVM = append(c.EVM, &cfg) diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 095ea1a35c9..eb1a03530ae 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -31,6 +31,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "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" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -38,7 +39,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func NewChainScopedConfig(t testing.TB, cfg legacyevm.AppConfig) evmconfig.ChainScopedConfig { @@ -46,7 +46,7 @@ func NewChainScopedConfig(t testing.TB, cfg legacyevm.AppConfig) evmconfig.Chain if len(cfg.EVMConfigs()) > 0 { evmCfg = cfg.EVMConfigs()[0] } else { - var chainID = (*utils.Big)(testutils.FixtureChainID) + var chainID = (*ubig.Big)(testutils.FixtureChainID) evmCfg = &evmtoml.EVMConfig{ ChainID: chainID, Chain: evmtoml.Defaults(chainID), @@ -267,7 +267,7 @@ func (mo *TestConfigs) NodeStatusesPaged(offset int, limit int, chainIDs ...stri return } -func legacyNode(n *evmtoml.Node, chainID *utils.Big) (v2 evmtypes.Node) { +func legacyNode(n *evmtoml.Node, chainID *ubig.Big) (v2 evmtypes.Node) { v2.Name = *n.Name v2.EVMChainID = *chainID if n.HTTPURL != nil { diff --git a/core/internal/testutils/evmtest/v2/evmtest.go b/core/internal/testutils/evmtest/v2/evmtest.go index fa22588c8fb..22b2bc5e0ca 100644 --- a/core/internal/testutils/evmtest/v2/evmtest.go +++ b/core/internal/testutils/evmtest/v2/evmtest.go @@ -5,9 +5,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func ChainEthMainnet(t *testing.T) config.ChainScopedConfig { return scopedConfig(t, 1) } @@ -16,7 +16,7 @@ func ChainArbitrumMainnet(t *testing.T) config.ChainScopedConfig { return scoped func ChainArbitrumRinkeby(t *testing.T) config.ChainScopedConfig { return scopedConfig(t, 421611) } func scopedConfig(t *testing.T, chainID int64) config.ChainScopedConfig { - id := utils.NewBigI(chainID) + id := big.NewI(chainID) evmCfg := toml.EVMConfig{ChainID: id, Chain: toml.Defaults(id)} return config.NewTOMLChainScopedConfig(configtest.NewTestGeneralConfig(t), &evmCfg, logger.TestLogger(t)) } diff --git a/core/scripts/functions/src/fetching.go b/core/scripts/functions/src/fetching.go index 0b22a93252d..9be624a40b6 100644 --- a/core/scripts/functions/src/fetching.go +++ b/core/scripts/functions/src/fetching.go @@ -11,7 +11,6 @@ import ( "github.com/urfave/cli" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -114,7 +113,7 @@ func findEvmOCR2Bundle(ocr2Bundles []ocr2Bundle) int { func findFirstGoodEthKeyAddress(chainID int64, ethKeys []presenters.ETHKeyResource) (string, error) { for _, ethKey := range ethKeys { - if ethKey.EVMChainID.Equal(utils.NewBigI(chainID)) && !ethKey.Disabled { + if ethKey.EVMChainID.Equal(ubig.NewI(chainID)) && !ethKey.Disabled { if ethKey.EthBalance.IsZero() { fmt.Println("WARN: selected ETH address has zero balance", ethKey.Address) } diff --git a/core/services/blockhashstore/delegate_test.go b/core/services/blockhashstore/delegate_test.go index 0096ac5ca9e..6fffcfdd493 100644 --- a/core/services/blockhashstore/delegate_test.go +++ b/core/services/blockhashstore/delegate_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "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/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -26,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestDelegate_JobType(t *testing.T) { @@ -90,7 +90,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { defaultWaitBlocks := (int32)(testData.legacyChains.Slice()[0].Config().EVM().FinalityDepth()) t.Run("happy", func(t *testing.T) { - spec := job.Job{BlockhashStoreSpec: &job.BlockhashStoreSpec{WaitBlocks: defaultWaitBlocks, EVMChainID: (*utils.Big)(testutils.FixtureChainID)}} + spec := job.Job{BlockhashStoreSpec: &job.BlockhashStoreSpec{WaitBlocks: defaultWaitBlocks, EVMChainID: (*big.Big)(testutils.FixtureChainID)}} services, err := delegate.ServicesForSpec(spec) require.NoError(t, err) @@ -107,7 +107,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { CoordinatorV1Address: &coordinatorV1, CoordinatorV2Address: &coordinatorV2, CoordinatorV2PlusAddress: &coordinatorV2Plus, - EVMChainID: (*utils.Big)(testutils.FixtureChainID), + EVMChainID: (*big.Big)(testutils.FixtureChainID), }} services, err := delegate.ServicesForSpec(spec) @@ -123,7 +123,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { t.Run("wrong EVMChainID", func(t *testing.T) { spec := job.Job{BlockhashStoreSpec: &job.BlockhashStoreSpec{ - EVMChainID: utils.NewBigI(123), + EVMChainID: big.NewI(123), }} _, err := delegate.ServicesForSpec(spec) assert.Error(t, err) @@ -152,7 +152,7 @@ func TestDelegate_StartStop(t *testing.T) { WaitBlocks: defaultWaitBlocks, PollPeriod: time.Second, RunTimeout: testutils.WaitTimeout(t), - EVMChainID: (*utils.Big)(testutils.FixtureChainID), + EVMChainID: (*big.Big)(testutils.FixtureChainID), }} services, err := delegate.ServicesForSpec(spec) diff --git a/core/services/blockhashstore/validate_test.go b/core/services/blockhashstore/validate_test.go index 0b7110a7528..48487bb5489 100644 --- a/core/services/blockhashstore/validate_test.go +++ b/core/services/blockhashstore/validate_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestValidate(t *testing.T) { @@ -49,7 +49,7 @@ fromAddresses = ["0x469aA2CD13e037DC5236320783dCfd0e641c0559"]`, os.BlockhashStoreSpec.BlockhashStoreAddress) require.Equal(t, 23*time.Second, os.BlockhashStoreSpec.PollPeriod) require.Equal(t, 7*time.Second, os.BlockhashStoreSpec.RunTimeout) - require.Equal(t, utils.NewBigI(4), os.BlockhashStoreSpec.EVMChainID) + require.Equal(t, big.NewI(4), os.BlockhashStoreSpec.EVMChainID) require.Equal(t, fromAddresses, os.BlockhashStoreSpec.FromAddresses) }, diff --git a/core/services/blockheaderfeeder/validate_test.go b/core/services/blockheaderfeeder/validate_test.go index c58058d1620..cdab0322a40 100644 --- a/core/services/blockheaderfeeder/validate_test.go +++ b/core/services/blockheaderfeeder/validate_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestValidate(t *testing.T) { @@ -59,7 +59,7 @@ storeBlockhashesBatchSize = 10 os.BlockHeaderFeederSpec.BatchBlockhashStoreAddress) require.Equal(t, 23*time.Second, os.BlockHeaderFeederSpec.PollPeriod) require.Equal(t, 7*time.Second, os.BlockHeaderFeederSpec.RunTimeout) - require.Equal(t, utils.NewBigI(4), os.BlockHeaderFeederSpec.EVMChainID) + require.Equal(t, big.NewI(4), os.BlockHeaderFeederSpec.EVMChainID) require.Equal(t, fromAddresses, os.BlockHeaderFeederSpec.FromAddresses) require.Equal(t, uint16(20), @@ -86,7 +86,7 @@ fromAddresses = ["0x469aA2CD13e037DC5236320783dCfd0e641c0559"] require.Equal(t, int32(256), os.BlockHeaderFeederSpec.WaitBlocks) require.Equal(t, 15*time.Second, os.BlockHeaderFeederSpec.PollPeriod) require.Equal(t, 30*time.Second, os.BlockHeaderFeederSpec.RunTimeout) - require.Equal(t, utils.NewBigI(4), os.BlockHeaderFeederSpec.EVMChainID) + require.Equal(t, big.NewI(4), os.BlockHeaderFeederSpec.EVMChainID) require.Equal(t, fromAddresses, os.BlockHeaderFeederSpec.FromAddresses) require.Equal(t, uint16(100), diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index b2f60384ec8..192fbb311d3 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -28,9 +28,9 @@ import ( // When adding a new field: // - consider including a unit suffix with the field name // - TOML is limited to int64/float64, so fields requiring greater range/precision must use non-standard types -// implementing encoding.TextMarshaler/TextUnmarshaler, like utils.Big and decimal.Decimal +// implementing encoding.TextMarshaler/TextUnmarshaler, like big.Big and decimal.Decimal // - std lib types that don't implement encoding.TextMarshaler/TextUnmarshaler (time.Duration, url.URL, big.Int) won't -// work as expected, and require wrapper types. See models.Duration, models.URL, utils.Big. +// work as expected, and require wrapper types. See models.Duration, models.URL, big.Big. type Config struct { toml.Core diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 6453fe05e00..85a4c99b862 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -26,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" legacy "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -95,7 +96,7 @@ var ( }, EVM: []*evmcfg.EVMConfig{ { - ChainID: utils.NewBigI(1), + ChainID: big.NewI(1), Chain: evmcfg.Chain{ FinalityDepth: ptr[uint32](26), FinalityTagEnabled: ptr[bool](false), @@ -112,7 +113,7 @@ var ( }, }}, { - ChainID: utils.NewBigI(42), + ChainID: big.NewI(42), Chain: evmcfg.Chain{ GasEstimator: evmcfg.GasEstimator{ PriceDefault: assets.NewWeiI(math.MaxInt64), @@ -125,7 +126,7 @@ var ( }, }}, { - ChainID: utils.NewBigI(137), + ChainID: big.NewI(137), Chain: evmcfg.Chain{ GasEstimator: evmcfg.GasEstimator{ Mode: ptr("FixedPrice"), @@ -463,7 +464,7 @@ func TestConfig_Marshal(t *testing.T) { } full.EVM = []*evmcfg.EVMConfig{ { - ChainID: utils.NewBigI(1), + ChainID: big.NewI(1), Enabled: ptr(false), Chain: evmcfg.Chain{ AutoCreateKey: ptr(false), @@ -1466,7 +1467,7 @@ func assertValidationError(t *testing.T, invalid interface{ Validate() error }, func TestConfig_setDefaults(t *testing.T) { var c Config - c.EVM = evmcfg.EVMConfigs{{ChainID: utils.NewBigI(99999133712345)}} + c.EVM = evmcfg.EVMConfigs{{ChainID: big.NewI(99999133712345)}} c.Cosmos = coscfg.TOMLConfigs{{ChainID: ptr("unknown cosmos chain")}} c.Solana = solana.TOMLConfigs{{ChainID: ptr("unknown solana chain")}} c.Starknet = stkcfg.TOMLConfigs{{ChainID: ptr("unknown starknet chain")}} diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index a0754fa0139..d89fbce12db 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -18,6 +18,7 @@ import ( stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" + ubig "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/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -31,12 +32,11 @@ import ( evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestCoreRelayerChainInteroperators(t *testing.T) { - evmChainID1, evmChainID2 := utils.NewBig(big.NewInt(1)), utils.NewBig(big.NewInt(2)) + evmChainID1, evmChainID2 := ubig.New(big.NewInt(1)), ubig.New(big.NewInt(2)) solanaChainID1, solanaChainID2 := "solana-id-1", "solana-id-2" starknetChainID1, starknetChainID2 := "starknet-id-1", "starknet-id-2" cosmosChainID1, cosmosChainID2 := "cosmos-id-1", "cosmos-id-2" @@ -71,7 +71,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: cfg, Nodes: evmcfg.EVMNodes{&node1_1, &node1_2}, } - id2 := utils.NewBig(big.NewInt(2)) + id2 := ubig.New(big.NewInt(2)) c.EVM = append(c.EVM, &evmcfg.EVMConfig{ ChainID: evmChainID2, Chain: evmcfg.Defaults(id2), diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index 865edb1b481..be61cde4d60 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -34,7 +35,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" pipeline_mocks "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestDelegate_ServicesForSpec(t *testing.T) { @@ -59,7 +59,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { }) t.Run("Spec with DirectRequestSpec", func(t *testing.T) { - spec := job.Job{DirectRequestSpec: &job.DirectRequestSpec{EVMChainID: (*utils.Big)(testutils.FixtureChainID)}, PipelineSpec: &pipeline.Spec{}} + spec := job.Job{DirectRequestSpec: &job.DirectRequestSpec{EVMChainID: (*ubig.Big)(testutils.FixtureChainID)}, PipelineSpec: &pipeline.Spec{}} services, err := delegate.ServicesForSpec(spec) require.NoError(t, err) assert.Len(t, services, 1) diff --git a/core/services/directrequest/validate.go b/core/services/directrequest/validate.go index bc31f09b685..271e720660f 100644 --- a/core/services/directrequest/validate.go +++ b/core/services/directrequest/validate.go @@ -5,18 +5,18 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type DirectRequestToml struct { ContractAddress ethkey.EIP55Address `toml:"contractAddress"` Requesters models.AddressCollection `toml:"requesters"` MinContractPayment *assets.Link `toml:"minContractPaymentLinkJuels"` - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` MinIncomingConfirmations null.Uint32 `toml:"minIncomingConfirmations"` } diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index ea6e6cae5ab..a1b4e9b2837 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" + "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" @@ -32,7 +33,6 @@ import ( ocr2 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" ) @@ -1073,7 +1073,7 @@ func (s *service) findExistingJobForOCR2(j *job.Job, qopts pg.QOpt) (int32, erro // findExistingJobForOCRFlux looks for existing job for OCR or flux func (s *service) findExistingJobForOCRFlux(j *job.Job, qopts pg.QOpt) (int32, error) { var address ethkey.EIP55Address - var evmChainID *utils.Big + var evmChainID *big.Big switch j.Type { case job.OffchainReporting: diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index bd0993825e2..d37393eba9e 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "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/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -41,7 +42,6 @@ import ( evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/versioning" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" ) @@ -1543,7 +1543,7 @@ func Test_Service_ListSpecsByJobProposalIDs(t *testing.T) { } func Test_Service_ApproveSpec(t *testing.T) { - var evmChainID *utils.Big + var evmChainID *big.Big address := ethkey.EIP55AddressFromAddress(common.Address{}) externalJobID := uuid.New() diff --git a/core/services/fluxmonitorv2/orm_test.go b/core/services/fluxmonitorv2/orm_test.go index 6e06a1e65b8..bcbec4363e2 100644 --- a/core/services/fluxmonitorv2/orm_test.go +++ b/core/services/fluxmonitorv2/orm_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestORM_MostRecentFluxMonitorRoundID(t *testing.T) { @@ -159,7 +159,7 @@ func makeJob(t *testing.T) *job.Job { IdleTimerDisabled: false, CreatedAt: time.Now(), UpdatedAt: time.Now(), - EVMChainID: (*utils.Big)(testutils.FixtureChainID), + EVMChainID: (*big.Big)(testutils.FixtureChainID), }, } } diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 21035140f54..c0622ba8066 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -41,7 +42,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const mercuryOracleTOML = `name = 'LINK / ETH | 0x0000000000000000000000000000000000000000000000000000000000000001 | verifier_proxy 0x0000000000000000000000000000000000000001' @@ -695,7 +695,7 @@ func TestORM_CreateJob_EVMChainID_Validation(t *testing.T) { } func TestORM_CreateJob_OCR_DuplicatedContractAddress(t *testing.T) { - customChainID := utils.NewBig(testutils.NewRandomEVMChainID()) + customChainID := big.New(testutils.NewRandomEVMChainID()) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { enabled := true @@ -764,7 +764,7 @@ func TestORM_CreateJob_OCR_DuplicatedContractAddress(t *testing.T) { } func TestORM_CreateJob_OCR2_DuplicatedContractAddress(t *testing.T) { - customChainID := utils.NewBig(testutils.NewRandomEVMChainID()) + customChainID := big.New(testutils.NewRandomEVMChainID()) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { enabled := true @@ -825,7 +825,7 @@ func TestORM_CreateJob_OCR2_DuplicatedContractAddress(t *testing.T) { } func TestORM_CreateJob_OCR2_Sending_Keys_Transmitter_Keys_Validations(t *testing.T) { - customChainID := utils.NewBig(testutils.NewRandomEVMChainID()) + customChainID := big.New(testutils.NewRandomEVMChainID()) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { enabled := true @@ -1021,7 +1021,7 @@ func Test_FindJob(t *testing.T) { // Create a config with multiple EVM chains. The test fixtures already load 1337 // Additional chains will need additional fixture statements to add a chain to evm_chains. config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - chainID := utils.NewBigI(1337) + chainID := big.NewI(1337) enabled := true c.EVM = append(c.EVM, &evmcfg.EVMConfig{ ChainID: chainID, @@ -1154,7 +1154,7 @@ func Test_FindJob(t *testing.T) { assert.Equal(t, job.ID, jbID) - _, err2 = orm.FindJobIDByAddress("not-existing", utils.NewBigI(0)) + _, err2 = orm.FindJobIDByAddress("not-existing", big.NewI(0)) require.Error(t, err2) require.ErrorIs(t, err2, sql.ErrNoRows) }) @@ -1222,7 +1222,7 @@ func Test_FindJobsByPipelineSpecIDs(t *testing.T) { jb, err := directrequest.ValidatedDirectRequestSpec(testspecs.GetDirectRequestSpec()) require.NoError(t, err) - jb.DirectRequestSpec.EVMChainID = utils.NewBigI(0) + jb.DirectRequestSpec.EVMChainID = big.NewI(0) err = orm.CreateJob(&jb) require.NoError(t, err) diff --git a/core/services/job/mocks/orm.go b/core/services/job/mocks/orm.go index 66602c60053..062c6e936bc 100644 --- a/core/services/job/mocks/orm.go +++ b/core/services/job/mocks/orm.go @@ -3,9 +3,10 @@ package mocks import ( - context "context" - common "github.com/ethereum/go-ethereum/common" + big "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + + context "context" ethkey "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -17,8 +18,6 @@ import ( pipeline "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - utils "github.com/smartcontractkit/chainlink/v2/core/utils" - uuid "github.com/google/uuid" ) @@ -223,7 +222,7 @@ func (_m *ORM) FindJobByExternalJobID(_a0 uuid.UUID, qopts ...pg.QOpt) (job.Job, } // FindJobIDByAddress provides a mock function with given fields: address, evmChainID, qopts -func (_m *ORM) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils.Big, qopts ...pg.QOpt) (int32, error) { +func (_m *ORM) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *big.Big, qopts ...pg.QOpt) (int32, error) { _va := make([]interface{}, len(qopts)) for _i := range qopts { _va[_i] = qopts[_i] @@ -239,16 +238,16 @@ func (_m *ORM) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils var r0 int32 var r1 error - if rf, ok := ret.Get(0).(func(ethkey.EIP55Address, *utils.Big, ...pg.QOpt) (int32, error)); ok { + if rf, ok := ret.Get(0).(func(ethkey.EIP55Address, *big.Big, ...pg.QOpt) (int32, error)); ok { return rf(address, evmChainID, qopts...) } - if rf, ok := ret.Get(0).(func(ethkey.EIP55Address, *utils.Big, ...pg.QOpt) int32); ok { + if rf, ok := ret.Get(0).(func(ethkey.EIP55Address, *big.Big, ...pg.QOpt) int32); ok { r0 = rf(address, evmChainID, qopts...) } else { r0 = ret.Get(0).(int32) } - if rf, ok := ret.Get(1).(func(ethkey.EIP55Address, *utils.Big, ...pg.QOpt) error); ok { + if rf, ok := ret.Get(1).(func(ethkey.EIP55Address, *big.Big, ...pg.QOpt) error); ok { r1 = rf(address, evmChainID, qopts...) } else { r1 = ret.Error(1) diff --git a/core/services/job/models.go b/core/services/job/models.go index 18a0cb79e2a..b21ecc12e72 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -246,7 +247,7 @@ type OCROracleSpec struct { ContractConfigTrackerSubscribeInterval models.Interval `toml:"contractConfigTrackerSubscribeInterval"` ContractConfigTrackerPollInterval models.Interval `toml:"contractConfigTrackerPollInterval"` ContractConfigConfirmations uint16 `toml:"contractConfigConfirmations"` - EVMChainID *utils.Big `toml:"evmChainID" db:"evm_chain_id"` + EVMChainID *big.Big `toml:"evmChainID" db:"evm_chain_id"` DatabaseTimeout *models.Interval `toml:"databaseTimeout"` ObservationGracePeriod *models.Interval `toml:"observationGracePeriod"` ContractTransmitterTransmitTimeout *models.Interval `toml:"contractTransmitterTransmitTimeout"` @@ -434,7 +435,7 @@ type DirectRequestSpec struct { MinIncomingConfirmations clnull.Uint32 `toml:"minIncomingConfirmations"` Requesters models.AddressCollection `toml:"requesters"` MinContractPayment *commonassets.Link `toml:"minContractPaymentLinkJuels"` - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` } @@ -475,9 +476,9 @@ type FluxMonitorSpec struct { DrumbeatRandomDelay time.Duration DrumbeatEnabled bool MinPayment *commonassets.Link - EVMChainID *utils.Big `toml:"evmChainID"` - CreatedAt time.Time `toml:"-"` - UpdatedAt time.Time `toml:"-"` + EVMChainID *big.Big `toml:"evmChainID"` + CreatedAt time.Time `toml:"-"` + UpdatedAt time.Time `toml:"-"` } type KeeperSpec struct { @@ -485,7 +486,7 @@ type KeeperSpec struct { ContractAddress ethkey.EIP55Address `toml:"contractAddress"` MinIncomingConfirmations *uint32 `toml:"minIncomingConfirmations"` FromAddress ethkey.EIP55Address `toml:"fromAddress"` - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` } @@ -512,7 +513,7 @@ type VRFSpec struct { CoordinatorAddress ethkey.EIP55Address `toml:"coordinatorAddress"` PublicKey secp256k1.PublicKey `toml:"publicKey"` MinIncomingConfirmations uint32 `toml:"minIncomingConfirmations"` - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` FromAddresses []ethkey.EIP55Address `toml:"fromAddresses"` PollPeriod time.Duration `toml:"pollPeriod"` // For v2 jobs RequestedConfsDelay int64 `toml:"requestedConfsDelay"` // For v2 jobs. Optional, defaults to 0 if not provided. @@ -586,7 +587,7 @@ type BlockhashStoreSpec struct { RunTimeout time.Duration `toml:"runTimeout"` // EVMChainID defines the chain ID for monitoring and storing of blockhashes. - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` // FromAddress is the sender address that should be used to store blockhashes. FromAddresses []ethkey.EIP55Address `toml:"fromAddresses"` @@ -635,7 +636,7 @@ type BlockHeaderFeederSpec struct { RunTimeout time.Duration `toml:"runTimeout"` // EVMChainID defines the chain ID for monitoring and storing of blockhashes. - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` // FromAddress is the sender address that should be used to store blockhashes. FromAddresses []ethkey.EIP55Address `toml:"fromAddresses"` @@ -662,11 +663,11 @@ type LegacyGasStationServerSpec struct { ForwarderAddress ethkey.EIP55Address `toml:"forwarderAddress"` // EVMChainID defines the chain ID from which the meta-transaction request originates. - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` // CCIPChainSelector is the CCIP chain selector that corresponds to EVMChainID param. // This selector is equivalent to (source) chainID specified in SendTransaction request - CCIPChainSelector *utils.Big `toml:"ccipChainSelector"` + CCIPChainSelector *big.Big `toml:"ccipChainSelector"` // FromAddress is the sender address that should be used to send meta-transactions FromAddresses []ethkey.EIP55Address `toml:"fromAddresses"` @@ -699,10 +700,10 @@ type LegacyGasStationSidecarSpec struct { RunTimeout time.Duration `toml:"runTimeout"` // EVMChainID defines the chain ID for the on-chain events tracked by sidecar - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` // CCIPChainSelector is the CCIP chain selector that corresponds to EVMChainID param - CCIPChainSelector *utils.Big `toml:"ccipChainSelector"` + CCIPChainSelector *big.Big `toml:"ccipChainSelector"` // CreatedAt is the time this job was created. CreatedAt time.Time `toml:"-"` @@ -772,7 +773,7 @@ type EALSpec struct { ForwarderAddress ethkey.EIP55Address `toml:"forwarderAddress"` // EVMChainID defines the chain ID from which the meta-transaction request originates. - EVMChainID *utils.Big `toml:"evmChainID"` + EVMChainID *big.Big `toml:"evmChainID"` // FromAddress is the sender address that should be used to send meta-transactions FromAddresses []ethkey.EIP55Address `toml:"fromAddresses"` diff --git a/core/services/job/orm.go b/core/services/job/orm.go index 482d3d851e4..b2cf2b2af4b 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" @@ -32,7 +33,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -52,7 +52,7 @@ type ORM interface { FindJobTx(ctx context.Context, id int32) (Job, error) FindJob(ctx context.Context, id int32) (Job, error) FindJobByExternalJobID(uuid uuid.UUID, qopts ...pg.QOpt) (Job, error) - FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils.Big, qopts ...pg.QOpt) (int32, error) + FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *big.Big, qopts ...pg.QOpt) (int32, error) FindOCR2JobIDByAddress(contractID string, feedID *common.Hash, qopts ...pg.QOpt) (int32, error) FindJobIDsWithBridge(name string) ([]int32, error) DeleteJob(id int32, qopts ...pg.QOpt) error @@ -832,7 +832,7 @@ func (o *orm) FindJobByExternalJobID(externalJobID uuid.UUID, qopts ...pg.QOpt) } // FindJobIDByAddress - finds a job id by contract address. Currently only OCR and FM jobs are supported -func (o *orm) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils.Big, qopts ...pg.QOpt) (jobID int32, err error) { +func (o *orm) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *big.Big, qopts ...pg.QOpt) (jobID int32, err error) { q := o.q.WithOpts(qopts...) err = q.Transaction(func(tx pg.Queryer) error { stmt := ` diff --git a/core/services/keeper/integration_test.go b/core/services/keeper/integration_test.go index 29a0b68702d..c35ebc81b7f 100644 --- a/core/services/keeper/integration_test.go +++ b/core/services/keeper/integration_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/basic_upkeep_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_logic1_3" @@ -36,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keeper" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" webpresenters "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -405,7 +405,7 @@ func TestKeeperForwarderEthIntegration(t *testing.T) { c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) // disable reorg protection for this test c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) // helps prevent missed heads c.EVM[0].Transactions.ForwardersEnabled = ptr(true) // Enable Operator Forwarder flow - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) scopedConfig := evmtest.NewChainScopedConfig(t, config) korm := keeper.NewORM(db, logger.TestLogger(t), scopedConfig.Database()) @@ -414,7 +414,7 @@ func TestKeeperForwarderEthIntegration(t *testing.T) { require.NoError(t, app.Start(testutils.Context(t))) forwarderORM := forwarders.NewORM(db, logger.TestLogger(t), config.Database()) - chainID := utils.Big(*backend.ConfiguredChainID()) + chainID := ubig.Big(*backend.ConfiguredChainID()) _, err = forwarderORM.CreateForwarder(fwdrAddress, chainID) require.NoError(t, err) @@ -431,7 +431,7 @@ func TestKeeperForwarderEthIntegration(t *testing.T) { KeeperSpec: &job.KeeperSpec{ FromAddress: nodeAddressEIP55, ContractAddress: regAddrEIP55, - EVMChainID: (*utils.Big)(testutils.SimulatedChainID), + EVMChainID: (*ubig.Big)(testutils.SimulatedChainID), }, SchemaVersion: 1, ForwardingAllowed: true, diff --git a/core/services/keeper/models.go b/core/services/keeper/models.go index fd5538b6047..8f72f0b22c9 100644 --- a/core/services/keeper/models.go +++ b/core/services/keeper/models.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -34,7 +35,7 @@ type UpkeepRegistration struct { LastRunBlockHeight int64 RegistryID int64 Registry Registry - UpkeepID *utils.Big + UpkeepID *big.Big LastKeeperIndex null.Int64 PositioningConstant int32 } @@ -60,16 +61,16 @@ func (upkeep UpkeepRegistration) PrettyID() string { return NewUpkeepIdentifier(upkeep.UpkeepID).String() } -func NewUpkeepIdentifier(i *utils.Big) *UpkeepIdentifier { +func NewUpkeepIdentifier(i *big.Big) *UpkeepIdentifier { val := UpkeepIdentifier(*i) return &val } -type UpkeepIdentifier utils.Big +type UpkeepIdentifier big.Big // String produces a hex encoded value, zero padded, prefixed with UpkeepPrefix func (ui UpkeepIdentifier) String() string { - val := utils.Big(ui) + val := big.Big(ui) result, err := utils.Uint256ToBytes(val.ToInt()) if err != nil { panic(errors.Wrap(err, "invariant, invalid upkeepID")) diff --git a/core/services/keeper/models_test.go b/core/services/keeper/models_test.go index ad81f47d8ee..729d1bbf231 100644 --- a/core/services/keeper/models_test.go +++ b/core/services/keeper/models_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) func TestUpkeepIdentifer_String(t *testing.T) { @@ -26,7 +26,7 @@ func TestUpkeepIdentifer_String(t *testing.T) { return } - result := NewUpkeepIdentifier(utils.NewBig(o)).String() + result := NewUpkeepIdentifier(ubig.New(o)).String() require.Equal(t, test.hex, result) }) } diff --git a/core/services/keeper/orm.go b/core/services/keeper/orm.go index 91883f8056c..fc8770cd864 100644 --- a/core/services/keeper/orm.go +++ b/core/services/keeper/orm.go @@ -7,10 +7,10 @@ import ( "github.com/lib/pq" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ORM implements ORM layer using PostgreSQL @@ -86,7 +86,7 @@ RETURNING * } // UpdateUpkeepLastKeeperIndex updates the last keeper index for an upkeep -func (korm ORM) UpdateUpkeepLastKeeperIndex(jobID int32, upkeepID *utils.Big, fromAddress ethkey.EIP55Address) error { +func (korm ORM) UpdateUpkeepLastKeeperIndex(jobID int32, upkeepID *big.Big, fromAddress ethkey.EIP55Address) error { _, err := korm.q.Exec(` UPDATE upkeep_registrations SET @@ -98,7 +98,7 @@ func (korm ORM) UpdateUpkeepLastKeeperIndex(jobID int32, upkeepID *utils.Big, fr } // BatchDeleteUpkeepsForJob deletes all upkeeps by the given IDs for the job with the given ID -func (korm ORM) BatchDeleteUpkeepsForJob(jobID int32, upkeepIDs []utils.Big) (int64, error) { +func (korm ORM) BatchDeleteUpkeepsForJob(jobID int32, upkeepIDs []big.Big) (int64, error) { strIds := []string{} for _, upkeepID := range upkeepIDs { strIds = append(strIds, upkeepID.String()) @@ -202,7 +202,7 @@ func loadUpkeepsRegistry(q pg.Queryer, upkeeps []UpkeepRegistration) error { return nil } -func (korm ORM) AllUpkeepIDsForRegistry(regID int64) (upkeeps []utils.Big, err error) { +func (korm ORM) AllUpkeepIDsForRegistry(regID int64) (upkeeps []big.Big, err error) { err = korm.q.Select(&upkeeps, ` SELECT upkeep_id FROM upkeep_registrations @@ -212,7 +212,7 @@ WHERE registry_id = $1 } // SetLastRunInfoForUpkeepOnJob sets the last run block height and the associated keeper index only if the new block height is greater than the previous. -func (korm ORM) SetLastRunInfoForUpkeepOnJob(jobID int32, upkeepID *utils.Big, height int64, fromAddress ethkey.EIP55Address, qopts ...pg.QOpt) (int64, error) { +func (korm ORM) SetLastRunInfoForUpkeepOnJob(jobID int32, upkeepID *big.Big, height int64, fromAddress ethkey.EIP55Address, qopts ...pg.QOpt) (int64, error) { res, err := korm.q.WithOpts(qopts...).Exec(` UPDATE upkeep_registrations SET last_run_block_height = $1, diff --git a/core/services/keeper/orm_test.go b/core/services/keeper/orm_test.go index d67baa09a06..e5b56e9511e 100644 --- a/core/services/keeper/orm_test.go +++ b/core/services/keeper/orm_test.go @@ -15,6 +15,7 @@ import ( "github.com/jmoiron/sqlx" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" @@ -45,7 +46,7 @@ func setupKeeperDB(t *testing.T) ( func newUpkeep(registry keeper.Registry, upkeepID int64) keeper.UpkeepRegistration { return keeper.UpkeepRegistration{ - UpkeepID: utils.NewBigI(upkeepID), + UpkeepID: ubig.NewI(upkeepID), ExecuteGas: executeGas, Registry: registry, RegistryID: registry.ID, @@ -103,7 +104,7 @@ func TestKeeperDB_UpsertUpkeep(t *testing.T) { registry, _ := cltest.MustInsertKeeperRegistry(t, db, orm, ethKeyStore, 0, 1, 20) upkeep := keeper.UpkeepRegistration{ - UpkeepID: utils.NewBigI(0), + UpkeepID: ubig.NewI(0), ExecuteGas: executeGas, Registry: registry, RegistryID: registry.ID, @@ -139,7 +140,7 @@ func TestKeeperDB_BatchDeleteUpkeepsForJob(t *testing.T) { registry, job := cltest.MustInsertKeeperRegistry(t, db, orm, ethKeyStore, 0, 1, 20) expectedUpkeepID := cltest.MustInsertUpkeepForRegistry(t, db, config.Database(), registry).UpkeepID - var upkeepIDs []utils.Big + var upkeepIDs []ubig.Big for i := 0; i < 2; i++ { upkeep := cltest.MustInsertUpkeepForRegistry(t, db, config.Database(), registry) upkeepIDs = append(upkeepIDs, *upkeep.UpkeepID) @@ -180,7 +181,7 @@ func TestKeeperDB_EligibleUpkeeps_Shuffle(t *testing.T) { assert.NoError(t, err) require.Len(t, eligibleUpkeeps, 100) - shuffled := [100]*utils.Big{} + shuffled := [100]*ubig.Big{} for i := 0; i < 100; i++ { shuffled[i] = eligibleUpkeeps[i].UpkeepID } @@ -372,8 +373,8 @@ func TestKeeperDB_AllUpkeepIDsForRegistry(t *testing.T) { require.NoError(t, err) // No upkeeps returned require.Len(t, upkeepIDs, 2) - require.Contains(t, upkeepIDs, *utils.NewBig(big.NewInt(3))) - require.Contains(t, upkeepIDs, *utils.NewBig(big.NewInt(8))) + require.Contains(t, upkeepIDs, *ubig.New(big.NewInt(3))) + require.Contains(t, upkeepIDs, *ubig.New(big.NewInt(8))) } func TestKeeperDB_UpdateUpkeepLastKeeperIndex(t *testing.T) { diff --git a/core/services/keeper/registry1_1_synchronizer_test.go b/core/services/keeper/registry1_1_synchronizer_test.go index fb0b1866c41..a4f03d4d34a 100644 --- a/core/services/keeper/registry1_1_synchronizer_test.go +++ b/core/services/keeper/registry1_1_synchronizer_test.go @@ -16,6 +16,7 @@ import ( evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" registry1_1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -24,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var registryConfig1_1 = registry1_1.GetConfig{ @@ -123,7 +123,7 @@ func Test_RegistrySynchronizer1_1_Start(t *testing.T) { func Test_RegistrySynchronizer_CalcPositioningConstant(t *testing.T) { t.Parallel() for _, upkeepID := range []int64{0, 1, 100, 10_000} { - _, err := keeper.CalcPositioningConstant(utils.NewBigI(upkeepID), cltest.NewEIP55Address()) + _, err := keeper.CalcPositioningConstant(ubig.NewI(upkeepID), cltest.NewEIP55Address()) require.NoError(t, err) } } diff --git a/core/services/keeper/registry1_3_synchronizer_test.go b/core/services/keeper/registry1_3_synchronizer_test.go index 6e3be4ea785..77bb873e1d0 100644 --- a/core/services/keeper/registry1_3_synchronizer_test.go +++ b/core/services/keeper/registry1_3_synchronizer_test.go @@ -17,6 +17,7 @@ import ( evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" registry1_3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -25,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var registryConfig1_3 = registry1_3.Config{ @@ -654,7 +654,7 @@ func Test_RegistrySynchronizer1_3_UpkeepPausedLog_UpkeepUnpausedLog(t *testing.T cltest.WaitForCount(t, db, "upkeep_registrations", 3) var upkeep keeper.UpkeepRegistration - err := db.Get(&upkeep, `SELECT * FROM upkeep_registrations WHERE upkeep_id = $1`, utils.NewBig(upkeepId)) + err := db.Get(&upkeep, `SELECT * FROM upkeep_registrations WHERE upkeep_id = $1`, ubig.New(upkeepId)) require.NoError(t, err) require.Equal(t, upkeepId.String(), upkeep.UpkeepID.String()) @@ -714,7 +714,7 @@ func Test_RegistrySynchronizer1_3_UpkeepCheckDataUpdatedLog(t *testing.T) { g.Eventually(func() []byte { var upkeep keeper.UpkeepRegistration - err := db.Get(&upkeep, `SELECT * FROM upkeep_registrations WHERE upkeep_id = $1`, utils.NewBig(upkeepId)) + err := db.Get(&upkeep, `SELECT * FROM upkeep_registrations WHERE upkeep_id = $1`, ubig.New(upkeepId)) require.NoError(t, err) return upkeep.CheckData }, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.Equal(newCheckData)) diff --git a/core/services/keeper/registry_synchronizer_process_logs.go b/core/services/keeper/registry_synchronizer_process_logs.go index 9e1aa3b410b..7b82f49ae4c 100644 --- a/core/services/keeper/registry_synchronizer_process_logs.go +++ b/core/services/keeper/registry_synchronizer_process_logs.go @@ -7,11 +7,11 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" registry1_1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" registry1_2 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2" registry1_3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func (rs *RegistrySynchronizer) processLogs() { @@ -109,7 +109,7 @@ func (rs *RegistrySynchronizer) handleUpkeepCancelled(broadcast log.Broadcast) e return errors.Wrap(err, "Unable to fetch cancelled upkeep ID from log") } - affected, err := rs.orm.BatchDeleteUpkeepsForJob(rs.job.ID, []utils.Big{*utils.NewBig(cancelledID)}) + affected, err := rs.orm.BatchDeleteUpkeepsForJob(rs.job.ID, []big.Big{*big.New(cancelledID)}) if err != nil { return errors.Wrap(err, "unable to batch delete upkeeps") } @@ -130,7 +130,7 @@ func (rs *RegistrySynchronizer) handleUpkeepRegistered(broadcast log.Broadcast) return errors.Wrap(err, "Unable to fetch upkeep ID from registration log") } - err = rs.syncUpkeep(&rs.registryWrapper, registry, utils.NewBig(upkeepID)) + err = rs.syncUpkeep(&rs.registryWrapper, registry, big.New(upkeepID)) if err != nil { return errors.Wrapf(err, "failed to sync upkeep, log: %v", broadcast.String()) } @@ -144,7 +144,7 @@ func (rs *RegistrySynchronizer) handleUpkeepPerformed(broadcast log.Broadcast) e if err != nil { return errors.Wrap(err, "Unable to fetch upkeep ID from performed log") } - rowsAffected, err := rs.orm.SetLastRunInfoForUpkeepOnJob(rs.job.ID, utils.NewBig(log.UpkeepID), int64(broadcast.RawLog().BlockNumber), ethkey.EIP55AddressFromAddress(log.FromKeeper)) + rowsAffected, err := rs.orm.SetLastRunInfoForUpkeepOnJob(rs.job.ID, big.New(log.UpkeepID), int64(broadcast.RawLog().BlockNumber), ethkey.EIP55AddressFromAddress(log.FromKeeper)) if err != nil { return errors.Wrap(err, "failed to set last run to 0") } @@ -171,7 +171,7 @@ func (rs *RegistrySynchronizer) handleUpkeepGasLimitSet(broadcast log.Broadcast) return errors.Wrap(err, "Unable to fetch upkeep ID from gas limit set log") } - err = rs.syncUpkeep(&rs.registryWrapper, registry, utils.NewBig(upkeepID)) + err = rs.syncUpkeep(&rs.registryWrapper, registry, big.New(upkeepID)) if err != nil { return errors.Wrapf(err, "failed to sync upkeep, log: %v", broadcast.String()) } @@ -191,7 +191,7 @@ func (rs *RegistrySynchronizer) handleUpkeepReceived(broadcast log.Broadcast) er return errors.Wrap(err, "Unable to fetch upkeep ID from received log") } - err = rs.syncUpkeep(&rs.registryWrapper, registry, utils.NewBig(upkeepID)) + err = rs.syncUpkeep(&rs.registryWrapper, registry, big.New(upkeepID)) if err != nil { return errors.Wrapf(err, "failed to sync upkeep, log: %v", broadcast.String()) } @@ -206,7 +206,7 @@ func (rs *RegistrySynchronizer) handleUpkeepMigrated(broadcast log.Broadcast) er return errors.Wrap(err, "Unable to fetch migrated upkeep ID from log") } - affected, err := rs.orm.BatchDeleteUpkeepsForJob(rs.job.ID, []utils.Big{*utils.NewBig(migratedID)}) + affected, err := rs.orm.BatchDeleteUpkeepsForJob(rs.job.ID, []big.Big{*big.New(migratedID)}) if err != nil { return errors.Wrap(err, "unable to batch delete upkeeps") } @@ -222,7 +222,7 @@ func (rs *RegistrySynchronizer) handleUpkeepPaused(broadcast log.Broadcast) erro return errors.Wrap(err, "Unable to fetch upkeep ID from upkeep paused log") } - _, err = rs.orm.BatchDeleteUpkeepsForJob(rs.job.ID, []utils.Big{*utils.NewBig(pausedUpkeepId)}) + _, err = rs.orm.BatchDeleteUpkeepsForJob(rs.job.ID, []big.Big{*big.New(pausedUpkeepId)}) if err != nil { return errors.Wrap(err, "unable to batch delete upkeeps") } @@ -243,7 +243,7 @@ func (rs *RegistrySynchronizer) handleUpkeepUnpaused(broadcast log.Broadcast) er return errors.Wrap(err, "Unable to fetch upkeep ID from upkeep unpaused log") } - err = rs.syncUpkeep(&rs.registryWrapper, registry, utils.NewBig(unpausedUpkeepId)) + err = rs.syncUpkeep(&rs.registryWrapper, registry, big.New(unpausedUpkeepId)) if err != nil { return errors.Wrapf(err, "failed to sync upkeep, log: %s", broadcast.String()) } @@ -264,7 +264,7 @@ func (rs *RegistrySynchronizer) handleUpkeepCheckDataUpdated(broadcast log.Broad return errors.Wrap(err, "Unable to parse update log from upkeep check data updated log") } - err = rs.syncUpkeep(&rs.registryWrapper, registry, utils.NewBig(updateLog.UpkeepID)) + err = rs.syncUpkeep(&rs.registryWrapper, registry, big.New(updateLog.UpkeepID)) if err != nil { return errors.Wrapf(err, "unable to update check data for upkeep %s", updateLog.UpkeepID.String()) } diff --git a/core/services/keeper/registry_synchronizer_sync.go b/core/services/keeper/registry_synchronizer_sync.go index 649ccd94066..f90e0bc85d7 100644 --- a/core/services/keeper/registry_synchronizer_sync.go +++ b/core/services/keeper/registry_synchronizer_sync.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -53,15 +54,15 @@ func (rs *RegistrySynchronizer) fullSyncUpkeeps(reg Registry) error { } activeSet := make(map[string]bool) - allActiveUpkeeps := make([]utils.Big, 0) + allActiveUpkeeps := make([]big.Big, 0) for _, upkeepID := range activeUpkeepIDs { activeSet[upkeepID.String()] = true - allActiveUpkeeps = append(allActiveUpkeeps, *utils.NewBig(upkeepID)) + allActiveUpkeeps = append(allActiveUpkeeps, *big.New(upkeepID)) } rs.batchSyncUpkeepsOnRegistry(reg, allActiveUpkeeps) // All upkeeps in existingUpkeepIDs, not in activeUpkeepIDs should be deleted - canceled := make([]utils.Big, 0) + canceled := make([]big.Big, 0) for _, upkeepID := range existingUpkeepIDs { if _, found := activeSet[upkeepID.ToInt().String()]; !found { canceled = append(canceled, upkeepID) @@ -75,7 +76,7 @@ func (rs *RegistrySynchronizer) fullSyncUpkeeps(reg Registry) error { // batchSyncUpkeepsOnRegistry syncs upkeeps at a time in parallel // for all the IDs within newUpkeeps slice -func (rs *RegistrySynchronizer) batchSyncUpkeepsOnRegistry(reg Registry, newUpkeeps []utils.Big) { +func (rs *RegistrySynchronizer) batchSyncUpkeepsOnRegistry(reg Registry, newUpkeeps []big.Big) { wg := sync.WaitGroup{} wg.Add(len(newUpkeeps)) chSyncUpkeepQueue := make(chan struct{}, rs.syncUpkeepQueueSize) @@ -93,7 +94,7 @@ func (rs *RegistrySynchronizer) batchSyncUpkeepsOnRegistry(reg Registry, newUpke wg.Wait() } -func (rs *RegistrySynchronizer) syncUpkeepWithCallback(getter upkeepGetter, registry Registry, upkeepID *utils.Big, doneCallback func()) { +func (rs *RegistrySynchronizer) syncUpkeepWithCallback(getter upkeepGetter, registry Registry, upkeepID *big.Big, doneCallback func()) { defer doneCallback() if err := rs.syncUpkeep(getter, registry, upkeepID); err != nil { @@ -104,7 +105,7 @@ func (rs *RegistrySynchronizer) syncUpkeepWithCallback(getter upkeepGetter, regi } } -func (rs *RegistrySynchronizer) syncUpkeep(getter upkeepGetter, registry Registry, upkeepID *utils.Big) error { +func (rs *RegistrySynchronizer) syncUpkeep(getter upkeepGetter, registry Registry, upkeepID *big.Big) error { upkeep, err := getter.GetUpkeep(nil, upkeepID.ToInt()) if err != nil { return errors.Wrap(err, "failed to get upkeep config") @@ -173,9 +174,9 @@ func (rs *RegistrySynchronizer) newRegistryFromChain() (Registry, error) { // CalcPositioningConstant calculates a positioning constant. // The positioning constant is fixed because upkeepID and registryAddress are immutable -func CalcPositioningConstant(upkeepID *utils.Big, registryAddress ethkey.EIP55Address) (int32, error) { +func CalcPositioningConstant(upkeepID *big.Big, registryAddress ethkey.EIP55Address) (int32, error) { upkeepBytes := make([]byte, binary.MaxVarintLen64) - binary.PutVarint(upkeepBytes, upkeepID.Mod(utils.NewBigI(math.MaxInt64)).Int64()) + binary.PutVarint(upkeepBytes, upkeepID.Mod(big.NewI(math.MaxInt64)).Int64()) bytesToHash := utils.ConcatBytes(upkeepBytes, registryAddress.Bytes()) checksum, err := utils.Keccak256(bytesToHash) if err != nil { diff --git a/core/services/keeper/registry_synchronizer_sync_test.go b/core/services/keeper/registry_synchronizer_sync_test.go index 10a51679c5e..e6f42a83201 100644 --- a/core/services/keeper/registry_synchronizer_sync_test.go +++ b/core/services/keeper/registry_synchronizer_sync_test.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // GetUpkeepFailure implements the upkeepGetter interface with an induced error and nil @@ -42,7 +42,7 @@ func TestSyncUpkeepWithCallback_UpkeepNotFound(t *testing.T) { t.FailNow() } - id := utils.NewBig(o) + id := ubig.New(o) count := 0 doneFunc := func() { count++ diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 123b1dc0de1..61ccca956f4 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -23,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "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/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -39,7 +40,7 @@ import ( ) func newHead() evmtypes.Head { - return evmtypes.NewHead(big.NewInt(20), utils.NewHash(), utils.NewHash(), 1000, utils.NewBigI(0)) + return evmtypes.NewHead(big.NewInt(20), utils.NewHash(), utils.NewHash(), 1000, ubig.NewI(0)) } func mockEstimator(t *testing.T) gas.EvmFeeEstimator { @@ -128,7 +129,7 @@ func Test_UpkeepExecuter_PerformsUpkeep_Happy(t *testing.T) { t.Run("runs upkeep on triggering block number", func(t *testing.T) { db, config, ethMock, executer, registry, upkeep, job, jpv2, txm, _, _, _ := setup(t, mockEstimator(t), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) gasLimit := 5_000_000 + config.Keeper().Registry().PerformGasOverhead() @@ -173,7 +174,7 @@ func Test_UpkeepExecuter_PerformsUpkeep_Happy(t *testing.T) { runTest := func(t *testing.T, eip1559 bool) { db, config, ethMock, executer, registry, upkeep, job, jpv2, txm, _, _, _ := setup(t, mockEstimator(t), func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = &eip1559 - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) gasLimit := 5_000_000 + config.Keeper().Registry().PerformGasOverhead() @@ -226,7 +227,7 @@ func Test_UpkeepExecuter_PerformsUpkeep_Happy(t *testing.T) { t.Run("errors if submission key not found", func(t *testing.T) { _, _, ethMock, executer, registry, _, job, jpv2, _, keyStore, _, _ := setup(t, mockEstimator(t), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) // replace expected key with random one @@ -263,7 +264,7 @@ func Test_UpkeepExecuter_PerformsUpkeep_Happy(t *testing.T) { registry, jb := cltest.MustInsertKeeperRegistry(t, db, orm, keyStore.Eth(), 0, 1, 20) // change chain ID to non-configured chain - jb.KeeperSpec.EVMChainID = (*utils.Big)(big.NewInt(999)) + jb.KeeperSpec.EVMChainID = (*ubig.Big)(big.NewInt(999)) cltest.MustInsertUpkeepForRegistry(t, db, ch.Config().Database(), registry) lggr := logger.TestLogger(t) executer := keeper.NewUpkeepExecuter(jb, orm, jpv2.Pr, ethMock, ch.HeadBroadcaster(), ch.GasEstimator(), lggr, ch.Config().Keeper(), jb.KeeperSpec.FromAddress.Address()) @@ -278,7 +279,7 @@ func Test_UpkeepExecuter_PerformsUpkeep_Happy(t *testing.T) { t.Run("triggers if heads are skipped but later heads arrive within range", func(t *testing.T) { db, config, ethMock, executer, registry, upkeep, job, jpv2, txm, _, _, _ := setup(t, mockEstimator(t), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) etxs := []cltest.Awaiter{ @@ -321,7 +322,7 @@ func Test_UpkeepExecuter_PerformsUpkeep_Error(t *testing.T) { db, _, ethMock, executer, registry, _, _, _, _, _, _, _ := setup(t, mockEstimator(t), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) var wasCalled atomic.Bool diff --git a/core/services/keeper/upkeep_executer_unit_test.go b/core/services/keeper/upkeep_executer_unit_test.go index a8fc46319cd..8589720ca5f 100644 --- a/core/services/keeper/upkeep_executer_unit_test.go +++ b/core/services/keeper/upkeep_executer_unit_test.go @@ -7,11 +7,11 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type registry struct { @@ -34,7 +34,7 @@ func TestBuildJobSpec(t *testing.T) { ContractAddress: contract, }} - upkeepID := utils.NewBigI(4) + upkeepID := big.NewI(4) upkeep := UpkeepRegistration{ Registry: Registry{ FromAddress: from, diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index 42d6c575376..3935a44558b 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -673,7 +674,7 @@ func Test_EthKeyStore_Delete(t *testing.T) { _, addr1 := cltest.MustInsertRandomKey(t, ks) _, addr2 := cltest.MustInsertRandomKey(t, ks) - cltest.MustInsertRandomKey(t, ks, *utils.NewBig(testutils.SimulatedChainID)) + cltest.MustInsertRandomKey(t, ks, *ubig.New(testutils.SimulatedChainID)) require.NoError(t, ks.Add(addr1, testutils.SimulatedChainID)) require.NoError(t, ks.Enable(addr1, testutils.SimulatedChainID)) diff --git a/core/services/keystore/keys/ethkey/models.go b/core/services/keystore/keys/ethkey/models.go index b90503c3ed6..df4c474b7b9 100644 --- a/core/services/keystore/keys/ethkey/models.go +++ b/core/services/keystore/keys/ethkey/models.go @@ -3,13 +3,13 @@ package ethkey import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) type State struct { ID int32 Address EIP55Address - EVMChainID utils.Big + EVMChainID big.Big Disabled bool CreatedAt time.Time UpdatedAt time.Time diff --git a/core/services/ocr/database.go b/core/services/ocr/database.go index 524dfa0e7bb..cec9596bb91 100644 --- a/core/services/ocr/database.go +++ b/core/services/ocr/database.go @@ -15,9 +15,9 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type db struct { @@ -161,7 +161,7 @@ func (d *db) WriteConfig(ctx context.Context, c ocrtypes.ContractConfig) error { } func (d *db) StorePendingTransmission(ctx context.Context, k ocrtypes.ReportTimestamp, p ocrtypes.PendingTransmission) error { - median := utils.NewBig(p.Median) + median := big.New(p.Median) var rs [][]byte var ss [][]byte // Note: p.Rs and p.Ss are of type [][32]byte. @@ -232,7 +232,7 @@ WHERE ocr_oracle_spec_id = $1 AND config_digest = $2 k := ocrtypes.ReportTimestamp{} p := ocrtypes.PendingTransmission{} - var median utils.Big + var median big.Big var rs [][]byte var ss [][]byte var vs []byte diff --git a/core/services/ocr2/delegate_test.go b/core/services/ocr2/delegate_test.go index b55e128119d..3da0c9cbfd6 100644 --- a/core/services/ocr2/delegate_test.go +++ b/core/services/ocr2/delegate_test.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -23,11 +24,10 @@ import ( ocr2validate "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestGetEVMEffectiveTransmitterID(t *testing.T) { - customChainID := utils.NewBig(testutils.NewRandomEVMChainID()) + customChainID := big.New(testutils.NewRandomEVMChainID()) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { enabled := true diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry_test.go index 0e0ceba7160..51448db35cf 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry_test.go @@ -16,8 +16,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestGetActiveUpkeepKeys(t *testing.T) { @@ -176,8 +176,8 @@ func TestPollLogs(t *testing.T) { InputStart: 250, InputEnd: 500, OutputLogs: []logpoller.Log{ - {EvmChainId: utils.NewBig(big.NewInt(5)), LogIndex: 1}, - {EvmChainId: utils.NewBig(big.NewInt(6)), LogIndex: 2}, + {EvmChainId: ubig.New(big.NewInt(5)), LogIndex: 1}, + {EvmChainId: ubig.New(big.NewInt(6)), LogIndex: 2}, }, OutputErr: nil, }, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go index 89b19b4a819..65fe3c85fb8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go @@ -21,12 +21,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestLogRecoverer_GetRecoverables(t *testing.T) { @@ -1023,7 +1023,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { LogsWithSigsFn: func(start, end int64, eventSigs []common.Hash, address common.Address, qopts ...pg.QOpt) ([]logpoller.Log, error) { return []logpoller.Log{ { - EvmChainId: utils.NewBig(big.NewInt(1)), + EvmChainId: ubig.New(big.NewInt(1)), LogIndex: 3, BlockHash: [32]byte{1}, BlockNumber: 80, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go index f3e4402092c..0d097ceadb4 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go @@ -19,6 +19,7 @@ import ( types3 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" @@ -27,7 +28,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestPollLogs(t *testing.T) { @@ -132,8 +132,8 @@ func TestPollLogs(t *testing.T) { InputStart: 250, InputEnd: 500, OutputLogs: []logpoller.Log{ - {EvmChainId: utils.NewBig(big.NewInt(5)), LogIndex: 1}, - {EvmChainId: utils.NewBig(big.NewInt(6)), LogIndex: 2}, + {EvmChainId: ubig.New(big.NewInt(5)), LogIndex: 1}, + {EvmChainId: ubig.New(big.NewInt(6)), LogIndex: 2}, }, OutputErr: nil, }, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm.go index c918ad595fa..a5bd738de4c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm.go @@ -7,18 +7,18 @@ import ( "github.com/jmoiron/sqlx" "github.com/lib/pq" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type orm struct { - chainID *utils.Big + chainID *ubig.Big q pg.Q } type persistedStateRecord struct { - UpkeepID *utils.Big + UpkeepID *ubig.Big WorkID string CompletionState uint8 BlockNumber int64 @@ -29,7 +29,7 @@ type persistedStateRecord struct { // NewORM creates an ORM scoped to chainID. func NewORM(chainID *big.Int, db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) *orm { return &orm{ - chainID: utils.NewBig(chainID), + chainID: ubig.New(chainID), q: pg.NewQ(db, lggr.Named("ORM"), cfg), } } @@ -43,12 +43,12 @@ func (o *orm) BatchInsertRecords(state []persistedStateRecord, qopts ...pg.QOpt) } type row struct { - EvmChainId *utils.Big + EvmChainId *ubig.Big WorkId string CompletionState uint8 BlockNumber int64 InsertedAt time.Time - UpkeepId *utils.Big + UpkeepId *ubig.Big IneligibilityReason uint8 } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm_test.go index 54ca7285dd0..bfd131b5055 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm_test.go @@ -9,10 +9,10 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "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/utils" ) func TestInsertSelectDelete(t *testing.T) { @@ -23,7 +23,7 @@ func TestInsertSelectDelete(t *testing.T) { inserted := []persistedStateRecord{ { - UpkeepID: utils.NewBig(big.NewInt(2)), + UpkeepID: ubig.New(big.NewInt(2)), WorkID: "0x1", CompletionState: 100, BlockNumber: 2, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go index 4a4de5ea1ad..19b3c46f502 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -225,7 +226,7 @@ func (u *upkeepStateStore) upsertStateRecord(ctx context.Context, workID string, u.cache[workID] = record u.pendingRecords = append(u.pendingRecords, persistedStateRecord{ - UpkeepID: utils.NewBig(upkeepID), + UpkeepID: ubig.New(upkeepID), WorkID: record.workID, CompletionState: uint8(record.state), IneligibilityReason: reason, diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index 58c1e38e017..6674b0828b1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -37,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/basic_upkeep_contract" @@ -60,7 +61,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( @@ -448,7 +448,7 @@ func setupForwarderForNode( // add forwarder address to be tracked in db forwarderORM := forwarders.NewORM(app.GetSqlxDB(), logger.TestLogger(t), app.GetConfig().Database()) - chainID := utils.Big(*backend.Blockchain().Config().ChainID) + chainID := ubig.Big(*backend.Blockchain().Config().ChainID) _, err = forwarderORM.CreateForwarder(faddr, chainID) require.NoError(t, err) diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index 4a583e5db3f..c559fb27fb7 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -33,6 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/mock_v3_aggregator_contract" @@ -285,7 +286,7 @@ func setupNodeOCR2( // Add the forwarder to the node's forwarder manager. forwarderORM := forwarders.NewORM(app.GetSqlxDB(), logger.TestLogger(t), config.Database()) - chainID := utils.Big(*b.Blockchain().Config().ChainID) + chainID := ubig.Big(*b.Blockchain().Config().ChainID) _, err = forwarderORM.CreateForwarder(faddr, chainID) require.NoError(t, err) effectiveTransmitter = faddr diff --git a/core/services/ocr2/plugins/s4/integration_test.go b/core/services/ocr2/plugins/s4/integration_test.go index 54f0f02ad98..8efe38f8e2d 100644 --- a/core/services/ocr2/plugins/s4/integration_test.go +++ b/core/services/ocr2/plugins/s4/integration_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -16,7 +17,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/s4" "github.com/smartcontractkit/chainlink/v2/core/services/pg" s4_svc "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -357,14 +357,14 @@ func TestS4Integration_RandomState(t *testing.T) { type user struct { privateKey *ecdsa.PrivateKey - address *utils.Big + address *big.Big } nUsers := 100 users := make([]user, nUsers) for i := 0; i < nUsers; i++ { pk, addr := testutils.NewPrivateKeyAndAddress(t) - users[i] = user{pk, utils.NewBig(addr.Big())} + users[i] = user{pk, big.New(addr.Big())} } // generating test records diff --git a/core/services/ocr2/plugins/s4/messages.go b/core/services/ocr2/plugins/s4/messages.go index 8f3a64f4e23..c9695d2db76 100644 --- a/core/services/ocr2/plugins/s4/messages.go +++ b/core/services/ocr2/plugins/s4/messages.go @@ -4,8 +4,8 @@ import ( "bytes" "math/big" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum/common" "google.golang.org/protobuf/proto" @@ -58,8 +58,8 @@ func UnmarshalRows(data []byte) ([]*Row, error) { return rows.Rows, nil } -func UnmarshalAddress(address []byte) *utils.Big { - return utils.NewBig(new(big.Int).SetBytes(address)) +func UnmarshalAddress(address []byte) *ubig.Big { + return ubig.New(new(big.Int).SetBytes(address)) } func (row *Row) VerifySignature() error { diff --git a/core/services/ocr2/plugins/s4/plugin.go b/core/services/ocr2/plugins/s4/plugin.go index 68bd9fd2142..fcb025b21cd 100644 --- a/core/services/ocr2/plugins/s4/plugin.go +++ b/core/services/ocr2/plugins/s4/plugin.go @@ -4,9 +4,9 @@ import ( "context" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/pkg/errors" "github.com/smartcontractkit/libocr/commontypes" @@ -123,7 +123,7 @@ func (c *plugin) Observation(ctx context.Context, ts types.ReportTimestamp, quer c.logger.Error("ORM GetSnapshot error", commontypes.LogFields{"err": err}) } else { type rkey struct { - address *utils.Big + address *big.Big slotID uint } diff --git a/core/services/ocr2/plugins/s4/plugin_test.go b/core/services/ocr2/plugins/s4/plugin_test.go index e2b5d21b847..e0aa84183e1 100644 --- a/core/services/ocr2/plugins/s4/plugin_test.go +++ b/core/services/ocr2/plugins/s4/plugin_test.go @@ -5,13 +5,13 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/s4" s4_svc "github.com/smartcontractkit/chainlink/v2/core/services/s4" s4_mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -51,7 +51,7 @@ func generateTestRows(t *testing.T, n int, ttl time.Duration) []*s4.Row { func generateTestOrmRow(t *testing.T, ttl time.Duration, version uint64, confimed bool) *s4_svc.Row { priv, addr := testutils.NewPrivateKeyAndAddress(t) row := &s4_svc.Row{ - Address: utils.NewBig(addr.Big()), + Address: big.New(addr.Big()), SlotId: 0, Version: version, Confirmed: confimed, @@ -296,7 +296,7 @@ func TestPlugin_Query(t *testing.T) { for i := 0; i < 256; i++ { var thisAddress common.Address thisAddress[0] = byte(i) - ormRows[i].Address = utils.NewBig(thisAddress.Big()) + ormRows[i].Address = big.New(thisAddress.Big()) } versions := rowsToShapshotRows(ormRows) @@ -322,7 +322,7 @@ func TestPlugin_Query(t *testing.T) { assert.Len(t, qq.Rows, 16) for _, r := range qq.Rows { thisAddress := s4.UnmarshalAddress(r.Address) - assert.True(t, ar.Contains((*utils.Big)(thisAddress))) + assert.True(t, ar.Contains((*big.Big)(thisAddress))) } ar.Advance() diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index 9c90eea180a..7627a627dea 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -19,6 +19,7 @@ import ( mercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" mercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -147,7 +148,7 @@ func TestGetChainID(t *testing.T) { } j.Type = job.Type(pipeline.OffchainReportingJobType) - j.OCROracleSpec.EVMChainID = (*utils.Big)(big.NewInt(1234567890)) + j.OCROracleSpec.EVMChainID = (*ubig.Big)(big.NewInt(1234567890)) assert.Equal(t, "1234567890", e.getChainID()) j.Type = job.Type(pipeline.OffchainReporting2JobType) @@ -206,7 +207,7 @@ func TestSendEATelemetry(t *testing.T) { OCROracleSpec: &job.OCROracleSpec{ ContractAddress: ethkey.EIP55AddressFromAddress(feedAddress), CaptureEATelemetry: true, - EVMChainID: (*utils.Big)(big.NewInt(9)), + EVMChainID: (*ubig.Big)(big.NewInt(9)), }, } diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index dcbbfd9c97e..92a6c25da39 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -13,6 +13,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -531,7 +532,7 @@ func Test_GetUnfinishedRuns_Keepers(t *testing.T) { FromAddress: cltest.NewEIP55Address(), CreatedAt: timestamp, UpdatedAt: timestamp, - EVMChainID: (*utils.Big)(&cltest.FixtureChainID), + EVMChainID: (*big.Big)(&cltest.FixtureChainID), }, ExternalJobID: uuid.MustParse("0EEC7E1D-D0D2-476C-A1A8-72DFB6633F46"), PipelineSpec: &pipeline.Spec{ @@ -630,7 +631,7 @@ func Test_GetUnfinishedRuns_DirectRequest(t *testing.T) { ContractAddress: cltest.NewEIP55Address(), CreatedAt: timestamp, UpdatedAt: timestamp, - EVMChainID: (*utils.Big)(&cltest.FixtureChainID), + EVMChainID: (*big.Big)(&cltest.FixtureChainID), }, ExternalJobID: uuid.MustParse("0EEC7E1D-D0D2-476C-A1A8-72DFB6633F46"), PipelineSpec: &pipeline.Spec{ diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 1cebba2faf9..7b9930e4daa 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "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/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" @@ -29,7 +30,7 @@ import ( ) func newHead() evmtypes.Head { - return evmtypes.Head{Number: 42, EVMChainID: utils.NewBigI(0)} + return evmtypes.Head{Number: 42, EVMChainID: ubig.NewI(0)} } func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainContainer { diff --git a/core/services/relay/evm/median_test.go b/core/services/relay/evm/median_test.go index 4286290d289..9c474006aa7 100644 --- a/core/services/relay/evm/median_test.go +++ b/core/services/relay/evm/median_test.go @@ -9,11 +9,11 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestNewMedianProvider(t *testing.T) { @@ -37,7 +37,7 @@ func TestNewMedianProvider(t *testing.T) { }) t.Run("invalid contractID", func(t *testing.T) { - relayConfig := evmtypes.RelayConfig{ChainID: utils.NewBig(chainID)} + relayConfig := evmtypes.RelayConfig{ChainID: big.New(chainID)} rc, err2 := json.Marshal(&relayConfig) require.NoError(t, err2) rargsBadContractID := commontypes.RelayArgs{ContractID: "NotAContractID", RelayConfig: rc} diff --git a/core/services/relay/evm/relayer_extender_test.go b/core/services/relay/evm/relayer_extender_test.go index 3f4a3749ac8..af15461aee9 100644 --- a/core/services/relay/evm/relayer_extender_test.go +++ b/core/services/relay/evm/relayer_extender_test.go @@ -10,6 +10,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -17,7 +18,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestChainRelayExtenders(t *testing.T) { @@ -28,7 +28,7 @@ func TestChainRelayExtenders(t *testing.T) { one := uint32(1) c.EVM[0].MinIncomingConfirmations = &one t := true - c.EVM = append(c.EVM, &toml.EVMConfig{ChainID: utils.NewBig(newId), Enabled: &t, Chain: toml.Defaults(nil)}) + c.EVM = append(c.EVM, &toml.EVMConfig{ChainID: ubig.New(newId), Enabled: &t, Chain: toml.Defaults(nil)}) }) db := pgtest.NewSqlxDB(t) kst := cltest.NewKeyStore(t, db, cfg.Database()) diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index 24afb65c55a..129ccb4a5e6 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) type ChainReaderConfig struct { @@ -47,7 +47,7 @@ const ( ) type RelayConfig struct { - ChainID *utils.Big `json:"chainID"` + ChainID *big.Big `json:"chainID"` FromBlock uint64 `json:"fromBlock"` EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` ConfigContractAddress *common.Address `json:"configContractAddress"` diff --git a/core/services/relay/evm/types/types_test.go b/core/services/relay/evm/types/types_test.go index dec368614ec..6952c35a706 100644 --- a/core/services/relay/evm/types/types_test.go +++ b/core/services/relay/evm/types/types_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -// ChainID *utils.Big `json:"chainID"` +// ChainID *big.Big `json:"chainID"` // FromBlock uint64 `json:"fromBlock"` // // Contract-specific diff --git a/core/services/s4/address_range.go b/core/services/s4/address_range.go index 679bb3b846a..e7b60ecb479 100644 --- a/core/services/s4/address_range.go +++ b/core/services/s4/address_range.go @@ -5,23 +5,23 @@ import ( "errors" "math/big" - "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/ethereum/go-ethereum/common" + + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) // AddressRange represents a range of Ethereum addresses. type AddressRange struct { // MinAddress (inclusive). - MinAddress *utils.Big + MinAddress *ubig.Big // MaxAddress (inclusive). - MaxAddress *utils.Big + MaxAddress *ubig.Big } var ( ErrInvalidIntervals = errors.New("invalid intervals value") - MinAddress = utils.NewBig(common.BytesToAddress(bytes.Repeat([]byte{0x00}, common.AddressLength)).Big()) - MaxAddress = utils.NewBig(common.BytesToAddress(bytes.Repeat([]byte{0xff}, common.AddressLength)).Big()) + MinAddress = ubig.New(common.BytesToAddress(bytes.Repeat([]byte{0x00}, common.AddressLength)).Big()) + MaxAddress = ubig.New(common.BytesToAddress(bytes.Repeat([]byte{0xff}, common.AddressLength)).Big()) ) // NewFullAddressRange creates AddressRange for all address space: 0x00..-0xFF.. @@ -33,7 +33,7 @@ func NewFullAddressRange() *AddressRange { } // NewSingleAddressRange creates AddressRange for a single address. -func NewSingleAddressRange(address *utils.Big) (*AddressRange, error) { +func NewSingleAddressRange(address *ubig.Big) (*AddressRange, error) { if address == nil || address.Cmp(MinAddress) < 0 || address.Cmp(MaxAddress) > 0 { return nil, errors.New("invalid address") } @@ -56,12 +56,12 @@ func NewInitialAddressRangeForIntervals(intervals uint) (*AddressRange, error) { } divisor := big.NewInt(int64(intervals)) - maxPlusOne := MaxAddress.Add(utils.NewBigI(1)) - interval := utils.NewBig(new(big.Int).Div(maxPlusOne.ToInt(), divisor)) + maxPlusOne := MaxAddress.Add(ubig.NewI(1)) + interval := ubig.New(new(big.Int).Div(maxPlusOne.ToInt(), divisor)) return &AddressRange{ MinAddress: MinAddress, - MaxAddress: MinAddress.Add(interval).Sub(utils.NewBigI(1)), + MaxAddress: MinAddress.Add(interval).Sub(ubig.NewI(1)), }, nil } @@ -80,7 +80,7 @@ func (r *AddressRange) Advance() { if r.MinAddress.Cmp(MaxAddress) >= 0 { r.MinAddress = MinAddress - r.MaxAddress = MinAddress.Add(interval).Sub(utils.NewBigI(1)) + r.MaxAddress = MinAddress.Add(interval).Sub(ubig.NewI(1)) } if r.MaxAddress.Cmp(MaxAddress) > 0 { @@ -89,7 +89,7 @@ func (r *AddressRange) Advance() { } // Contains returns true if the given address belongs to the range. -func (r *AddressRange) Contains(address *utils.Big) bool { +func (r *AddressRange) Contains(address *ubig.Big) bool { if r == nil { return false } @@ -97,9 +97,9 @@ func (r *AddressRange) Contains(address *utils.Big) bool { } // Interval returns the interval between max and min address plus one. -func (r *AddressRange) Interval() *utils.Big { +func (r *AddressRange) Interval() *ubig.Big { if r == nil { return nil } - return r.MaxAddress.Sub(r.MinAddress).Add(utils.NewBigI(1)) + return r.MaxAddress.Sub(r.MinAddress).Add(ubig.NewI(1)) } diff --git a/core/services/s4/address_range_test.go b/core/services/s4/address_range_test.go index bbd4d3baa54..2b12acd08eb 100644 --- a/core/services/s4/address_range_test.go +++ b/core/services/s4/address_range_test.go @@ -3,8 +3,8 @@ package s4_test import ( "testing" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" ) @@ -26,7 +26,7 @@ func TestAddressRange_NewFullAddressRange(t *testing.T) { func TestAddressRange_NewSingleAddressRange(t *testing.T) { t.Parallel() - addr := utils.NewBigI(0x123) + addr := big.NewI(0x123) sar, err := s4.NewSingleAddressRange(addr) assert.NoError(t, err) assert.Equal(t, addr, sar.MinAddress) @@ -94,10 +94,10 @@ func TestAddressRange_Contains(t *testing.T) { assert.NoError(t, err) assert.True(t, r.Contains(r.MinAddress)) assert.True(t, r.Contains(r.MaxAddress)) - assert.False(t, r.Contains(r.MaxAddress.Add(utils.NewBigI(1)))) + assert.False(t, r.Contains(r.MaxAddress.Add(big.NewI(1)))) r.Advance() assert.True(t, r.Contains(r.MinAddress)) assert.True(t, r.Contains(r.MaxAddress)) - assert.False(t, r.Contains(r.MinAddress.Sub(utils.NewBigI(1)))) + assert.False(t, r.Contains(r.MinAddress.Sub(big.NewI(1)))) } diff --git a/core/services/s4/in_memory_orm.go b/core/services/s4/in_memory_orm.go index bb67d2d63a4..28b50ce430c 100644 --- a/core/services/s4/in_memory_orm.go +++ b/core/services/s4/in_memory_orm.go @@ -5,8 +5,8 @@ import ( "sync" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type key struct { @@ -32,7 +32,7 @@ func NewInMemoryORM() ORM { } } -func (o *inMemoryOrm) Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*Row, error) { +func (o *inMemoryOrm) Get(address *big.Big, slotId uint, qopts ...pg.QOpt) (*Row, error) { o.mu.RLock() defer o.mu.RUnlock() @@ -103,7 +103,7 @@ func (o *inMemoryOrm) GetSnapshot(addressRange *AddressRange, qopts ...pg.QOpt) for _, mrow := range o.rows { if mrow.Row.Expiration > now { rows = append(rows, &SnapshotRow{ - Address: utils.NewBig(mrow.Row.Address.ToInt()), + Address: big.New(mrow.Row.Address.ToInt()), SlotId: mrow.Row.SlotId, Version: mrow.Row.Version, Expiration: mrow.Row.Expiration, diff --git a/core/services/s4/in_memory_orm_test.go b/core/services/s4/in_memory_orm_test.go index 68bff00634f..318db5f1a44 100644 --- a/core/services/s4/in_memory_orm_test.go +++ b/core/services/s4/in_memory_orm_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" @@ -21,7 +21,7 @@ func TestInMemoryORM(t *testing.T) { signature := testutils.Random32Byte() expiration := time.Now().Add(time.Minute).UnixMilli() row := &s4.Row{ - Address: utils.NewBig(address.Big()), + Address: big.New(address.Big()), SlotId: slotId, Payload: payload[:], Version: 3, @@ -33,7 +33,7 @@ func TestInMemoryORM(t *testing.T) { orm := s4.NewInMemoryORM() t.Run("row not found", func(t *testing.T) { - _, err := orm.Get(utils.NewBig(address.Big()), slotId) + _, err := orm.Get(big.New(address.Big()), slotId) assert.ErrorIs(t, err, s4.ErrNotFound) }) @@ -41,7 +41,7 @@ func TestInMemoryORM(t *testing.T) { err := orm.Update(row) assert.NoError(t, err) - e, err := orm.Get(utils.NewBig(address.Big()), slotId) + e, err := orm.Get(big.New(address.Big()), slotId) assert.NoError(t, err) assert.Equal(t, row, e) }) @@ -59,7 +59,7 @@ func TestInMemoryORM(t *testing.T) { err = orm.Update(row) assert.NoError(t, err) - e, err := orm.Get(utils.NewBig(address.Big()), slotId) + e, err := orm.Get(big.New(address.Big()), slotId) assert.NoError(t, err) assert.Equal(t, row, e) }) @@ -76,7 +76,7 @@ func TestInMemoryORM_DeleteExpired(t *testing.T) { thisAddress[0] = byte(i) row := &s4.Row{ - Address: utils.NewBig(thisAddress.Big()), + Address: big.New(thisAddress.Big()), SlotId: 1, Payload: []byte{}, Version: 1, @@ -109,7 +109,7 @@ func TestInMemoryORM_GetUnconfirmedRows(t *testing.T) { thisAddress[0] = byte(i) row := &s4.Row{ - Address: utils.NewBig(thisAddress.Big()), + Address: big.New(thisAddress.Big()), SlotId: 1, Payload: []byte{}, Version: 1, @@ -139,7 +139,7 @@ func TestInMemoryORM_GetSnapshot(t *testing.T) { thisAddress[0] = byte(i) row := &s4.Row{ - Address: utils.NewBig(thisAddress.Big()), + Address: big.New(thisAddress.Big()), SlotId: 1, Payload: []byte{}, Version: uint64(i), diff --git a/core/services/s4/mocks/orm.go b/core/services/s4/mocks/orm.go index 706c1194293..3b8cac8e76d 100644 --- a/core/services/s4/mocks/orm.go +++ b/core/services/s4/mocks/orm.go @@ -3,13 +3,14 @@ package mocks import ( + big "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + mock "github.com/stretchr/testify/mock" + pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" + s4 "github.com/smartcontractkit/chainlink/v2/core/services/s4" - mock "github.com/stretchr/testify/mock" time "time" - - utils "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ORM is an autogenerated mock type for the ORM type @@ -53,7 +54,7 @@ func (_m *ORM) DeleteExpired(limit uint, utcNow time.Time, qopts ...pg.QOpt) (in } // Get provides a mock function with given fields: address, slotId, qopts -func (_m *ORM) Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*s4.Row, error) { +func (_m *ORM) Get(address *big.Big, slotId uint, qopts ...pg.QOpt) (*s4.Row, error) { _va := make([]interface{}, len(qopts)) for _i := range qopts { _va[_i] = qopts[_i] @@ -69,10 +70,10 @@ func (_m *ORM) Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*s4.Row, var r0 *s4.Row var r1 error - if rf, ok := ret.Get(0).(func(*utils.Big, uint, ...pg.QOpt) (*s4.Row, error)); ok { + if rf, ok := ret.Get(0).(func(*big.Big, uint, ...pg.QOpt) (*s4.Row, error)); ok { return rf(address, slotId, qopts...) } - if rf, ok := ret.Get(0).(func(*utils.Big, uint, ...pg.QOpt) *s4.Row); ok { + if rf, ok := ret.Get(0).(func(*big.Big, uint, ...pg.QOpt) *s4.Row); ok { r0 = rf(address, slotId, qopts...) } else { if ret.Get(0) != nil { @@ -80,7 +81,7 @@ func (_m *ORM) Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*s4.Row, } } - if rf, ok := ret.Get(1).(func(*utils.Big, uint, ...pg.QOpt) error); ok { + if rf, ok := ret.Get(1).(func(*big.Big, uint, ...pg.QOpt) error); ok { r1 = rf(address, slotId, qopts...) } else { r1 = ret.Error(1) diff --git a/core/services/s4/orm.go b/core/services/s4/orm.go index 1e0227b1191..59f3410e143 100644 --- a/core/services/s4/orm.go +++ b/core/services/s4/orm.go @@ -3,13 +3,13 @@ package s4 import ( "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Row represents a data row persisted by ORM. type Row struct { - Address *utils.Big + Address *big.Big SlotId uint Payload []byte Version uint64 @@ -20,7 +20,7 @@ type Row struct { // SnapshotRow(s) are returned by GetSnapshot function. type SnapshotRow struct { - Address *utils.Big + Address *big.Big SlotId uint Version uint64 Expiration int64 @@ -35,7 +35,7 @@ type ORM interface { // Get reads a row for the given address and slotId combination. // If such row does not exist, ErrNotFound is returned. // There is no filter on Expiration. - Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*Row, error) + Get(address *big.Big, slotId uint, qopts ...pg.QOpt) (*Row, error) // Update inserts or updates the row identified by (Address, SlotId) pair. // When updating, the new row must have greater or equal version, @@ -59,7 +59,7 @@ type ORM interface { func (r Row) Clone() *Row { clone := Row{ - Address: utils.NewBig(r.Address.ToInt()), + Address: big.New(r.Address.ToInt()), SlotId: r.SlotId, Payload: make([]byte, len(r.Payload)), Version: r.Version, diff --git a/core/services/s4/postgres_orm.go b/core/services/s4/postgres_orm.go index 1f91270fd08..dba98b64aa6 100644 --- a/core/services/s4/postgres_orm.go +++ b/core/services/s4/postgres_orm.go @@ -5,9 +5,9 @@ import ( "fmt" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/jmoiron/sqlx" "github.com/pkg/errors" @@ -34,7 +34,7 @@ func NewPostgresORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, tableName, } } -func (o orm) Get(address *utils.Big, slotId uint, qopts ...pg.QOpt) (*Row, error) { +func (o orm) Get(address *big.Big, slotId uint, qopts ...pg.QOpt) (*Row, error) { row := &Row{} q := o.q.WithOpts(qopts...) diff --git a/core/services/s4/postgres_orm_test.go b/core/services/s4/postgres_orm_test.go index c233fe2361a..4d07524b4ea 100644 --- a/core/services/s4/postgres_orm_test.go +++ b/core/services/s4/postgres_orm_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "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/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" ) @@ -36,7 +36,7 @@ func generateTestRows(t *testing.T, n int) []*s4.Row { rows := make([]*s4.Row, n) for i := 0; i < n; i++ { row := &s4.Row{ - Address: utils.NewBig(testutils.NewAddress().Big()), + Address: big.New(testutils.NewAddress().Big()), SlotId: 1, Payload: cltest.MustRandomBytes(t, 32), Version: 1 + uint64(i), diff --git a/core/services/s4/storage.go b/core/services/s4/storage.go index 65aa2f4bab5..7c9a92d1f68 100644 --- a/core/services/s4/storage.go +++ b/core/services/s4/storage.go @@ -3,6 +3,7 @@ package s4 import ( "context" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -92,7 +93,7 @@ func (s *storage) Get(ctx context.Context, key *Key) (*Record, *Metadata, error) return nil, nil, ErrSlotIdTooBig } - bigAddress := utils.NewBig(key.Address.Big()) + bigAddress := big.New(key.Address.Big()) row, err := s.orm.Get(bigAddress, key.SlotId, pg.WithParentCtx(ctx)) if err != nil { return nil, nil, err @@ -118,7 +119,7 @@ func (s *storage) Get(ctx context.Context, key *Key) (*Record, *Metadata, error) } func (s *storage) List(ctx context.Context, address common.Address) ([]*SnapshotRow, error) { - bigAddress := utils.NewBig(address.Big()) + bigAddress := big.New(address.Big()) sar, err := NewSingleAddressRange(bigAddress) if err != nil { return nil, err @@ -148,7 +149,7 @@ func (s *storage) Put(ctx context.Context, key *Key, record *Record, signature [ } row := &Row{ - Address: utils.NewBig(key.Address.Big()), + Address: big.New(key.Address.Big()), SlotId: key.SlotId, Payload: make([]byte, len(record.Payload)), Version: key.Version, diff --git a/core/services/s4/storage_test.go b/core/services/s4/storage_test.go index 11a8f6544ce..86161f298e4 100644 --- a/core/services/s4/storage_test.go +++ b/core/services/s4/storage_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/s4" @@ -51,7 +52,7 @@ func TestStorage_Errors(t *testing.T) { SlotId: 1, Version: 0, } - ormMock.On("Get", utils.NewBig(key.Address.Big()), uint(key.SlotId), mock.Anything).Return(nil, s4.ErrNotFound) + ormMock.On("Get", big.New(key.Address.Big()), uint(key.SlotId), mock.Anything).Return(nil, s4.ErrNotFound) _, _, err := storage.Get(testutils.Context(t), key) assert.ErrorIs(t, err, s4.ErrNotFound) }) @@ -179,8 +180,8 @@ func TestStorage_PutAndGet(t *testing.T) { assert.NoError(t, err) ormMock.On("Update", mock.Anything, mock.Anything).Return(nil) - ormMock.On("Get", utils.NewBig(key.Address.Big()), uint(2), mock.Anything).Return(&s4.Row{ - Address: utils.NewBig(key.Address.Big()), + ormMock.On("Get", big.New(key.Address.Big()), uint(2), mock.Anything).Return(&s4.Row{ + Address: big.New(key.Address.Big()), SlotId: key.SlotId, Version: key.Version, Payload: record.Payload, @@ -217,7 +218,7 @@ func TestStorage_List(t *testing.T) { }, } - addressRange, err := s4.NewSingleAddressRange(utils.NewBig(address.Big())) + addressRange, err := s4.NewSingleAddressRange(big.New(address.Big())) assert.NoError(t, err) ormMock.On("GetSnapshot", addressRange, mock.Anything).Return(ormRows, nil) diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 3b643d19b0b..663080c86a0 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -15,8 +15,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" diff --git a/core/services/vrf/v1/integration_test.go b/core/services/vrf/v1/integration_test.go index a7dca56776f..3e9cfbe0870 100644 --- a/core/services/vrf/v1/integration_test.go +++ b/core/services/vrf/v1/integration_test.go @@ -16,6 +16,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -29,7 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestIntegration_VRF_JPV2(t *testing.T) { @@ -47,7 +47,7 @@ func TestIntegration_VRF_JPV2(t *testing.T) { t.Run(test.name, func(t *testing.T) { config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = &test.eip1559 - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) key1 := cltest.MustGenerateRandomKey(t) key2 := cltest.MustGenerateRandomKey(t) @@ -134,7 +134,7 @@ func TestIntegration_VRF_WithBHS(t *testing.T) { c.Feature.LogPoller = ptr(true) c.EVM[0].FinalityDepth = ptr[uint32](2) c.EVM[0].LogPollInterval = models.MustNewDuration(time.Second) - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) key := cltest.MustGenerateRandomKey(t) cu := vrftesthelpers.NewVRFCoordinatorUniverse(t, key) diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index 03d96cadf20..d8a7da70a86 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_consumer_v2_upgradeable_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_external_sub_owner_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_transparent_upgradeable_proxy" @@ -540,7 +541,7 @@ func testSingleConsumerHappyPathBatchFulfillment( })(c, s) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) c.Feature.LogPoller = ptr(true) c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) @@ -1645,7 +1646,7 @@ func testMaliciousConsumer( c.EVM[0].GasEstimator.PriceMax = assets.GWei(1) c.EVM[0].GasEstimator.PriceDefault = assets.GWei(1) c.EVM[0].GasEstimator.FeeCapDefault = assets.GWei(1) - c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) c.Feature.LogPoller = ptr(true) c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index fa95b694f98..8d6354c4fd8 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -40,6 +40,7 @@ import ( evmlogger "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" @@ -2057,7 +2058,7 @@ func TestStartingCountsV1(t *testing.T) { md2, err := json.Marshal(&m2) md2SQL := sqlutil.JSON(md2) require.NoError(t, err) - chainID := utils.NewBig(testutils.SimulatedChainID) + chainID := ubig.New(testutils.SimulatedChainID) confirmedTxes := []txmgr.Tx{ { Sequence: &n1, diff --git a/core/services/vrf/v2/listener_v2_log_listener_test.go b/core/services/vrf/v2/listener_v2_log_listener_test.go index 11b299abc40..6f5177c230a 100644 --- a/core/services/vrf/v2/listener_v2_log_listener_test.go +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" @@ -761,8 +762,8 @@ func TestUpdateLastProcessedBlock_UnfulfilledNFulfilledVRFReqs(t *testing.T) { * TestGetUnfulfilled_UnfulfilledNFulfilledVRFReqs */ -func SetupGetUnfulfilledTH(t *testing.T) (*listenerV2, *utils.Big) { - chainID := utils.NewBig(big.NewInt(12345)) +func SetupGetUnfulfilledTH(t *testing.T) (*listenerV2, *ubig.Big) { + chainID := ubig.New(big.NewInt(12345)) lggr := logger.TestLogger(t) j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ RequestedConfsDelay: 10, diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index 43ddd41d56f..10c698e96fd 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -16,6 +16,7 @@ import ( evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -29,7 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/store/migrate" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var migrationDir = "migrations" @@ -417,7 +417,7 @@ func TestMigrate(t *testing.T) { func TestSetMigrationENVVars(t *testing.T) { t.Run("ValidEVMConfig", func(t *testing.T) { - chainID := utils.NewBig(big.NewInt(1337)) + chainID := ubig.New(big.NewInt(1337)) testConfig := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { evmEnabled := true c.EVM = evmcfg.EVMConfigs{&evmcfg.EVMConfig{ @@ -433,7 +433,7 @@ func TestSetMigrationENVVars(t *testing.T) { }) t.Run("EVMConfigMissing", func(t *testing.T) { - chainID := utils.NewBig(big.NewInt(1337)) + chainID := ubig.New(big.NewInt(1337)) testConfig := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM = nil }) require.NoError(t, migrate.SetMigrationENVVars(testConfig)) @@ -535,7 +535,7 @@ func BenchmarkBackfillingRecordsWithMigration202(b *testing.B) { var blocks []logpoller.LogPollerBlock for i := 0; i < maxLogsSize; i++ { blocks = append(blocks, logpoller.LogPollerBlock{ - EvmChainId: utils.NewBigI(int64(j + 1)), + EvmChainId: ubig.NewI(int64(j + 1)), BlockHash: testutils.Random32Byte(), BlockNumber: int64(i + 1000), FinalizedBlockNumber: 0, diff --git a/core/store/models/common.go b/core/store/models/common.go index 10f391861e1..93cc708fe0b 100644 --- a/core/store/models/common.go +++ b/core/store/models/common.go @@ -18,7 +18,7 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) // CronParser is the global parser for crontabs. @@ -365,7 +365,7 @@ type SendEtherRequest struct { DestinationAddress common.Address `json:"address"` FromAddress common.Address `json:"from"` Amount assets.Eth `json:"amount"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` AllowHigherAmounts bool `json:"allowHigherAmounts"` SkipWaitTxAttempt bool `json:"skipWaitTxAttempt"` WaitAttemptTimeout *time.Duration `json:"waitAttemptTimeout"` diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index d99992c0f56..fe76e8863ef 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -11,6 +11,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -19,7 +20,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" "github.com/ethereum/go-ethereum/common" @@ -393,7 +393,7 @@ func (ekc *ETHKeysController) getLinkBalance(ctx context.Context, state ethkey.S // gets the key specific max gas price from the chain config and sets it on the // resource. func (ekc *ETHKeysController) setKeyMaxGasPriceWei(price *assets.Wei) presenters.NewETHKeyOption { - return presenters.SetETHKeyMaxGasPriceWei(utils.NewBig(price.ToInt())) + return presenters.SetETHKeyMaxGasPriceWei(ubig.New(price.ToInt())) } func (ekc *ETHKeysController) getKeyMaxGasPriceWei(state ethkey.State, keyAddress common.Address) *assets.Wei { diff --git a/core/web/evm_chains_controller_test.go b/core/web/evm_chains_controller_test.go index 3d5a4e3eedd..ea3d5476cec 100644 --- a/core/web/evm_chains_controller_test.go +++ b/core/web/evm_chains_controller_test.go @@ -12,12 +12,12 @@ import ( "github.com/stretchr/testify/require" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -25,7 +25,7 @@ import ( func Test_EVMChainsController_Show(t *testing.T) { t.Parallel() - validId := utils.NewBig(testutils.NewRandomEVMChainID()) + validId := ubig.New(testutils.NewRandomEVMChainID()) testCases := []struct { name string @@ -111,9 +111,9 @@ func Test_EVMChainsController_Index(t *testing.T) { }) configuredChains := evmcfg.EVMConfigs{ - {ChainID: utils.NewBig(chainIDs[0]), Chain: evmcfg.Defaults(nil)}, + {ChainID: ubig.New(chainIDs[0]), Chain: evmcfg.Defaults(nil)}, { - ChainID: utils.NewBig(chainIDs[1]), + ChainID: ubig.New(chainIDs[1]), Chain: evmcfg.Defaults(nil, &evmcfg.Chain{ RPCBlockQueryDelay: ptr[uint16](13), GasEstimator: evmcfg.GasEstimator{ @@ -126,7 +126,7 @@ func Test_EVMChainsController_Index(t *testing.T) { }), }, { - ChainID: utils.NewBig(chainIDs[2]), + ChainID: ubig.New(chainIDs[2]), Chain: evmcfg.Defaults(nil, &evmcfg.Chain{ RPCBlockQueryDelay: ptr[uint16](5), GasEstimator: evmcfg.GasEstimator{ diff --git a/core/web/evm_forwarders_controller.go b/core/web/evm_forwarders_controller.go index 6228723506d..56d1285c88e 100644 --- a/core/web/evm_forwarders_controller.go +++ b/core/web/evm_forwarders_controller.go @@ -8,10 +8,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -43,7 +43,7 @@ func (cc *EVMForwardersController) Index(c *gin.Context, size, page, offset int) // TrackEVMForwarderRequest is a JSONAPI request for creating an EVM forwarder. type TrackEVMForwarderRequest struct { - EVMChainID *utils.Big `json:"evmChainId"` + EVMChainID *ubig.Big `json:"evmChainId"` Address common.Address `json:"address"` } diff --git a/core/web/evm_forwarders_controller_test.go b/core/web/evm_forwarders_controller_test.go index 31e49f20ecc..49671157cbd 100644 --- a/core/web/evm_forwarders_controller_test.go +++ b/core/web/evm_forwarders_controller_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -42,7 +43,7 @@ func setupEVMForwardersControllerTest(t *testing.T, overrideFn func(c *chainlink func Test_EVMForwardersController_Track(t *testing.T) { t.Parallel() - chainId := utils.NewBig(testutils.NewRandomEVMChainID()) + chainId := big.New(testutils.NewRandomEVMChainID()) controller := setupEVMForwardersControllerTest(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM = evmcfg.EVMConfigs{ {ChainID: chainId, Enabled: ptr(true), Chain: evmcfg.Defaults(chainId)}, @@ -79,7 +80,7 @@ func Test_EVMForwardersController_Track(t *testing.T) { func Test_EVMForwardersController_Index(t *testing.T) { t.Parallel() - chainId := utils.NewBig(testutils.NewRandomEVMChainID()) + chainId := big.New(testutils.NewRandomEVMChainID()) controller := setupEVMForwardersControllerTest(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM = evmcfg.EVMConfigs{ {ChainID: chainId, Enabled: ptr(true), Chain: evmcfg.Defaults(chainId)}, diff --git a/core/web/evm_transfer_controller_test.go b/core/web/evm_transfer_controller_test.go index c41219e1894..dd083a8cd63 100644 --- a/core/web/evm_transfer_controller_test.go +++ b/core/web/evm_transfer_controller_test.go @@ -13,13 +13,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -55,7 +55,7 @@ func TestTransfersController_CreateSuccess_From(t *testing.T) { FromAddress: key.Address, Amount: amount, SkipWaitTxAttempt: true, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), } body, err := json.Marshal(&request) @@ -96,7 +96,7 @@ func TestTransfersController_CreateSuccess_From_WEI(t *testing.T) { FromAddress: key.Address, Amount: amount, SkipWaitTxAttempt: true, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), } body, err := json.Marshal(&request) @@ -142,7 +142,7 @@ func TestTransfersController_CreateSuccess_From_BalanceMonitorDisabled(t *testin FromAddress: key.Address, Amount: amount, SkipWaitTxAttempt: true, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), } body, err := json.Marshal(&request) @@ -172,7 +172,7 @@ func TestTransfersController_TransferZeroAddressError(t *testing.T) { DestinationAddress: common.HexToAddress("0xFA01FA015C8A5332987319823728982379128371"), FromAddress: common.HexToAddress("0x0000000000000000000000000000000000000000"), Amount: amount, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), } body, err := json.Marshal(&request) @@ -207,7 +207,7 @@ func TestTransfersController_TransferBalanceToLowError(t *testing.T) { DestinationAddress: common.HexToAddress("0xFA01FA015C8A5332987319823728982379128371"), Amount: amount, AllowHigherAmounts: false, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), } body, err := json.Marshal(&request) @@ -245,7 +245,7 @@ func TestTransfersController_TransferBalanceToLowError_ZeroBalance(t *testing.T) DestinationAddress: common.HexToAddress("0xFA01FA015C8A5332987319823728982379128371"), Amount: amount, AllowHigherAmounts: false, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, app.Config.EVMConfigs())), } body, err := json.Marshal(&request) @@ -288,7 +288,7 @@ func TestTransfersController_CreateSuccess_eip1559(t *testing.T) { config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) c.EVM[0].GasEstimator.Mode = ptr("FixedPrice") - c.EVM[0].ChainID = (*utils.Big)(testutils.FixtureChainID) + c.EVM[0].ChainID = (*ubig.Big)(testutils.FixtureChainID) // NOTE: FallbackPollInterval is used in this test to quickly create TxAttempts // Testing triggers requires committing transactions and does not work with transactional tests c.Database.Listener.FallbackPollInterval = models.MustNewDuration(time.Second) @@ -308,7 +308,7 @@ func TestTransfersController_CreateSuccess_eip1559(t *testing.T) { FromAddress: key.Address, Amount: amount, WaitAttemptTimeout: &timeout, - EVMChainID: utils.NewBig(evmtest.MustGetDefaultChainID(t, config.EVMConfigs())), + EVMChainID: ubig.New(evmtest.MustGetDefaultChainID(t, config.EVMConfigs())), } body, err := json.Marshal(&request) diff --git a/core/web/loader/loader_test.go b/core/web/loader/loader_test.go index 9bd1feb05bf..cbd73a575a9 100644 --- a/core/web/loader/loader_test.go +++ b/core/web/loader/loader_test.go @@ -17,6 +17,7 @@ import ( "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" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" coremocks "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" @@ -35,9 +36,9 @@ func TestLoader_Chains(t *testing.T) { app := coremocks.NewApplication(t) ctx := InjectDataloader(testutils.Context(t), app) - one := utils.NewBigI(1) + one := ubig.NewI(1) chain := toml.EVMConfig{ChainID: one, Chain: toml.Defaults(one)} - two := utils.NewBigI(2) + two := ubig.NewI(2) chain2 := toml.EVMConfig{ChainID: two, Chain: toml.Defaults(two)} evmORM := evmtest.NewTestConfigs(&chain, &chain2) app.On("EVMORM").Return(evmORM) diff --git a/core/web/presenters/eth_key.go b/core/web/presenters/eth_key.go index 3d952dabeda..d661d4334cd 100644 --- a/core/web/presenters/eth_key.go +++ b/core/web/presenters/eth_key.go @@ -5,22 +5,22 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ETHKeyResource represents a ETH key JSONAPI resource. It holds the hex // representation of the address plus its ETH & LINK balances type ETHKeyResource struct { JAID - EVMChainID utils.Big `json:"evmChainID"` + EVMChainID big.Big `json:"evmChainID"` Address string `json:"address"` EthBalance *assets.Eth `json:"ethBalance"` LinkBalance *commonassets.Link `json:"linkBalance"` Disabled bool `json:"disabled"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - MaxGasPriceWei *utils.Big `json:"maxGasPriceWei"` + MaxGasPriceWei *big.Big `json:"maxGasPriceWei"` } // GetName implements the api2go EntityNamer interface @@ -69,7 +69,7 @@ func SetETHKeyLinkBalance(linkBalance *commonassets.Link) NewETHKeyOption { } } -func SetETHKeyMaxGasPriceWei(maxGasPriceWei *utils.Big) NewETHKeyOption { +func SetETHKeyMaxGasPriceWei(maxGasPriceWei *big.Big) NewETHKeyOption { return func(r *ETHKeyResource) { r.MaxGasPriceWei = maxGasPriceWei } diff --git a/core/web/presenters/eth_key_test.go b/core/web/presenters/eth_key_test.go index 85d005cf610..8be13de74a1 100644 --- a/core/web/presenters/eth_key_test.go +++ b/core/web/presenters/eth_key_test.go @@ -7,8 +7,8 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum/common" "github.com/manyminds/api2go/jsonapi" @@ -31,7 +31,7 @@ func TestETHKeyResource(t *testing.T) { state := ethkey.State{ ID: 1, - EVMChainID: *utils.NewBigI(42), + EVMChainID: *big.NewI(42), Address: eip55address, CreatedAt: now, UpdatedAt: now, @@ -41,12 +41,12 @@ func TestETHKeyResource(t *testing.T) { r := NewETHKeyResource(key, state, SetETHKeyEthBalance(assets.NewEth(1)), SetETHKeyLinkBalance(commonassets.NewLinkFromJuels(1)), - SetETHKeyMaxGasPriceWei(utils.NewBigI(12345)), + SetETHKeyMaxGasPriceWei(big.NewI(12345)), ) assert.Equal(t, assets.NewEth(1), r.EthBalance) assert.Equal(t, commonassets.NewLinkFromJuels(1), r.LinkBalance) - assert.Equal(t, utils.NewBigI(12345), r.MaxGasPriceWei) + assert.Equal(t, big.NewI(12345), r.MaxGasPriceWei) b, err := jsonapi.Marshal(r) require.NoError(t, err) diff --git a/core/web/presenters/eth_tx.go b/core/web/presenters/eth_tx.go index 2c2b5b90ff2..f944a99213f 100644 --- a/core/web/presenters/eth_tx.go +++ b/core/web/presenters/eth_tx.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) // EthTxResource represents a Ethereum Transaction JSONAPI resource. @@ -25,7 +25,7 @@ type EthTxResource struct { SentAt string `json:"sentAt"` To *common.Address `json:"to"` Value string `json:"value"` - EVMChainID utils.Big `json:"evmChainID"` + EVMChainID big.Big `json:"evmChainID"` } // GetName implements the api2go EntityNamer interface @@ -50,7 +50,7 @@ func NewEthTxResource(tx txmgr.Tx) EthTxResource { } if tx.ChainID != nil { - r.EVMChainID = *utils.NewBig(tx.ChainID) + r.EVMChainID = *big.New(tx.ChainID) } return r } @@ -65,7 +65,7 @@ func NewEthTxResourceFromAttempt(txa txmgr.TxAttempt) EthTxResource { r.Hex = hexutil.Encode(txa.SignedRawTx) if txa.Tx.ChainID != nil { - r.EVMChainID = *utils.NewBig(txa.Tx.ChainID) + r.EVMChainID = *big.New(txa.Tx.ChainID) } if tx.Sequence != nil { diff --git a/core/web/presenters/evm_forwarder.go b/core/web/presenters/evm_forwarder.go index c91bfc088f1..43c27644850 100644 --- a/core/web/presenters/evm_forwarder.go +++ b/core/web/presenters/evm_forwarder.go @@ -6,14 +6,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) // EVMForwarderResource is an EVM forwarder JSONAPI resource. type EVMForwarderResource struct { JAID Address common.Address `json:"address"` - EVMChainID utils.Big `json:"evmChainId"` + EVMChainID big.Big `json:"evmChainId"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index 9b0a3cb3425..a2a9e70c793 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -9,6 +9,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -16,7 +17,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // JobSpecType defines the the the spec type of the job @@ -49,7 +49,7 @@ type DirectRequestSpec struct { Initiator string `json:"initiator"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` } // NewDirectRequestSpec initializes a new DirectRequestSpec from a @@ -84,7 +84,7 @@ type FluxMonitorSpec struct { MinPayment *commonassets.Link `json:"minPayment"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` } // NewFluxMonitorSpec initializes a new DirectFluxMonitorSpec from a @@ -131,7 +131,7 @@ type OffChainReportingSpec struct { ContractConfigConfirmations uint16 `json:"contractConfigConfirmations"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` DatabaseTimeout *models.Interval `json:"databaseTimeout"` ObservationGracePeriod *models.Interval `json:"observationGracePeriod"` ContractTransmitterTransmitTimeout *models.Interval `json:"contractTransmitterTransmitTimeout"` @@ -220,7 +220,7 @@ type KeeperSpec struct { FromAddress ethkey.EIP55Address `json:"fromAddress"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` } // NewKeeperSpec generates a new KeeperSpec from a job.KeeperSpec @@ -275,7 +275,7 @@ type VRFSpec struct { MinIncomingConfirmations uint32 `json:"confirmations"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` ChunkSize uint32 `json:"chunkSize"` RequestTimeout models.Duration `json:"requestTimeout"` BackoffInitialDelay models.Duration `json:"backoffInitialDelay"` @@ -317,7 +317,7 @@ type BlockhashStoreSpec struct { TrustedBlockhashStoreBatchSize int32 `json:"trustedBlockhashStoreBatchSize"` PollPeriod time.Duration `json:"pollPeriod"` RunTimeout time.Duration `json:"runTimeout"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` FromAddresses []ethkey.EIP55Address `json:"fromAddresses"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` @@ -353,7 +353,7 @@ type BlockHeaderFeederSpec struct { BatchBlockhashStoreAddress ethkey.EIP55Address `json:"batchBlockhashStoreAddress"` PollPeriod time.Duration `json:"pollPeriod"` RunTimeout time.Duration `json:"runTimeout"` - EVMChainID *utils.Big `json:"evmChainID"` + EVMChainID *big.Big `json:"evmChainID"` FromAddresses []ethkey.EIP55Address `json:"fromAddresses"` GetBlockhashesBatchSize uint16 `json:"getBlockhashesBatchSize"` StoreBlockhashesBatchSize uint16 `json:"storeBlockhashesBatchSize"` diff --git a/core/web/presenters/job_test.go b/core/web/presenters/job_test.go index 260cce0caf6..a5d6db0df18 100644 --- a/core/web/presenters/job_test.go +++ b/core/web/presenters/job_test.go @@ -14,12 +14,12 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -29,7 +29,7 @@ func TestJob(t *testing.T) { contractAddress, err := ethkey.NewEIP55Address("0x9E40733cC9df84636505f4e6Db28DCa0dC5D1bba") require.NoError(t, err) cronSchedule := "0 0 0 1 1 *" - evmChainID := utils.NewBigI(42) + evmChainID := big.NewI(42) fromAddress, err := ethkey.NewEIP55Address("0xa8037A20989AFcBC51798de9762b351D63ff462e") require.NoError(t, err) @@ -484,7 +484,7 @@ func TestJob(t *testing.T) { BlockhashStoreAddress: contractAddress, PollPeriod: 25 * time.Second, RunTimeout: 10 * time.Second, - EVMChainID: utils.NewBigI(4), + EVMChainID: big.NewI(4), FromAddresses: []ethkey.EIP55Address{fromAddress}, TrustedBlockhashStoreAddress: &trustedBlockhashStoreAddress, TrustedBlockhashStoreBatchSize: trustedBlockhashStoreBatchSize, @@ -564,7 +564,7 @@ func TestJob(t *testing.T) { BatchBlockhashStoreAddress: batchBHSAddress, PollPeriod: 25 * time.Second, RunTimeout: 10 * time.Second, - EVMChainID: utils.NewBigI(4), + EVMChainID: big.NewI(4), FromAddresses: []ethkey.EIP55Address{fromAddress}, GetBlockhashesBatchSize: 5, StoreBlockhashesBatchSize: 10, diff --git a/core/web/replay_controller.go b/core/web/replay_controller.go index 5006b68c845..84c54e3836e 100644 --- a/core/web/replay_controller.go +++ b/core/web/replay_controller.go @@ -7,8 +7,8 @@ import ( "github.com/gin-gonic/gin" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ReplayController struct { @@ -64,14 +64,14 @@ func (bdc *ReplayController) ReplayFromBlock(c *gin.Context) { response := ReplayResponse{ Message: "Replay started", - EVMChainID: utils.NewBig(chainID), + EVMChainID: big.New(chainID), } jsonAPIResponse(c, &response, "response") } type ReplayResponse struct { - Message string `json:"message"` - EVMChainID *utils.Big `json:"evmChainID"` + Message string `json:"message"` + EVMChainID *big.Big `json:"evmChainID"` } // GetID returns the jsonapi ID. diff --git a/core/web/resolver/chain_test.go b/core/web/resolver/chain_test.go index c3cafd329b4..700963cd4da 100644 --- a/core/web/resolver/chain_test.go +++ b/core/web/resolver/chain_test.go @@ -9,12 +9,12 @@ import ( "github.com/stretchr/testify/require" evmtoml "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) func TestResolver_Chains(t *testing.T) { var ( - chainID = *utils.NewBigI(1) + chainID = *big.NewI(1) query = ` query GetChains { chains { @@ -100,7 +100,7 @@ ResendAfterThreshold = '1h0m0s' func TestResolver_Chain(t *testing.T) { var ( - chainID = *utils.NewBigI(1) + chainID = *big.NewI(1) query = ` query GetChain { chain(id: "1") { diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index ea106a4b30c..1874e4c68e0 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -14,12 +14,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "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/keystore/keys/ethkey" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type mockEvmConfig struct { @@ -80,13 +80,13 @@ func TestResolver_ETHKeys(t *testing.T) { states := []ethkey.State{ { Address: ethkey.MustEIP55Address(address.Hex()), - EVMChainID: *utils.NewBigI(12), + EVMChainID: *big.NewI(12), Disabled: false, CreatedAt: f.Timestamp(), UpdatedAt: f.Timestamp(), }, } - chainID := *utils.NewBigI(12) + chainID := *big.NewI(12) linkAddr := common.HexToAddress("0x5431F5F973781809D18643b87B44921b11355d81") cfg := configtest.NewGeneralConfig(t, nil) @@ -139,13 +139,13 @@ func TestResolver_ETHKeys(t *testing.T) { states := []ethkey.State{ { Address: ethkey.MustEIP55Address(address.Hex()), - EVMChainID: *utils.NewBigI(12), + EVMChainID: *big.NewI(12), Disabled: false, CreatedAt: f.Timestamp(), UpdatedAt: f.Timestamp(), }, } - chainID := *utils.NewBigI(12) + chainID := *big.NewI(12) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(nil, evmrelay.ErrNoChains) f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) @@ -225,7 +225,7 @@ func TestResolver_ETHKeys(t *testing.T) { states := []ethkey.State{ { Address: ethkey.MustEIP55Address(address.Hex()), - EVMChainID: *utils.NewBigI(12), + EVMChainID: *big.NewI(12), Disabled: false, CreatedAt: f.Timestamp(), UpdatedAt: f.Timestamp(), @@ -257,7 +257,7 @@ func TestResolver_ETHKeys(t *testing.T) { states := []ethkey.State{ { Address: ethkey.MustEIP55Address(address.Hex()), - EVMChainID: *utils.NewBigI(12), + EVMChainID: *big.NewI(12), Disabled: false, CreatedAt: f.Timestamp(), UpdatedAt: f.Timestamp(), @@ -288,13 +288,13 @@ func TestResolver_ETHKeys(t *testing.T) { states := []ethkey.State{ { Address: ethkey.MustEIP55Address(address.Hex()), - EVMChainID: *utils.NewBigI(12), + EVMChainID: *big.NewI(12), Disabled: false, CreatedAt: f.Timestamp(), UpdatedAt: f.Timestamp(), }, } - chainID := *utils.NewBigI(12) + chainID := *big.NewI(12) linkAddr := common.HexToAddress("0x5431F5F973781809D18643b87B44921b11355d81") f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) @@ -342,13 +342,13 @@ func TestResolver_ETHKeys(t *testing.T) { states := []ethkey.State{ { Address: ethkey.EIP55AddressFromAddress(address), - EVMChainID: *utils.NewBigI(12), + EVMChainID: *big.NewI(12), Disabled: false, CreatedAt: f.Timestamp(), UpdatedAt: f.Timestamp(), }, } - chainID := *utils.NewBigI(12) + chainID := *big.NewI(12) linkAddr := common.HexToAddress("0x5431F5F973781809D18643b87B44921b11355d81") f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) diff --git a/core/web/resolver/eth_transaction_test.go b/core/web/resolver/eth_transaction_test.go index a719c838e81..238aa9d1679 100644 --- a/core/web/resolver/eth_transaction_test.go +++ b/core/web/resolver/eth_transaction_test.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) func TestResolver_EthTransaction(t *testing.T) { @@ -54,7 +54,7 @@ func TestResolver_EthTransaction(t *testing.T) { "hash": "0x5431F5F973781809D18643b87B44921b11355d81", } hash := common.HexToHash("0x5431F5F973781809D18643b87B44921b11355d81") - chainID := *utils.NewBigI(22) + chainID := *ubig.NewI(22) gError := errors.New("error") testCases := []GQLTestCase{ diff --git a/core/web/resolver/node_test.go b/core/web/resolver/node_test.go index 24a31b986f1..a209a60fc3c 100644 --- a/core/web/resolver/node_test.go +++ b/core/web/resolver/node_test.go @@ -8,16 +8,16 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestResolver_Nodes(t *testing.T) { t.Parallel() var ( - chainID = *utils.NewBigI(1) + chainID = *big.NewI(1) query = ` query GetNodes { diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index ef89dafa048..277aac851a6 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -13,13 +13,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Specs are only embedded on the job and are not fetchable by it's own id, so @@ -95,7 +95,7 @@ func TestResolver_DirectRequestSpec(t *testing.T) { DirectRequestSpec: &job.DirectRequestSpec{ ContractAddress: contractAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), MinIncomingConfirmations: clnull.NewUint32(1, true), MinContractPayment: commonassets.NewLinkFromJuels(1000), Requesters: models.AddressCollection{requesterAddress}, @@ -160,7 +160,7 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { FluxMonitorSpec: &job.FluxMonitorSpec{ ContractAddress: contractAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), DrumbeatEnabled: false, IdleTimerDisabled: false, IdleTimerPeriod: time.Duration(1 * time.Hour), @@ -227,7 +227,7 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { FluxMonitorSpec: &job.FluxMonitorSpec{ ContractAddress: contractAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), DrumbeatEnabled: true, DrumbeatRandomDelay: time.Duration(1 * time.Second), DrumbeatSchedule: "CRON_TZ=UTC 0 0 1 1 *", @@ -310,7 +310,7 @@ func TestResolver_KeeperSpec(t *testing.T) { KeeperSpec: &job.KeeperSpec{ ContractAddress: contractAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), FromAddress: ethkey.EIP55AddressFromAddress(fromAddress), }, }, nil) @@ -381,7 +381,7 @@ func TestResolver_OCRSpec(t *testing.T) { ObservationGracePeriod: models.NewInterval(4 * time.Second), ContractTransmitterTransmitTimeout: models.NewInterval(555 * time.Millisecond), CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), IsBootstrapPeer: false, EncryptedOCRKeyBundleID: &keyBundleID, ObservationTimeout: models.Interval(2 * time.Minute), @@ -584,7 +584,7 @@ func TestResolver_VRFSpec(t *testing.T) { MinIncomingConfirmations: 1, CoordinatorAddress: coordinatorAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), FromAddresses: []ethkey.EIP55Address{fromAddress1, fromAddress2}, PollPeriod: 1 * time.Minute, PublicKey: pubKey, @@ -745,7 +745,7 @@ func TestResolver_BlockhashStoreSpec(t *testing.T) { CoordinatorV2Address: &coordinatorV2Address, CoordinatorV2PlusAddress: &coordinatorV2PlusAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), FromAddresses: []ethkey.EIP55Address{fromAddress1, fromAddress2}, PollPeriod: 1 * time.Minute, RunTimeout: 37 * time.Second, @@ -849,7 +849,7 @@ func TestResolver_BlockHeaderFeederSpec(t *testing.T) { CoordinatorV2Address: &coordinatorV2Address, CoordinatorV2PlusAddress: &coordinatorV2PlusAddress, CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), + EVMChainID: ubig.NewI(42), FromAddresses: []ethkey.EIP55Address{fromAddress}, PollPeriod: 1 * time.Minute, RunTimeout: 37 * time.Second, diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index abc6ef30e41..e6e1de25e41 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -8,8 +8,8 @@ import ( "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // EIServiceConfig represents External Initiator service config @@ -1420,6 +1420,6 @@ type ReplayResponseData struct { } type ReplayResponseAttributes struct { - Message string `json:"message"` - EVMChainID *utils.Big `json:"evmChainID"` + Message string `json:"message"` + EVMChainID *big.Big `json:"evmChainID"` } diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 0337274bb1c..7436c05a107 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -162,7 +163,7 @@ func SetChainConfig( } cfg.EVM = evmcfg.EVMConfigs{ { - ChainID: utils.NewBig(big.NewInt(chain.ChainID)), + ChainID: ubig.New(big.NewInt(chain.ChainID)), Chain: chainConfig, Nodes: nodes, }, @@ -179,7 +180,7 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { var evmConfigs []*evmcfg.EVMConfig for _, network := range networks { evmConfigs = append(evmConfigs, &evmcfg.EVMConfig{ - ChainID: utils.NewBig(big.NewInt(network.ChainID)), + ChainID: ubig.New(big.NewInt(network.ChainID)), Chain: evmcfg.Chain{ AutoCreateKey: ptr.Ptr(true), FinalityDepth: ptr.Ptr[uint32](50), From 96b7ab6ee1daa751a58fe34d1f67b90066f26afd Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 12 Dec 2023 15:24:26 -0600 Subject: [PATCH 041/234] bump toml/v2 and prometheus to latest patch (#11541) Co-authored-by: Vyzaldy Sanchez --- core/scripts/go.mod | 4 ++-- core/scripts/go.sum | 8 ++++---- go.mod | 4 ++-- go.sum | 8 ++++---- integration-tests/go.mod | 4 ++-- integration-tests/go.sum | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 2f4d45e746f..c5327596272 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -17,7 +17,7 @@ require ( github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 - github.com/pelletier/go-toml/v2 v2.1.0 + github.com/pelletier/go-toml/v2 v2.1.1 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 @@ -224,7 +224,7 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.48.0 // indirect + github.com/prometheus/prometheus v0.48.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index a59590fa124..28750a076d5 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1029,8 +1029,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= @@ -1076,8 +1076,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= -github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= +github.com/prometheus/prometheus v0.48.1 h1:CTszphSNTXkuCG6O0IfpKdHcJkvvnAAE1GbELKS+NFk= +github.com/prometheus/prometheus v0.48.1/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= diff --git a/go.mod b/go.mod index d8394633c3f..e7b93c40bdb 100644 --- a/go.mod +++ b/go.mod @@ -51,13 +51,13 @@ require ( github.com/onsi/gomega v1.30.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pelletier/go-toml v1.9.5 - github.com/pelletier/go-toml/v2 v2.1.0 + github.com/pelletier/go-toml/v2 v2.1.1 github.com/pkg/errors v0.9.1 github.com/pressly/goose/v3 v3.16.0 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.5.0 github.com/prometheus/common v0.45.0 - github.com/prometheus/prometheus v0.48.0 + github.com/prometheus/prometheus v0.48.1 github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/go-internal v1.11.0 github.com/scylladb/go-reflectx v1.0.1 diff --git a/go.sum b/go.sum index d702fd37e90..e9a7f0267ad 100644 --- a/go.sum +++ b/go.sum @@ -1015,8 +1015,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= @@ -1062,8 +1062,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= -github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= +github.com/prometheus/prometheus v0.48.1 h1:CTszphSNTXkuCG6O0IfpKdHcJkvvnAAE1GbELKS+NFk= +github.com/prometheus/prometheus v0.48.1/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c080f617552..84da65e9a5d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -18,7 +18,7 @@ require ( github.com/lib/pq v1.10.9 github.com/manifoldco/promptui v0.9.0 github.com/onsi/gomega v1.30.0 - github.com/pelletier/go-toml/v2 v2.1.0 + github.com/pelletier/go-toml/v2 v2.1.1 github.com/rs/zerolog v1.30.0 github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 @@ -330,7 +330,7 @@ require ( github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.48.0 // indirect + github.com/prometheus/prometheus v0.48.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index e8cefd67d6d..854978e3353 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1278,8 +1278,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= @@ -1341,8 +1341,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= -github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= +github.com/prometheus/prometheus v0.48.1 h1:CTszphSNTXkuCG6O0IfpKdHcJkvvnAAE1GbELKS+NFk= +github.com/prometheus/prometheus v0.48.1/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw= github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= From 00e1c55ddb5fa3736fccaa545c12cff91b702b68 Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:34:28 +0000 Subject: [PATCH 042/234] Change keepers to use the default contract transmitter (#11308) * Switch keepers to use the default contract transmitter Pass the gas limit into the transmitter constructor so we can specify the automation gas limit Update tests * Fix streams import * Clean up function calls * Remove pipeline runner dependency for automation * Use contractTransmitterOpts to specify a pluginGasLimit * Make the pluginGasLimit a pointer * Clean up the pipeline transmitter * Attempt to listen for transmits Clean up linter Extract function Intentionally fail test Rework transmit listen Try filtering for transmits Update test * Revert "Attempt to listen for transmits" This reverts commit 198e6669a6f64768c84acb82e01b2773c89426ce. * Listen for performed events * Goimports * Add wrapper function that lets us specify a count of performed * Update integration test * Pass the configWatcher as a parameter to indicate that its required --- core/services/ocr2/delegate.go | 4 +- .../plugins/ocr2keeper/integration_21_test.go | 93 +++++++----------- .../plugins/ocr2keeper/integration_test.go | 19 ---- core/services/ocr2/plugins/ocr2keeper/util.go | 14 +-- .../ocrcommon/transmitter_pipeline.go | 97 ------------------- .../ocrcommon/transmitter_pipeline_test.go | 77 --------------- core/services/relay/evm/evm.go | 63 ++---------- core/services/relay/evm/ocr2keeper.go | 25 +++-- core/services/relay/evm/ocr2vrf.go | 4 +- 9 files changed, 67 insertions(+), 329 deletions(-) delete mode 100644 core/services/ocrcommon/transmitter_pipeline.go delete mode 100644 core/services/ocrcommon/transmitter_pipeline_test.go diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 5200866e3af..1b7be2b7f0e 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -1084,7 +1084,7 @@ func (d *Delegate) newServicesOCR2Keepers21( return nil, fmt.Errorf("keeper2 services: failed to get chain %s: %w", rid.ChainID, err2) } - keeperProvider, services, err2 := ocr2keeper.EVMDependencies21(jb, d.db, lggr, chain, d.pipelineRunner, mc, kb, d.cfg.Database()) + keeperProvider, services, err2 := ocr2keeper.EVMDependencies21(jb, d.db, lggr, chain, mc, kb, d.cfg.Database(), d.ethKs) if err2 != nil { return nil, errors.Wrap(err2, "could not build dependencies for ocr2 keepers") } @@ -1201,7 +1201,7 @@ func (d *Delegate) newServicesOCR2Keepers20( return nil, fmt.Errorf("keepers2.0 services: failed to get chain (%s): %w", rid.ChainID, err2) } - keeperProvider, rgstry, encoder, logProvider, err2 := ocr2keeper.EVMDependencies20(jb, d.db, lggr, chain, d.pipelineRunner) + keeperProvider, rgstry, encoder, logProvider, err2 := ocr2keeper.EVMDependencies20(jb, d.db, lggr, chain, d.ethKs) if err2 != nil { return nil, errors.Wrap(err2, "could not build dependencies for ocr2 keepers") } diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index c2b6612f664..81a35a5ced2 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -54,7 +54,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) @@ -117,7 +116,7 @@ func TestIntegration_KeeperPluginConditionalUpkeep(t *testing.T) { require.NoError(t, err) registry := deployKeeper21Registry(t, steve, backend, linkAddr, linkFeedAddr, gasFeedAddr) - nodes, _ := setupNodes(t, nodeKeys, registry, backend, steve) + setupNodes(t, nodeKeys, registry, backend, steve) <-time.After(time.Second * 5) @@ -160,8 +159,6 @@ func TestIntegration_KeeperPluginConditionalUpkeep(t *testing.T) { } g.Eventually(receivedBytes, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.Equal(payload1)) - checkPipelineRuns(t, nodes, 1) - // change payload _, err = upkeepContract.SetBytesToSend(carrol, payload2) require.NoError(t, err) @@ -204,7 +201,7 @@ func TestIntegration_KeeperPluginLogUpkeep(t *testing.T) { require.NoError(t, err) registry := deployKeeper21Registry(t, steve, backend, linkAddr, linkFeedAddr, gasFeedAddr) - nodes, _ := setupNodes(t, nodeKeys, registry, backend, steve) + setupNodes(t, nodeKeys, registry, backend, steve) upkeeps := 1 _, err = linkToken.Transfer(sergey, carrol.From, big.NewInt(0).Mul(oneHunEth, big.NewInt(int64(upkeeps+1)))) @@ -228,35 +225,36 @@ func TestIntegration_KeeperPluginLogUpkeep(t *testing.T) { g.Eventually(listener, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.BeTrue()) done() - runs := checkPipelineRuns(t, nodes, 1) - t.Run("recover logs", func(t *testing.T) { - addr, contract := addrs[0], contracts[0] upkeepID := registerUpkeep(t, registry, addr, carrol, steve, backend) backend.Commit() t.Logf("Registered new upkeep %s for address %s", upkeepID.String(), addr.String()) // Emit 100 logs in a burst - emits := 100 + recoverEmits := 100 i := 0 emitEvents(testutils.Context(t), t, 100, []*log_upkeep_counter_wrapper.LogUpkeepCounter{contract}, carrol, func() { i++ - if i%(emits/4) == 0 { + if i%(recoverEmits/4) == 0 { backend.Commit() time.Sleep(time.Millisecond * 250) // otherwise we get "invalid transaction nonce" errors } }) - // Mine enough blocks to ensre these logs don't fall into log provider range + + beforeDummyBlocks := backend.Blockchain().CurrentBlock().Number.Uint64() + + // Mine enough blocks to ensure these logs don't fall into log provider range dummyBlocks := 500 for i := 0; i < dummyBlocks; i++ { backend.Commit() time.Sleep(time.Millisecond * 10) } - t.Logf("Mined %d blocks, waiting for logs to be recovered", dummyBlocks) - expectedPostRecover := runs + emits - waitPipelineRuns(t, nodes, expectedPostRecover, testutils.WaitTimeout(t), cltest.DBPollingInterval) + t.Logf("Mined %d blocks, waiting for logs to be recovered", dummyBlocks) + listener, done := listenPerformedN(t, backend, registry, ids, int64(beforeDummyBlocks), recoverEmits) + g.Eventually(listener, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.BeTrue()) + done() }) } @@ -296,7 +294,7 @@ func TestIntegration_KeeperPluginLogUpkeep_Retry(t *testing.T) { registry := deployKeeper21Registry(t, registryOwner, backend, linkAddr, linkFeedAddr, gasFeedAddr) - nodes, mercuryServer := setupNodes(t, nodeKeys, registry, backend, registryOwner) + _, mercuryServer := setupNodes(t, nodeKeys, registry, backend, registryOwner) const upkeepCount = 10 const mercuryFailCount = upkeepCount * 3 * 2 @@ -374,39 +372,6 @@ func TestIntegration_KeeperPluginLogUpkeep_Retry(t *testing.T) { g.Eventually(listener, testutils.WaitTimeout(t)-(5*time.Second), cltest.DBPollingInterval).Should(gomega.BeTrue()) done() - - _ = checkPipelineRuns(t, nodes, 1*len(nodes)) // TODO: TBD -} - -func waitPipelineRuns(t *testing.T, nodes []Node, n int, timeout, interval time.Duration) { - ctx, cancel := context.WithTimeout(testutils.Context(t), timeout) - defer cancel() - var allRuns []pipeline.Run - for len(allRuns) < n && ctx.Err() == nil { - allRuns = []pipeline.Run{} - for _, node := range nodes { - runs, err := node.App.PipelineORM().GetAllRuns() - require.NoError(t, err) - allRuns = append(allRuns, runs...) - } - time.Sleep(interval) - } - runs := len(allRuns) - t.Logf("found %d pipeline runs", runs) - require.GreaterOrEqual(t, runs, n) -} - -func checkPipelineRuns(t *testing.T, nodes []Node, n int) int { - var allRuns []pipeline.Run - for _, node := range nodes { - runs, err2 := node.App.PipelineORM().GetAllRuns() - require.NoError(t, err2) - allRuns = append(allRuns, runs...) - } - runs := len(allRuns) - t.Logf("found %d pipeline runs", runs) - require.GreaterOrEqual(t, runs, n) - return runs } func emitEvents(ctx context.Context, t *testing.T, n int, contracts []*log_upkeep_counter_wrapper.LogUpkeepCounter, carrol *bind.TransactOpts, afterEmit func()) { @@ -424,32 +389,32 @@ func mapListener(m *sync.Map, n int) func() bool { return func() bool { count := 0 m.Range(func(key, value interface{}) bool { - count++ + count += value.(int) return true }) return count > n } } -func listenPerformed(t *testing.T, backend *backends.SimulatedBackend, registry *iregistry21.IKeeperRegistryMaster, ids []*big.Int, startBlock int64) (func() bool, func()) { +func listenPerformedN(t *testing.T, backend *backends.SimulatedBackend, registry *iregistry21.IKeeperRegistryMaster, ids []*big.Int, startBlock int64, count int) (func() bool, func()) { cache := &sync.Map{} ctx, cancel := context.WithCancel(testutils.Context(t)) start := startBlock go func() { for ctx.Err() == nil { - bl := backend.Blockchain().CurrentBlock().Number.Uint64() + currentBlock := backend.Blockchain().CurrentBlock().Number.Uint64() - sc := make([]bool, len(ids)) - for i := range sc { - sc[i] = true + success := make([]bool, len(ids)) + for i := range success { + success[i] = true } iter, err := registry.FilterUpkeepPerformed(&bind.FilterOpts{ Start: uint64(start), - End: &bl, + End: ¤tBlock, Context: ctx, - }, ids, sc) + }, ids, success) if ctx.Err() != nil { return @@ -460,7 +425,15 @@ func listenPerformed(t *testing.T, backend *backends.SimulatedBackend, registry for iter.Next() { if iter.Event != nil { t.Logf("[automation-ocr3 | EvmRegistry] upkeep performed event emitted for id %s", iter.Event.Id.String()) - cache.Store(iter.Event.Id.String(), true) + + //cache.Store(iter.Event.Id.String(), true) + count, ok := cache.Load(iter.Event.Id.String()) + if !ok { + cache.Store(iter.Event.Id.String(), 1) + continue + } + countI := count.(int) + cache.Store(iter.Event.Id.String(), countI+1) } } @@ -470,7 +443,11 @@ func listenPerformed(t *testing.T, backend *backends.SimulatedBackend, registry } }() - return mapListener(cache, 0), cancel + return mapListener(cache, count), cancel +} + +func listenPerformed(t *testing.T, backend *backends.SimulatedBackend, registry *iregistry21.IKeeperRegistryMaster, ids []*big.Int, startBlock int64) (func() bool, func()) { + return listenPerformedN(t, backend, registry, ids, startBlock, 0) } func setupNodes(t *testing.T, nodeKeys [5]ethkey.KeyV2, registry *iregistry21.IKeeperRegistryMaster, backend *backends.SimulatedBackend, usr *bind.TransactOpts) ([]Node, *SimulatedMercuryServer) { diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index 6674b0828b1..d0a93f77631 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -58,7 +58,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -411,15 +410,6 @@ func TestIntegration_KeeperPluginBasic(t *testing.T) { } g.Eventually(receivedBytes, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.Equal(payload1)) - // check pipeline runs - var allRuns []pipeline.Run - for _, node := range nodes { - runs, err2 := node.App.PipelineORM().GetAllRuns() - require.NoError(t, err2) - allRuns = append(allRuns, runs...) - } - require.GreaterOrEqual(t, len(allRuns), 1) - // change payload _, err = upkeepContract.SetBytesToSend(carrol, payload2) require.NoError(t, err) @@ -683,15 +673,6 @@ func TestIntegration_KeeperPluginForwarderEnabled(t *testing.T) { } g.Eventually(receivedBytes, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.Equal(payload1)) - // check pipeline runs - var allRuns []pipeline.Run - for _, node := range nodes { - runs, err2 := node.App.PipelineORM().GetAllRuns() - require.NoError(t, err2) - allRuns = append(allRuns, runs...) - } - require.GreaterOrEqual(t, len(allRuns), 1) - // change payload _, err = upkeepContract.SetBytesToSend(carrol, payload2) require.NoError(t, err) diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 76e5bb6e00e..c3c60ad58b1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -17,13 +17,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" evmregistry20 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20" evmregistry21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" evmregistry21transmit "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) @@ -43,9 +43,9 @@ var ( ErrNoChainFromSpec = fmt.Errorf("could not create chain from spec") ) -func EVMProvider(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, spec job.Job, pr pipeline.Runner) (evmrelay.OCR2KeeperProvider, error) { +func EVMProvider(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, spec job.Job, ethKeystore keystore.Eth) (evmrelay.OCR2KeeperProvider, error) { oSpec := spec.OCR2OracleSpec - ocr2keeperRelayer := evmrelay.NewOCR2KeeperRelayer(db, chain, pr, spec, lggr.Named("OCR2KeeperRelayer")) + ocr2keeperRelayer := evmrelay.NewOCR2KeeperRelayer(db, chain, lggr.Named("OCR2KeeperRelayer"), ethKeystore) keeperProvider, err := ocr2keeperRelayer.NewOCR2KeeperProvider( types.RelayArgs{ @@ -71,7 +71,7 @@ func EVMDependencies20( db *sqlx.DB, lggr logger.Logger, chain legacyevm.Chain, - pr pipeline.Runner, + ethKeystore keystore.Eth, ) (evmrelay.OCR2KeeperProvider, *evmregistry20.EvmRegistry, Encoder20, *evmregistry20.LogProvider, error) { var err error @@ -79,7 +79,7 @@ func EVMDependencies20( var registry *evmregistry20.EvmRegistry // the provider will be returned as a dependency - if keeperProvider, err = EVMProvider(db, chain, lggr, spec, pr); err != nil { + if keeperProvider, err = EVMProvider(db, chain, lggr, spec, ethKeystore); err != nil { return nil, nil, nil, nil, err } @@ -112,17 +112,17 @@ func EVMDependencies21( db *sqlx.DB, lggr logger.Logger, chain legacyevm.Chain, - pr pipeline.Runner, mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, dbCfg pg.QConfig, + ethKeystore keystore.Eth, ) (evmrelay.OCR2KeeperProvider, evmregistry21.AutomationServices, error) { var err error var keeperProvider evmrelay.OCR2KeeperProvider oSpec := spec.OCR2OracleSpec // the provider will be returned as a dependency - if keeperProvider, err = EVMProvider(db, chain, lggr, spec, pr); err != nil { + if keeperProvider, err = EVMProvider(db, chain, lggr, spec, ethKeystore); err != nil { return nil, nil, err } diff --git a/core/services/ocrcommon/transmitter_pipeline.go b/core/services/ocrcommon/transmitter_pipeline.go deleted file mode 100644 index e62f745a941..00000000000 --- a/core/services/ocrcommon/transmitter_pipeline.go +++ /dev/null @@ -1,97 +0,0 @@ -package ocrcommon - -import ( - "context" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - - "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" -) - -const txObservationSource = ` - transmit_tx [type=ethtx - minConfirmations=0 - to="$(jobSpec.contractAddress)" - from="[$(jobSpec.fromAddress)]" - evmChainID="$(jobSpec.evmChainID)" - data="$(jobSpec.data)" - gasLimit="$(jobSpec.gasLimit)" - forwardingAllowed="$(jobSpec.forwardingAllowed)" - transmitChecker="$(jobSpec.transmitChecker)"] - transmit_tx -` - -type pipelineTransmitter struct { - lgr logger.Logger - fromAddress common.Address - gasLimit uint32 - effectiveTransmitterAddress common.Address - strategy types.TxStrategy - checker txmgr.TransmitCheckerSpec - pr pipeline.Runner - spec job.Job - chainID string -} - -// NewPipelineTransmitter creates a new eth transmitter using the job pipeline mechanism -func NewPipelineTransmitter( - lgr logger.Logger, - fromAddress common.Address, - gasLimit uint32, - effectiveTransmitterAddress common.Address, - strategy types.TxStrategy, - checker txmgr.TransmitCheckerSpec, - pr pipeline.Runner, - spec job.Job, - chainID string, -) Transmitter { - return &pipelineTransmitter{ - lgr: lgr, - fromAddress: fromAddress, - gasLimit: gasLimit, - effectiveTransmitterAddress: effectiveTransmitterAddress, - strategy: strategy, - checker: checker, - pr: pr, - spec: spec, - chainID: chainID, - } -} - -func (t *pipelineTransmitter) CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, _ *txmgr.TxMeta) error { - // t.strategy is ignored currently as pipeline does not support passing this (sc-55115) - vars := pipeline.NewVarsFrom(map[string]interface{}{ - "jobSpec": map[string]interface{}{ - "contractAddress": toAddress.String(), - "fromAddress": t.fromAddress.String(), - "gasLimit": t.gasLimit, - "evmChainID": t.chainID, - "forwardingAllowed": t.spec.ForwardingAllowed, - "data": payload, - "transmitChecker": t.checker, - }, - }) - - t.spec.PipelineSpec.DotDagSource = txObservationSource - run := pipeline.NewRun(*t.spec.PipelineSpec, vars) - - if _, err := t.pr.Run(ctx, run, t.lgr, true, nil); err != nil { - return errors.Wrap(err, "Skipped OCR transmission") - } - - if run.State != pipeline.RunStatusCompleted { - return fmt.Errorf("unexpected pipeline run state: %s with fatal errors %w", run.State, run.FatalErrors.ToError()) - } - - return nil -} - -func (t *pipelineTransmitter) FromAddress() common.Address { - return t.effectiveTransmitterAddress -} diff --git a/core/services/ocrcommon/transmitter_pipeline_test.go b/core/services/ocrcommon/transmitter_pipeline_test.go deleted file mode 100644 index e0114d0aa0d..00000000000 --- a/core/services/ocrcommon/transmitter_pipeline_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package ocrcommon_test - -import ( - "testing" - - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - pipelinemocks "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" -) - -func Test_PipelineTransmitter_CreateEthTransaction(t *testing.T) { - t.Parallel() - - lggr := logger.TestLogger(t) - db := pgtest.NewSqlxDB(t) - cfg := configtest.NewTestGeneralConfig(t) - ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() - - _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - - chainID := "12345" - gasLimit := uint32(1000) - effectiveTransmitterAddress := fromAddress - toAddress := testutils.NewAddress() - payload := []byte{1, 2, 3} - strategy := newMockTxStrategy(t) - checker := txmgr.TransmitCheckerSpec{CheckerType: txmgr.TransmitCheckerTypeSimulate} - runner := pipelinemocks.NewRunner(t) - - transmitter := ocrcommon.NewPipelineTransmitter( - lggr, - fromAddress, - gasLimit, - effectiveTransmitterAddress, - strategy, - checker, - runner, - job.Job{ - PipelineSpec: &pipeline.Spec{}, - }, - chainID, - ) - - runner.On("Run", mock.Anything, mock.AnythingOfType("*pipeline.Run"), mock.Anything, mock.Anything, mock.Anything). - Return(false, nil). - Run(func(args mock.Arguments) { - run := args.Get(1).(*pipeline.Run) - require.Equal(t, map[string]interface{}{ - "jobSpec": map[string]interface{}{ - "contractAddress": toAddress.String(), - "fromAddress": fromAddress.String(), - "gasLimit": gasLimit, - "evmChainID": chainID, - "forwardingAllowed": false, - "data": payload, - "transmitChecker": checker, - }, - }, run.Inputs.Val) - - save := args.Get(3).(bool) - require.True(t, save) - - run.State = pipeline.RunStatusCompleted - }).Once() - - require.NoError(t, transmitter.CreateEthTransaction(testutils.Context(t), toAddress, payload, nil)) -} diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index aea704adacf..83540e22bb7 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -27,13 +27,11 @@ import ( txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" mercuryconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" @@ -373,7 +371,12 @@ func newConfigProvider(lggr logger.Logger, chain legacyevm.Chain, opts *types.Re return newConfigWatcher(lggr, aggregatorAddress, contractABI, offchainConfigDigester, cp, chain, relayConfig.FromBlock, opts.New), nil } -func newContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, transmitterID string, configWatcher *configWatcher, ethKeystore keystore.Eth) (*contractTransmitter, error) { +type configTransmitterOpts struct { + // override the gas limit default provided in the config watcher + pluginGasLimit *uint32 +} + +func newContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, transmitterID string, ethKeystore keystore.Eth, configWatcher *configWatcher, opts configTransmitterOpts) (*contractTransmitter, error) { var relayConfig types.RelayConfig if err := json.Unmarshal(rargs.RelayConfig, &relayConfig); err != nil { return nil, err @@ -415,6 +418,9 @@ func newContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, tra if ocr2Limit != nil { gasLimit = *ocr2Limit } + if opts.pluginGasLimit != nil { + gasLimit = *opts.pluginGasLimit + } transmitter, err := ocrcommon.NewTransmitter( configWatcher.chain.TxManager(), @@ -442,55 +448,6 @@ func newContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, tra ) } -func newPipelineContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, transmitterID string, pluginGasLimit *uint32, configWatcher *configWatcher, spec job.Job, pr pipeline.Runner) (*contractTransmitter, error) { - var relayConfig types.RelayConfig - if err := json.Unmarshal(rargs.RelayConfig, &relayConfig); err != nil { - return nil, err - } - - if !relayConfig.EffectiveTransmitterID.Valid { - return nil, pkgerrors.New("EffectiveTransmitterID must be specified") - } - effectiveTransmitterAddress := common.HexToAddress(relayConfig.EffectiveTransmitterID.String) - transmitterAddress := common.HexToAddress(transmitterID) - scoped := configWatcher.chain.Config() - strategy := txmgrcommon.NewQueueingTxStrategy(rargs.ExternalJobID, scoped.OCR2().DefaultTransactionQueueDepth(), scoped.Database().DefaultQueryTimeout()) - - var checker txm.TransmitCheckerSpec - if configWatcher.chain.Config().OCR2().SimulateTransactions() { - checker.CheckerType = txm.TransmitCheckerTypeSimulate - } - - gasLimit := configWatcher.chain.Config().EVM().GasEstimator().LimitDefault() - ocr2Limit := configWatcher.chain.Config().EVM().GasEstimator().LimitJobType().OCR2() - if ocr2Limit != nil { - gasLimit = *ocr2Limit - } - if pluginGasLimit != nil { - gasLimit = *pluginGasLimit - } - - return NewOCRContractTransmitter( - configWatcher.contractAddress, - configWatcher.chain.Client(), - configWatcher.contractABI, - ocrcommon.NewPipelineTransmitter( - lggr, - transmitterAddress, - gasLimit, - effectiveTransmitterAddress, - strategy, - checker, - pr, - spec, - configWatcher.chain.ID().String(), - ), - configWatcher.chain.LogPoller(), - lggr, - nil, - ) -} - func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.MedianProvider, error) { lggr := r.lggr.Named("MedianProvider").Named(rargs.ExternalJobID.String()) relayOpts := types.NewRelayOpts(rargs) @@ -513,7 +470,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } reportCodec := evmreportcodec.ReportCodec{} - contractTransmitter, err := newContractTransmitter(lggr, rargs, pargs.TransmitterID, configWatcher, r.ks.Eth()) + contractTransmitter, err := newContractTransmitter(lggr, rargs, pargs.TransmitterID, r.ks.Eth(), configWatcher, configTransmitterOpts{}) if err != nil { return nil, err } diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index abc03c7abb1..55c4d78e7b4 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -22,8 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -51,21 +50,19 @@ type OCR2KeeperRelayer interface { // ocr2keeperRelayer is the relayer with added DKG and OCR2Keeper provider functions. type ocr2keeperRelayer struct { - db *sqlx.DB - chain legacyevm.Chain - pr pipeline.Runner - spec job.Job - lggr logger.Logger + db *sqlx.DB + chain legacyevm.Chain + lggr logger.Logger + ethKeystore keystore.Eth } // NewOCR2KeeperRelayer is the constructor of ocr2keeperRelayer -func NewOCR2KeeperRelayer(db *sqlx.DB, chain legacyevm.Chain, pr pipeline.Runner, spec job.Job, lggr logger.Logger) OCR2KeeperRelayer { +func NewOCR2KeeperRelayer(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, ethKeystore keystore.Eth) OCR2KeeperRelayer { return &ocr2keeperRelayer{ - db: db, - chain: chain, - pr: pr, - spec: spec, - lggr: lggr, + db: db, + chain: chain, + lggr: lggr, + ethKeystore: ethKeystore, } } @@ -76,7 +73,7 @@ func (r *ocr2keeperRelayer) NewOCR2KeeperProvider(rargs commontypes.RelayArgs, p } gasLimit := cfgWatcher.chain.Config().EVM().OCR2().Automation().GasLimit() - contractTransmitter, err := newPipelineContractTransmitter(r.lggr, rargs, pargs.TransmitterID, &gasLimit, cfgWatcher, r.spec, r.pr) + contractTransmitter, err := newContractTransmitter(r.lggr, rargs, pargs.TransmitterID, r.ethKeystore, cfgWatcher, configTransmitterOpts{pluginGasLimit: &gasLimit}) if err != nil { return nil, err } diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index 39d0503b8b1..1e05f89d9de 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -67,7 +67,7 @@ func (r *ocr2vrfRelayer) NewDKGProvider(rargs commontypes.RelayArgs, pargs commo if err != nil { return nil, err } - contractTransmitter, err := newContractTransmitter(r.lggr, rargs, pargs.TransmitterID, configWatcher, r.ethKeystore) + contractTransmitter, err := newContractTransmitter(r.lggr, rargs, pargs.TransmitterID, r.ethKeystore, configWatcher, configTransmitterOpts{}) if err != nil { return nil, err } @@ -90,7 +90,7 @@ func (r *ocr2vrfRelayer) NewOCR2VRFProvider(rargs commontypes.RelayArgs, pargs c if err != nil { return nil, err } - contractTransmitter, err := newContractTransmitter(r.lggr, rargs, pargs.TransmitterID, configWatcher, r.ethKeystore) + contractTransmitter, err := newContractTransmitter(r.lggr, rargs, pargs.TransmitterID, r.ethKeystore, configWatcher, configTransmitterOpts{}) if err != nil { return nil, err } From f3d5417948c9a837b50327d4bf6a218fac915a92 Mon Sep 17 00:00:00 2001 From: Connor Stein Date: Tue, 12 Dec 2023 17:11:55 -0500 Subject: [PATCH 043/234] Standardize LP filter logging (#11515) * Standardize logs * Comments * More PR review * More * Fix test --- core/chains/evm/logpoller/log_poller.go | 15 +++++++++------ .../evm/logpoller/log_poller_internal_test.go | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 77b80253591..991cc8d4309 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -217,6 +217,7 @@ func (filter *Filter) Contains(other *Filter) bool { // Generally speaking this is harmless. We enforce that EventSigs and Addresses are non-empty, // which means that anonymous events are not supported and log.Topics >= 1 always (log.Topics[0] is the event signature). // The filter may be unregistered later by Filter.Name +// Warnings/debug information is keyed by filter name. func (lp *logPoller) RegisterFilter(filter Filter, qopts ...pg.QOpt) error { if len(filter.Addresses) == 0 { return errors.Errorf("at least one address must be specified") @@ -242,33 +243,35 @@ func (lp *logPoller) RegisterFilter(filter Filter, qopts ...pg.QOpt) error { if existingFilter, ok := lp.filters[filter.Name]; ok { if existingFilter.Contains(&filter) { // Nothing new in this Filter + lp.lggr.Warnw("Filter already present, no-op", "name", filter.Name, "filter", filter) return nil } - lp.lggr.Warnw("Updating existing filter with more events or addresses", "filter", filter) - } else { - lp.lggr.Debugw("Creating new filter", "filter", filter) + lp.lggr.Warnw("Updating existing filter with more events or addresses", "name", filter.Name, "filter", filter) } if err := lp.orm.InsertFilter(filter, qopts...); err != nil { - return errors.Wrap(err, "RegisterFilter failed to save filter to db") + return errors.Wrap(err, "error inserting filter") } lp.filters[filter.Name] = filter lp.filterDirty = true return nil } +// UnregisterFilter will remove the filter with the given name. +// If the name does not exist, it will log an error but not return an error. +// Warnings/debug information is keyed by filter name. func (lp *logPoller) UnregisterFilter(name string, qopts ...pg.QOpt) error { lp.filterMu.Lock() defer lp.filterMu.Unlock() _, ok := lp.filters[name] if !ok { - lp.lggr.Errorf("Filter %s not found", name) + lp.lggr.Warnw("Filter not found", "name", name) return nil } if err := lp.orm.DeleteFilter(name, qopts...); err != nil { - return errors.Wrapf(err, "Failed to delete filter %s", name) + return errors.Wrap(err, "error deleting filter") } delete(lp.filters, name) lp.filterDirty = true diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index f840eefc7b8..09e1deb864f 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -57,7 +57,7 @@ func TestLogPoller_RegisterFilter(t *testing.T) { a1 := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbb") a2 := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbc") - lggr, observedLogs := logger.TestObserved(t, zapcore.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zapcore.WarnLevel) chainID := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) From ac9338f723d43283554e76bd9a996ebcaa90024d Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 13 Dec 2023 10:37:42 +0100 Subject: [PATCH 044/234] bump Foundry to the December release (#11540) --- .github/workflows/solidity-foundry.yml | 2 +- contracts/GNUmakefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index 7f6fa4f482e..b629106972e 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -58,7 +58,7 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 with: # Has to match the `make foundry` version. - version: nightly-09fe3e041369a816365a020f715ad6f94dbce9f2 + version: nightly-5b7e4cb3c882b28f3c32ba580de27ce7381f415a - name: Run Forge build if: needs.changes.outputs.changes == 'true' diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile index f5be193249c..8ec536d520f 100644 --- a/contracts/GNUmakefile +++ b/contracts/GNUmakefile @@ -43,7 +43,7 @@ mockery: $(mockery) ## Install mockery. .PHONY: foundry foundry: ## Install foundry. - foundryup --version nightly-09fe3e041369a816365a020f715ad6f94dbce9f2 + foundryup --version nightly-5b7e4cb3c882b28f3c32ba580de27ce7381f415a .PHONY: foundry-refresh foundry-refresh: foundry From 57236b327a4f3f86d378ad2622572f3833256d3f Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 13 Dec 2023 09:20:12 -0500 Subject: [PATCH 045/234] Removes Optimism Goerli from Scheduled Tests (#11559) * Removes Optimism Goerli from Scheduled Tests * Missed a spot --- .github/workflows/live-testnet-tests.yml | 57 +----------------------- 1 file changed, 2 insertions(+), 55 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 2e9809505f2..d060b5c510d 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -33,9 +33,6 @@ env: BSC_TESTNET_URLS: ${{ secrets.QA_BSC_TESTNET_URLS }} BSC_TESTNET_HTTP_URLS: ${{ secrets.QA_BSC_TESTNET_HTTP_URLS }} - OPTIMISM_GOERLI_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_URLS }} - OPTIMISM_GOERLI_HTTP_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_HTTP_URLS }} - OPTIMISM_SEPOLIA_URLS: ${{ secrets.QA_OPTIMISM_SEPOLIA_URLS }} OPTIMISM_SEPOLIA_HTTP_URLS: ${{ secrets.QA_OPTIMISM_SEPOLIA_HTTP_URLS }} @@ -147,7 +144,7 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: [sepolia-smoke-tests, bsc-testnet-tests, optimism-goerli-smoke-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, scroll-sepolia-smoke-tests, linea-goerli-smoke-tests] + needs: [sepolia-smoke-tests, bsc-testnet-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, scroll-sepolia-smoke-tests, linea-goerli-smoke-tests] steps: - name: Debug Result run: echo ${{ join(needs.*.result, ',') }} @@ -208,7 +205,7 @@ jobs: strategy: fail-fast: false matrix: - network: [Sepolia, Optimism Goerli, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Scroll Sepolia, Linea Goerli] + network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Scroll Sepolia, Linea Goerli] steps: - name: Get Results id: test-results @@ -391,56 +388,6 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - optimism-goerli-smoke-tests: - environment: integration - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: OPTIMISM_GOERLI - strategy: - fail-fast: false - max-parallel: 1 - matrix: - include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations - - product: OCR - test: TestOCRBasic - - product: Automation Conditional - test: TestAutomationBasic/registry_2_1_conditional - - product: Automation Log Trigger - test: TestAutomationBasic/registry_2_1_logtrigger - name: Optimism Goerli ${{ matrix.product }} Tests - runs-on: ubuntu-latest - steps: - - name: Download Tests Binary - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - with: - name: tests - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-goerli - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - with: - test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} - binary_name: tests - cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./logs - token: ${{ secrets.GITHUB_TOKEN }} - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: "true" - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - optimism-sepolia-smoke-tests: environment: integration permissions: From 1a26acd5515ddeaf14e17f98cfbf474249d05157 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 13 Dec 2023 08:35:28 -0600 Subject: [PATCH 046/234] fix health monitoring (#11558) --- core/chains/evm/gas/models.go | 31 +++- core/chains/evm/gas/models_test.go | 71 +++++---- core/chains/evm/txmgr/broadcaster_test.go | 12 +- core/chains/evm/txmgr/confirmer_test.go | 13 +- core/services/chainlink/relayer_factory.go | 6 +- core/services/relay/evm/evm.go | 4 +- core/web/health_controller.go | 11 +- core/web/health_controller_test.go | 70 ++++++++- core/web/testdata/body/health.json | 166 +++++++++++++++++++++ 9 files changed, 333 insertions(+), 51 deletions(-) create mode 100644 core/web/testdata/body/health.json diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index c7476d58ba4..8d977df0991 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -71,19 +71,31 @@ func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config, ge if rollups.IsRollupWithL1Support(cfg.ChainType()) { l1Oracle = rollups.NewL1GasPriceOracle(lggr, ethClient, cfg.ChainType()) } + var newEstimator func(logger.Logger) EvmEstimator switch s { case "Arbitrum": - return NewWrappedEvmEstimator(NewArbitrumEstimator(lggr, geCfg, ethClient, ethClient), df, l1Oracle) + newEstimator = func(l logger.Logger) EvmEstimator { + return NewArbitrumEstimator(lggr, geCfg, ethClient, ethClient) + } case "BlockHistory": - return NewWrappedEvmEstimator(NewBlockHistoryEstimator(lggr, ethClient, cfg, geCfg, bh, *ethClient.ConfiguredChainID()), df, l1Oracle) + newEstimator = func(l logger.Logger) EvmEstimator { + return NewBlockHistoryEstimator(lggr, ethClient, cfg, geCfg, bh, *ethClient.ConfiguredChainID()) + } case "FixedPrice": - return NewWrappedEvmEstimator(NewFixedPriceEstimator(geCfg, bh, lggr), df, l1Oracle) + newEstimator = func(l logger.Logger) EvmEstimator { + return NewFixedPriceEstimator(geCfg, bh, lggr) + } case "L2Suggested", "SuggestedPrice": - return NewWrappedEvmEstimator(NewSuggestedPriceEstimator(lggr, ethClient), df, l1Oracle) + newEstimator = func(l logger.Logger) EvmEstimator { + return NewSuggestedPriceEstimator(lggr, ethClient) + } default: lggr.Warnf("GasEstimator: unrecognised mode '%s', falling back to FixedPriceEstimator", s) - return NewWrappedEvmEstimator(NewFixedPriceEstimator(geCfg, bh, lggr), df, l1Oracle) + newEstimator = func(l logger.Logger) EvmEstimator { + return NewFixedPriceEstimator(geCfg, bh, lggr) + } } + return NewWrappedEvmEstimator(lggr, newEstimator, df, l1Oracle) } // DynamicFee encompasses both FeeCap and TipCap for EIP1559 transactions @@ -150,6 +162,7 @@ func (fee EvmFee) ValidDynamic() bool { // WrappedEvmEstimator provides a struct that wraps the EVM specific dynamic and legacy estimators into one estimator that conforms to the generic FeeEstimator type WrappedEvmEstimator struct { services.StateMachine + lggr logger.Logger EvmEstimator EIP1559Enabled bool l1Oracle rollups.L1Oracle @@ -157,16 +170,18 @@ type WrappedEvmEstimator struct { var _ EvmFeeEstimator = (*WrappedEvmEstimator)(nil) -func NewWrappedEvmEstimator(e EvmEstimator, eip1559Enabled bool, l1Oracle rollups.L1Oracle) EvmFeeEstimator { +func NewWrappedEvmEstimator(lggr logger.Logger, newEstimator func(logger.Logger) EvmEstimator, eip1559Enabled bool, l1Oracle rollups.L1Oracle) EvmFeeEstimator { + lggr = logger.Named(lggr, "WrappedEvmEstimator") return &WrappedEvmEstimator{ - EvmEstimator: e, + lggr: lggr, + EvmEstimator: newEstimator(lggr), EIP1559Enabled: eip1559Enabled, l1Oracle: l1Oracle, } } func (e *WrappedEvmEstimator) Name() string { - return fmt.Sprintf("WrappedEvmEstimator(%s)", e.EvmEstimator.Name()) + return e.lggr.Name() } func (e *WrappedEvmEstimator) Start(ctx context.Context) error { diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index a2dce58ee3f..95a7a471eba 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" @@ -27,39 +28,41 @@ func TestWrappedEvmEstimator(t *testing.T) { FeeCap: assets.NewWeiI(20), TipCap: assets.NewWeiI(1), } - - e := mocks.NewEvmEstimator(t) - e.On("GetDynamicFee", mock.Anything, mock.Anything, mock.Anything). + est := mocks.NewEvmEstimator(t) + est.On("GetDynamicFee", mock.Anything, mock.Anything, mock.Anything). Return(dynamicFee, gasLimit, nil).Twice() - e.On("GetLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + est.On("GetLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(legacyFee, gasLimit, nil).Twice() - e.On("BumpDynamicFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + est.On("BumpDynamicFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(dynamicFee, gasLimit, nil).Once() - e.On("BumpLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + est.On("BumpLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(legacyFee, gasLimit, nil).Once() + getRootEst := func(logger.Logger) gas.EvmEstimator { return est } - mockEvmEstimatorName := "MockEstimator" - mockEstimatorName := "WrappedEvmEstimator(MockEstimator)" + mockEstimatorName := "WrappedEvmEstimator" + mockEvmEstimatorName := "WrappedEvmEstimator.MockEstimator" // L1Oracle returns the correct L1Oracle interface t.Run("L1Oracle", func(t *testing.T) { + lggr := logger.Test(t) // expect nil - estimator := gas.NewWrappedEvmEstimator(e, false, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getRootEst, false, nil) l1Oracle := estimator.L1Oracle() assert.Nil(t, l1Oracle) // expect l1Oracle oracle := rollupMocks.NewL1Oracle(t) - estimator = gas.NewWrappedEvmEstimator(e, false, oracle) + estimator = gas.NewWrappedEvmEstimator(lggr, getRootEst, false, oracle) l1Oracle = estimator.L1Oracle() assert.Equal(t, oracle, l1Oracle) }) // GetFee returns gas estimation based on configuration value t.Run("GetFee", func(t *testing.T) { + lggr := logger.Test(t) // expect legacy fee data dynamicFees := false - estimator := gas.NewWrappedEvmEstimator(e, dynamicFees, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getRootEst, dynamicFees, nil) fee, max, err := estimator.GetFee(ctx, nil, 0, nil) require.NoError(t, err) assert.Equal(t, gasLimit, max) @@ -69,7 +72,7 @@ func TestWrappedEvmEstimator(t *testing.T) { // expect dynamic fee data dynamicFees = true - estimator = gas.NewWrappedEvmEstimator(e, dynamicFees, nil) + estimator = gas.NewWrappedEvmEstimator(lggr, getRootEst, dynamicFees, nil) fee, max, err = estimator.GetFee(ctx, nil, 0, nil) require.NoError(t, err) assert.Equal(t, gasLimit, max) @@ -80,8 +83,9 @@ func TestWrappedEvmEstimator(t *testing.T) { // BumpFee returns bumped fee type based on original fee calculation t.Run("BumpFee", func(t *testing.T) { + lggr := logger.Test(t) dynamicFees := false - estimator := gas.NewWrappedEvmEstimator(e, dynamicFees, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getRootEst, dynamicFees, nil) // expect legacy fee data fee, max, err := estimator.BumpFee(ctx, gas.EvmFee{Legacy: assets.NewWeiI(0)}, 0, nil, nil) @@ -114,11 +118,12 @@ func TestWrappedEvmEstimator(t *testing.T) { }) t.Run("GetMaxCost", func(t *testing.T) { + lggr := logger.Test(t) val := assets.NewEthValue(1) // expect legacy fee data dynamicFees := false - estimator := gas.NewWrappedEvmEstimator(e, dynamicFees, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getRootEst, dynamicFees, nil) total, err := estimator.GetMaxCost(ctx, val, nil, gasLimit, nil) require.NoError(t, err) fee := new(big.Int).Mul(legacyFee.ToInt(), big.NewInt(int64(gasLimit))) @@ -126,7 +131,7 @@ func TestWrappedEvmEstimator(t *testing.T) { // expect dynamic fee data dynamicFees = true - estimator = gas.NewWrappedEvmEstimator(e, dynamicFees, nil) + estimator = gas.NewWrappedEvmEstimator(lggr, getRootEst, dynamicFees, nil) total, err = estimator.GetMaxCost(ctx, val, nil, gasLimit, nil) require.NoError(t, err) fee = new(big.Int).Mul(dynamicFee.FeeCap.ToInt(), big.NewInt(int64(gasLimit))) @@ -134,33 +139,38 @@ func TestWrappedEvmEstimator(t *testing.T) { }) t.Run("Name", func(t *testing.T) { - evmEstimator := mocks.NewEvmEstimator(t) - oracle := rollupMocks.NewL1Oracle(t) + lggr := logger.Test(t) + oracle := rollupMocks.NewL1Oracle(t) + evmEstimator := mocks.NewEvmEstimator(t) evmEstimator.On("Name").Return(mockEvmEstimatorName, nil).Once() - estimator := gas.NewWrappedEvmEstimator(evmEstimator, false, oracle) - name := estimator.Name() - require.Equal(t, mockEstimatorName, name) + estimator := gas.NewWrappedEvmEstimator(lggr, func(logger.Logger) gas.EvmEstimator { + return evmEstimator + }, false, oracle) + + require.Equal(t, mockEstimatorName, estimator.Name()) + require.Equal(t, mockEvmEstimatorName, evmEstimator.Name()) }) t.Run("Start and stop calls both EVM estimator and L1Oracle", func(t *testing.T) { - evmEstimator := mocks.NewEvmEstimator(t) + lggr := logger.Test(t) oracle := rollupMocks.NewL1Oracle(t) + evmEstimator := mocks.NewEvmEstimator(t) - evmEstimator.On("Name").Return(mockEvmEstimatorName, nil).Times(4) evmEstimator.On("Start", mock.Anything).Return(nil).Twice() evmEstimator.On("Close").Return(nil).Twice() oracle.On("Start", mock.Anything).Return(nil).Once() oracle.On("Close").Return(nil).Once() + getEst := func(logger.Logger) gas.EvmEstimator { return evmEstimator } - estimator := gas.NewWrappedEvmEstimator(evmEstimator, false, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getEst, false, nil) err := estimator.Start(ctx) require.NoError(t, err) err = estimator.Close() require.NoError(t, err) - estimator = gas.NewWrappedEvmEstimator(evmEstimator, false, oracle) + estimator = gas.NewWrappedEvmEstimator(lggr, getEst, false, oracle) err = estimator.Start(ctx) require.NoError(t, err) err = estimator.Close() @@ -168,22 +178,25 @@ func TestWrappedEvmEstimator(t *testing.T) { }) t.Run("Read calls both EVM estimator and L1Oracle", func(t *testing.T) { + lggr := logger.Test(t) evmEstimator := mocks.NewEvmEstimator(t) oracle := rollupMocks.NewL1Oracle(t) evmEstimator.On("Ready").Return(nil).Twice() oracle.On("Ready").Return(nil).Once() + getEst := func(logger.Logger) gas.EvmEstimator { return evmEstimator } - estimator := gas.NewWrappedEvmEstimator(evmEstimator, false, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getEst, false, nil) err := estimator.Ready() require.NoError(t, err) - estimator = gas.NewWrappedEvmEstimator(evmEstimator, false, oracle) + estimator = gas.NewWrappedEvmEstimator(lggr, getEst, false, oracle) err = estimator.Ready() require.NoError(t, err) }) t.Run("HealthReport merges report from EVM estimator and L1Oracle", func(t *testing.T) { + lggr := logger.Test(t) evmEstimator := mocks.NewEvmEstimator(t) oracle := rollupMocks.NewL1Oracle(t) @@ -192,17 +205,17 @@ func TestWrappedEvmEstimator(t *testing.T) { oracleKey := "oracle" oracleError := errors.New("oracle error") - evmEstimator.On("Name").Return(mockEvmEstimatorName, nil).Twice() evmEstimator.On("HealthReport").Return(map[string]error{evmEstimatorKey: evmEstimatorError}).Twice() oracle.On("HealthReport").Return(map[string]error{oracleKey: oracleError}).Once() + getEst := func(logger.Logger) gas.EvmEstimator { return evmEstimator } - estimator := gas.NewWrappedEvmEstimator(evmEstimator, false, nil) + estimator := gas.NewWrappedEvmEstimator(lggr, getEst, false, nil) report := estimator.HealthReport() require.True(t, errors.Is(report[evmEstimatorKey], evmEstimatorError)) require.Nil(t, report[oracleKey]) require.NotNil(t, report[mockEstimatorName]) - estimator = gas.NewWrappedEvmEstimator(evmEstimator, false, oracle) + estimator = gas.NewWrappedEvmEstimator(lggr, getEst, false, oracle) report = estimator.HealthReport() require.True(t, errors.Is(report[evmEstimatorKey], evmEstimatorError)) require.True(t, errors.Is(report[oracleKey], oracleError)) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index b9e8fe99e31..f676d3d18e2 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -64,7 +64,9 @@ func NewTestEthBroadcaster( lggr := logger.Test(t) ge := config.EVM().GasEstimator() - estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(config.EVM().GasEstimator(), ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) + estimator := gas.NewWrappedEvmEstimator(lggr, func(lggr logger.Logger) gas.EvmEstimator { + return gas.NewFixedPriceEstimator(config.EVM().GasEstimator(), ge.BlockHistory(), lggr) + }, ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, keyStore, estimator) txNonceSyncer := txmgr.NewNonceSyncer(txStore, lggr, ethClient) ethBroadcaster := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(config.EVM().GasEstimator()), config.EVM().Transactions(), config.Database().Listener(), keyStore, txBuilder, txNonceSyncer, lggr, checkerFactory, nonceAutoSync) @@ -1134,7 +1136,9 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // same as the parent test, but callback is set by ctor t.Run("callback set by ctor", func(t *testing.T) { lggr := logger.Test(t) - estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr), evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) + estimator := gas.NewWrappedEvmEstimator(lggr, func(lggr logger.Logger) gas.EvmEstimator { + return gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr) + }, evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), evmcfg.EVM().GasEstimator(), ethKeyStore, estimator) localNextNonce = getLocalNextNonce(t, eb, fromAddress) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() @@ -1759,7 +1763,9 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { ethNodeNonce := uint64(22) - estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr), evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) + estimator := gas.NewWrappedEvmEstimator(lggr, func(lggr logger.Logger) gas.EvmEstimator { + return gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr) + }, evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) checkerFactory := &testCheckerFactory{} ge := evmcfg.EVM().GasEstimator() diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 3acbfe9800c..30b2a391a7f 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -122,9 +122,10 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { cltest.MustInsertRandomKey(t, ethKeyStore) cltest.MustInsertRandomKey(t, ethKeyStore) estimator := gasmocks.NewEvmEstimator(t) + newEst := func(logger.Logger) gas.EvmEstimator { return estimator } lggr := logger.Test(t) ge := config.EVM().GasEstimator() - feeEstimator := gas.NewWrappedEvmEstimator(estimator, ge.EIP1559DynamicFees(), nil) + feeEstimator := gas.NewWrappedEvmEstimator(lggr, newEst, ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ethKeyStore, feeEstimator) ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), config.Database(), ethKeyStore, txBuilder, lggr) ctx := testutils.Context(t) @@ -1639,9 +1640,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing kst := ksmocks.NewEth(t) estimator := gasmocks.NewEvmEstimator(t) + newEst := func(logger.Logger) gas.EvmEstimator { return estimator } estimator.On("BumpLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, uint32(0), pkgerrors.Wrapf(commonfee.ErrConnectivity, "transaction...")) ge := ccfg.EVM().GasEstimator() - feeEstimator := gas.NewWrappedEvmEstimator(estimator, ge.EIP1559DynamicFees(), nil) + feeEstimator := gas.NewWrappedEvmEstimator(lggr, newEst, ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, kst, feeEstimator) addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Maybe() @@ -1685,9 +1687,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing estimator := gasmocks.NewEvmEstimator(t) estimator.On("BumpDynamicFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.DynamicFee{}, uint32(0), pkgerrors.Wrapf(commonfee.ErrConnectivity, "transaction...")) + newEst := func(logger.Logger) gas.EvmEstimator { return estimator } // Create confirmer with necessary state ge := ccfg.EVM().GasEstimator() - feeEstimator := gas.NewWrappedEvmEstimator(estimator, ge.EIP1559DynamicFees(), nil) + feeEstimator := gas.NewWrappedEvmEstimator(lggr, newEst, ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, kst, feeEstimator) addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Maybe() @@ -3079,7 +3082,9 @@ func ptr[T any](t T) *T { return &t } func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Client, config evmconfig.ChainScopedConfig, ks keystore.Eth, fn txmgrcommon.ResumeCallback) *txmgr.Confirmer { lggr := logger.Test(t) ge := config.EVM().GasEstimator() - estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(ge, ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) + estimator := gas.NewWrappedEvmEstimator(lggr, func(lggr logger.Logger) gas.EvmEstimator { + return gas.NewFixedPriceEstimator(ge, ge.BlockHistory(), lggr) + }, ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), config.Database(), ks, txBuilder, lggr) ec.SetResumeCallback(fn) diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 4ed73d8e53b..b4bd530d080 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -45,9 +45,11 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m relayers := make(map[relay.ID]evmrelay.LoopRelayAdapter) + lggr := r.Logger.Named("EVM") + // override some common opts with the factory values. this seems weird... maybe other signatures should change, or this should take a different type... ccOpts := legacyevm.ChainRelayExtenderConfig{ - Logger: r.Logger.Named("EVM"), + Logger: lggr, KeyStore: config.CSAETHKeystore.Eth(), ChainOpts: config.ChainOpts, } @@ -71,7 +73,7 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m EventBroadcaster: ccOpts.EventBroadcaster, MercuryPool: r.MercuryPool, } - relayer, err2 := evmrelay.NewRelayer(r.Logger.Named("EVM").Named(relayID.ChainID), chain, relayerOpts) + relayer, err2 := evmrelay.NewRelayer(lggr.Named(relayID.ChainID), chain, relayerOpts) if err2 != nil { err = errors.Join(err, err2) continue diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 83540e22bb7..303cdd3ba0e 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -13,6 +13,7 @@ import ( "github.com/jmoiron/sqlx" pkgerrors "github.com/pkg/errors" "go.uber.org/multierr" + "golang.org/x/exp/maps" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" @@ -121,11 +122,12 @@ func (r *Relayer) Close() error { // Ready does noop: always ready func (r *Relayer) Ready() error { - return nil + return r.chain.Ready() } func (r *Relayer) HealthReport() (report map[string]error) { report = make(map[string]error) + maps.Copy(report, r.chain.HealthReport()) return } diff --git a/core/web/health_controller.go b/core/web/health_controller.go index d6a7edb2340..d6490e5542a 100644 --- a/core/web/health_controller.go +++ b/core/web/health_controller.go @@ -1,7 +1,10 @@ package web import ( + "cmp" "net/http" + "slices" + "testing" "github.com/gin-gonic/gin" @@ -94,6 +97,12 @@ func (hc *HealthController) Health(c *gin.Context) { }) } + if testing.Testing() { + slices.SortFunc(checks, func(a, b presenters.Check) int { + return cmp.Compare(a.Name, b.Name) + }) + } + // return a json description of all the checks - jsonAPIResponse(c, checks, "checks") + jsonAPIResponseWithStatus(c, checks, "checks", status) } diff --git a/core/web/health_controller_test.go b/core/web/health_controller_test.go index d380b279d00..7e7c42141ca 100644 --- a/core/web/health_controller_test.go +++ b/core/web/health_controller_test.go @@ -1,15 +1,19 @@ package web_test import ( + "bytes" + _ "embed" + "encoding/json" + "io" "net/http" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/mocks" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestHealthController_Readyz(t *testing.T) { @@ -47,3 +51,63 @@ func TestHealthController_Readyz(t *testing.T) { }) } } + +func TestHealthController_Health_status(t *testing.T) { + var tt = []struct { + name string + ready bool + status int + }{ + { + name: "not ready", + ready: false, + status: http.StatusServiceUnavailable, + }, + { + name: "ready", + ready: true, + status: http.StatusOK, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + app := cltest.NewApplicationWithKey(t) + healthChecker := new(mocks.Checker) + healthChecker.On("Start").Return(nil).Once() + healthChecker.On("IsHealthy").Return(tc.ready, nil).Once() + healthChecker.On("Close").Return(nil).Once() + + app.HealthChecker = healthChecker + require.NoError(t, app.Start(testutils.Context(t))) + + client := app.NewHTTPClient(nil) + resp, cleanup := client.Get("/health") + t.Cleanup(cleanup) + assert.Equal(t, tc.status, resp.StatusCode) + }) + } +} + +var ( + //go:embed testdata/body/health.json + healthJSON string +) + +func TestHealthController_Health_body(t *testing.T) { + app := cltest.NewApplicationWithKey(t) + require.NoError(t, app.Start(testutils.Context(t))) + + client := app.NewHTTPClient(nil) + resp, cleanup := client.Get("/health") + t.Cleanup(cleanup) + assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + // pretty print for comparison + var b bytes.Buffer + require.NoError(t, json.Indent(&b, body, "", " ")) + body = b.Bytes() + + assert.Equal(t, healthJSON, string(body)) +} diff --git a/core/web/testdata/body/health.json b/core/web/testdata/body/health.json new file mode 100644 index 00000000000..d8418560543 --- /dev/null +++ b/core/web/testdata/body/health.json @@ -0,0 +1,166 @@ +{ + "data": [ + { + "type": "checks", + "id": "EVM.0", + "attributes": { + "name": "EVM.0", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.BalanceMonitor", + "attributes": { + "name": "EVM.0.BalanceMonitor", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.HeadBroadcaster", + "attributes": { + "name": "EVM.0.HeadBroadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.HeadTracker", + "attributes": { + "name": "EVM.0.HeadTracker", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.HeadTracker.HeadListener", + "attributes": { + "name": "EVM.0.HeadTracker.HeadListener", + "status": "failing", + "output": "Listener is not connected" + } + }, + { + "type": "checks", + "id": "EVM.0.LogBroadcaster", + "attributes": { + "name": "EVM.0.LogBroadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.Txm", + "attributes": { + "name": "EVM.0.Txm", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.Txm.BlockHistoryEstimator", + "attributes": { + "name": "EVM.0.Txm.BlockHistoryEstimator", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.Txm.Broadcaster", + "attributes": { + "name": "EVM.0.Txm.Broadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.Txm.Confirmer", + "attributes": { + "name": "EVM.0.Txm.Confirmer", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.0.Txm.WrappedEvmEstimator", + "attributes": { + "name": "EVM.0.Txm.WrappedEvmEstimator", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "JobSpawner", + "attributes": { + "name": "JobSpawner", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mercury.WSRPCPool", + "attributes": { + "name": "Mercury.WSRPCPool", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Monitor", + "attributes": { + "name": "Monitor", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PipelineORM", + "attributes": { + "name": "PipelineORM", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PipelineRunner", + "attributes": { + "name": "PipelineRunner", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PromReporter", + "attributes": { + "name": "PromReporter", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "TelemetryManager", + "attributes": { + "name": "TelemetryManager", + "status": "passing", + "output": "" + } + } + ] +} \ No newline at end of file From 6f13447cca0105bccd0aeea6c6cf69526f3e1585 Mon Sep 17 00:00:00 2001 From: Gabriel Paradiso Date: Wed, 13 Dec 2023 15:49:01 +0100 Subject: [PATCH 047/234] [FUN-990] s4 observability improvements (#11512) * chore: count s4 number of updates performed by nodes * chore: add storage total use and slots occupied * chore: add plugin side of counting updates * fix: modify total size counter to use snapshot len * chore: take into account the payload size for total size * chore: fix lint errors * fix: fetch only payload_size, reword metrics help * chore: remove don_id label * chore: remove don_id for consistency --- core/services/functions/connector_handler.go | 10 ++++++++ core/services/ocr2/plugins/s4/plugin.go | 27 +++++++++++++++++--- core/services/s4/orm.go | 11 ++++---- core/services/s4/postgres_orm.go | 2 +- core/services/s4/postgres_orm_test.go | 1 + 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 5496bbdefc1..1594dc6eb56 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -13,6 +13,8 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "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/services" @@ -57,6 +59,13 @@ var ( _ connector.GatewayConnectorHandler = &functionsConnectorHandler{} ) +var ( + promStorageUserUpdatesCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "storage_user_updates", + Help: "Number of storage updates performed by users", + }, []string{}) +) + // internal request ID is a hash of (sender, requestID) func InternalId(sender []byte, requestId []byte) RequestID { return RequestID(crypto.Keccak256Hash(append(sender, requestId...)).Bytes()) @@ -185,6 +194,7 @@ func (h *functionsConnectorHandler) handleSecretsSet(ctx context.Context, gatewa err = h.storage.Put(ctx, &key, &record, request.Signature) if err == nil { response.Success = true + promStorageUserUpdatesCount.WithLabelValues().Inc() } else { response.ErrorMessage = fmt.Sprintf("Failed to set secret: %v", err) } diff --git a/core/services/ocr2/plugins/s4/plugin.go b/core/services/ocr2/plugins/s4/plugin.go index fcb025b21cd..677743c091d 100644 --- a/core/services/ocr2/plugins/s4/plugin.go +++ b/core/services/ocr2/plugins/s4/plugin.go @@ -4,13 +4,28 @@ import ( "context" "time" + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/s4" +) - "github.com/pkg/errors" - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +var ( + promStoragePluginUpdatesCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "storage_plugin_updates", + Help: "Number of storage updates fetched from other nodes", + }, []string{}) + + promStorageTotalByteSize = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "storage_total_byte_size", + Help: "Current byte size of data stored in S4", + }, []string{}) ) type plugin struct { @@ -59,6 +74,7 @@ func (c *plugin) Query(ctx context.Context, ts types.ReportTimestamp) (types.Que return nil, errors.Wrap(err, "failed to GetVersions in Query()") } + var storageTotalByteSize uint64 rows := make([]*SnapshotRow, len(snapshot)) for i, v := range snapshot { rows[i] = &SnapshotRow{ @@ -66,6 +82,8 @@ func (c *plugin) Query(ctx context.Context, ts types.ReportTimestamp) (types.Que Slotid: uint32(v.SlotId), Version: v.Version, } + + storageTotalByteSize += v.PayloadSize } queryBytes, err := MarshalQuery(rows, c.addressRange) @@ -76,6 +94,8 @@ func (c *plugin) Query(ctx context.Context, ts types.ReportTimestamp) (types.Que promReportingPluginsQueryRowsCount.WithLabelValues(c.config.ProductName).Set(float64(len(rows))) promReportingPluginsQueryByteSize.WithLabelValues(c.config.ProductName).Set(float64(len(queryBytes))) + promStorageTotalByteSize.WithLabelValues().Set(float64(storageTotalByteSize)) + c.addressRange.Advance() c.logger.Debug("S4StorageReporting Query", commontypes.LogFields{ @@ -268,6 +288,7 @@ func (c *plugin) ShouldAcceptFinalizedReport(ctx context.Context, ts types.Repor c.logger.Error("Failed to Update a row in ShouldAcceptFinalizedReport()", commontypes.LogFields{"err": err}) continue } + promStoragePluginUpdatesCount.WithLabelValues().Inc() } c.logger.Debug("S4StorageReporting ShouldAcceptFinalizedReport", commontypes.LogFields{ diff --git a/core/services/s4/orm.go b/core/services/s4/orm.go index 59f3410e143..4d3cee9312a 100644 --- a/core/services/s4/orm.go +++ b/core/services/s4/orm.go @@ -20,11 +20,12 @@ type Row struct { // SnapshotRow(s) are returned by GetSnapshot function. type SnapshotRow struct { - Address *big.Big - SlotId uint - Version uint64 - Expiration int64 - Confirmed bool + Address *big.Big + SlotId uint + Version uint64 + Expiration int64 + Confirmed bool + PayloadSize uint64 } //go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore diff --git a/core/services/s4/postgres_orm.go b/core/services/s4/postgres_orm.go index dba98b64aa6..1f92f2e1281 100644 --- a/core/services/s4/postgres_orm.go +++ b/core/services/s4/postgres_orm.go @@ -90,7 +90,7 @@ func (o orm) GetSnapshot(addressRange *AddressRange, qopts ...pg.QOpt) ([]*Snaps q := o.q.WithOpts(qopts...) rows := make([]*SnapshotRow, 0) - stmt := fmt.Sprintf(`SELECT address, slot_id, version, expiration, confirmed FROM %s WHERE namespace = $1 AND address >= $2 AND address <= $3;`, o.tableName) + stmt := fmt.Sprintf(`SELECT address, slot_id, version, expiration, confirmed, octet_length(payload) AS payload_size FROM %s WHERE namespace = $1 AND address >= $2 AND address <= $3;`, o.tableName) if err := q.Select(&rows, stmt, o.namespace, addressRange.MinAddress, addressRange.MaxAddress); err != nil { if !errors.Is(err, sql.ErrNoRows) { return nil, err diff --git a/core/services/s4/postgres_orm_test.go b/core/services/s4/postgres_orm_test.go index 4d07524b4ea..d26f082ce5b 100644 --- a/core/services/s4/postgres_orm_test.go +++ b/core/services/s4/postgres_orm_test.go @@ -181,6 +181,7 @@ func TestPostgresORM_GetSnapshot(t *testing.T) { assert.Equal(t, snapshotRow.Version, sr.Version) assert.Equal(t, snapshotRow.Expiration, sr.Expiration) assert.Equal(t, snapshotRow.Confirmed, sr.Confirmed) + assert.Equal(t, snapshotRow.PayloadSize, uint64(len(sr.Payload))) } }) From 9b500414c022acc7fa704b516dfe65194d49fea4 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 13 Dec 2023 10:04:45 -0500 Subject: [PATCH 048/234] [TT-367] [TT-745] Quick and Dirty OCRv2 Soak Test (#11487) * Quick and Dirty OCRv2 Soak Test * Enable version in on-demand action * Remove inputs * Default input * Fix echos * Debug * Bash shell * Increment another way * No 2130 silly * When did incrementing get hard? * Cleanup debug * Include in reporter * Fix version input * Instantiate map * Fix OCR2 job names * Change default intpus * Bridge work * Build pair IDs properly * Cleanup paths * Build more bridges * Fix configuration * Fix config build * Fix import --- .github/workflows/on-demand-ocr-soak-test.yml | 45 ++- integration-tests/actions/ocr2_helpers.go | 146 +++++++-- .../actions/ocr2_helpers_local.go | 9 +- integration-tests/actions/ocr_helpers.go | 2 +- .../actions/ocr_helpers_local.go | 2 +- integration-tests/client/chainlink.go | 9 +- .../contracts/contract_deployer.go | 20 ++ integration-tests/load/README.md | 17 +- integration-tests/load/ocr/README.md | 15 +- integration-tests/load/ocr/ocr_test.go | 5 +- integration-tests/load/ocr/vu.go | 1 + integration-tests/scripts/entrypoint | 4 + integration-tests/testreporters/ocr.go | 7 +- integration-tests/testsetups/ocr.go | 298 +++++++++++++----- 14 files changed, 447 insertions(+), 133 deletions(-) diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 567d9510de9..a4289dd0129 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -55,18 +55,16 @@ on: description: Container image version for the Chainlink nodes required: true default: "2.7.0" - testDuration: - description: Duration of the test (time string) - required: false - default: 15m - chainlinkNodeFunding: - description: How much to fund each Chainlink node (in ETH) - required: false - default: ".001" - timeBetweenRounds: - description: How long to wait before starting a new round + ocrVersion: + description: Version of OCR to Use + type: choice + options: + - 1 + - 2 + testInputs: + description: Duration;Funding;TimeBetweenRounds required: false - default: 1m + default: "10m;.1;1m" jobs: ocr_soak_test: @@ -84,9 +82,7 @@ jobs: SELECTED_NETWORKS: ${{ inputs.network }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: ${{ secrets.QA_SLACK_CHANNEL }} - OCR_TEST_DURATION: ${{ inputs.testDuration }} - OCR_CHAINLINK_NODE_FUNDING: ${{ inputs.chainlinkNodeFunding }} - OCR_TIME_BETWEEN_ROUNDS: ${{ inputs.timeBetweenRounds }} + OCR_VERSION: ${{ inputs.ocrVersion }} TEST_LOG_LEVEL: debug REF_NAME: ${{ github.head_ref || github.ref_name }} ENV_JOB_IMAGE_BASE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests @@ -101,6 +97,7 @@ jobs: continue-on-error: true - name: Get Inputs run: | + # Mask the sensitive inputs EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) @@ -115,6 +112,26 @@ jobs: echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV + + # Read in our test inputs + IFS=';' read -ra parts <<< "${{ inputs.testInputs }}" + index=0 + for part in "${parts[@]}"; do + # A little hacky, but good enough for this + if [ $index -eq 0 ]; then + echo "OCR_TEST_DURATION=$part" + echo "OCR_TEST_DURATION=$part" >> $GITHUB_ENV + elif [ $index -eq 1 ]; then + echo "OCR_CHAINLINK_NODE_FUNDING=$part" + echo "OCR_CHAINLINK_NODE_FUNDING=$part" >> $GITHUB_ENV + elif [ $index -eq 2 ]; then + echo "OCR_TIME_BETWEEN_ROUNDS=$part" + echo "OCR_TIME_BETWEEN_ROUNDS=$part" >> $GITHUB_ENV + else + echo "Additional Unregistered Input: $part" + fi + ((index+=1)) + done - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index 02ce73e813e..c4bc30c7c5e 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" "github.com/lib/pq" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -23,6 +24,7 @@ import ( ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -104,12 +106,15 @@ func ConfigureOCRv2AggregatorContracts( } // BuildMedianOCR2Config builds a default OCRv2 config for the given chainlink nodes for a standard median aggregation job -func BuildMedianOCR2Config(workerNodes []*client.ChainlinkK8sClient) (*contracts.OCRv2Config, error) { +func BuildMedianOCR2Config( + workerNodes []*client.ChainlinkK8sClient, + ocrOffchainOptions contracts.OffchainOptions, +) (*contracts.OCRv2Config, error) { S, oracleIdentities, err := GetOracleIdentities(workerNodes) if err != nil { return nil, err } - signerKeys, transmitterAccounts, f_, onchainConfig, offchainConfigVersion, offchainConfig, err := confighelper.ContractSetConfigArgsForTests( + signerKeys, transmitterAccounts, f_, _, offchainConfigVersion, offchainConfig, err := confighelper.ContractSetConfigArgsForTests( 30*time.Second, // deltaProgress time.Duration, 30*time.Second, // deltaResend time.Duration, 10*time.Second, // deltaRound time.Duration, @@ -149,6 +154,8 @@ func BuildMedianOCR2Config(workerNodes []*client.ChainlinkK8sClient) (*contracts transmitterAddresses = append(transmitterAddresses, common.HexToAddress(string(account))) } + onchainConfig, err := testhelpers.GenerateDefaultOCR2OnchainConfig(ocrOffchainOptions.MinimumAnswer, ocrOffchainOptions.MaximumAnswer) + return &contracts.OCRv2Config{ Signers: signerAddresses, Transmitters: transmitterAddresses, @@ -156,7 +163,7 @@ func BuildMedianOCR2Config(workerNodes []*client.ChainlinkK8sClient) (*contracts OnchainConfig: onchainConfig, OffchainConfigVersion: offchainConfigVersion, OffchainConfig: []byte(fmt.Sprintf("0x%s", offchainConfig)), - }, nil + }, err } // GetOracleIdentities retrieves all chainlink nodes' OCR2 config identities with defaul key index @@ -257,7 +264,6 @@ func CreateOCRv2Jobs( bootstrapNode *client.ChainlinkK8sClient, workerChainlinkNodes []*client.ChainlinkK8sClient, mockserver *ctfClient.MockserverClient, - mockServerPath string, // Path on the mock server for the Chainlink nodes to query mockServerValue int, // Value to get from the mock server when querying the path chainId uint64, // EVM chain ID forwardingAllowed bool, @@ -268,20 +274,28 @@ func CreateOCRv2Jobs( return err } p2pV2Bootstrapper := fmt.Sprintf("%s@%s:%d", bootstrapP2PIds.Data[0].Attributes.PeerID, bootstrapNode.InternalIP(), 6690) - // Set the value for the jobs to report on - err = mockserver.SetValuePath(mockServerPath, mockServerValue) - if err != nil { - return err - } + mockJuelsPath := "ocr2/juelsPerFeeCoinSource" // Set the juelsPerFeeCoinSource config value - err = mockserver.SetValuePath(fmt.Sprintf("%s/juelsPerFeeCoinSource", mockServerPath), mockServerValue) + err = mockserver.SetValuePath(mockJuelsPath, mockServerValue) if err != nil { return err } + // Create the juels bridge for each node only once + juelsBridge := &client.BridgeTypeAttributes{ + Name: "juels", + URL: fmt.Sprintf("%s/%s", mockserver.Config.ClusterURL, mockJuelsPath), + } + for _, chainlinkNode := range workerChainlinkNodes { + err = chainlinkNode.MustCreateBridge(juelsBridge) + if err != nil { + return fmt.Errorf("failed creating bridge %s on CL node : %w", juelsBridge.Name, err) + } + } + for _, ocrInstance := range ocrInstances { bootstrapSpec := &client.OCR2TaskJobSpec{ - Name: "ocr2 bootstrap node", + Name: fmt.Sprintf("ocr2-bootstrap-%s", ocrInstance.Address()), JobType: "bootstrap", OCR2OracleSpec: job.OCR2OracleSpec{ ContractID: ocrInstance.Address(), @@ -289,7 +303,7 @@ func CreateOCRv2Jobs( RelayConfig: map[string]interface{}{ "chainID": chainId, }, - MonitoringEndpoint: null.StringFrom(fmt.Sprintf("%s/%s", mockserver.Config.ClusterURL, mockServerPath)), + MonitoringEndpoint: null.StringFrom(fmt.Sprintf("%s/%s", mockserver.Config.ClusterURL, "ocr2")), ContractConfigTrackerPollInterval: *models.NewInterval(15 * time.Second), }, } @@ -309,25 +323,22 @@ func CreateOCRv2Jobs( } nodeOCRKeyId := nodeOCRKeys.Data[0].ID - bta := &client.BridgeTypeAttributes{ - Name: mockServerPath, - URL: fmt.Sprintf("%s/%s", mockserver.Config.ClusterURL, mockServerPath), + nodeContractPairID, err := BuildOCR2NodeContractPairID(chainlinkNode, ocrInstance) + if err != nil { + return err } - juelsBridge := &client.BridgeTypeAttributes{ - Name: "juels", - URL: fmt.Sprintf("%s/%s/juelsPerFeeCoinSource", mockserver.Config.ClusterURL, mockServerPath), + bta := &client.BridgeTypeAttributes{ + Name: nodeContractPairID, + URL: fmt.Sprintf("%s/%s", mockserver.Config.ClusterURL, strings.TrimPrefix(nodeContractPairID, "/")), } + err = chainlinkNode.MustCreateBridge(bta) if err != nil { - return fmt.Errorf("creating bridge job have failed: %w", err) - } - err = chainlinkNode.MustCreateBridge(juelsBridge) - if err != nil { - return fmt.Errorf("creating bridge job have failed: %w", err) + return fmt.Errorf("failed creating bridge %s on CL node: %w", bta.Name, err) } ocrSpec := &client.OCR2TaskJobSpec{ - Name: "ocr2", + Name: fmt.Sprintf("ocr2-%s", uuid.NewString()), JobType: "offchainreporting2", MaxTaskDuration: "1m", ObservationSource: client.ObservationSourceSpecBridge(bta), @@ -379,3 +390,90 @@ func StartNewOCR2Round( } return nil } + +// SetOCR2AdapterResponse sets a single adapter response that correlates with an ocr contract and a chainlink node +// used for OCR2 tests +func SetOCR2AdapterResponse( + response int, + ocrInstance contracts.OffchainAggregatorV2, + chainlinkNode *client.ChainlinkK8sClient, + mockserver *ctfClient.MockserverClient, +) error { + nodeContractPairID, err := BuildOCR2NodeContractPairID(chainlinkNode, ocrInstance) + if err != nil { + return err + } + path := fmt.Sprintf("/%s", nodeContractPairID) + err = mockserver.SetValuePath(path, response) + if err != nil { + return fmt.Errorf("setting mockserver value path failed: %w", err) + } + return nil +} + +// SetOCR2AllAdapterResponsesToTheSameValue sets the mock responses in mockserver that are read by chainlink nodes +// to simulate different adapters. This sets all adapter responses for each node and contract to the same response +// used for OCR2 tests +func SetOCR2AllAdapterResponsesToTheSameValue( + response int, + ocrInstances []contracts.OffchainAggregatorV2, + chainlinkNodes []*client.ChainlinkK8sClient, + mockserver *ctfClient.MockserverClient, +) error { + eg := &errgroup.Group{} + for _, o := range ocrInstances { + ocrInstance := o + for _, n := range chainlinkNodes { + node := n + eg.Go(func() error { + return SetOCR2AdapterResponse(response, ocrInstance, node, mockserver) + }) + } + } + return eg.Wait() +} + +// SetOCR2AllAdapterResponsesToDifferentValues sets the mock responses in mockserver that are read by chainlink nodes +// to simulate different adapters. This sets all adapter responses for each node and contract to different responses +// used for OCR2 tests +func SetOCR2AllAdapterResponsesToDifferentValues( + responses []int, + ocrInstances []contracts.OffchainAggregatorV2, + chainlinkNodes []*client.ChainlinkK8sClient, + mockserver *ctfClient.MockserverClient, +) error { + if len(responses) != len(ocrInstances)*len(chainlinkNodes) { + return fmt.Errorf( + "amount of responses %d should be equal to the amount of OCR instances %d times the amount of Chainlink nodes %d", + len(responses), len(ocrInstances), len(chainlinkNodes), + ) + } + eg := &errgroup.Group{} + for _, o := range ocrInstances { + ocrInstance := o + for ni := 1; ni < len(chainlinkNodes); ni++ { + nodeIndex := ni + eg.Go(func() error { + return SetOCR2AdapterResponse(responses[nodeIndex-1], ocrInstance, chainlinkNodes[nodeIndex], mockserver) + }) + } + } + return eg.Wait() +} + +// BuildOCR2NodeContractPairID builds a UUID based on a related pair of a Chainlink node and OCRv2 contract +func BuildOCR2NodeContractPairID(node *client.ChainlinkK8sClient, ocrInstance contracts.OffchainAggregatorV2) (string, error) { + if node == nil { + return "", fmt.Errorf("chainlink node is nil") + } + if ocrInstance == nil { + return "", fmt.Errorf("OCR Instance is nil") + } + nodeAddress, err := node.PrimaryEthAddress() + if err != nil { + return "", fmt.Errorf("getting chainlink node's primary ETH address failed: %w", err) + } + shortNodeAddr := nodeAddress[2:12] + shortOCRAddr := ocrInstance.Address()[2:12] + return strings.ToLower(fmt.Sprintf("node_%s_contract_%s", shortNodeAddr, shortOCRAddr)), nil +} diff --git a/integration-tests/actions/ocr2_helpers_local.go b/integration-tests/actions/ocr2_helpers_local.go index e1b470bd0d4..4a08921b8d1 100644 --- a/integration-tests/actions/ocr2_helpers_local.go +++ b/integration-tests/actions/ocr2_helpers_local.go @@ -20,12 +20,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" + + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) func CreateOCRv2JobsLocal( @@ -95,11 +96,11 @@ func CreateOCRv2JobsLocal( } err = chainlinkNode.MustCreateBridge(bta) if err != nil { - return fmt.Errorf("creating bridge job have failed: %w", err) + return fmt.Errorf("creating bridge on CL node failed: %w", err) } err = chainlinkNode.MustCreateBridge(juelsBridge) if err != nil { - return fmt.Errorf("creating bridge job have failed: %w", err) + return fmt.Errorf("creating bridge on CL node failed: %w", err) } ocrSpec := &client.OCR2TaskJobSpec{ diff --git a/integration-tests/actions/ocr_helpers.go b/integration-tests/actions/ocr_helpers.go index 4f713dcdd6d..17e536ec839 100644 --- a/integration-tests/actions/ocr_helpers.go +++ b/integration-tests/actions/ocr_helpers.go @@ -236,7 +236,7 @@ func CreateOCRJobs( } err = node.MustCreateBridge(bta) if err != nil { - return fmt.Errorf("creating bridge job have failed: %w", err) + return fmt.Errorf("creating bridge on CL node failed: %w", err) } bootstrapPeers := []*client.ChainlinkClient{bootstrapNode.ChainlinkClient} diff --git a/integration-tests/actions/ocr_helpers_local.go b/integration-tests/actions/ocr_helpers_local.go index fb0fd0bd47d..e9cad3f67ea 100644 --- a/integration-tests/actions/ocr_helpers_local.go +++ b/integration-tests/actions/ocr_helpers_local.go @@ -196,7 +196,7 @@ func CreateOCRJobsLocal( } err = node.MustCreateBridge(bta) if err != nil { - return fmt.Errorf("creating bridge job have failed: %w", err) + return fmt.Errorf("creating bridge on CL node failed: %w", err) } bootstrapPeers := []*client.ChainlinkClient{bootstrapNode} diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 16299a1ac8a..56670812fbc 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -267,6 +267,7 @@ func (c *ChainlinkClient) DeleteSpec(id string) (*http.Response, error) { // MustCreateBridge creates a bridge on the Chainlink node based on the provided attributes and returns error if // the request is unsuccessful func (c *ChainlinkClient) MustCreateBridge(bta *BridgeTypeAttributes) error { + c.l.Debug().Str(NodeURL, c.Config.URL).Str("Name", bta.Name).Msg("Creating Bridge") resp, err := c.CreateBridge(bta) if err != nil { return err @@ -275,7 +276,7 @@ func (c *ChainlinkClient) MustCreateBridge(bta *BridgeTypeAttributes) error { } func (c *ChainlinkClient) CreateBridge(bta *BridgeTypeAttributes) (*http.Response, error) { - c.l.Info().Str(NodeURL, c.Config.URL).Str("Name", bta.Name).Msg("Creating Bridge") + c.l.Debug().Str(NodeURL, c.Config.URL).Str("Name", bta.Name).Msg("Creating Bridge") resp, err := c.APIClient.R(). SetBody(bta). Post("/v2/bridge_types") @@ -288,7 +289,7 @@ func (c *ChainlinkClient) CreateBridge(bta *BridgeTypeAttributes) (*http.Respons // ReadBridge reads a bridge from the Chainlink node based on the provided name func (c *ChainlinkClient) ReadBridge(name string) (*BridgeType, *http.Response, error) { bt := BridgeType{} - c.l.Info().Str(NodeURL, c.Config.URL).Str("Name", name).Msg("Reading Bridge") + c.l.Debug().Str(NodeURL, c.Config.URL).Str("Name", name).Msg("Reading Bridge") resp, err := c.APIClient.R(). SetPathParams(map[string]string{ "name": name, @@ -304,7 +305,7 @@ func (c *ChainlinkClient) ReadBridge(name string) (*BridgeType, *http.Response, // ReadBridges reads bridges from the Chainlink node func (c *ChainlinkClient) ReadBridges() (*Bridges, *resty.Response, error) { result := &Bridges{} - c.l.Info().Str(NodeURL, c.Config.URL).Msg("Getting all bridges") + c.l.Debug().Str(NodeURL, c.Config.URL).Msg("Getting all bridges") resp, err := c.APIClient.R(). SetResult(&result). Get("/v2/bridge_types") @@ -316,7 +317,7 @@ func (c *ChainlinkClient) ReadBridges() (*Bridges, *resty.Response, error) { // DeleteBridge deletes a bridge on the Chainlink node based on the provided name func (c *ChainlinkClient) DeleteBridge(name string) (*http.Response, error) { - c.l.Info().Str(NodeURL, c.Config.URL).Str("Name", name).Msg("Deleting Bridge") + c.l.Debug().Str(NodeURL, c.Config.URL).Str("Name", name).Msg("Deleting Bridge") resp, err := c.APIClient.R(). SetPathParams(map[string]string{ "name": name, diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 528f07ec68e..464ae7a340b 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -135,6 +135,7 @@ type ContractDeployer interface { DeployOffchainAggregatorEventsMock() (OffchainAggregatorEventsMock, error) DeployMockAggregatorProxy(aggregatorAddr string) (MockAggregatorProxy, error) DeployOffchainAggregatorV2(linkAddr string, offchainOptions OffchainOptions) (OffchainAggregatorV2, error) + LoadOffChainAggregatorV2(address *common.Address) (OffchainAggregatorV2, error) DeployKeeperRegistryCheckUpkeepGasUsageWrapper(keeperRegistryAddr string) (KeeperRegistryCheckUpkeepGasUsageWrapper, error) DeployKeeperRegistry11Mock() (KeeperRegistry11Mock, error) DeployKeeperRegistrar12Mock() (KeeperRegistrar12Mock, error) @@ -1584,6 +1585,25 @@ func (e *EthereumContractDeployer) DeployOffchainAggregatorV2( }, err } +// LoadOffChainAggregatorV2 loads an already deployed offchain aggregator v2 contract +func (e *EthereumContractDeployer) LoadOffChainAggregatorV2(address *common.Address) (OffchainAggregatorV2, error) { + instance, err := e.client.LoadContract("OffChainAggregatorV2", *address, func( + address common.Address, + backend bind.ContractBackend, + ) (interface{}, error) { + return ocr2aggregator.NewOCR2Aggregator(address, backend) + }) + if err != nil { + return nil, err + } + return &EthereumOffchainAggregatorV2{ + client: e.client, + contract: instance.(*ocr2aggregator.OCR2Aggregator), + address: address, + l: e.l, + }, err +} + func (e *EthereumContractDeployer) DeployMercuryVerifierContract(verifierProxyAddr common.Address) (MercuryVerifier, error) { address, _, instance, err := e.client.DeployContract("Mercury Verifier", func( auth *bind.TransactOpts, diff --git a/integration-tests/load/README.md b/integration-tests/load/README.md index 1ed1ee8b94e..3738a1d9ace 100644 --- a/integration-tests/load/README.md +++ b/integration-tests/load/README.md @@ -1,9 +1,11 @@ -## Performance tests for CL jobs +# Performance tests for CL jobs This folder container performance e2e tests for different job types, currently implemented: + - VRFv2 All the tests have 4 groups: + - one product soak - one product load - multiple product instances soak @@ -12,11 +14,13 @@ All the tests have 4 groups: When you develop an e2e performance suite for a new product you can implement the tests one by one to answer the questions: What are performance characteristics of a one instance of a product (jobs + contracts): + - is our product stable at all, no memory leaks, no flaking performance under some RPS? (test #1) - what are the limits for one product instance, figuring out the max/optimal performance params by increasing RPS and varying configuration (test #2) - update test #1 with optimal params and workload to constantly run in CI What are performance and capacity characteristics of Chainlink node(s) that run multiple products of the same type simultaneously: + - how many products of the same type we can run at once at a stable load with optimal configuration? (test #3) - what are the limits if we add more and more products of the same type, each product have a stable RPS, we vary only amount of products - update test #3 with optimal params and workload to constantly run in CI @@ -24,9 +28,9 @@ What are performance and capacity characteristics of Chainlink node(s) that run Implementing test #1 is **mandatory** for each product. Tests #2,#3,#4 are optional if you need to figure out your product scaling or node/cluster capacity. - ## Usage -``` + +```sh export LOKI_TOKEN=... export LOKI_URL=... @@ -34,10 +38,12 @@ go test -v -run TestVRFV2Load/vrfv2_soak_test ``` ### Dashboards + Each product has its own generated dashboard in `cmd/dashboard.go` Deploying dashboard: -``` + +```sh export GRAFANA_URL=... export GRAFANA_TOKEN=... export DATA_SOURCE_NAME=grafanacloud-logs @@ -56,8 +62,9 @@ If you need to assert some metrics in `Prometheus/Loki`, here is an [example](ht Do not mix workload logic with assertions, separate them. ### Implementation + To implement a standard e2e performance suite for a new product please look at `gun.go` and `vu.go`. Gun should be working with one instance of your product. -VU(Virtual user) creates a new instance of your product and works with it in `Call()` \ No newline at end of file +VU(Virtual user) creates a new instance of your product and works with it in `Call()` diff --git a/integration-tests/load/ocr/README.md b/integration-tests/load/ocr/README.md index 61951ba700f..3c231b50278 100644 --- a/integration-tests/load/ocr/README.md +++ b/integration-tests/load/ocr/README.md @@ -1,12 +1,21 @@ -### OCR Load tests +# OCR Load tests ## Setup + These tests can connect to any cluster create with [chainlink-cluster](../../../charts/chainlink-cluster/README.md) +<<<<<<< HEAD +Create your cluster + +```sh +kubectl create ns my-cluster +devspace use namespace my-cluster +======= Create your cluster, if you already have one just use `kubefwd` ``` kubectl create ns cl-cluster devspace use namespace cl-cluster +>>>>>>> 06656fac80999d1539e16951a54b87c6df13a9c7 devspace deploy sudo kubefwd svc -n cl-cluster ``` @@ -17,7 +26,7 @@ If you haven't changed anything in [devspace.yaml](../../../charts/chainlink-clu ## Usage -``` +```sh export LOKI_TOKEN=... export LOKI_URL=... @@ -25,4 +34,4 @@ go test -v -run TestOCRLoad go test -v -run TestOCRVolume ``` -Check test configuration [here](config.toml) \ No newline at end of file +Check test configuration [here](config.toml) diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index 13aea441b2a..49e624ecd89 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -3,12 +3,13 @@ package ocr import ( "testing" - "github.com/smartcontractkit/chainlink/integration-tests/k8s" + "github.com/stretchr/testify/require" "github.com/smartcontractkit/wasp" - "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + + "github.com/smartcontractkit/chainlink/integration-tests/k8s" ) var ( diff --git a/integration-tests/load/ocr/vu.go b/integration-tests/load/ocr/vu.go index a905ec011df..96be77c701a 100644 --- a/integration-tests/load/ocr/vu.go +++ b/integration-tests/load/ocr/vu.go @@ -13,6 +13,7 @@ import ( "go.uber.org/ratelimit" client2 "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" diff --git a/integration-tests/scripts/entrypoint b/integration-tests/scripts/entrypoint index cb5c98fde6a..d2297abaa98 100755 --- a/integration-tests/scripts/entrypoint +++ b/integration-tests/scripts/entrypoint @@ -24,6 +24,7 @@ echo "Test exit code: ${exit_code}" # 3 is the code for an interrupted test, we only want to restart the test when the test is interrupted and in a state # that it can recover from. Otherwise we mark the test as "passed" as far as K8s is concerned so it doesn't restart it. if [ $exit_code -eq 3 ]; then +echo "Test was interrupted, exiting with 1 exit code to trigger K8s to restart" exit 1 # Exiting with non-zero status to trigger pod restart fi @@ -38,3 +39,6 @@ fi if [ -n "${UPLOAD_MEM_PROFILE}" ]; then upload_to_slack memprofile.out "MEM Profile for ${TEST_NAME}" fi + +echo "Exiting with 0 exit code as test is either completed, or failed and cannot be restarted" +exit 0 \ No newline at end of file diff --git a/integration-tests/testreporters/ocr.go b/integration-tests/testreporters/ocr.go index abbb261fa74..31f5eeab1b9 100644 --- a/integration-tests/testreporters/ocr.go +++ b/integration-tests/testreporters/ocr.go @@ -21,6 +21,7 @@ import ( type OCRSoakTestReporter struct { StartTime time.Time AnomaliesDetected bool + OCRVersion string anomalies [][]string timeLine [][]string @@ -159,7 +160,7 @@ func (o *OCRSoakTestReporter) SetNamespace(namespace string) { // WriteReport writes OCR Soak test report to a CSV file and final report func (o *OCRSoakTestReporter) WriteReport(folderLocation string) error { - log.Debug().Msg("Writing OCR Soak Test Report") + log.Debug().Msgf("Writing OCRv%s Soak Test Report", o.OCRVersion) reportLocation := filepath.Join(folderLocation, "./ocr_soak_report.csv") o.csvLocation = reportLocation ocrReportFile, err := os.Create(reportLocation) @@ -170,7 +171,7 @@ func (o *OCRSoakTestReporter) WriteReport(folderLocation string) error { ocrReportWriter := csv.NewWriter(ocrReportFile) - err = ocrReportWriter.Write([]string{"OCR Soak Test Report"}) + err = ocrReportWriter.Write([]string{fmt.Sprintf("OCRv%s Soak Test Report", o.OCRVersion)}) if err != nil { return err } @@ -240,7 +241,7 @@ func (o *OCRSoakTestReporter) SendSlackNotification(t *testing.T, slackClient *s } testFailed := t.Failed() - headerText := ":white_check_mark: OCR Soak Test PASSED :white_check_mark:" + headerText := fmt.Sprintf(":white_check_mark: OCRv%s Soak Test PASSED :white_check_mark:", o.OCRVersion) if testFailed { headerText = ":x: OCR Soak Test FAILED :x:" } else if o.AnomaliesDetected { diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index a35d915ea92..f7e0e65d36d 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" @@ -72,12 +73,16 @@ type OCRSoakTest struct { ocrRoundStates []*testreporters.OCRRoundState testIssues []*testreporters.TestIssue - ocrInstances []contracts.OffchainAggregator - ocrInstanceMap map[string]contracts.OffchainAggregator // address : instance + ocrV1Instances []contracts.OffchainAggregator + ocrV1InstanceMap map[string]contracts.OffchainAggregator // address : instance + + ocrV2Instances []contracts.OffchainAggregatorV2 + ocrV2InstanceMap map[string]contracts.OffchainAggregatorV2 // address : instance } // OCRSoakTestInputs define required inputs to run an OCR soak test type OCRSoakTestInputs struct { + OCRVersion string `envconfig:"OCR_VERSION" default:"1"` // Version of OCR to use (1 or 2) TestDuration time.Duration `envconfig:"TEST_DURATION" default:"15m"` // How long to run the test for NumberOfContracts int `envconfig:"NUMBER_CONTRACTS" default:"2"` // Number of OCR contracts to launch ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with @@ -86,6 +91,7 @@ type OCRSoakTestInputs struct { } func (i OCRSoakTestInputs) setForRemoteRunner() { + os.Setenv("TEST_OCR_VERSION", i.OCRVersion) os.Setenv("TEST_OCR_TEST_DURATION", i.TestDuration.String()) os.Setenv("TEST_OCR_NUMBER_CONTRACTS", fmt.Sprint(i.NumberOfContracts)) os.Setenv("TEST_OCR_CHAINLINK_NODE_FUNDING", strconv.FormatFloat(i.ChainlinkNodeFunding, 'f', -1, 64)) @@ -113,14 +119,16 @@ func NewOCRSoakTest(t *testing.T, forwarderFlow bool) (*OCRSoakTest, error) { Inputs: &testInputs, OperatorForwarderFlow: forwarderFlow, TestReporter: testreporters.OCRSoakTestReporter{ - StartTime: time.Now(), + OCRVersion: testInputs.OCRVersion, + StartTime: time.Now(), }, - t: t, - startTime: time.Now(), - timeLeft: testInputs.TestDuration, - log: logging.GetTestLogger(t), - ocrRoundStates: make([]*testreporters.OCRRoundState, 0), - ocrInstanceMap: make(map[string]contracts.OffchainAggregator), + t: t, + startTime: time.Now(), + timeLeft: testInputs.TestDuration, + log: logging.GetTestLogger(t), + ocrRoundStates: make([]*testreporters.OCRRoundState, 0), + ocrV1InstanceMap: make(map[string]contracts.OffchainAggregator), + ocrV2InstanceMap: make(map[string]contracts.OffchainAggregatorV2), } return test, test.ensureInputValues() } @@ -128,7 +136,7 @@ func NewOCRSoakTest(t *testing.T, forwarderFlow bool) (*OCRSoakTest, error) { // DeployEnvironment deploys the test environment, starting all Chainlink nodes and other components for the test func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { network := networks.MustGetSelectedNetworksFromEnv()[0] // Environment currently being used to soak test on - nsPre := "soak-ocr-" + nsPre := fmt.Sprintf("soak-ocr-v%s-", o.Inputs.OCRVersion) if o.OperatorForwarderFlow { nsPre = fmt.Sprintf("%sforwarder-", nsPre) } @@ -140,9 +148,15 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { PreventPodEviction: true, } + var conf string + if o.Inputs.OCRVersion == "1" { + conf = config.BaseOCR1Config + } else if o.Inputs.OCRVersion == "2" { + conf = config.BaseOCR2Config + } cd := chainlink.New(0, map[string]any{ "replicas": 6, - "toml": networks.AddNetworkDetailedConfig(config.BaseOCR1Config, customChainlinkNetworkTOML, network), + "toml": networks.AddNetworkDetailedConfig(conf, customChainlinkNetworkTOML, network), "db": map[string]any{ "stateful": true, // stateful DB by default for soak tests }, @@ -228,7 +242,7 @@ func (o *OCRSoakTest) Setup() { err = o.chainClient.WaitForEvents() } - o.ocrInstances = actions.DeployOCRContractsForwarderFlow( + o.ocrV1Instances = actions.DeployOCRContractsForwarderFlow( o.t, o.Inputs.NumberOfContracts, linkTokenContract, @@ -237,8 +251,8 @@ func (o *OCRSoakTest) Setup() { authorizedForwarders, o.chainClient, ) - } else { - o.ocrInstances, err = actions.DeployOCRContracts( + } else if o.Inputs.OCRVersion == "1" { + o.ocrV1Instances, err = actions.DeployOCRContracts( o.Inputs.NumberOfContracts, linkTokenContract, contractDeployer, @@ -246,13 +260,41 @@ func (o *OCRSoakTest) Setup() { o.chainClient, ) require.NoError(o.t, err) + } else if o.Inputs.OCRVersion == "2" { + var transmitters []string + for _, node := range o.workerNodes { + nodeAddress, err := node.PrimaryEthAddress() + require.NoError(o.t, err, "Error getting node's primary ETH address") + transmitters = append(transmitters, nodeAddress) + } + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() + o.ocrV2Instances, err = actions.DeployOCRv2Contracts( + o.Inputs.NumberOfContracts, + linkTokenContract, + contractDeployer, + transmitters, + o.chainClient, + ocrOffchainOptions, + ) + require.NoError(o.t, err, "Error deploying OCRv2 contracts") + contractConfig, err := actions.BuildMedianOCR2Config(o.workerNodes, ocrOffchainOptions) + require.NoError(o.t, err, "Error building median config") + err = actions.ConfigureOCRv2AggregatorContracts(o.chainClient, contractConfig, o.ocrV2Instances) + require.NoError(o.t, err, "Error configuring OCRv2 aggregator contracts") } err = o.chainClient.WaitForEvents() require.NoError(o.t, err, "Error waiting for OCR contracts to be deployed") - for _, ocrInstance := range o.ocrInstances { - o.ocrInstanceMap[ocrInstance.Address()] = ocrInstance + if o.Inputs.OCRVersion == "1" { + for _, ocrInstance := range o.ocrV1Instances { + o.ocrV1InstanceMap[ocrInstance.Address()] = ocrInstance + } + } else if o.Inputs.OCRVersion == "2" { + for _, ocrInstance := range o.ocrV2Instances { + o.ocrV2InstanceMap[ocrInstance.Address()] = ocrInstance + } } + o.log.Info().Msg("OCR Soak Test Setup Complete") } @@ -266,15 +308,19 @@ func (o *OCRSoakTest) Run() { startingValue := 5 if o.OperatorForwarderFlow { - actions.CreateOCRJobsWithForwarder(o.t, o.ocrInstances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) - } else { - err := actions.CreateOCRJobs(o.ocrInstances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + actions.CreateOCRJobsWithForwarder(o.t, o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + } else if o.Inputs.OCRVersion == "1" { + err := actions.CreateOCRJobs(o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + require.NoError(o.t, err, "Error creating OCR jobs") + } else if o.Inputs.OCRVersion == "2" { + err := actions.CreateOCRv2Jobs(o.ocrV2Instances, o.bootstrapNode, o.workerNodes, o.mockServer, startingValue, o.chainClient.GetChainID().Uint64(), o.OperatorForwarderFlow) require.NoError(o.t, err, "Error creating OCR jobs") } o.log.Info(). Str("Test Duration", o.Inputs.TestDuration.Truncate(time.Second).String()). - Int("Number of OCR Contracts", len(o.ocrInstances)). + Int("Number of OCR Contracts", o.Inputs.NumberOfContracts). + Str("OCR Version", o.Inputs.OCRVersion). Msg("Starting OCR Soak Test") o.testLoop(o.Inputs.TestDuration, startingValue) @@ -306,6 +352,7 @@ type OCRSoakTestState struct { TimeRunning time.Duration `toml:"timeRunning"` TestDuration time.Duration `toml:"testDuration"` OCRContractAddresses []string `toml:"ocrContractAddresses"` + OCRVersion string `toml:"ocrVersion"` BootStrapNodeURL string `toml:"bootstrapNodeURL"` WorkerNodeURLs []string `toml:"workerNodeURLs"` @@ -315,10 +362,7 @@ type OCRSoakTestState struct { // SaveState saves the current state of the test to a TOML file func (o *OCRSoakTest) SaveState() error { - ocrAddresses := make([]string, len(o.ocrInstances)) - for i, ocrInstance := range o.ocrInstances { - ocrAddresses[i] = ocrInstance.Address() - } + ocrAddresses := o.getContractAddressesString() workerNodeURLs := make([]string, len(o.workerNodes)) for i, workerNode := range o.workerNodes { workerNodeURLs[i] = workerNode.URL() @@ -333,6 +377,7 @@ func (o *OCRSoakTest) SaveState() error { TimeRunning: time.Since(o.startTime), TestDuration: o.Inputs.TestDuration, OCRContractAddresses: ocrAddresses, + OCRVersion: o.Inputs.OCRVersion, ChainURL: o.chainClient.GetNetworkConfig().URL, MockServerURL: "http://mockserver:1080", // TODO: Make this dynamic @@ -378,7 +423,8 @@ func (o *OCRSoakTest) LoadState() error { o.namespace = testState.Namespace o.TestReporter = testreporters.OCRSoakTestReporter{ - StartTime: testState.StartTime, + OCRVersion: testState.OCRVersion, + StartTime: testState.StartTime, } o.ocrRoundStates = testState.OCRRoundStates o.testIssues = testState.TestIssues @@ -386,6 +432,7 @@ func (o *OCRSoakTest) LoadState() error { o.timeLeft = testState.TestDuration - testState.TimeRunning o.startTime = testState.StartTime o.startingBlockNum = testState.StartingBlockNum + o.Inputs.OCRVersion = testState.OCRVersion network := networks.MustGetSelectedNetworksFromEnv()[0] o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) @@ -405,15 +452,28 @@ func (o *OCRSoakTest) LoadState() error { return err } - o.ocrInstances = make([]contracts.OffchainAggregator, len(testState.OCRContractAddresses)) - for i, addr := range testState.OCRContractAddresses { - address := common.HexToAddress(addr) - instance, err := contractDeployer.LoadOffChainAggregator(&address) - if err != nil { - return err + if testState.OCRVersion == "1" { + o.ocrV1Instances = make([]contracts.OffchainAggregator, len(testState.OCRContractAddresses)) + for i, addr := range testState.OCRContractAddresses { + address := common.HexToAddress(addr) + instance, err := contractDeployer.LoadOffChainAggregator(&address) + if err != nil { + return err + } + o.ocrV1Instances[i] = instance + } + } else if testState.OCRVersion == "2" { + o.ocrV2Instances = make([]contracts.OffchainAggregatorV2, len(testState.OCRContractAddresses)) + for i, addr := range testState.OCRContractAddresses { + address := common.HexToAddress(addr) + instance, err := contractDeployer.LoadOffChainAggregatorV2(&address) + if err != nil { + return err + } + o.ocrV2Instances[i] = instance } - o.ocrInstances[i] = instance } + o.mockServer, err = ctfClient.ConnectMockServerURL(testState.MockServerURL) if err != nil { return err @@ -432,16 +492,30 @@ func (o *OCRSoakTest) Resume() { Str("Time Left", o.timeLeft.String()). Msg("Resuming OCR Soak Test") - ocrAddresses := make([]common.Address, len(o.ocrInstances)) - for i, ocrInstance := range o.ocrInstances { - ocrAddresses[i] = common.HexToAddress(ocrInstance.Address()) - } - contractABI, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() - require.NoError(o.t, err, "Error retrieving OCR contract ABI") - o.filterQuery = geth.FilterQuery{ - Addresses: ocrAddresses, - Topics: [][]common.Hash{{contractABI.Events["AnswerUpdated"].ID}}, - FromBlock: big.NewInt(0).SetUint64(o.startingBlockNum), + ocrAddresses := make([]common.Address, o.Inputs.NumberOfContracts) + + if o.Inputs.OCRVersion == "1" { + for i, ocrInstance := range o.ocrV1Instances { + ocrAddresses[i] = common.HexToAddress(ocrInstance.Address()) + } + contractABI, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() + require.NoError(o.t, err, "Error retrieving OCR contract ABI") + o.filterQuery = geth.FilterQuery{ + Addresses: ocrAddresses, + Topics: [][]common.Hash{{contractABI.Events["AnswerUpdated"].ID}}, + FromBlock: big.NewInt(0).SetUint64(o.startingBlockNum), + } + } else if o.Inputs.OCRVersion == "2" { + for i, ocrInstance := range o.ocrV2Instances { + ocrAddresses[i] = common.HexToAddress(ocrInstance.Address()) + } + contractABI, err := ocr2aggregator.AggregatorInterfaceMetaData.GetAbi() + require.NoError(o.t, err, "Error retrieving OCR contract ABI") + o.filterQuery = geth.FilterQuery{ + Addresses: ocrAddresses, + Topics: [][]common.Hash{{contractABI.Events["AnswerUpdated"].ID}}, + FromBlock: big.NewInt(0).SetUint64(o.startingBlockNum), + } } startingValue := 5 @@ -449,7 +523,7 @@ func (o *OCRSoakTest) Resume() { o.log.Info().Msg("Test Complete, collecting on-chain events") - err = o.collectEvents() + err := o.collectEvents() o.log.Error().Err(err).Interface("Query", o.filterQuery).Msg("Error collecting on-chain events, expect malformed report") o.TestReporter.RecordEvents(o.ocrRoundStates, o.testIssues) } @@ -537,10 +611,7 @@ func (o *OCRSoakTest) complete() { // setFilterQuery to look for all events that happened func (o *OCRSoakTest) setFilterQuery() { - ocrAddresses := make([]common.Address, len(o.ocrInstances)) - for i, ocrInstance := range o.ocrInstances { - ocrAddresses[i] = common.HexToAddress(ocrInstance.Address()) - } + ocrAddresses := o.getContractAddresses() contractABI, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() require.NoError(o.t, err, "Error retrieving OCR contract ABI") o.filterQuery = geth.FilterQuery{ @@ -570,21 +641,39 @@ func (o *OCRSoakTest) observeOCREvents() error { for { select { case event := <-eventLogs: - answerUpdated, err := o.ocrInstances[0].ParseEventAnswerUpdated(event) - if err != nil { - o.log.Warn(). - Err(err). + if o.Inputs.OCRVersion == "1" { + answerUpdated, err := o.ocrV1Instances[0].ParseEventAnswerUpdated(event) + if err != nil { + o.log.Warn(). + Err(err). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Msg("Error parsing event as AnswerUpdated") + continue + } + o.log.Info(). Str("Address", event.Address.Hex()). Uint64("Block Number", event.BlockNumber). - Msg("Error parsing event as AnswerUpdated") - continue + Uint64("Round ID", answerUpdated.RoundId.Uint64()). + Int64("Answer", answerUpdated.Current.Int64()). + Msg("Answer Updated Event") + } else if o.Inputs.OCRVersion == "2" { + answerUpdated, err := o.ocrV2Instances[0].ParseEventAnswerUpdated(event) + if err != nil { + o.log.Warn(). + Err(err). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Msg("Error parsing event as AnswerUpdated") + continue + } + o.log.Info(). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Uint64("Round ID", answerUpdated.RoundId.Uint64()). + Int64("Answer", answerUpdated.Current.Int64()). + Msg("Answer Updated Event") } - o.log.Info(). - Str("Address", event.Address.Hex()). - Uint64("Block Number", event.BlockNumber). - Uint64("Round ID", answerUpdated.RoundId.Uint64()). - Int64("Answer", answerUpdated.Current.Int64()). - Msg("Answer Updated Event") case err = <-eventSub.Err(): backoff := time.Second for err != nil { @@ -614,7 +703,12 @@ func (o *OCRSoakTest) triggerNewRound(newValue int) error { o.ocrRoundStates[len(o.ocrRoundStates)-1].EndTime = time.Now() } - err := actions.SetAllAdapterResponsesToTheSameValue(newValue, o.ocrInstances, o.workerNodes, o.mockServer) + var err error + if o.Inputs.OCRVersion == "1" { + err = actions.SetAllAdapterResponsesToTheSameValue(newValue, o.ocrV1Instances, o.workerNodes, o.mockServer) + } else if o.Inputs.OCRVersion == "2" { + err = actions.SetOCR2AllAdapterResponsesToTheSameValue(newValue, o.ocrV2Instances, o.workerNodes, o.mockServer) + } if err != nil { return err } @@ -624,9 +718,16 @@ func (o *OCRSoakTest) triggerNewRound(newValue int) error { Answer: int64(newValue), FoundEvents: make(map[string][]*testreporters.FoundEvent), } - for _, ocrInstance := range o.ocrInstances { - expectedState.FoundEvents[ocrInstance.Address()] = make([]*testreporters.FoundEvent, 0) + if o.Inputs.OCRVersion == "1" { + for _, ocrInstance := range o.ocrV1Instances { + expectedState.FoundEvents[ocrInstance.Address()] = make([]*testreporters.FoundEvent, 0) + } + } else if o.Inputs.OCRVersion == "2" { + for _, ocrInstance := range o.ocrV2Instances { + expectedState.FoundEvents[ocrInstance.Address()] = make([]*testreporters.FoundEvent, 0) + } } + o.ocrRoundStates = append(o.ocrRoundStates, expectedState) o.log.Info(). Int("Value", newValue). @@ -662,17 +763,31 @@ func (o *OCRSoakTest) collectEvents() error { sortedFoundEvents := make([]*testreporters.FoundEvent, 0) for _, event := range contractEvents { - answerUpdated, err := o.ocrInstances[0].ParseEventAnswerUpdated(event) - if err != nil { - return fmt.Errorf("error parsing EventAnswerUpdated for event: %v, %w", event, err) + if o.Inputs.OCRVersion == "1" { + answerUpdated, err := o.ocrV1Instances[0].ParseEventAnswerUpdated(event) + if err != nil { + return fmt.Errorf("error parsing EventAnswerUpdated for event: %v, %w", event, err) + } + sortedFoundEvents = append(sortedFoundEvents, &testreporters.FoundEvent{ + StartTime: time.Unix(answerUpdated.UpdatedAt.Int64(), 0), + Address: event.Address.Hex(), + Answer: answerUpdated.Current.Int64(), + RoundID: answerUpdated.RoundId.Uint64(), + BlockNumber: event.BlockNumber, + }) + } else if o.Inputs.OCRVersion == "2" { + answerUpdated, err := o.ocrV2Instances[0].ParseEventAnswerUpdated(event) + if err != nil { + return fmt.Errorf("error parsing EventAnswerUpdated for event: %v, %w", event, err) + } + sortedFoundEvents = append(sortedFoundEvents, &testreporters.FoundEvent{ + StartTime: time.Unix(answerUpdated.UpdatedAt.Int64(), 0), + Address: event.Address.Hex(), + Answer: answerUpdated.Current.Int64(), + RoundID: answerUpdated.RoundId.Uint64(), + BlockNumber: event.BlockNumber, + }) } - sortedFoundEvents = append(sortedFoundEvents, &testreporters.FoundEvent{ - StartTime: time.Unix(answerUpdated.UpdatedAt.Int64(), 0), - Address: event.Address.Hex(), - Answer: answerUpdated.Current.Int64(), - RoundID: answerUpdated.RoundId.Uint64(), - BlockNumber: event.BlockNumber, - }) } // Sort our events by time to make sure they are in order (don't trust RPCs) @@ -705,6 +820,9 @@ func (o *OCRSoakTest) collectEvents() error { // ensureValues ensures that all values needed to run the test are present func (o *OCRSoakTest) ensureInputValues() error { inputs := o.Inputs + if inputs.OCRVersion != "1" && inputs.OCRVersion != "2" { + return fmt.Errorf("OCR version must be 1 or 2, found %s", inputs.OCRVersion) + } if inputs.NumberOfContracts <= 0 { return fmt.Errorf("Number of OCR contracts must be greater than 0, found %d", inputs.NumberOfContracts) } @@ -723,3 +841,39 @@ func (o *OCRSoakTest) ensureInputValues() error { o.Inputs.bigChainlinkNodeFunding = big.NewFloat(inputs.ChainlinkNodeFunding) return nil } + +// getContractAddressesString returns the addresses of all OCR contracts deployed as a string slice +func (o *OCRSoakTest) getContractAddressesString() []string { + contractAddresses := []string{} + if len(o.ocrV1Instances) != 0 { + for _, ocrInstance := range o.ocrV1Instances { + contractAddresses = append(contractAddresses, ocrInstance.Address()) + } + } else if len(o.ocrV2Instances) != 0 { + if len(o.ocrV2Instances) != 0 { + for _, ocrInstance := range o.ocrV2Instances { + contractAddresses = append(contractAddresses, ocrInstance.Address()) + } + } + } + + return contractAddresses +} + +// getContractAddresses returns the addresses of all OCR contracts deployed +func (o *OCRSoakTest) getContractAddresses() []common.Address { + contractAddresses := []common.Address{} + if len(o.ocrV1Instances) != 0 { + for _, ocrInstance := range o.ocrV1Instances { + contractAddresses = append(contractAddresses, common.HexToAddress(ocrInstance.Address())) + } + } else if len(o.ocrV2Instances) != 0 { + if len(o.ocrV2Instances) != 0 { + for _, ocrInstance := range o.ocrV2Instances { + contractAddresses = append(contractAddresses, common.HexToAddress(ocrInstance.Address())) + } + } + } + + return contractAddresses +} From 862f79ab2f469a36bae5f6623432ba37878d6be3 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 13 Dec 2023 12:18:35 -0600 Subject: [PATCH 049/234] bump common; use SugaredLogger methods (#11556) --- common/client/multi_node.go | 17 +++-- common/client/node_lifecycle.go | 29 +++++---- common/headtracker/head_tracker.go | 8 +-- common/txmgr/broadcaster.go | 18 +++--- common/txmgr/confirmer.go | 23 ++++--- common/txmgr/resender.go | 4 +- common/txmgr/txmgr.go | 10 +-- common/txmgr/types/client.go | 4 +- common/txmgr/types/tx.go | 6 +- core/chains/evm/client/chain_client.go | 8 +-- core/chains/evm/client/client.go | 8 +-- core/chains/evm/client/errors.go | 8 +-- core/chains/evm/client/helpers_test.go | 2 +- core/chains/evm/client/node.go | 61 +++++++++--------- core/chains/evm/client/node_lifecycle.go | 29 +++++---- core/chains/evm/client/pool.go | 12 ++-- core/chains/evm/client/rpc_client.go | 63 +++++++++---------- .../chains/evm/gas/block_history_estimator.go | 16 ++--- .../evm/gas/rollups/l1_gas_price_oracle.go | 6 +- core/chains/evm/log/helpers_test.go | 6 +- core/chains/evm/log/registrations.go | 24 +++---- core/chains/evm/logpoller/log_poller.go | 8 +-- core/chains/evm/txmgr/broadcaster_test.go | 2 +- core/chains/evm/txmgr/client.go | 8 +-- core/chains/evm/txmgr/evm_tx_store.go | 6 +- core/chains/evm/txmgr/transmitchecker.go | 10 +-- core/chains/evm/txmgr/transmitchecker_test.go | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/pg/q.go | 6 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 5 +- 34 files changed, 203 insertions(+), 220 deletions(-) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index dfd6585b642..7d55784e68f 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -86,7 +86,7 @@ type multiNode[ sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] chainID CHAIN_ID chainType config.ChainType - lggr logger.Logger + lggr logger.SugaredLogger selectionMode string noNewHeadsThreshold time.Duration nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] @@ -118,7 +118,7 @@ func NewMultiNode[ HEAD types.Head[BLOCK_HASH], RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], ]( - l logger.Logger, + lggr logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, @@ -131,9 +131,6 @@ func NewMultiNode[ ) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { nodeSelector := newNodeSelector(selectionMode, nodes) - lggr := logger.Named(l, "MultiNode") - lggr = logger.With(lggr, "chainID", chainID.String()) - // 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 @@ -142,7 +139,7 @@ func NewMultiNode[ sendonlys: sendonlys, chainID: chainID, chainType: chainType, - lggr: lggr, + lggr: logger.Sugared(lggr).Named("MultiNode").With("chainID", chainID.String()), selectionMode: selectionMode, noNewHeadsThreshold: noNewHeadsThreshold, nodeSelector: nodeSelector, @@ -249,7 +246,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.activeNode = c.nodeSelector.Select() if c.activeNode == nil { - logger.Criticalw(c.lggr, "No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name()) + c.lggr.Criticalw("No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name()) errmsg := fmt.Errorf("no live nodes available for chain %s", c.chainID.String()) c.SvcErrBuffer.Append(errmsg) err = ErroringNodeError @@ -351,10 +348,10 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } live := total - dead - logger.Tracew(c.lggr, fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) if total == dead { rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total) - logger.Criticalw(c.lggr, rerr.Error(), "nodeStates", nodeStates) + c.lggr.Criticalw(rerr.Error(), "nodeStates", nodeStates) c.SvcErrBuffer.Append(rerr) } else if dead > 0 { c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) @@ -405,7 +402,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP if err != nil { c.lggr.Debugw("Secondary node BatchCallContext failed", "err", err) } else { - logger.Trace(c.lggr, "Secondary node BatchCallContext success") + c.lggr.Trace("Secondary node BatchCallContext success") } }(n) } diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index eda137d5100..af8f27d498d 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -93,9 +93,8 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold() pollInterval := n.nodePoolCfg.PollInterval() - lggr := logger.Named(n.lfcLog, "Alive") - lggr = logger.With(lggr, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) - logger.Tracew(lggr, "Alive loop starting", "nodeState", n.State()) + lggr := logger.Sugared(n.lfcLog).Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) + lggr.Tracew("Alive loop starting", "nodeState", n.State()) headsC := make(chan HEAD) sub, err := n.rpc.Subscribe(n.nodeCtx, headsC, rpcSubscriptionMethodNewHeads) @@ -146,7 +145,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { case <-pollCh: var version string promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - logger.Tracew(lggr, "Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) ctx, cancel := context.WithTimeout(n.nodeCtx, pollInterval) version, err := n.RPC().ClientVersion(ctx) cancel() @@ -166,7 +165,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.State()) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - logger.Criticalf(lggr, "RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) + lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) continue } } @@ -178,7 +177,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { // note: there must be another live node for us to be out of sync lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) if liveNodes < 2 { - logger.Criticalf(lggr, "RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) + lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) continue } n.declareOutOfSync(n.isOutOfSync) @@ -191,13 +190,13 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return } promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() - logger.Tracew(lggr, "Got head", "head", bh) + lggr.Tracew("Got head", "head", bh) if bh.BlockNumber() > highestReceivedBlockNumber { promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.BlockNumber())) - logger.Tracew(lggr, "Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) highestReceivedBlockNumber = bh.BlockNumber() } else { - logger.Tracew(lggr, "Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) } if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) @@ -213,7 +212,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - logger.Criticalf(lggr, "RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) + lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) // We don't necessarily want to wait the full timeout to check again, we should // check regularly and log noisily in this state outOfSyncT.Reset(zombieNodeCheckInterval(n.noNewHeadsThreshold)) @@ -279,7 +278,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td outOfSyncAt := time.Now() - lggr := logger.Named(n.lfcLog, "OutOfSync") + lggr := logger.Sugared(logger.Named(n.lfcLog, "OutOfSync")) lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) // Need to redial since out-of-sync nodes are automatically disconnected @@ -296,7 +295,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td return } - logger.Tracew(lggr, "Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) ch := make(chan HEAD) sub, err := n.rpc.Subscribe(n.nodeCtx, ch, rpcSubscriptionMethodNewHeads) @@ -328,7 +327,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td case <-time.After(zombieNodeCheckInterval(n.noNewHeadsThreshold)): if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 1 { - logger.Critical(lggr, "RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") + lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") n.declareInSync() return } @@ -358,7 +357,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { unreachableAt := time.Now() - lggr := logger.Named(n.lfcLog, "Unreachable") + lggr := logger.Sugared(logger.Named(n.lfcLog, "Unreachable")) lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) dialRetryBackoff := iutils.NewRedialBackoff() @@ -368,7 +367,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { case <-n.nodeCtx.Done(): return case <-time.After(dialRetryBackoff.Duration()): - logger.Tracew(lggr, "Trying to re-dial RPC node", "nodeState", n.State()) + lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) err := n.rpc.Dial(n.nodeCtx) if err != nil { diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 373aa5a958f..4cc152fb9fe 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -40,7 +40,7 @@ type HeadTracker[ BLOCK_HASH types.Hashable, ] struct { services.StateMachine - log logger.Logger + log logger.SugaredLogger headBroadcaster types.HeadBroadcaster[HTH, BLOCK_HASH] headSaver types.HeadSaver[HTH, BLOCK_HASH] mailMon *mailbox.Monitor @@ -81,7 +81,7 @@ func NewHeadTracker[ chainID: client.ConfiguredChainID(), config: config, htConfig: htConfig, - log: lggr, + log: logger.Sugared(lggr), backfillMB: mailbox.NewSingle[HTH](), broadcastMB: mailbox.New[HTH](HeadsBufferSize), chStop: chStop, @@ -227,7 +227,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context prevUnFinalizedHead := prevHead.BlockNumber() - int64(ht.config.FinalityDepth()) if head.BlockNumber() < prevUnFinalizedHead { promOldHead.WithLabelValues(ht.chainID.String()).Inc() - logger.Criticalf(ht.log, "Got very old block with number %d (highest seen was %d). This is a problem and either means a very deep re-org occurred, one of the RPC nodes has gotten far out of sync, or the chain went backwards in block numbers. This node may not function correctly without manual intervention.", head.BlockNumber(), prevHead.BlockNumber()) + ht.log.Criticalf("Got very old block with number %d (highest seen was %d). This is a problem and either means a very deep re-org occurred, one of the RPC nodes has gotten far out of sync, or the chain went backwards in block numbers. This node may not function correctly without manual intervention.", head.BlockNumber(), prevHead.BlockNumber()) ht.SvcErrBuffer.Append(errors.New("got very old block")) } } @@ -310,7 +310,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) backfill(ctx context.Context, hea } mark := time.Now() fetched := 0 - l := logger.With(ht.log, "blockNumber", headBlockNumber, + l := ht.log.With("blockNumber", headBlockNumber, "n", headBlockNumber-baseHeight, "fromBlockHeight", baseHeight, "toBlockHeight", headBlockNumber-1) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index dba2b976c33..a62b3df8699 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -82,7 +82,7 @@ type TransmitChecker[ // is returned. Errors should only be returned if the checker can confirm that a transaction // should not be sent, other errors (for example connection or other unexpected errors) should // be logged and swallowed. - Check(ctx context.Context, l logger.Logger, tx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], a txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error + Check(ctx context.Context, l logger.SugaredLogger, tx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], a txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error } // Broadcaster monitors txes for transactions that need to @@ -108,7 +108,7 @@ type Broadcaster[ FEE feetypes.Fee, ] struct { services.StateMachine - lggr logger.Logger + lggr logger.SugaredLogger txStore txmgrtypes.TransactionStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, SEQ, FEE] client txmgrtypes.TransactionClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] @@ -172,7 +172,7 @@ func NewBroadcaster[ ) *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { lggr = logger.Named(lggr, "Broadcaster") b := &Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{ - lggr: lggr, + lggr: logger.Sugared(lggr), txStore: txStore, client: client, TxAttemptBuilder: txAttemptBuilder, @@ -311,7 +311,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) getS if err == nil { return seq, nil } - logger.Criticalw(eb.lggr, "failed to retrieve next sequence from on-chain for address: ", "address", address.String()) + eb.lggr.Criticalw("failed to retrieve next sequence from on-chain for address: ", "address", address.String()) return seq, err } @@ -399,7 +399,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Sync localSequence, err := eb.GetNextSequence(ctx, addr) // Address not found in map so skip sync if err != nil { - logger.Criticalw(eb.lggr, "Failed to retrieve local next sequence for address", "address", addr.String(), "err", err) + eb.lggr.Criticalw("Failed to retrieve local next sequence for address", "address", addr.String(), "err", err) return } @@ -414,7 +414,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Sync newNextSequence, err := eb.sequenceSyncer.Sync(ctx, addr, localSequence) if err != nil { if attempt > 5 { - logger.Criticalw(eb.lggr, "Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) + eb.lggr.Criticalw("Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) eb.SvcErrBuffer.Append(err) } else { eb.lggr.Warnw("Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) @@ -537,7 +537,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand return fmt.Errorf("building transmit checker: %w", err), false } - lgr := etx.GetLogger(logger.With(eb.lggr, "fee", attempt.TxFee)) + lgr := etx.GetLogger(eb.lggr.With("fee", attempt.TxFee)) // If the transmit check does not complete within the timeout, the transaction will be sent // anyway. @@ -647,14 +647,14 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // If there is only one RPC node, or all RPC nodes have the same // configured cap, this transaction will get stuck and keep repeating // forever until the issue is resolved. - logger.Criticalw(lgr, `RPC node rejected this tx as outside Fee Cap`) + lgr.Criticalw(`RPC node rejected this tx as outside Fee Cap`) fallthrough default: // Every error that doesn't fall under one of the above categories will be treated as Unknown. fallthrough case client.Unknown: eb.SvcErrBuffer.Append(err) - logger.Criticalw(lgr, `Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+ + lgr.Criticalw(`Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+ `Urgent resolution required, Chainlink is currently operating in a degraded state and may miss transactions`, "err", err, "etx", etx, "attempt", attempt) nextSequence, e := eb.client.PendingSequenceAt(ctx, etx.FromAddress) if e != nil { diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index aabdf45ae32..fbc6ea8a108 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -118,7 +118,7 @@ type Confirmer[ ] struct { services.StateMachine txStore txmgrtypes.TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] - lggr logger.Logger + lggr logger.SugaredLogger client txmgrtypes.TxmClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] resumeCallback ResumeCallback @@ -166,7 +166,7 @@ func NewConfirmer[ lggr = logger.Named(lggr, "Confirmer") return &Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ txStore: txStore, - lggr: lggr, + lggr: logger.Sugared(lggr), client: client, TxAttemptBuilder: txAttemptBuilder, resumeCallback: nil, @@ -520,8 +520,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat } } - lggr := logger.Named(ec.lggr, "BatchFetchReceipts") - lggr = logger.With(lggr, "blockNum", blockNum) + lggr := ec.lggr.Named("BatchFetchReceipts").With("blockNum", blockNum) txReceipts, txErrs, err := ec.client.BatchGetReceipts(ctx, attempts) if err != nil { @@ -533,9 +532,9 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat receipt := txReceipts[i] err := txErrs[i] - l := logger.Sugared(logger.With(attempt.Tx.GetLogger(lggr), "txHash", attempt.Hash.String(), "txAttemptID", attempt.ID, + l := attempt.Tx.GetLogger(lggr).With("txHash", attempt.Hash.String(), "txAttemptID", attempt.ID, "txID", attempt.TxID, "err", err, "sequence", attempt.Tx.Sequence, - )) + ) if err != nil { l.Error("FetchReceipt failed") @@ -554,7 +553,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat continue } - l = logger.Sugared(logger.With(l, "blockHash", receipt.GetBlockHash().String(), "status", receipt.GetStatus(), "transactionIndex", receipt.GetTransactionIndex())) + l = l.With("blockHash", receipt.GetBlockHash().String(), "status", receipt.GetStatus(), "transactionIndex", receipt.GetTransactionIndex()) if receipt.IsUnmined() { l.Debug("Got receipt for transaction but it's still in the mempool and not included in a block yet") @@ -811,7 +810,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bum return bumpedAttempt, fmt.Errorf("error bumping gas: %w", err) } -func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleInProgressAttempt(ctx context.Context, lggr logger.Logger, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], blockHeight int64) error { +func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleInProgressAttempt(ctx context.Context, lggr logger.SugaredLogger, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], blockHeight int64) error { if attempt.State != txmgrtypes.TxAttemptInProgress { return fmt.Errorf("invariant violation: expected tx_attempt %v to be in_progress, it was %s", attempt.ID, attempt.State) @@ -833,12 +832,12 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han } if len(etx.TxAttempts) == 0 { err := errors.New("expected to find at least 1 attempt") - logger.Sugared(ec.lggr).AssumptionViolationw(err.Error(), "err", err, "attempt", attempt) + ec.lggr.AssumptionViolationw(err.Error(), "err", err, "attempt", attempt) return err } if attempt.ID != etx.TxAttempts[0].ID { err := errors.New("expected highest priced attempt to be the current in_progress attempt") - logger.Sugared(ec.lggr).AssumptionViolationw(err.Error(), "err", err, "attempt", attempt, "txAttempts", etx.TxAttempts) + ec.lggr.AssumptionViolationw(err.Error(), "err", err, "attempt", attempt, "txAttempts", etx.TxAttempts) return err } replacementAttempt, err := ec.bumpGas(ctx, etx, etx.TxAttempts) @@ -846,7 +845,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han return fmt.Errorf("could not bump gas for terminally underpriced transaction: %w", err) } promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() - logger.With(lggr, + lggr.With( "sendError", sendError, "maxGasPriceConfig", ec.feeConfig.MaxFeePrice(), "previousAttempt", attempt, @@ -867,7 +866,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han // Should NEVER be fatal this is an invariant violation. The // Broadcaster can never create a TxAttempt that will // fatally error. - logger.Criticalw(lggr, "Invariant violation: fatal error while re-attempting transaction", + lggr.Criticalw("Invariant violation: fatal error while re-attempting transaction", "err", sendError, "fee", attempt.TxFee, "feeLimit", etx.FeeLimit, diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index 74cf3d1389c..dded59f55d4 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -53,7 +53,7 @@ type Resender[ interval time.Duration config txmgrtypes.ResenderChainConfig txConfig txmgrtypes.ResenderTransactionsConfig - logger logger.Logger + logger logger.SugaredLogger lastAlertTimestamps map[string]time.Time ctx context.Context @@ -93,7 +93,7 @@ func NewResender[ pollInterval, config, txConfig, - logger.Named(lggr, "Resender"), + logger.Sugared(logger.Named(lggr, "Resender")), make(map[string]time.Time), ctx, cancel, diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index e43a16b29ef..36a6a1304ae 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -82,7 +82,7 @@ type Txm[ FEE feetypes.Fee, ] struct { services.StateMachine - logger logger.Logger + logger logger.SugaredLogger txStore txmgrtypes.TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] config txmgrtypes.TransactionManagerChainConfig txConfig txmgrtypes.TransactionManagerTransactionsConfig @@ -142,7 +142,7 @@ func NewTxm[ tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], ) *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { b := Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ - logger: lggr, + logger: logger.Sugared(lggr), txStore: txStore, config: cfg, txConfig: txCfg, @@ -349,7 +349,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() select { case <-time.After(backoff.Duration()): if err := b.broadcaster.startInternal(ctx); err != nil { - logger.Criticalw(b.logger, "Failed to start Broadcaster", "err", err) + b.logger.Criticalw("Failed to start Broadcaster", "err", err) b.SvcErrBuffer.Append(err) continue } @@ -368,7 +368,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() select { case <-time.After(backoff.Duration()): if err := b.confirmer.startInternal(); err != nil { - logger.Criticalw(b.logger, "Failed to start Confirmer", "err", err) + b.logger.Criticalw("Failed to start Confirmer", "err", err) b.SvcErrBuffer.Append(err) continue } @@ -433,7 +433,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() } enabledAddresses, err := b.keyStore.EnabledAddressesForChain(b.chainID) if err != nil { - logger.Criticalf(b.logger, "Failed to reload key states after key change") + b.logger.Critical("Failed to reload key states after key change") b.SvcErrBuffer.Append(err) continue } diff --git a/common/txmgr/types/client.go b/common/txmgr/types/client.go index b44c41e4176..0db50e97ad3 100644 --- a/common/txmgr/types/client.go +++ b/common/txmgr/types/client.go @@ -47,7 +47,7 @@ type TransactionClient[ ctx context.Context, attempts []TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], bathSize int, - lggr logger.Logger, + lggr logger.SugaredLogger, ) ( txCodes []client.SendTxReturnCode, txErrs []error, @@ -58,7 +58,7 @@ type TransactionClient[ ctx context.Context, tx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], - lggr logger.Logger, + lggr logger.SugaredLogger, ) (client.SendTxReturnCode, error) SendEmptyTransaction( ctx context.Context, diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index caac763fc0f..0f5d651ae29 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -254,7 +254,7 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetMeta() (*TxMeta[A } // GetLogger returns a new logger with metadata fields. -func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger.Logger) logger.Logger { +func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger.Logger) logger.SugaredLogger { lgr = logger.With(lgr, "txID", e.ID, "sequence", e.Sequence, @@ -265,7 +265,7 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger meta, err := e.GetMeta() if err != nil { lgr.Errorw("failed to get meta of the transaction", "err", err) - return lgr + return logger.Sugared(lgr) } if meta != nil { @@ -315,7 +315,7 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger } } - return lgr + return logger.Sugared(lgr) } // GetChecker returns an Tx's transmit checker spec in struct form, unmarshalling it from JSON diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 3efc5645e22..255d802720a 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -37,11 +37,11 @@ type chainClient struct { *evmtypes.Head, RPCCLient, ] - logger logger.Logger + logger logger.SugaredLogger } func NewChainClient( - logger logger.Logger, + lggr logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, @@ -64,7 +64,7 @@ func NewChainClient( *evmtypes.Head, RPCCLient, ]( - logger, + lggr, selectionMode, leaseDuration, noNewHeadsThreshold, @@ -77,7 +77,7 @@ func NewChainClient( ) return &chainClient{ multiNode: multiNode, - logger: logger, + logger: logger.Sugared(lggr), } } diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index b85331a62a1..f32ec011445 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -102,7 +102,7 @@ func ContextWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc // client represents an abstract client that manages connections to // multiple nodes for a single chain id type client struct { - logger logger.Logger + logger logger.SugaredLogger pool *Pool } @@ -113,10 +113,10 @@ var _ htrktypes.Client[*evmtypes.Head, ethereum.Subscription, *big.Int, common.H // Currently only supports one primary // // Deprecated: use [NewChainClient] -func NewClientWithNodes(logger logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, primaryNodes []Node, sendOnlyNodes []SendOnlyNode, chainID *big.Int, chainType config.ChainType) (*client, error) { - pool := NewPool(logger, selectionMode, leaseDuration, noNewHeadsThreshold, primaryNodes, sendOnlyNodes, chainID, chainType) +func NewClientWithNodes(lggr logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, primaryNodes []Node, sendOnlyNodes []SendOnlyNode, chainID *big.Int, chainType config.ChainType) (*client, error) { + pool := NewPool(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaryNodes, sendOnlyNodes, chainID, chainType) return &client{ - logger: logger, + logger: logger.Sugared(lggr), pool: pool, }, nil } diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 143a5f8806f..66cc30f74b4 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -413,13 +413,13 @@ func ExtractRPCError(baseErr error) (*JsonError, error) { return &jErr, nil } -func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (commonclient.SendTxReturnCode, error) { +func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (commonclient.SendTxReturnCode, error) { sendError := NewSendError(err) if sendError == nil { return commonclient.Successful, err } if sendError.Fatal() { - logger.Criticalw(lggr, "Fatal error sending transaction", "err", sendError, "etx", tx) + lggr.Criticalw("Fatal error sending transaction", "err", sendError, "etx", tx) // Attempt is thrown away in this case; we don't need it since it never got accepted by a node return commonclient.Fatal, err } @@ -462,7 +462,7 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro return commonclient.Retryable, err } if sendError.IsInsufficientEth() { - logger.Criticalw(lggr, fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+ + lggr.Criticalw(fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+ "ACTION REQUIRED: Chainlink wallet with address 0x%x is OUT OF FUNDS", tx.Hash(), tx.Type(), sendError.Error(), fromAddress, ), "err", sendError) @@ -472,7 +472,7 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro return commonclient.Retryable, errors.Wrapf(sendError, "timeout while sending transaction %s", tx.Hash().Hex()) } if sendError.IsTxFeeExceedsCap() { - logger.Criticalw(lggr, fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), + lggr.Criticalw(fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), "etx", tx, "err", sendError, "id", "RPCTxFeeCapExceeded", diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 27b335534da..c2f60e13f55 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -42,7 +42,7 @@ func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeads return nil, errors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) } - lggr := logger.Test(t) + lggr := logger.Sugared(logger.Test(t)) n := NewNode(nodePoolCfg, noNewHeadsThreshold, lggr, *parsed, rpcHTTPURL, "eth-primary-0", id, chainID, 1) n.(*node).setLatestReceived(0, big.NewInt(0)) primaries := []Node{n} diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index a2c8b807ba2..a27321535ed 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -137,7 +137,7 @@ type rawclient struct { type node struct { services.StateMachine lfcLog logger.Logger - rpcLog logger.Logger + rpcLog logger.SugaredLogger name string id int32 chainID *big.Int @@ -206,7 +206,7 @@ func NewNode(nodeCfg config.NodePool, noNewHeadsThreshold time.Duration, lggr lo "mode", n.getNodeMode(), ) n.lfcLog = logger.Named(lggr, "Lifecycle") - n.rpcLog = logger.Named(lggr, "RPC") + n.rpcLog = logger.Sugared(lggr).Named("RPC") n.stateLatestBlockNumber = -1 return n @@ -453,7 +453,7 @@ func (n *node) CallContext(ctx context.Context, result interface{}, method strin return err } defer cancel() - lggr := logger.With(n.newRqLggr(), + lggr := n.newRqLggr().With( "method", method, "args", args, ) @@ -478,9 +478,9 @@ func (n *node) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { return err } defer cancel() - lggr := logger.With(n.newRqLggr(), "nBatchElems", len(b), "batchElems", b) + lggr := n.newRqLggr().With("nBatchElems", len(b), "batchElems", b) - logger.Trace(lggr, "RPC call: evmclient.Client#BatchCallContext") + lggr.Trace("RPC call: evmclient.Client#BatchCallContext") start := time.Now() if http != nil { err = n.wrapHTTP(http.rpc.BatchCallContext(ctx, b)) @@ -500,7 +500,7 @@ func (n *node) EthSubscribe(ctx context.Context, channel chan<- *evmtypes.Head, return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "args", args) + lggr := n.newRqLggr().With("args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() @@ -523,7 +523,7 @@ func (n *node) TransactionReceipt(ctx context.Context, txHash common.Hash) (rece return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "txHash", txHash) + lggr := n.newRqLggr().With("txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionReceipt") @@ -550,7 +550,7 @@ func (n *node) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *t return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "txHash", txHash) + lggr := n.newRqLggr().With("txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionByHash") @@ -577,7 +577,7 @@ func (n *node) HeaderByNumber(ctx context.Context, number *big.Int) (header *typ return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "number", number) + lggr := n.newRqLggr().With("number", number) lggr.Debug("RPC call: evmclient.Client#HeaderByNumber") start := time.Now() @@ -601,7 +601,7 @@ func (n *node) HeaderByHash(ctx context.Context, hash common.Hash) (header *type return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "hash", hash) + lggr := n.newRqLggr().With("hash", hash) lggr.Debug("RPC call: evmclient.Client#HeaderByHash") start := time.Now() @@ -627,7 +627,7 @@ func (n *node) SendTransaction(ctx context.Context, tx *types.Transaction) error return err } defer cancel() - lggr := logger.With(n.newRqLggr(), "tx", tx) + lggr := n.newRqLggr().With("tx", tx) lggr.Debug("RPC call: evmclient.Client#SendTransaction") start := time.Now() @@ -650,7 +650,7 @@ func (n *node) PendingNonceAt(ctx context.Context, account common.Address) (nonc return 0, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "account", account) + lggr := n.newRqLggr().With("account", account) lggr.Debug("RPC call: evmclient.Client#PendingNonceAt") start := time.Now() @@ -679,7 +679,7 @@ func (n *node) NonceAt(ctx context.Context, account common.Address, blockNumber return 0, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "account", account, "blockNumber", blockNumber) + lggr := n.newRqLggr().With("account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#NonceAt") start := time.Now() @@ -705,7 +705,7 @@ func (n *node) PendingCodeAt(ctx context.Context, account common.Address) (code return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "account", account) + lggr := n.newRqLggr().With("account", account) lggr.Debug("RPC call: evmclient.Client#PendingCodeAt") start := time.Now() @@ -731,7 +731,7 @@ func (n *node) CodeAt(ctx context.Context, account common.Address, blockNumber * return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "account", account, "blockNumber", blockNumber) + lggr := n.newRqLggr().With("account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#CodeAt") start := time.Now() @@ -757,7 +757,7 @@ func (n *node) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint return 0, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "call", call) + lggr := n.newRqLggr().With("call", call) lggr.Debug("RPC call: evmclient.Client#EstimateGas") start := time.Now() @@ -809,7 +809,7 @@ func (n *node) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumb return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "callMsg", msg, "blockNumber", blockNumber) + lggr := n.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#CallContract") start := time.Now() @@ -836,7 +836,7 @@ func (n *node) BlockByNumber(ctx context.Context, number *big.Int) (b *types.Blo return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "number", number) + lggr := n.newRqLggr().With("number", number) lggr.Debug("RPC call: evmclient.Client#BlockByNumber") start := time.Now() @@ -862,7 +862,7 @@ func (n *node) BlockByHash(ctx context.Context, hash common.Hash) (b *types.Bloc return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "hash", hash) + lggr := n.newRqLggr().With("hash", hash) lggr.Debug("RPC call: evmclient.Client#BlockByHash") start := time.Now() @@ -914,7 +914,7 @@ func (n *node) BalanceAt(ctx context.Context, account common.Address, blockNumbe return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "account", account.Hex(), "blockNumber", blockNumber) + lggr := n.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#BalanceAt") start := time.Now() @@ -940,7 +940,7 @@ func (n *node) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []type return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "q", q) + lggr := n.newRqLggr().With("q", q) lggr.Debug("RPC call: evmclient.Client#FilterLogs") start := time.Now() @@ -966,7 +966,7 @@ func (n *node) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, return nil, err } defer cancel() - lggr := logger.With(n.newRqLggr(), "q", q) + lggr := n.newRqLggr().With("q", q) lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") start := time.Now() @@ -1011,10 +1011,8 @@ func (n *node) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error func (n *node) ChainID() (chainID *big.Int) { return n.chainID } // newRqLggr generates a new logger with a unique request ID -func (n *node) newRqLggr() logger.Logger { - return logger.With(n.rpcLog, - "requestID", uuid.New(), - ) +func (n *node) newRqLggr() logger.SugaredLogger { + return n.rpcLog.With("requestID", uuid.New()) } func (n *node) logResult( @@ -1025,17 +1023,14 @@ func (n *node) logResult( callName string, results ...interface{}, ) { - lggr = logger.With(lggr, "duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) + slggr := logger.Sugared(lggr).With("duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) promEVMPoolRPCNodeCalls.WithLabelValues(n.chainID.String(), n.name).Inc() if err == nil { promEVMPoolRPCNodeCallsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc() - logger.Tracew(lggr, - fmt.Sprintf("evmclient.Client#%s RPC call success", callName), - results..., - ) + slggr.Tracew(fmt.Sprintf("evmclient.Client#%s RPC call success", callName), results...) } else { promEVMPoolRPCNodeCallsFailed.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Debugw( + slggr.Debugw( fmt.Sprintf("evmclient.Client#%s RPC call failure", callName), append(results, "err", err)..., ) @@ -1062,7 +1057,7 @@ func (n *node) wrapHTTP(err error) error { if err != nil { n.rpcLog.Debugw("Call failed", "err", err) } else { - logger.Trace(n.rpcLog, "Call succeeded") + n.rpcLog.Trace("Call succeeded") } return err } diff --git a/core/chains/evm/client/node_lifecycle.go b/core/chains/evm/client/node_lifecycle.go index 4e984de00f0..f2232a14935 100644 --- a/core/chains/evm/client/node_lifecycle.go +++ b/core/chains/evm/client/node_lifecycle.go @@ -92,9 +92,8 @@ func (n *node) aliveLoop() { pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold() pollInterval := n.nodePoolCfg.PollInterval() - lggr := logger.Named(n.lfcLog, "Alive") - lggr = logger.With(lggr, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) - logger.Tracew(lggr, "Alive loop starting", "nodeState", n.State()) + lggr := logger.Sugared(n.lfcLog).Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) + lggr.Tracew("Alive loop starting", "nodeState", n.State()) headsC := make(chan *evmtypes.Head) sub, err := n.EthSubscribe(n.nodeCtx, headsC, "newHeads") @@ -143,7 +142,7 @@ func (n *node) aliveLoop() { case <-pollCh: var version string promEVMPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - logger.Tracew(lggr, "Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) ctx, cancel := context.WithTimeout(n.nodeCtx, pollInterval) ctx, cancel2 := n.makeQueryCtx(ctx) err := n.CallContext(ctx, &version, "web3_clientVersion") @@ -165,7 +164,7 @@ func (n *node) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.State()) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - logger.Criticalf(lggr, "RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) + lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) continue } } @@ -177,7 +176,7 @@ func (n *node) aliveLoop() { // note: there must be another live node for us to be out of sync lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) if liveNodes < 2 { - logger.Criticalf(lggr, "RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) + lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) continue } n.declareOutOfSync(n.isOutOfSync) @@ -190,13 +189,13 @@ func (n *node) aliveLoop() { return } promEVMPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() - logger.Tracew(lggr, "Got head", "head", bh) + lggr.Tracew("Got head", "head", bh) if bh.Number > highestReceivedBlockNumber { promEVMPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.Number)) - logger.Tracew(lggr, "Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) + lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) highestReceivedBlockNumber = bh.Number } else { - logger.Tracew(lggr, "Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) + lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) } if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) @@ -212,7 +211,7 @@ func (n *node) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - logger.Criticalf(lggr, "RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) + lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) // We don't necessarily want to wait the full timeout to check again, we should // check regularly and log noisily in this state outOfSyncT.Reset(zombieNodeCheckInterval(n.noNewHeadsThreshold)) @@ -278,7 +277,7 @@ func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *big.Int) bool) { outOfSyncAt := time.Now() - lggr := logger.Named(n.lfcLog, "OutOfSync") + lggr := logger.Sugared(logger.Named(n.lfcLog, "OutOfSync")) lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) // Need to redial since out-of-sync nodes are automatically disconnected @@ -295,7 +294,7 @@ func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *big.Int) bool) { return } - logger.Tracew(lggr, "Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) ch := make(chan *evmtypes.Head) subCtx, cancel := n.makeQueryCtx(n.nodeCtx) @@ -330,7 +329,7 @@ func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *big.Int) bool) { case <-time.After(zombieNodeCheckInterval(n.noNewHeadsThreshold)): if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 1 { - logger.Critical(lggr, "RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") + lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") n.declareInSync() return } @@ -360,7 +359,7 @@ func (n *node) unreachableLoop() { unreachableAt := time.Now() - lggr := logger.Named(n.lfcLog, "Unreachable") + lggr := logger.Sugared(logger.Named(n.lfcLog, "Unreachable")) lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) dialRetryBackoff := utils.NewRedialBackoff() @@ -370,7 +369,7 @@ func (n *node) unreachableLoop() { case <-n.nodeCtx.Done(): return case <-time.After(dialRetryBackoff.Duration()): - logger.Tracew(lggr, "Trying to re-dial RPC node", "nodeState", n.State()) + lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) err := n.dial(n.nodeCtx) if err != nil { diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index afe592533c3..b2d5a4847a5 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -68,7 +68,7 @@ type Pool struct { sendonlys []SendOnlyNode chainID *big.Int chainType config.ChainType - logger logger.Logger + logger logger.SugaredLogger selectionMode string noNewHeadsThreshold time.Duration nodeSelector NodeSelector @@ -113,7 +113,7 @@ func NewPool(lggr logger.Logger, selectionMode string, leaseDuration time.Durati sendonlys: sendonlys, chainID: chainID, chainType: chainType, - logger: lggr, + logger: logger.Sugared(lggr), selectionMode: selectionMode, noNewHeadsThreshold: noNewHeadsTreshold, nodeSelector: nodeSelector, @@ -272,10 +272,10 @@ func (p *Pool) report() { } live := total - dead - logger.Tracew(p.logger, fmt.Sprintf("Pool state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + p.logger.Tracew(fmt.Sprintf("Pool state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) if total == dead { rerr := fmt.Errorf("no EVM primary nodes available: 0/%d nodes are alive", total) - logger.Criticalw(p.logger, rerr.Error(), "nodeStates", nodeStates) + p.logger.Criticalw(rerr.Error(), "nodeStates", nodeStates) p.SvcErrBuffer.Append(rerr) } else if dead > 0 { p.logger.Errorw(fmt.Sprintf("At least one EVM primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) @@ -320,7 +320,7 @@ func (p *Pool) selectNode() (node Node) { p.activeNode = p.nodeSelector.Select() if p.activeNode == nil { - logger.Criticalw(p.logger, "No live RPC nodes available", "NodeSelectionMode", p.nodeSelector.Name()) + p.logger.Criticalw("No live RPC nodes available", "NodeSelectionMode", p.nodeSelector.Name()) errmsg := fmt.Errorf("no live nodes available for chain %s", p.chainID.String()) p.SvcErrBuffer.Append(errmsg) return &erroringNode{errMsg: errmsg.Error()} @@ -367,7 +367,7 @@ func (p *Pool) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error if err != nil { p.logger.Debugw("Secondary node BatchCallContext failed", "err", err) } else { - logger.Trace(p.logger, "Secondary node BatchCallContext success") + p.logger.Trace("Secondary node BatchCallContext success") } }(n) } diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 3cc90c4d8d9..627a2833109 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -55,7 +55,7 @@ type RPCCLient interface { } type rpcClient struct { - rpcLog logger.Logger + rpcLog logger.SugaredLogger name string id int32 chainID *big.Int @@ -106,7 +106,7 @@ func NewRPCClient( "client", r.String(), "evmChainID", chainID, ) - r.rpcLog = logger.Named(lggr, "RPC") + r.rpcLog = logger.Sugared(lggr).Named("RPC") return r } @@ -117,9 +117,9 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { defer cancel() promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() - lggr := logger.With(r.rpcLog, "wsuri", r.ws.uri.Redacted()) + lggr := r.rpcLog.With("wsuri", r.ws.uri.Redacted()) if r.http != nil { - lggr = logger.With(lggr, "httpuri", r.http.uri.Redacted()) + lggr = lggr.With("httpuri", r.http.uri.Redacted()) } lggr.Debugw("RPC dial: evmclient.Client#dial") @@ -148,7 +148,7 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { // It can only return error if the URL is malformed. func (r *rpcClient) DialHTTP() error { promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() - lggr := logger.With(r.rpcLog, "httpuri", r.ws.uri.Redacted()) + lggr := r.rpcLog.With("httpuri", r.ws.uri.Redacted()) lggr.Debugw("RPC dial: evmclient.Client#dial") var httprpc *rpc.Client @@ -206,10 +206,7 @@ func (r *rpcClient) logResult( promEVMPoolRPCNodeCalls.WithLabelValues(r.chainID.String(), r.name).Inc() if err == nil { promEVMPoolRPCNodeCallsSuccess.WithLabelValues(r.chainID.String(), r.name).Inc() - logger.Tracew(lggr, - fmt.Sprintf("evmclient.Client#%s RPC call success", callName), - results..., - ) + logger.Sugared(lggr).Tracew(fmt.Sprintf("evmclient.Client#%s RPC call success", callName), results...) } else { promEVMPoolRPCNodeCallsFailed.WithLabelValues(r.chainID.String(), r.name).Inc() lggr.Debugw( @@ -299,7 +296,7 @@ func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method return err } defer cancel() - lggr := logger.With(r.newRqLggr(), + lggr := r.newRqLggr().With( "method", method, "args", args, ) @@ -328,9 +325,9 @@ func (r *rpcClient) BatchCallContext(ctx context.Context, b []any) error { batch[i] = arg.(rpc.BatchElem) } defer cancel() - lggr := logger.With(r.newRqLggr(), "nBatchElems", len(b), "batchElems", b) + lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) - logger.Trace(lggr, "RPC call: evmclient.Client#BatchCallContext") + lggr.Trace("RPC call: evmclient.Client#BatchCallContext") start := time.Now() if http != nil { err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, batch)) @@ -350,7 +347,7 @@ func (r *rpcClient) Subscribe(ctx context.Context, channel chan<- *evmtypes.Head return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "args", args) + lggr := r.newRqLggr().With("args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() @@ -385,7 +382,7 @@ func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Ha return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "txHash", txHash) + lggr := r.newRqLggr().With("txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionReceipt") @@ -411,7 +408,7 @@ func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) ( return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "txHash", txHash) + lggr := r.newRqLggr().With("txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionByHash") @@ -438,7 +435,7 @@ func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "number", number) + lggr := r.newRqLggr().With("number", number) lggr.Debug("RPC call: evmclient.Client#HeaderByNumber") start := time.Now() @@ -462,7 +459,7 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "hash", hash) + lggr := r.newRqLggr().With("hash", hash) lggr.Debug("RPC call: evmclient.Client#HeaderByHash") start := time.Now() @@ -515,7 +512,7 @@ func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (bloc return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "hash", hash) + lggr := r.newRqLggr().With("hash", hash) lggr.Debug("RPC call: evmclient.Client#BlockByHash") start := time.Now() @@ -541,7 +538,7 @@ func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (blo return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "number", number) + lggr := r.newRqLggr().With("number", number) lggr.Debug("RPC call: evmclient.Client#BlockByNumber") start := time.Now() @@ -567,7 +564,7 @@ func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) return err } defer cancel() - lggr := logger.With(r.newRqLggr(), "tx", tx) + lggr := r.newRqLggr().With("tx", tx) lggr.Debug("RPC call: evmclient.Client#SendTransaction") start := time.Now() @@ -607,7 +604,7 @@ func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Addres return 0, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "account", account) + lggr := r.newRqLggr().With("account", account) lggr.Debug("RPC call: evmclient.Client#PendingNonceAt") start := time.Now() @@ -639,7 +636,7 @@ func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, bloc return 0, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "account", account, "blockNumber", blockNumber) + lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#NonceAt") start := time.Now() @@ -668,7 +665,7 @@ func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) ( return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "account", account) + lggr := r.newRqLggr().With("account", account) lggr.Debug("RPC call: evmclient.Client#PendingCodeAt") start := time.Now() @@ -694,7 +691,7 @@ func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNum return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "account", account, "blockNumber", blockNumber) + lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#CodeAt") start := time.Now() @@ -721,7 +718,7 @@ func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, } defer cancel() call := c.(ethereum.CallMsg) - lggr := logger.With(r.newRqLggr(), "call", call) + lggr := r.newRqLggr().With("call", call) lggr.Debug("RPC call: evmclient.Client#EstimateGas") start := time.Now() @@ -773,7 +770,7 @@ func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumb return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "callMsg", msg, "blockNumber", blockNumber) + lggr := r.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) message := msg.(ethereum.CallMsg) lggr.Debug("RPC call: evmclient.Client#CallContract") @@ -833,7 +830,7 @@ func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, block return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "account", account.Hex(), "blockNumber", blockNumber) + lggr := r.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#BalanceAt") start := time.Now() @@ -892,7 +889,7 @@ func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l [ return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "q", q) + lggr := r.newRqLggr().With("q", q) lggr.Debug("RPC call: evmclient.Client#FilterLogs") start := time.Now() @@ -923,7 +920,7 @@ func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQu return nil, err } defer cancel() - lggr := logger.With(r.newRqLggr(), "q", q) + lggr := r.newRqLggr().With("q", q) lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") start := time.Now() @@ -983,10 +980,8 @@ 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.Logger { - return logger.With(r.rpcLog, - "requestID", uuid.New(), - ) +func (r *rpcClient) newRqLggr() logger.SugaredLogger { + return r.rpcLog.With("requestID", uuid.New()) } func wrapCallError(err error, tp string) error { @@ -1009,7 +1004,7 @@ func (r *rpcClient) wrapHTTP(err error) error { if err != nil { r.rpcLog.Debugw("Call failed", "err", err) } else { - logger.Trace(r.rpcLog, "Call succeeded") + r.rpcLog.Trace("Call succeeded") } return err } diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 844b9e547f2..dc95240fd42 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -198,7 +198,7 @@ func (b *BlockHistoryEstimator) getBlocks() []evmtypes.Block { // The provided context can be used to terminate Start sequence. func (b *BlockHistoryEstimator) Start(ctx context.Context) error { return b.StartOnce("BlockHistoryEstimator", func() error { - logger.Trace(b.logger, "Starting") + b.logger.Trace("Starting") if b.bhConfig.CheckInclusionBlocks() > 0 { b.logger.Infof("Inclusion checking enabled, bumping will be prevented on transactions that have been priced above the %d percentile for %d blocks", b.bhConfig.CheckInclusionPercentile(), b.bhConfig.CheckInclusionBlocks()) @@ -228,7 +228,7 @@ func (b *BlockHistoryEstimator) Start(ctx context.Context) error { b.wg.Add(1) go b.runLoop() - logger.Trace(b.logger, "Started") + b.logger.Trace("Started") return nil }) } @@ -291,7 +291,7 @@ func (b *BlockHistoryEstimator) BumpLegacyGas(_ context.Context, originalGasPric if b.bhConfig.CheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { if errors.Is(err, commonfee.ErrConnectivity) { - logger.Criticalw(b.logger, BumpingHaltedLabel, "err", err) + b.logger.Criticalw(BumpingHaltedLabel, "err", err) b.SvcErrBuffer.Append(err) promBlockHistoryEstimatorConnectivityFailureCount.WithLabelValues(b.chainID.String(), "legacy").Inc() } @@ -467,7 +467,7 @@ func (b *BlockHistoryEstimator) BumpDynamicFee(_ context.Context, originalFee Dy if b.bhConfig.CheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { if errors.Is(err, commonfee.ErrConnectivity) { - logger.Criticalw(b.logger, BumpingHaltedLabel, "err", err) + b.logger.Criticalw(BumpingHaltedLabel, "err", err) b.SvcErrBuffer.Append(err) promBlockHistoryEstimatorConnectivityFailureCount.WithLabelValues(b.chainID.String(), "eip1559").Inc() } @@ -508,7 +508,7 @@ func (b *BlockHistoryEstimator) FetchBlocksAndRecalculate(ctx context.Context, h func (b *BlockHistoryEstimator) Recalculate(head *evmtypes.Head) { percentile := int(b.bhConfig.TransactionPercentile()) - lggr := logger.With(b.logger, "head", head) + lggr := b.logger.With("head", head) blockHistory := b.getBlocks() if len(blockHistory) == 0 { @@ -630,9 +630,9 @@ func (b *BlockHistoryEstimator) FetchBlocks(ctx context.Context, head *evmtypes. reqs = append(reqs, req) } - lggr := logger.With(b.logger, "head", head) + lggr := b.logger.With("head", head) - logger.Tracew(lggr, fmt.Sprintf("Fetching %v blocks (%v in local history)", len(reqs), len(blocks)), "n", len(reqs), "inHistory", len(blocks), "blockNum", head.Number) + lggr.Tracew(fmt.Sprintf("Fetching %v blocks (%v in local history)", len(reqs), len(blocks)), "n", len(reqs), "inHistory", len(blocks), "blockNum", head.Number) if err := b.batchFetch(ctx, reqs); err != nil { return err } @@ -713,7 +713,7 @@ func (b *BlockHistoryEstimator) batchFetch(ctx context.Context, reqs []rpc.Batch j = len(reqs) } - logger.Tracew(b.logger, fmt.Sprintf("Batch fetching blocks %v thru %v", HexToInt64(reqs[i].Args[0]), HexToInt64(reqs[j-1].Args[0]))) + b.logger.Tracew(fmt.Sprintf("Batch fetching blocks %v thru %v", HexToInt64(reqs[i].Args[0]), HexToInt64(reqs[j-1].Args[0]))) err := b.ethClient.BatchCallContext(ctx, reqs[i:j]) if errors.Is(err, context.DeadlineExceeded) { diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index 6a384fa9c54..ce1a50aa320 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -31,7 +31,7 @@ type l1GasPriceOracle struct { services.StateMachine client ethClient pollPeriod time.Duration - logger logger.Logger + logger logger.SugaredLogger address string callArgs string @@ -94,7 +94,7 @@ func NewL1GasPriceOracle(lggr logger.Logger, ethClient ethClient, chainType conf return &l1GasPriceOracle{ client: ethClient, pollPeriod: PollPeriod, - logger: logger.Named(lggr, fmt.Sprintf("L1GasPriceOracle(%s)", chainType)), + logger: logger.Sugared(logger.Named(lggr, fmt.Sprintf("L1GasPriceOracle(%s)", chainType))), address: address, callArgs: callArgs, chInitialised: make(chan struct{}), @@ -159,7 +159,7 @@ func (o *l1GasPriceOracle) refresh() (t *time.Timer) { } if len(b) != 32 { // returns uint256; - logger.Criticalf(o.logger, "return data length (%d) different than expected (%d)", len(b), 32) + o.logger.Criticalf("return data length (%d) different than expected (%d)", len(b), 32) return } price := new(big.Int).SetBytes(b) diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index 13dfe1ffab6..cea2e361184 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -241,7 +241,7 @@ func (rec *received) logsOnBlocks() []logOnBlock { type simpleLogListener struct { name string - lggr logger.Logger + lggr logger.SugaredLogger cfg pg.QConfig received *received t *testing.T @@ -266,7 +266,7 @@ func (helper *broadcasterHelper) newLogListenerWithJob(name string) *simpleLogLi var rec received return &simpleLogListener{ db: db, - lggr: logger.Test(t), + lggr: logger.Sugared(logger.Test(t)), cfg: helper.config.Database(), name: name, received: &rec, @@ -282,7 +282,7 @@ func (listener *simpleLogListener) SkipMarkingConsumed(skip bool) { func (listener *simpleLogListener) HandleLog(lb log.Broadcast) { listener.received.Lock() defer listener.received.Unlock() - logger.Tracef(listener.lggr, "Listener %v HandleLog for block %v %v received at %v %v", listener.name, lb.RawLog().BlockNumber, lb.RawLog().BlockHash, lb.LatestBlockNumber(), lb.LatestBlockHash()) + listener.lggr.Tracef("Listener %v HandleLog for block %v %v received at %v %v", listener.name, lb.RawLog().BlockNumber, lb.RawLog().BlockHash, lb.LatestBlockNumber(), lb.LatestBlockHash()) listener.received.logs = append(listener.received.logs, lb.RawLog()) listener.received.broadcasts = append(listener.received.broadcasts, lb) diff --git a/core/chains/evm/log/registrations.go b/core/chains/evm/log/registrations.go index 73f197a6ab6..346a6776e86 100644 --- a/core/chains/evm/log/registrations.go +++ b/core/chains/evm/log/registrations.go @@ -46,7 +46,7 @@ type ( // handlersByConfs maps numConfirmations => *handler handlersByConfs map[uint32]*handler - logger logger.Logger + logger logger.SugaredLogger evmChainID big.Int // highest 'NumConfirmations' per all listeners, used to decide about deleting older logs if it's higher than EvmFinalityDepth @@ -57,7 +57,7 @@ type ( handler struct { lookupSubs map[common.Address]map[common.Hash]subscribers // contractAddress => logTopic => *subscriber => topicValueFilters evmChainID big.Int - logger logger.Logger + logger logger.SugaredLogger } // The Listener responds to log events through HandleLog. @@ -76,7 +76,7 @@ func newRegistrations(lggr logger.Logger, evmChainID big.Int) *registrations { jobIDAddrs: make(map[int32]map[common.Address]struct{}), handlersByConfs: make(map[uint32]*handler), evmChainID: evmChainID, - logger: logger.Named(lggr, "Registrations"), + logger: logger.Sugared(logger.Named(lggr, "Registrations")), } } @@ -85,7 +85,7 @@ func (r *registrations) addSubscriber(sub *subscriber) (needsResubscribe bool) { r.logger.Panicw(err.Error(), "err", err, "addr", sub.opts.Contract.Hex(), "jobID", sub.listener.JobID()) } - logger.Tracef(r.logger, "Added subscription %p with job ID %v", sub, sub.listener.JobID()) + r.logger.Tracef("Added subscription %p with job ID %v", sub, sub.listener.JobID()) handler, exists := r.handlersByConfs[sub.opts.MinIncomingConfirmations] if !exists { @@ -142,7 +142,7 @@ func (r *registrations) removeSubscriber(sub *subscriber) (needsResubscribe bool if err := r.checkRemoveSubscriber(sub); err != nil { r.logger.Panicw(err.Error(), "err", err, "addr", sub.opts.Contract.Hex(), "jobID", sub.listener.JobID()) } - logger.Tracef(r.logger, "Removed subscription %p with job ID %v", sub, sub.listener.JobID()) + r.logger.Tracef("Removed subscription %p with job ID %v", sub, sub.listener.JobID()) handlers, exists := r.handlersByConfs[sub.opts.MinIncomingConfirmations] if !exists { @@ -263,7 +263,7 @@ func filtersContainValues(topicValues []common.Hash, filters [][]Topic) bool { return true } -func newHandler(lggr logger.Logger, evmChainID big.Int) *handler { +func newHandler(lggr logger.SugaredLogger, evmChainID big.Int) *handler { return &handler{ lookupSubs: make(map[common.Address]map[common.Hash]subscribers), evmChainID: evmChainID, @@ -284,7 +284,7 @@ func (r *handler) addSubscriber(sub *subscriber, handlersWithGreaterConfs []*han for topic, topicValueFilters := range sub.opts.LogsWithTopics { if _, exists := r.lookupSubs[addr][topic]; !exists { - logger.Tracef(r.logger, "No existing sub for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + r.logger.Tracef("No existing sub for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) r.lookupSubs[addr][topic] = make(subscribers) func() { @@ -295,11 +295,11 @@ func (r *handler) addSubscriber(sub *subscriber, handlersWithGreaterConfs []*han // again since even the worst case lookback is already covered for _, existingHandler := range handlersWithGreaterConfs { if _, exists := existingHandler.lookupSubs[addr][topic]; exists { - logger.Tracef(r.logger, "Sub already exists for addr %s and topic %s at greater than this MinIncomingConfirmations of %v. Resubscribe is not required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + r.logger.Tracef("Sub already exists for addr %s and topic %s at greater than this MinIncomingConfirmations of %v. Resubscribe is not required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) return } } - logger.Tracef(r.logger, "No sub exists for addr %s and topic %s at this or greater MinIncomingConfirmations of %v. Resubscribe is required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + r.logger.Tracef("No sub exists for addr %s and topic %s at this or greater MinIncomingConfirmations of %v. Resubscribe is required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) needsResubscribe = true } }() @@ -332,7 +332,7 @@ func (r *handler) removeSubscriber(sub *subscriber, allHandlers map[uint32]*hand // cleanup and resubscribe if necessary if len(topicMap) == 0 { - logger.Tracef(r.logger, "No subs left for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + r.logger.Tracef("No subs left for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) func() { if !needsResubscribe { @@ -344,12 +344,12 @@ func (r *handler) removeSubscriber(sub *subscriber, allHandlers map[uint32]*hand continue } if _, exists := otherHandler.lookupSubs[addr][topic]; exists { - logger.Tracef(r.logger, "Sub still exists for addr %s and topic %s. Resubscribe will not be performed", addr.Hex(), topic.Hex()) + r.logger.Tracef("Sub still exists for addr %s and topic %s. Resubscribe will not be performed", addr.Hex(), topic.Hex()) return } } - logger.Tracef(r.logger, "No sub exists for addr %s and topic %s. Resubscribe will be performed", addr.Hex(), topic.Hex()) + r.logger.Tracef("No sub exists for addr %s and topic %s. Resubscribe will be performed", addr.Hex(), topic.Hex()) needsResubscribe = true } }() diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 991cc8d4309..7bc131afef4 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -98,7 +98,7 @@ type logPoller struct { services.StateMachine ec Client orm ORM - lggr logger.Logger + lggr logger.SugaredLogger pollPeriod time.Duration // poll period set by block production rate useFinalityTag bool // indicates whether logPoller should use chain's finality or pick a fixed depth for finality finalityDepth int64 // finality depth is taken to mean that block (head - finality) is finalized. If `useFinalityTag` is set to true, this value is ignored, because finalityDepth is fetched from chain @@ -138,7 +138,7 @@ func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Durati cancel: cancel, ec: ec, orm: orm, - lggr: logger.Named(lggr, "LogPoller"), + lggr: logger.Sugared(logger.Named(lggr, "LogPoller")), replayStart: make(chan int64), replayComplete: make(chan error), pollPeriod: pollPeriod, @@ -666,7 +666,7 @@ func (lp *logPoller) backfill(ctx context.Context, start, end int64) error { } } if batchSize == 1 { - logger.Criticalw(lp.lggr, "Too many log results in a single block, failed to retrieve logs! Node may be running in a degraded state.", "err", err, "from", from, "to", to, "LogBackfillBatchSize", lp.backfillBatchSize) + lp.lggr.Criticalw("Too many log results in a single block, failed to retrieve logs! Node may be running in a degraded state.", "err", err, "from", from, "to", to, "LogBackfillBatchSize", lp.backfillBatchSize) return err } batchSize /= 2 @@ -921,7 +921,7 @@ func (lp *logPoller) findBlockAfterLCA(ctx context.Context, current *evmtypes.He return nil, err } } - logger.Criticalw(lp.lggr, "Reorg greater than finality depth detected", "finalityTag", lp.useFinalityTag, "current", current.Number, "latestFinalized", latestFinalizedBlockNumber) + lp.lggr.Criticalw("Reorg greater than finality depth detected", "finalityTag", lp.useFinalityTag, "current", current.Number, "latestFinalized", latestFinalizedBlockNumber) rerr := errors.New("Reorg greater than finality depth") lp.SvcErrBuffer.Append(rerr) return nil, rerr diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index f676d3d18e2..68a81299bf9 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -2033,7 +2033,7 @@ type testChecker struct { func (t *testChecker) Check( _ context.Context, - _ logger.Logger, + _ logger.SugaredLogger, _ txmgr.Tx, _ txmgr.TxAttempt, ) error { diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index d08274f74b6..dc7b62647c0 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -43,7 +43,7 @@ func (c *evmTxmClient) BatchSendTransactions( ctx context.Context, attempts []TxAttempt, batchSize int, - lggr logger.Logger, + lggr logger.SugaredLogger, ) ( codes []commonclient.SendTxReturnCode, txErrs []error, @@ -62,7 +62,7 @@ func (c *evmTxmClient) BatchSendTransactions( if len(reqs) != len(attempts) { lenErr := fmt.Errorf("Returned request data length (%d) != number of tx attempts (%d)", len(reqs), len(attempts)) err = errors.Join(err, lenErr) - logger.Criticalw(lggr, "Mismatched length", "err", err) + lggr.Criticalw("Mismatched length", "err", err) return } @@ -88,10 +88,10 @@ func (c *evmTxmClient) BatchSendTransactions( return } -func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.Logger) (commonclient.SendTxReturnCode, error) { +func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.SugaredLogger) (commonclient.SendTxReturnCode, error) { signedTx, err := GetGethSignedTx(attempt.SignedRawTx) if err != nil { - logger.Criticalw(lggr, "Fatal error signing transaction", "err", err, "etx", etx) + lggr.Criticalw("Fatal error signing transaction", "err", err, "etx", etx) return commonclient.Fatal, err } return c.client.SendTransactionReturnCode(ctx, signedTx, etx.FromAddress) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index bb2a30e51dd..f9014163675 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -75,7 +75,7 @@ type TestEvmTxStore interface { type evmTxStore struct { q pg.Q - logger logger.Logger + logger logger.SugaredLogger ctx context.Context ctxCancel context.CancelFunc } @@ -340,7 +340,7 @@ func NewTxStore( q := pg.NewQ(db, namedLogger, cfg, pg.WithParentCtx(ctx)) return &evmTxStore{ q: q, - logger: namedLogger, + logger: logger.Sugared(namedLogger), ctx: ctx, ctxCancel: cancel, } @@ -1499,7 +1499,7 @@ GROUP BY e.id txHashesHex[i] = common.BytesToAddress(r.TxHashes[i]) } - logger.Criticalw(o.logger, fmt.Sprintf("eth_tx with ID %v expired without ever getting a receipt for any of our attempts. "+ + o.logger.Criticalw(fmt.Sprintf("eth_tx with ID %v expired without ever getting a receipt for any of our attempts. "+ "Current block height is %v, transaction was broadcast before block height %v. This transaction may not have not been sent and will be marked as fatally errored. "+ "This can happen if there is another instance of chainlink running that is using the same private key, or if "+ "an external wallet has been used to send a transaction from account %s with nonce %v."+ diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go index 76dfcb9d51c..919fb509fee 100644 --- a/core/chains/evm/txmgr/transmitchecker.go +++ b/core/chains/evm/txmgr/transmitchecker.go @@ -110,7 +110,7 @@ type noChecker struct{} // Check satisfies the TransmitChecker interface. func (noChecker) Check( _ context.Context, - _ logger.Logger, + _ logger.SugaredLogger, _ Tx, _ TxAttempt, ) error { @@ -125,7 +125,7 @@ type SimulateChecker struct { // Check satisfies the TransmitChecker interface. func (s *SimulateChecker) Check( ctx context.Context, - l logger.Logger, + l logger.SugaredLogger, tx Tx, a TxAttempt, ) error { @@ -148,7 +148,7 @@ func (s *SimulateChecker) Check( err := s.Client.CallContext(ctx, &b, "eth_call", callArg, evmclient.ToBlockNumArg(nil)) if err != nil { if jErr := evmclient.ExtractRPCErrorOrNil(err); jErr != nil { - logger.Criticalw(l, "Transaction reverted during simulation", + l.Criticalw("Transaction reverted during simulation", "ethTxAttemptID", a.ID, "txHash", a.Hash, "err", err, "rpcErr", jErr.String(), "returnValue", b.String()) return errors.Errorf("transaction reverted during simulation: %s", jErr.String()) } @@ -175,7 +175,7 @@ type VRFV1Checker struct { // Check satisfies the TransmitChecker interface. func (v *VRFV1Checker) Check( ctx context.Context, - l logger.Logger, + l logger.SugaredLogger, tx Tx, _ TxAttempt, ) error { @@ -284,7 +284,7 @@ type VRFV2Checker struct { // Check satisfies the TransmitChecker interface. func (v *VRFV2Checker) Check( ctx context.Context, - l logger.Logger, + l logger.SugaredLogger, tx Tx, _ TxAttempt, ) error { diff --git a/core/chains/evm/txmgr/transmitchecker_test.go b/core/chains/evm/txmgr/transmitchecker_test.go index 6dd4edd91c6..d2f668da11b 100644 --- a/core/chains/evm/txmgr/transmitchecker_test.go +++ b/core/chains/evm/txmgr/transmitchecker_test.go @@ -106,7 +106,7 @@ func TestFactory(t *testing.T) { func TestTransmitCheckers(t *testing.T) { client := evmtest.NewEthClientMockWithDefaultChain(t) - log := logger.Test(t) + log := logger.Sugared(logger.Test(t)) ctx := testutils.Context(t) t.Run("no checker", func(t *testing.T) { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c5327596272..db71535723e 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -237,7 +237,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 28750a076d5..1c48074ea3a 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad h1:ysPjfbCPJuVxxFZa1Ifv8OPE20pzvnEHjJrPDUo4gT0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 h1:lSYiaiIfAA+5ac45/UD8ciytlNw/S6fnhK7bxFHYI88= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/pg/q.go b/core/services/pg/q.go index 050606c7937..e69e16ec48f 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -118,7 +118,7 @@ type Q struct { Queryer ParentCtx context.Context db *sqlx.DB - logger logger.Logger + logger logger.SugaredLogger config QConfig QueryTimeout time.Duration } @@ -130,7 +130,7 @@ func NewQ(db *sqlx.DB, lggr logger.Logger, config QConfig, qopts ...QOpt) (q Q) q.db = db // skip two levels since we use internal helpers and also want to point up the stack to the caller of the Q method. - q.logger = logger.Helper(lggr, 2) + q.logger = logger.Sugared(logger.Helper(lggr, 2)) q.config = config if q.Queryer == nil { @@ -356,7 +356,7 @@ func (q *queryLogger) postSqlLog(ctx context.Context, begin time.Time) { kvs := []any{"ms", elapsed.Milliseconds(), "timeout", timeout.Milliseconds(), "percent", strconv.FormatFloat(pct, 'f', 1, 64), "sql", q} if elapsed >= timeout { - logger.Criticalw(q.logger, "SLOW SQL QUERY", kvs...) + q.logger.Criticalw("SLOW SQL QUERY", kvs...) } else if errThreshold := timeout / 5; errThreshold > 0 && elapsed > errThreshold { q.logger.Errorw("SLOW SQL QUERY", kvs...) } else if warnThreshold := timeout / 10; warnThreshold > 0 && elapsed > warnThreshold { diff --git a/go.mod b/go.mod index e7b93c40bdb..c71705d9c92 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index e9a7f0267ad..b38b7ac2632 100644 --- a/go.sum +++ b/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad h1:ysPjfbCPJuVxxFZa1Ifv8OPE20pzvnEHjJrPDUo4gT0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 h1:lSYiaiIfAA+5ac45/UD8ciytlNw/S6fnhK7bxFHYI88= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 84da65e9a5d..9f0ff1b384c 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 854978e3353..f8a9529a4cc 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1411,7 +1411,6 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= @@ -1433,8 +1432,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad h1:ysPjfbCPJuVxxFZa1Ifv8OPE20pzvnEHjJrPDUo4gT0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231206181640-faad3f11cfad/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 h1:lSYiaiIfAA+5ac45/UD8ciytlNw/S6fnhK7bxFHYI88= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From 7cb552e9dfd7c344bfd3c180bb3253a486ad7b91 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Wed, 13 Dec 2023 21:02:27 +0200 Subject: [PATCH 050/234] =?UTF-8?q?VRF-798:=20fix=20"nonce=20too=20low"=20?= =?UTF-8?q?in=20load=20test=20setup=20when=20creating=20a=20subsc=E2=80=A6?= =?UTF-8?q?=20(#11560)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-798: fix "nonce too low" in load test setup when creating a subscription * VRF-798: fix runtime issue * VRF-798: PR comments --- integration-tests/load/vrfv2/vrfv2_test.go | 6 ++++++ .../load/vrfv2plus/vrfv2plus_test.go | 15 +++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 86f9b8d61f6..ec0f945870d 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -115,6 +115,12 @@ func TestVRFV2Performance(t *testing.T) { require.NoError(t, err) consumers, err = vrfv2_actions.DeployVRFV2Consumers(env.ContractDeployer, coordinator, 1) require.NoError(t, err) + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + l.Info(). + Str("Coordinator", cfg.ExistingEnvConfig.CoordinatorAddress). + Int("Number of Subs to create", vrfv2Config.NumberOfSubToCreate). + Msg("Creating and funding subscriptions, deploying and adding consumers to subs") subIDs, err = vrfv2_actions.CreateFundSubsAndAddConsumers( env, vrfv2Config, diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 6fe0d06e7da..6d298e075f0 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" "github.com/rs/zerolog" @@ -117,6 +116,12 @@ func TestVRFV2PlusPerformance(t *testing.T) { require.NoError(t, err) consumers, err = vrfv2plus.DeployVRFV2PlusConsumers(env.ContractDeployer, coordinator, 1) require.NoError(t, err) + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + l.Info(). + Str("Coordinator", cfg.ExistingEnvConfig.CoordinatorAddress). + Int("Number of Subs to create", vrfv2PlusConfig.NumberOfSubToCreate). + Msg("Creating and funding subscriptions, deploying and adding consumers to subs") subIDs, err = vrfv2plus.CreateFundSubsAndAddConsumers( env, vrfv2PlusConfig, @@ -308,13 +313,7 @@ func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l ze Str("Should have at least", fundingAtLeast.String()). Str("Funding Amount in ETH", fundingToSendEth.String()). Msg("Funding Node's Sending Key") - gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ - To: &address, - }) - if err != nil { - return err - } - err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + err := actions.FundAddress(client, sendingKey, fundingToSendEth) if err != nil { return err } From 43e9f277d4c42b224f83870586150ff5c02a8ced Mon Sep 17 00:00:00 2001 From: Makram Date: Wed, 13 Dec 2023 22:01:04 +0200 Subject: [PATCH 051/234] feat: add liquidity balancer specs table (#11564) Add the liquidity balancer specs table. This table will be used to house the specs for the liquidity balancer job which will be implemented in ccip rather than core. The entirety of the spec's content will be in the `liquidity_balancer_config` field. This is done to minimize future migrations. This doesn't mean we won't be doing strict checks on the contents of the config during the validation process. --- core/services/job/models.go | 8 +++ .../0213_liquidity_balancer_specs.sql | 49 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 core/store/migrate/migrations/0213_liquidity_balancer_specs.sql diff --git a/core/services/job/models.go b/core/services/job/models.go index b21ecc12e72..3c869f76056 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -152,6 +152,8 @@ type Job struct { GatewaySpecID *int32 EALSpec *EALSpec EALSpecID *int32 + LiquidityBalancerSpec *LiquidityBalancerSpec + LiquidityBalancerSpecID *int32 PipelineSpecID int32 PipelineSpec *pipeline.Spec JobSpecErrors []SpecError @@ -793,3 +795,9 @@ type EALSpec struct { // UpdatedAt is the time this job was last updated. UpdatedAt time.Time `toml:"-"` } + +type LiquidityBalancerSpec struct { + ID int32 + + LiquidityBalancerConfig string `toml:"liquidityBalancerConfig" db:"liquidity_balancer_config"` +} diff --git a/core/store/migrate/migrations/0213_liquidity_balancer_specs.sql b/core/store/migrate/migrations/0213_liquidity_balancer_specs.sql new file mode 100644 index 00000000000..cd717181f58 --- /dev/null +++ b/core/store/migrate/migrations/0213_liquidity_balancer_specs.sql @@ -0,0 +1,49 @@ +-- +goose Up +CREATE TABLE liquidity_balancer_specs ( + id BIGSERIAL PRIMARY KEY, + liquidity_balancer_config JSONB NOT NULL +); + +ALTER TABLE + jobs +ADD COLUMN + liquidity_balancer_spec_id BIGINT REFERENCES liquidity_balancer_specs(id), +DROP CONSTRAINT chk_only_one_spec, +ADD CONSTRAINT chk_only_one_spec CHECK ( + num_nonnulls( + ocr_oracle_spec_id, ocr2_oracle_spec_id, + direct_request_spec_id, flux_monitor_spec_id, + keeper_spec_id, cron_spec_id, webhook_spec_id, + vrf_spec_id, blockhash_store_spec_id, + block_header_feeder_spec_id, bootstrap_spec_id, + gateway_spec_id, + legacy_gas_station_server_spec_id, + legacy_gas_station_sidecar_spec_id, + eal_spec_id, + liquidity_balancer_spec_id + ) = 1 +); + +-- +goose Down +ALTER TABLE + jobs +DROP CONSTRAINT chk_only_one_spec, +ADD CONSTRAINT chk_only_one_spec CHECK ( + num_nonnulls( + ocr_oracle_spec_id, ocr2_oracle_spec_id, + direct_request_spec_id, flux_monitor_spec_id, + keeper_spec_id, cron_spec_id, webhook_spec_id, + vrf_spec_id, blockhash_store_spec_id, + block_header_feeder_spec_id, bootstrap_spec_id, + gateway_spec_id, + legacy_gas_station_server_spec_id, + legacy_gas_station_sidecar_spec_id, + eal_spec_id + ) = 1 +); +ALTER TABLE + jobs +DROP COLUMN + liquidity_balancer_spec_id; +DROP TABLE + liquidity_balancer_specs; From 19c7ccac1714dae991d7b3aecc864f99d722b2d4 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 12:07:31 -0800 Subject: [PATCH 052/234] Auto 8087 modular streams (#11489) * make streams lookup modular * polish * address comment to use pointer instead of array/map * rebase * get rid of slice * keep using array and index * remove duplicate logging * address err re-declarion lint issues --- core/scripts/chaincli/handler/debug.go | 223 +++++--- .../handler/mercury_lookup_handler.go | 536 ------------------ core/scripts/go.mod | 3 +- core/scripts/go.sum | 2 - .../v21/mercury/streams/streams.go | 105 ++-- .../v21/mercury/streams/streams_test.go | 20 +- 6 files changed, 222 insertions(+), 667 deletions(-) delete mode 100644 core/scripts/chaincli/handler/mercury_lookup_handler.go diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 5947337b187..815a0c9a030 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -18,13 +18,19 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" + "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" @@ -35,12 +41,7 @@ import ( const ( ConditionTrigger uint8 = iota LogTrigger - - blockNumber = "blockNumber" expectedTypeAndVersion = "KeeperRegistry 2.1.0" - feedIdHex = "feedIdHex" - feedIDs = "feedIDs" - timestamp = "timestamp" ) var packer = encoding.NewAbiPacker() @@ -51,24 +52,29 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if len(args) < 1 { failCheckArgs("no upkeepID supplied", nil) } + // test that we are connected to an archive node _, err := k.client.BalanceAt(ctx, gethcommon.Address{}, big.NewInt(1)) if err != nil { failCheckConfig("you are not connected to an archive node; try using infura or alchemy", err) } + chainIDBig, err := k.client.ChainID(ctx) if err != nil { failUnknown("unable to retrieve chainID from rpc client", err) } chainID := chainIDBig.Int64() + + var triggerCallOpts *bind.CallOpts // use latest block for conditionals, but use block from tx for log triggers + latestCallOpts := &bind.CallOpts{Context: ctx} // always use latest block + // connect to registry contract - latestCallOpts := &bind.CallOpts{Context: ctx} // always use latest block - triggerCallOpts := &bind.CallOpts{Context: ctx} // use latest block for conditionals, but use block from tx for log triggers registryAddress := gethcommon.HexToAddress(k.cfg.RegistryAddress) keeperRegistry21, err := iregistry21.NewIKeeperRegistryMaster(registryAddress, k.client) if err != nil { failUnknown("failed to connect to registry contract", err) } + // verify contract is correct typeAndVersion, err := keeperRegistry21.TypeAndVersion(latestCallOpts) if err != nil { @@ -124,17 +130,21 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var checkResult iregistry21.CheckUpkeep var blockNum uint64 var performData []byte + var workID [32]byte + var trigger ocr2keepers.Trigger upkeepNeeded := false // check upkeep if triggerType == ConditionTrigger { message("upkeep identified as conditional trigger") - tmpCheckResult, err := keeperRegistry21.CheckUpkeep0(latestCallOpts, upkeepID) + var tmpCheckResult iregistry21.CheckUpkeep0 + tmpCheckResult, err = keeperRegistry21.CheckUpkeep0(latestCallOpts, upkeepID) if err != nil { failUnknown("failed to check upkeep: ", err) } checkResult = iregistry21.CheckUpkeep(tmpCheckResult) // do tenderly simulation - rawCall, err := core.RegistryABI.Pack("checkUpkeep", upkeepID, []byte{}) + var rawCall []byte + rawCall, err = core.RegistryABI.Pack("checkUpkeep", upkeepID, []byte{}) if err != nil { failUnknown("failed to pack raw checkUpkeep call", err) } @@ -146,19 +156,26 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { failCheckArgs("txHash and log index must be supplied to command in order to debug log triggered upkeeps", nil) } txHash := gethcommon.HexToHash(args[1]) - logIndex, err := strconv.ParseInt(args[2], 10, 64) + + var logIndex int64 + logIndex, err = strconv.ParseInt(args[2], 10, 64) if err != nil { failCheckArgs("unable to parse log index", err) } - // find transaction receipt - _, isPending, err := k.client.TransactionByHash(ctx, txHash) + + // check that tx is confirmed + var isPending bool + _, isPending, err = k.client.TransactionByHash(ctx, txHash) if err != nil { log.Fatal("failed to get tx by hash", err) } if isPending { resolveIneligible(fmt.Sprintf("tx %s is still pending confirmation", txHash)) } - receipt, err := k.client.TransactionReceipt(ctx, txHash) + + // find transaction receipt + var receipt *types.Receipt + receipt, err = k.client.TransactionReceipt(ctx, txHash) if err != nil { failCheckArgs("failed to fetch tx receipt", err) } @@ -176,9 +193,11 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } // check that tx for this upkeep / tx was not already performed message(fmt.Sprintf("LogTrigger{blockNum: %d, blockHash: %s, txHash: %s, logIndex: %d}", blockNum, receipt.BlockHash.Hex(), txHash, logIndex)) - workID := mustUpkeepWorkID(upkeepID, blockNum, receipt.BlockHash, txHash, logIndex) + trigger = mustAutomationTrigger(txHash, logIndex, blockNum, receipt.BlockHash) + workID = mustUpkeepWorkID(upkeepID, trigger) message(fmt.Sprintf("workID computed: %s", hex.EncodeToString(workID[:]))) - hasKey, err := keeperRegistry21.HasDedupKey(latestCallOpts, workID) + var hasKey bool + hasKey, err = keeperRegistry21.HasDedupKey(latestCallOpts, workID) if err != nil { failUnknown("failed to check if upkeep was already performed: ", err) } @@ -186,11 +205,13 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { resolveIneligible("upkeep was already performed") } triggerCallOpts = &bind.CallOpts{Context: ctx, BlockNumber: big.NewInt(receipt.BlockNumber.Int64())} - rawTriggerConfig, err := keeperRegistry21.GetUpkeepTriggerConfig(triggerCallOpts, upkeepID) + var rawTriggerConfig []byte + rawTriggerConfig, err = keeperRegistry21.GetUpkeepTriggerConfig(triggerCallOpts, upkeepID) if err != nil { failUnknown("failed to fetch trigger config for upkeep", err) } - triggerConfig, err := packer.UnpackLogTriggerConfig(rawTriggerConfig) + var triggerConfig automation_utils_2_1.LogTriggerConfig + triggerConfig, err = packer.UnpackLogTriggerConfig(rawTriggerConfig) if err != nil { failUnknown("failed to unpack trigger config", err) } @@ -200,11 +221,13 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if !logMatchesTriggerConfig(triggeringEvent, triggerConfig) { resolveIneligible("log does not match trigger config") } - header, err := k.client.HeaderByHash(ctx, receipt.BlockHash) + var header *types.Header + header, err = k.client.HeaderByHash(ctx, receipt.BlockHash) if err != nil { failUnknown("failed to find block", err) } - triggerData, err := packTriggerData(triggeringEvent, header.Time) + var triggerData []byte + triggerData, err = packTriggerData(triggeringEvent, header.Time) if err != nil { failUnknown("failed to pack trigger data", err) } @@ -213,7 +236,8 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { failUnknown("failed to check upkeep", err) } // do tenderly simulations - rawCall, err := core.RegistryABI.Pack("checkUpkeep", upkeepID, triggerData) + var rawCall []byte + rawCall, err = core.RegistryABI.Pack("checkUpkeep", upkeepID, triggerData) if err != nil { failUnknown("failed to pack raw checkUpkeep call", err) } @@ -228,73 +252,88 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if checkResult.UpkeepFailureReason != 0 { message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) } + if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { - // TODO use the new streams lookup lib - //mc := &models.MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey} - //mercuryConfig := evm.NewMercuryConfig(mc, core.StreamsCompatibleABI) - //lggr, _ := logger.NewLogger() - //blockSub := &blockSubscriber{k.client} - //_ = streams.NewStreamsLookup(packer, mercuryConfig, blockSub, keeperRegistry21, k.rpcClient, lggr) - - streamsLookupErr, err := packer.DecodeStreamsLookupRequest(checkResult.PerformData) + mc := &models.MercuryCredentials{LegacyURL: k.cfg.MercuryLegacyURL, URL: k.cfg.MercuryURL, Username: k.cfg.MercuryID, Password: k.cfg.MercuryKey} + mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) + lggr, _ := logger.NewLogger() + blockSub := &blockSubscriber{k.client} + streams := streams.NewStreamsLookup(packer, mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) + + var streamsLookupErr *mercury.StreamsLookupError + streamsLookupErr, err = packer.DecodeStreamsLookupRequest(checkResult.PerformData) if err == nil { message("upkeep reverted with StreamsLookup") message(fmt.Sprintf("StreamsLookup data: {FeedParamKey: %s, Feeds: %v, TimeParamKey: %s, Time: %d, ExtraData: %s}", streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time.Uint64(), hexutil.Encode(streamsLookupErr.ExtraData))) - if streamsLookupErr.FeedParamKey == feedIdHex && streamsLookupErr.TimeParamKey == blockNumber { + + streamsLookup := &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: streamsLookupErr.FeedParamKey, + Feeds: streamsLookupErr.Feeds, + TimeParamKey: streamsLookupErr.TimeParamKey, + Time: streamsLookupErr.Time, + ExtraData: streamsLookupErr.ExtraData, + }, + UpkeepId: upkeepID, + Block: blockNum, + } + + if streamsLookup.IsMercuryV02() { message("using mercury lookup v0.2") - // handle v0.2 - cfg, err := keeperRegistry21.GetUpkeepPrivilegeConfig(triggerCallOpts, upkeepID) + // check if upkeep is allowed to use mercury v0.2 + var allowed bool + _, _, _, allowed, err = streams.AllowedToUseMercury(latestCallOpts, upkeepID) if err != nil { - failUnknown("failed to get upkeep privilege config ", err) - } - allowed := false - if len(cfg) > 0 { - var privilegeConfig streams.UpkeepPrivilegeConfig - if err := json.Unmarshal(cfg, &privilegeConfig); err != nil { - failUnknown("failed to unmarshal privilege config ", err) - } - allowed = privilegeConfig.MercuryEnabled + failUnknown("failed to check if upkeep is allowed to use mercury", err) } if !allowed { resolveIneligible("upkeep reverted with StreamsLookup but is not allowed to access streams") } - } else if streamsLookupErr.FeedParamKey != feedIDs || streamsLookupErr.TimeParamKey != timestamp { + } else if streamsLookup.IsMercuryV03() { // handle v0.3 - resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") - } else { message("using mercury lookup v0.3") + } else { + resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") } - streamsLookup := &StreamsLookup{streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time, streamsLookupErr.ExtraData, upkeepID, blockNum} if k.cfg.MercuryLegacyURL == "" || k.cfg.MercuryURL == "" || k.cfg.MercuryID == "" || k.cfg.MercuryKey == "" { failCheckConfig("Mercury configs not set properly, check your MERCURY_LEGACY_URL, MERCURY_URL, MERCURY_ID and MERCURY_KEY", nil) } - handler := NewMercuryLookupHandler(&MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey}, k.rpcClient) - state, failureReason, values, _, err := handler.doMercuryRequest(ctx, streamsLookup) - if failureReason == UpkeepFailureReasonInvalidRevertDataInput { + + // do mercury request + automationCheckResult := mustAutomationCheckResult(upkeepID, checkResult, trigger) + checkResults := []ocr2keepers.CheckResult{automationCheckResult} + + var values [][]byte + values, err = streams.DoMercuryRequest(ctx, streamsLookup, checkResults, 0) + + if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { resolveIneligible("upkeep used invalid revert data") } - if state == InvalidMercuryRequest { + if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { resolveIneligible("the mercury request data is invalid") } if err != nil { failCheckConfig("failed to do mercury request ", err) } - callbackResult, err := keeperRegistry21.CheckCallback(triggerCallOpts, upkeepID, values, streamsLookup.extraData) + + // do checkCallback + err = streams.CheckCallback(ctx, values, streamsLookup, checkResults, 0) if err != nil { failUnknown("failed to execute mercury callback ", err) } - if callbackResult.UpkeepFailureReason != 0 { - message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) + if automationCheckResult.IneligibilityReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", automationCheckResult.IneligibilityReason)) } - upkeepNeeded, performData = callbackResult.UpkeepNeeded, callbackResult.PerformData - // do tenderly simulations - rawCall, err := core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.extraData) + upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData + // do tenderly simulations for checkCallback + var rawCall []byte + rawCall, err = core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) if err != nil { failUnknown("failed to pack raw checkCallback call", err) } addLink("checkCallback simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) - rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.extraData) + rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.ExtraData) if err != nil { failUnknown("failed to pack raw checkCallback (direct) call", err) } @@ -316,6 +355,39 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } +func mustAutomationCheckResult(upkeepID *big.Int, checkResult iregistry21.CheckUpkeep, trigger ocr2keepers.Trigger) ocr2keepers.CheckResult { + upkeepIdentifier := mustUpkeepIdentifier(upkeepID) + checkResult2 := ocr2keepers.CheckResult{ + Eligible: checkResult.UpkeepNeeded, + IneligibilityReason: checkResult.UpkeepFailureReason, + UpkeepID: upkeepIdentifier, + Trigger: trigger, + WorkID: core.UpkeepWorkID(upkeepIdentifier, trigger), + GasAllocated: 0, + PerformData: checkResult.PerformData, + FastGasWei: checkResult.FastGasWei, + LinkNative: checkResult.LinkNative, + } + + return checkResult2 +} + +type blockSubscriber struct { + ethClient *ethclient.Client +} + +func (bs *blockSubscriber) LatestBlock() *ocr2keepers.BlockKey { + header, err := bs.ethClient.HeaderByNumber(context.Background(), nil) + if err != nil { + return nil + } + + return &ocr2keepers.BlockKey{ + Number: ocr2keepers.BlockNumber(header.Number.Uint64()), + Hash: header.Hash(), + } +} + func logMatchesTriggerConfig(log *types.Log, config automation_utils_2_1.LogTriggerConfig) bool { if log.Topics[0] != config.Topic0 { return false @@ -353,9 +425,27 @@ func packTriggerData(log *types.Log, blockTime uint64) ([]byte, error) { return b, nil } -func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, txHash [32]byte, logIndex int64) [32]byte { - // TODO - this is a copy of the code in core.UpkeepWorkID - // We should refactor that code to be more easily exported ex not rely on Trigger structs +func mustUpkeepWorkID(upkeepID *big.Int, trigger ocr2keepers.Trigger) [32]byte { + upkeepIdentifier := mustUpkeepIdentifier(upkeepID) + + workID := core.UpkeepWorkID(upkeepIdentifier, trigger) + workIDBytes, err := hex.DecodeString(workID) + if err != nil { + failUnknown("failed to decode workID", err) + } + + var result [32]byte + copy(result[:], workIDBytes[:]) + return result +} + +func mustUpkeepIdentifier(upkeepID *big.Int) ocr2keepers.UpkeepIdentifier { + upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} + upkeepIdentifier.FromBigInt(upkeepID) + return *upkeepIdentifier +} + +func mustAutomationTrigger(txHash [32]byte, logIndex int64, blockNum uint64, blockHash [32]byte) ocr2keepers.Trigger { trigger := ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ TxHash: txHash, @@ -364,16 +454,7 @@ func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, tx BlockHash: blockHash, }, } - upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} - upkeepIdentifier.FromBigInt(upkeepID) - workID := core.UpkeepWorkID(*upkeepIdentifier, trigger) - workIDBytes, err := hex.DecodeString(workID) - if err != nil { - failUnknown("failed to decode workID", err) - } - var result [32]byte - copy(result[:], workIDBytes[:]) - return result + return trigger } func message(msg string) { @@ -385,11 +466,11 @@ func warning(msg string) { } func resolveIneligible(msg string) { - exit(fmt.Sprintf("✅ %s: this upkeep is not currently elligible", msg), nil, 0) + exit(fmt.Sprintf("✅ %s: this upkeep is not currently eligible", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently elligible", nil, 0) + exit("❌ this upkeep is currently eligible", nil, 0) } func rerun(msg string, err error) { @@ -490,5 +571,3 @@ func tenderlySimLink(cfg *config.Config, chainID int64, blockNumber uint64, inpu } return common.TenderlySimLink(responseJSON.Simulation.Id) } - -// TODO - link to performUpkeep tx if exists diff --git a/core/scripts/chaincli/handler/mercury_lookup_handler.go b/core/scripts/chaincli/handler/mercury_lookup_handler.go deleted file mode 100644 index 1165d83921d..00000000000 --- a/core/scripts/chaincli/handler/mercury_lookup_handler.go +++ /dev/null @@ -1,536 +0,0 @@ -package handler - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/avast/retry-go" - ethabi "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" -) - -// MercuryLookupHandler is responsible for initiating the calls to the Mercury server -// to determine whether the upkeeps are eligible -type MercuryLookupHandler struct { - credentials *MercuryCredentials - httpClient HttpClient - rpcClient *rpc.Client -} - -func NewMercuryLookupHandler( - credentials *MercuryCredentials, - rpcClient *rpc.Client, -) *MercuryLookupHandler { - return &MercuryLookupHandler{ - credentials: credentials, - httpClient: http.DefaultClient, - rpcClient: rpcClient, - } -} - -type MercuryVersion string - -type StreamsLookup struct { - feedParamKey string - feeds []string - timeParamKey string - time *big.Int - extraData []byte - upkeepId *big.Int - block uint64 -} - -//go:generate mockery --quiet --name HttpClient --output ./mocks/ --case=underscore -type HttpClient interface { - Do(req *http.Request) (*http.Response, error) -} - -type MercuryCredentials struct { - LegacyURL string - URL string - ClientID string - ClientKey string -} - -func (mc *MercuryCredentials) Validate() bool { - return mc.URL != "" && mc.ClientID != "" && mc.ClientKey != "" -} - -type MercuryData struct { - Index int - Error error - Retryable bool - Bytes [][]byte - State PipelineExecutionState -} - -// MercuryV02Response represents a JSON structure used by Mercury v0.2 -type MercuryV02Response struct { - ChainlinkBlob string `json:"chainlinkBlob"` -} - -// MercuryV03Response represents a JSON structure used by Mercury v0.3 -type MercuryV03Response struct { - Reports []MercuryV03Report `json:"reports"` -} - -type MercuryV03Report struct { - FeedID string `json:"feedID"` // feed id in hex encoded - ValidFromTimestamp uint32 `json:"validFromTimestamp"` - ObservationsTimestamp uint32 `json:"observationsTimestamp"` - FullReport string `json:"fullReport"` // the actual hex encoded mercury report of this feed, can be sent to verifier -} - -const ( - // DefaultAllowListExpiration decides how long an upkeep's allow list info will be valid for. - DefaultAllowListExpiration = 20 * time.Minute - // CleanupInterval decides when the expired items in cache will be deleted. - CleanupInterval = 25 * time.Minute -) - -const ( - ApplicationJson = "application/json" - BlockNumber = "blockNumber" // valid for v0.2 - FeedIDs = "feedIDs" // valid for v0.3 - FeedIdHex = "feedIdHex" // valid for v0.2 - HeaderAuthorization = "Authorization" - HeaderContentType = "Content-Type" - HeaderTimestamp = "X-Authorization-Timestamp" - HeaderSignature = "X-Authorization-Signature-SHA256" - HeaderUpkeepId = "X-Authorization-Upkeep-Id" - MercuryPathV2 = "/client?" // only used to access mercury v0.2 server - MercuryBatchPathV3 = "/api/v1/reports/bulk?" // only used to access mercury v0.3 server - RetryDelay = 500 * time.Millisecond - Timestamp = "timestamp" // valid for v0.3 - TotalAttempt = 3 - UserId = "userId" -) - -type UpkeepFailureReason uint8 -type PipelineExecutionState uint8 - -const ( - // upkeep failure onchain reasons - UpkeepFailureReasonNone UpkeepFailureReason = 0 - UpkeepFailureReasonUpkeepCancelled UpkeepFailureReason = 1 - UpkeepFailureReasonUpkeepPaused UpkeepFailureReason = 2 - UpkeepFailureReasonTargetCheckReverted UpkeepFailureReason = 3 - UpkeepFailureReasonUpkeepNotNeeded UpkeepFailureReason = 4 - UpkeepFailureReasonPerformDataExceedsLimit UpkeepFailureReason = 5 - UpkeepFailureReasonInsufficientBalance UpkeepFailureReason = 6 - UpkeepFailureReasonMercuryCallbackReverted UpkeepFailureReason = 7 - UpkeepFailureReasonRevertDataExceedsLimit UpkeepFailureReason = 8 - UpkeepFailureReasonRegistryPaused UpkeepFailureReason = 9 - // leaving a gap here for more onchain failure reasons in the future - // upkeep failure offchain reasons - UpkeepFailureReasonMercuryAccessNotAllowed UpkeepFailureReason = 32 - UpkeepFailureReasonTxHashNoLongerExists UpkeepFailureReason = 33 - UpkeepFailureReasonInvalidRevertDataInput UpkeepFailureReason = 34 - UpkeepFailureReasonSimulationFailed UpkeepFailureReason = 35 - UpkeepFailureReasonTxHashReorged UpkeepFailureReason = 36 - - // pipeline execution error - NoPipelineError PipelineExecutionState = 0 - CheckBlockTooOld PipelineExecutionState = 1 - CheckBlockInvalid PipelineExecutionState = 2 - RpcFlakyFailure PipelineExecutionState = 3 - MercuryFlakyFailure PipelineExecutionState = 4 - PackUnpackDecodeFailed PipelineExecutionState = 5 - MercuryUnmarshalError PipelineExecutionState = 6 - InvalidMercuryRequest PipelineExecutionState = 7 - InvalidMercuryResponse PipelineExecutionState = 8 // this will only happen if Mercury server sends bad responses - UpkeepNotAuthorized PipelineExecutionState = 9 -) - -// UpkeepPrivilegeConfig represents the administrative offchain config for each upkeep. It can be set by s_upkeepPrivilegeManager -// role on the registry. Upkeeps allowed to use Mercury server will have this set to true. -type UpkeepPrivilegeConfig struct { - MercuryEnabled bool `json:"mercuryEnabled"` -} - -// generateHMAC calculates a user HMAC for Mercury server authentication. -func (mlh *MercuryLookupHandler) generateHMAC(method string, path string, body []byte, clientId string, secret string, ts int64) string { - bodyHash := sha256.New() - bodyHash.Write(body) - hashString := fmt.Sprintf("%s %s %s %s %d", - method, - path, - hex.EncodeToString(bodyHash.Sum(nil)), - clientId, - ts) - signedMessage := hmac.New(sha256.New, []byte(secret)) - signedMessage.Write([]byte(hashString)) - userHmac := hex.EncodeToString(signedMessage.Sum(nil)) - return userHmac -} - -// singleFeedRequest sends a v0.2 Mercury request for a single feed report. -func (mlh *MercuryLookupHandler) singleFeedRequest(ctx context.Context, ch chan<- MercuryData, index int, ml *StreamsLookup) { - q := url.Values{ - ml.feedParamKey: {ml.feeds[index]}, - ml.timeParamKey: {ml.time.String()}, - } - mercuryURL := mlh.credentials.LegacyURL - reqUrl := fmt.Sprintf("%s%s%s", mercuryURL, MercuryPathV2, q.Encode()) - // mlh.logger.Debugf("request URL for upkeep %s feed %s: %s", ml.upkeepId.String(), ml.feeds[index], reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: index, Error: err, Retryable: false, State: InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := mlh.generateHMAC(http.MethodGet, MercuryPathV2+q.Encode(), []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) - req.Header.Set(HeaderContentType, ApplicationJson) - req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) - req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) - req.Header.Set(HeaderSignature, signature) - - // in the case of multiple retries here, use the last attempt's data - state := NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := mlh.httpClient.Do(req) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup GET request failed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) - retryable = true - state = MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - _ = "" // placate linter - // mlh.logger.Errorf("Encountered error when closing the body of the response in single feed: %s", err) - } - }(resp.Body) - - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = InvalidMercuryResponse - return err1 - } - - if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError { - // mlh.logger.Errorw("StreamsLookup received retryable status code", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "statusCode", resp.StatusCode, "feed", ml.feeds[index]) - retryable = true - state = MercuryFlakyFailure - return errors.New(strconv.FormatInt(int64(resp.StatusCode), 10)) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("StreamsLookup upkeep %s block %s received status code %d for feed %s", ml.upkeepId.String(), ml.time.String(), resp.StatusCode, ml.feeds[index]) - } - - // mlh.logger.Debugf("at block %s upkeep %s received status code %d from mercury v0.2 with BODY=%s", ml.time.String(), ml.upkeepId.String(), resp.StatusCode, hexutil.Encode(body)) - - var m MercuryV02Response - err1 = json.Unmarshal(body, &m) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) - retryable = false - state = MercuryUnmarshalError - return err1 - } - blobBytes, err1 := hexutil.Decode(m.ChainlinkBlob) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to decode chainlinkBlob for feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "blob", m.ChainlinkBlob, "feed", ml.feeds[index], "error", err1) - retryable = false - state = InvalidMercuryResponse - return err1 - } - ch <- MercuryData{ - Index: index, - Bytes: [][]byte{blobBytes}, - Retryable: false, - State: NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found or 500 Internal Server Error - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) - }), - retry.Context(ctx), - retry.Delay(RetryDelay), - retry.Attempts(TotalAttempt)) - - if !sent { - md := MercuryData{ - Index: index, - Bytes: [][]byte{}, - Retryable: retryable, - Error: fmt.Errorf("failed to request feed for %s: %w", ml.feeds[index], retryErr), - State: state, - } - ch <- md - } -} - -// multiFeedsRequest sends a Mercury v0.3 request for a multi-feed report -func (mlh *MercuryLookupHandler) multiFeedsRequest(ctx context.Context, ch chan<- MercuryData, ml *StreamsLookup) { - params := fmt.Sprintf("%s=%s&%s=%s", FeedIDs, strings.Join(ml.feeds, ","), Timestamp, ml.time.String()) - reqUrl := fmt.Sprintf("%s%s%s", mlh.credentials.URL, MercuryBatchPathV3, params) - // mlh.logger.Debugf("request URL for upkeep %s userId %s: %s", ml.upkeepId.String(), mlh.credentials.ClientID, reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: 0, Error: err, Retryable: false, State: InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := mlh.generateHMAC(http.MethodGet, MercuryBatchPathV3+params, []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) - req.Header.Set(HeaderContentType, ApplicationJson) - // username here is often referred to as user id - req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) - req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) - req.Header.Set(HeaderSignature, signature) - // mercury will inspect authorization headers above to make sure this user (in automation's context, this node) is eligible to access mercury - // and if it has an automation role. it will then look at this upkeep id to check if it has access to all the requested feeds. - req.Header.Set(HeaderUpkeepId, ml.upkeepId.String()) - - // in the case of multiple retries here, use the last attempt's data - state := NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := mlh.httpClient.Do(req) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup GET request fails for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) - retryable = true - state = MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - _ = "" // placate linter - // mlh.logger.Errorf("Encountered error when closing the body of the response in the multi feed: %s", err) - } - }(resp.Body) - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = InvalidMercuryResponse - return err1 - } - - // mlh.logger.Infof("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - if resp.StatusCode == http.StatusUnauthorized { - retryable = false - state = UpkeepNotAuthorized - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by unauthorized upkeep", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusBadRequest { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by invalid format of timestamp", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusInternalServerError { - retryable = true - state = MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusInternalServerError) - } else if resp.StatusCode == 420 { - // in 0.3, this will happen when missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } - - var response MercuryV03Response - err1 = json.Unmarshal(body, &response) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) - retryable = false - state = MercuryUnmarshalError - return err1 - } - // in v0.3, if some feeds are not available, the server will only return available feeds, but we need to make sure ALL feeds are retrieved before calling user contract - // hence, retry in this case. retry will help when we send a very new timestamp and reports are not yet generated - if len(response.Reports) != len(ml.feeds) { - // TODO: AUTO-5044: calculate what reports are missing and log a warning - retryable = true - state = MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusNotFound) - } - var reportBytes [][]byte - for _, rsp := range response.Reports { - b, err := hexutil.Decode(rsp.FullReport) - if err != nil { - retryable = false - state = InvalidMercuryResponse - return err - } - reportBytes = append(reportBytes, b) - } - ch <- MercuryData{ - Index: 0, - Bytes: reportBytes, - Retryable: false, - State: NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found or 500 Internal Server Error - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) - }), - retry.Context(ctx), - retry.Delay(RetryDelay), - retry.Attempts(TotalAttempt)) - - if !sent { - md := MercuryData{ - Index: 0, - Bytes: [][]byte{}, - Retryable: retryable, - Error: retryErr, - State: state, - } - ch <- md - } -} - -// doMercuryRequest sends requests to Mercury API to retrieve ChainlinkBlob. -func (mlh *MercuryLookupHandler) doMercuryRequest(ctx context.Context, ml *StreamsLookup) (PipelineExecutionState, UpkeepFailureReason, [][]byte, bool, error) { - var isMercuryV03 bool - resultLen := len(ml.feeds) - ch := make(chan MercuryData, resultLen) - if len(ml.feeds) == 0 { - return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) - } - if ml.feedParamKey == FeedIdHex && ml.timeParamKey == BlockNumber { - // only v0.2 - for i := range ml.feeds { - go mlh.singleFeedRequest(ctx, ch, i, ml) - } - } else if ml.feedParamKey == FeedIDs && ml.timeParamKey == Timestamp { - // only v0.3 - resultLen = 1 - isMercuryV03 = true - ch = make(chan MercuryData, resultLen) - go mlh.multiFeedsRequest(ctx, ch, ml) - } else { - return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) - } - - var reqErr error - results := make([][]byte, len(ml.feeds)) - retryable := true - allSuccess := true - // in v0.2, use the last execution error as the state, if no execution errors, state will be no error - state := NoPipelineError - for i := 0; i < resultLen; i++ { - m := <-ch - if m.Error != nil { - if reqErr == nil { - reqErr = errors.New(m.Error.Error()) - } else { - reqErr = errors.New(reqErr.Error() + m.Error.Error()) - } - retryable = retryable && m.Retryable - allSuccess = false - if m.State != NoPipelineError { - state = m.State - } - continue - } - if isMercuryV03 { - results = m.Bytes - } else { - results[m.Index] = m.Bytes[0] - } - } - // only retry when not all successful AND none are not retryable - return state, UpkeepFailureReasonNone, results, retryable && !allSuccess, reqErr -} - -// decodeStreamsLookup decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string timeParamKey, uint256 time, byte[] extraData) -// func (mlh *MercuryLookupHandler) decodeStreamsLookup(data []byte) (*StreamsLookup, error) { -// e := mlh.mercuryConfig.Abi.Errors["StreamsLookup"] -// unpack, err := e.Unpack(data) -// if err != nil { -// return nil, fmt.Errorf("unpack error: %w", err) -// } -// errorParameters := unpack.([]interface{}) - -// return &StreamsLookup{ -// feedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), -// feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), -// timeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), -// time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), -// extraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), -// }, nil -// } - -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if -// this upkeep is allowed to use Mercury service. -// func (mlh *MercuryLookupHandler) allowedToUseMercury(upkeep models.Upkeep) (bool, error) { -// allowed, ok := mlh.mercuryConfig.AllowListCache.Get(upkeep.Admin.Hex()) -// if ok { -// return allowed.(bool), nil -// } - -// if upkeep.UpkeepPrivilegeConfig == nil { -// return false, fmt.Errorf("the upkeep privilege config was not retrieved for upkeep with ID %s", upkeep.UpkeepID) -// } - -// if len(upkeep.UpkeepPrivilegeConfig) == 0 { -// return false, fmt.Errorf("the upkeep privilege config is empty") -// } - -// var a UpkeepPrivilegeConfig -// err := json.Unmarshal(upkeep.UpkeepPrivilegeConfig, &a) -// if err != nil { -// return false, fmt.Errorf("failed to unmarshal privilege config for upkeep ID %s: %v", upkeep.UpkeepID, err) -// } - -// mlh.mercuryConfig.AllowListCache.Set(upkeep.Admin.Hex(), a.MercuryEnabled, cache.DefaultExpiration) -// return a.MercuryEnabled, nil -// } - -func (mlh *MercuryLookupHandler) CheckCallback(ctx context.Context, values [][]byte, lookup *StreamsLookup, registryABI ethabi.ABI, registryAddress common.Address) (hexutil.Bytes, error) { - payload, err := registryABI.Pack("checkCallback", lookup.upkeepId, values, lookup.extraData) - if err != nil { - return nil, err - } - - var theBytes hexutil.Bytes - args := map[string]interface{}{ - "to": registryAddress.Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - err = mlh.rpcClient.CallContext(ctx, &theBytes, "eth_call", args, hexutil.EncodeUint64(lookup.block)) - if err != nil { - return nil, err - } - return theBytes, nil -} diff --git a/core/scripts/go.mod b/core/scripts/go.mod index db71535723e..895363a53f8 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -6,7 +6,6 @@ go 1.21.3 replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( - github.com/avast/retry-go v3.0.0+incompatible github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 github.com/ethereum/go-ethereum v1.12.0 @@ -18,7 +17,6 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.1 - github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 @@ -217,6 +215,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pressly/goose/v3 v3.16.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1c48074ea3a..dcf47d392cc 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -145,8 +145,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= -github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index 70db40c26bb..d64a9e7ef83 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -16,7 +16,6 @@ import ( "github.com/patrickmn/go-cache" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink-common/pkg/services" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" @@ -131,7 +130,6 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper // tried to call mercury lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) streamsLookupErr, err := s.packer.DecodeStreamsLookupRequest(checkResult.PerformData) - if err != nil { lookupLggr.Debugf("at block %d upkeep %s DecodeStreamsLookupRequest failed: %v", block, upkeepId, err) // user contract did not revert with StreamsLookup error @@ -149,7 +147,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper if streamsLookupResponse.IsMercuryV02() { // check permission on the registry for mercury v0.2 opts := s.buildCallOpts(ctx, block) - if state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId.BigInt()); err != nil { + if state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId.BigInt()); err != nil { lookupLggr.Warnf("at block %s upkeep %s failed to query mercury allow list: %s", block, upkeepId, err) checkResults[i].PipelineExecutionState = uint8(state) checkResults[i].IneligibilityReason = uint8(reason) @@ -178,65 +176,93 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, i int, checkResults []ocr2keepers.CheckResult) { defer wg.Done() - state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) - pluginRetryKey := generatePluginRetryKey(checkResults[i].WorkID, lookup.Block) + values, err := s.DoMercuryRequest(ctx, lookup, checkResults, i) + if err != nil { + s.lggr.Errorf("at block %d upkeep %s requested time %s DoMercuryRequest err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) + } - if lookup.IsMercuryV02() { - state, reason, values, retryable, retryInterval, err = s.v02Client.DoRequest(ctx, lookup, pluginRetryKey) - } else if lookup.IsMercuryV03() { - state, reason, values, retryable, retryInterval, err = s.v03Client.DoRequest(ctx, lookup, pluginRetryKey) + if err := s.CheckCallback(ctx, values, lookup, checkResults, i); err != nil { + s.lggr.Errorf("at block %d upkeep %s requested time %s CheckCallback err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) } +} +func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup, checkResults []ocr2keepers.CheckResult, i int) error { + payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s retryable %v retryInterval %s doMercuryRequest: %s", lookup.Block, lookup.UpkeepId, lookup.Time, retryable, retryInterval, err.Error()) - checkResults[i].Retryable = retryable - checkResults[i].RetryInterval = retryInterval - checkResults[i].PipelineExecutionState = uint8(state) - checkResults[i].IneligibilityReason = uint8(reason) - return + checkResults[i].Retryable = false + checkResults[i].PipelineExecutionState = uint8(mercury.PackUnpackDecodeFailed) + return err } - for j, v := range values { - s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) + var mercuryBytes hexutil.Bytes + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), } - state, retryable, mercuryBytes, err := s.checkCallback(ctx, values, lookup) - if err != nil { - s.lggr.Errorf("at block %d upkeep %s checkCallback err: %s", lookup.Block, lookup.UpkeepId, err.Error()) - checkResults[i].Retryable = retryable - checkResults[i].PipelineExecutionState = uint8(state) - return + // call checkCallback function at the block which OCR3 has agreed upon + if err = s.client.CallContext(ctx, &mercuryBytes, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { + checkResults[i].Retryable = true + checkResults[i].PipelineExecutionState = uint8(mercury.RpcFlakyFailure) + return err } + s.lggr.Infof("at block %d upkeep %s requested time %s checkCallback mercuryBytes: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(mercuryBytes)) unpackCallBackState, needed, performData, failureReason, _, err := s.packer.UnpackCheckCallbackResult(mercuryBytes) if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s UnpackCheckCallbackResult err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) checkResults[i].PipelineExecutionState = unpackCallBackState - return + return err } if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) s.lggr.Debugf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) - return + return nil + } if !needed { checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) s.lggr.Debugf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) - return + return nil } checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) checkResults[i].Eligible = true checkResults[i].PerformData = performData s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) + + return nil } -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if +func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsLookup, checkResults []ocr2keepers.CheckResult, i int) ([][]byte, error) { + state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) + pluginRetryKey := generatePluginRetryKey(checkResults[i].WorkID, lookup.Block) + + if lookup.IsMercuryV02() { + state, reason, values, retryable, retryInterval, err = s.v02Client.DoRequest(ctx, lookup, pluginRetryKey) + } else if lookup.IsMercuryV03() { + state, reason, values, retryable, retryInterval, err = s.v03Client.DoRequest(ctx, lookup, pluginRetryKey) + } + + if err != nil { + checkResults[i].Retryable = retryable + checkResults[i].RetryInterval = retryInterval + checkResults[i].PipelineExecutionState = uint8(state) + checkResults[i].IneligibilityReason = uint8(reason) + return nil, err + } + + for j, v := range values { + s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) + } + return values, nil +} + +// AllowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if // this upkeep is allowed to use Mercury service. -func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { +func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { allowed, ok := s.mercuryConfig.IsUpkeepAllowed(upkeepId.String()) if ok { return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, allowed.(bool), nil @@ -256,7 +282,6 @@ func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s "data": hexutil.Bytes(payload), } - // call checkCallback function at the block which OCR3 has agreed upon if err = s.client.CallContext(opts.Context, &resultBytes, "eth_call", args, hexutil.EncodeBig(opts.BlockNumber)); err != nil { return mercury.RpcFlakyFailure, mercury.MercuryUpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } @@ -282,26 +307,6 @@ func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil } -func (s *streams) checkCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup) (mercury.MercuryUpkeepState, bool, hexutil.Bytes, error) { - payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) - if err != nil { - return mercury.PackUnpackDecodeFailed, false, nil, err - } - - var b hexutil.Bytes - args := map[string]interface{}{ - "to": s.registry.Address().Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - if err := s.client.CallContext(ctx, &b, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { - return mercury.RpcFlakyFailure, true, nil, err - } - - return mercury.NoPipelineError, false, b, nil -} - func (s *streams) buildCallOpts(ctx context.Context, block *big.Int) *bind.CallOpts { opts := bind.CallOpts{ Context: ctx, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 32041d0f18b..caa3efb974b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -127,6 +127,7 @@ func TestStreams_CheckCallback(t *testing.T) { tests := []struct { name string lookup *mercury.StreamsLookup + input []ocr2keepers.CheckResult values [][]byte statusCode int @@ -154,6 +155,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -186,6 +190,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -217,6 +224,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{}, @@ -256,10 +266,10 @@ func TestStreams_CheckCallback(t *testing.T) { }).Once() s.client = client - state, retryable, _, err := s.checkCallback(testutils.Context(t), tt.values, tt.lookup) - tt.wantErr(t, err, fmt.Sprintf("Error asserion failed: %v", tt.name)) - assert.Equal(t, tt.state, state) - assert.Equal(t, tt.retryable, retryable) + err = s.CheckCallback(testutils.Context(t), tt.values, tt.lookup, tt.input, 0) + tt.wantErr(t, err, fmt.Sprintf("Error assertion failed: %v", tt.name)) + assert.Equal(t, uint8(tt.state), tt.input[0].PipelineExecutionState) + assert.Equal(t, tt.retryable, tt.input[0].Retryable) }) } } @@ -435,7 +445,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { BlockNumber: big.NewInt(10), } - state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId) + state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId) assert.Equal(t, tt.err, err) assert.Equal(t, tt.allowed, allowed) assert.Equal(t, tt.state, state) From ee2996fa748812dc406fd6147d54f2c1e53a4ade Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 13 Dec 2023 16:26:36 -0500 Subject: [PATCH 053/234] Adds MAKE Command for Building Plugin Image (#11567) --- integration-tests/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration-tests/Makefile b/integration-tests/Makefile index fb4bfa74f3e..86a266a996a 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -192,6 +192,12 @@ test_reorg_automation: ## Run the automation reorg tests build_docker_image: docker build -f ../core/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t $(image):$(tag) ../ +# image: the name for the chainlink image being built, example: image=chainlink +# tag: the tag for the chainlink image being built, example: tag=latest +# example usage: make build_docker_image image=chainlink tag=latest +.PHONY: build_plugin_docker_image +build_docker_image: + docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ # image: the name for the chainlink image being built, example: image=chainlink # tag: the tag for the chainlink image being built, example: tag=latest From 8b2c48dbcdd31ed5defff930f66b8cc7a49b9817 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 14:42:33 -0800 Subject: [PATCH 054/234] Mercury Packer (#11521) * Refactor encoding.Packer to have Mercury control its own interface and function implementations * rebase and update --- core/scripts/chaincli/handler/debug.go | 37 ++-- .../evmregistry/v21/encoding/interface.go | 7 - .../evmregistry/v21/encoding/packer.go | 48 ----- .../evmregistry/v21/encoding/packer_test.go | 186 ----------------- .../evmregistry/v21/mercury/mercury.go | 75 ++++++- .../evmregistry/v21/mercury/mercury_test.go | 193 ++++++++++++++++++ .../v21/mercury/streams/streams.go | 6 +- .../v21/mercury/streams/streams_test.go | 2 - .../ocr2keeper/evmregistry/v21/registry.go | 3 +- 9 files changed, 290 insertions(+), 267 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 815a0c9a030..3e8bd6d597e 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -23,7 +23,6 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" @@ -33,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" @@ -44,6 +44,7 @@ const ( expectedTypeAndVersion = "KeeperRegistry 2.1.0" ) +var mercuryPacker = mercury.NewAbiPacker() var packer = encoding.NewAbiPacker() var links []string @@ -258,10 +259,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) lggr, _ := logger.NewLogger() blockSub := &blockSubscriber{k.client} - streams := streams.NewStreamsLookup(packer, mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) + streams := streams.NewStreamsLookup(mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) var streamsLookupErr *mercury.StreamsLookupError - streamsLookupErr, err = packer.DecodeStreamsLookupRequest(checkResult.PerformData) + streamsLookupErr, err = mercuryPacker.DecodeStreamsLookupRequest(checkResult.PerformData) if err == nil { message("upkeep reverted with StreamsLookup") message(fmt.Sprintf("StreamsLookup data: {FeedParamKey: %s, Feeds: %v, TimeParamKey: %s, Time: %d, ExtraData: %s}", streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time.Uint64(), hexutil.Encode(streamsLookupErr.ExtraData))) @@ -282,7 +283,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { message("using mercury lookup v0.2") // check if upkeep is allowed to use mercury v0.2 var allowed bool - _, _, _, allowed, err = streams.AllowedToUseMercury(latestCallOpts, upkeepID) + _, _, _, allowed, err = streams.AllowedToUseMercury(triggerCallOpts, upkeepID) if err != nil { failUnknown("failed to check if upkeep is allowed to use mercury", err) } @@ -307,10 +308,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var values [][]byte values, err = streams.DoMercuryRequest(ctx, streamsLookup, checkResults, 0) - if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { + if checkResults[0].IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { resolveIneligible("upkeep used invalid revert data") } - if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { + if checkResults[0].PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { resolveIneligible("the mercury request data is invalid") } if err != nil { @@ -322,10 +323,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to execute mercury callback ", err) } - if automationCheckResult.IneligibilityReason != 0 { - message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", automationCheckResult.IneligibilityReason)) + if checkResults[0].IneligibilityReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResults[0].IneligibilityReason)) } - upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData + upkeepNeeded, performData = checkResults[0].Eligible, checkResults[0].PerformData // do tenderly simulations for checkCallback var rawCall []byte rawCall, err = core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) @@ -345,13 +346,23 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if !upkeepNeeded { resolveIneligible("upkeep is not needed") } - // simulate perform ukeep - simulateResult, err := keeperRegistry21.SimulatePerformUpkeep(latestCallOpts, upkeepID, performData) + // simulate perform upkeep + simulateResult, err := keeperRegistry21.SimulatePerformUpkeep(triggerCallOpts, upkeepID, performData) if err != nil { failUnknown("failed to simulate perform upkeep: ", err) } + + // do tenderly simulation + rawCall, err := core.RegistryABI.Pack("simulatePerformUpkeep", upkeepID, performData) + if err != nil { + failUnknown("failed to pack raw simulatePerformUpkeep call", err) + } + addLink("simulatePerformUpkeep simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) + if simulateResult.Success { resolveEligible() + } else { + resolveIneligible("simulate perform upkeep unsuccessful") } } @@ -466,11 +477,11 @@ func warning(msg string) { } func resolveIneligible(msg string) { - exit(fmt.Sprintf("✅ %s: this upkeep is not currently eligible", msg), nil, 0) + exit(fmt.Sprintf("❌ this upkeep is not eligible: %s", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently eligible", nil, 0) + exit("✅ this upkeep is eligible", nil, 0) } func rerun(msg string, err error) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go index 06a3e7b106b..ed3218ea405 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -1,13 +1,10 @@ package encoding import ( - "math/big" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) const ( @@ -51,12 +48,8 @@ type UpkeepInfo = iregistry21.KeeperRegistryBase21UpkeepInfo type Packer interface { UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw string) (ocr2keepers.CheckResult, error) - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) UnpackPerformResult(raw string) (uint8, bool, error) UnpackLogTriggerConfig(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) PackReport(report automation_utils_2_1.KeeperRegistryBase21Report) ([]byte, error) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistryBase21Report, error) - PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) - UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) - DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go index 57013a6277a..4a92b4a17ec 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go @@ -11,7 +11,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) // triggerWrapper is a wrapper for the different trigger types (log and condition triggers). @@ -67,35 +66,6 @@ func (p *abiPacker) UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw str return result, nil } -func (p *abiPacker) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { - return p.registryABI.Pack("getUpkeepPrivilegeConfig", upkeepId) -} - -func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) { - out, err := p.registryABI.Methods["getUpkeepPrivilegeConfig"].Outputs.UnpackValues(resp) - if err != nil { - return nil, fmt.Errorf("%w: unpack getUpkeepPrivilegeConfig return", err) - } - - bts := *abi.ConvertType(out[0], new([]byte)).(*[]byte) - - return bts, nil -} - -func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { - out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) - if err != nil { - return PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) - } - - upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) - rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) - failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) - gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) - - return NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil -} - func (p *abiPacker) UnpackPerformResult(raw string) (uint8, bool, error) { b, err := hexutil.Decode(raw) if err != nil { @@ -163,24 +133,6 @@ func (p *abiPacker) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistr return report, nil } -// DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) -func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) { - e := p.streamsABI.Errors["StreamsLookup"] - unpack, err := e.Unpack(data) - if err != nil { - return nil, fmt.Errorf("unpack error: %w", err) - } - errorParameters := unpack.([]interface{}) - - return &mercury.StreamsLookupError{ - FeedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), - Feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), - TimeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), - Time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), - ExtraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), - }, nil -} - // GetIneligibleCheckResultWithoutPerformData returns an ineligible check result with ineligibility reason and pipeline execution state but without perform data func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason uint8, state uint8, retryable bool) ocr2keepers.CheckResult { return ocr2keepers.CheckResult{ diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go index 42fcd40d618..71c2755f150 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go @@ -1,8 +1,6 @@ package encoding import ( - "encoding/json" - "errors" "fmt" "math/big" "testing" @@ -17,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" automation21Utils "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) func TestPacker_PackReport(t *testing.T) { @@ -231,62 +228,6 @@ func TestPacker_UnpackPerformResult(t *testing.T) { } } -func TestPacker_UnpackCheckCallbackResult(t *testing.T) { - tests := []struct { - Name string - CallbackResp []byte - UpkeepNeeded bool - PerformData []byte - FailureReason uint8 - GasUsed *big.Int - ErrorString string - State uint8 - }{ - { - Name: "unpack upkeep needed", - CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: true, - PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(UpkeepFailureReasonNone), - GasUsed: big.NewInt(11796), - }, - { - Name: "unpack upkeep not needed", - CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: false, - PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(UpkeepFailureReasonUpkeepNotNeeded), - GasUsed: big.NewInt(13008), - }, - { - Name: "unpack malformed data", - CallbackResp: []byte{0, 0, 0, 23, 4, 163, 66, 91, 228, 102, 200, 84, 144, 233, 218, 44, 168, 192, 191, 253, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: false, - PerformData: nil, - ErrorString: "abi: improperly encoded boolean value: unpack checkUpkeep return: ", - State: PackUnpackDecodeFailed, - }, - } - for _, test := range tests { - t.Run(test.Name, func(t *testing.T) { - packer := NewAbiPacker() - - state, needed, pd, failureReason, gasUsed, err := packer.UnpackCheckCallbackResult(test.CallbackResp) - - if test.ErrorString != "" { - assert.EqualError(t, err, test.ErrorString+hexutil.Encode(test.CallbackResp)) - } else { - assert.Nil(t, err) - } - assert.Equal(t, test.UpkeepNeeded, needed) - assert.Equal(t, test.PerformData, pd) - assert.Equal(t, test.FailureReason, failureReason) - assert.Equal(t, test.GasUsed, gasUsed) - assert.Equal(t, test.State, state) - }) - } -} - func TestPacker_UnpackLogTriggerConfig(t *testing.T) { tests := []struct { name string @@ -349,130 +290,3 @@ func TestPacker_PackReport_UnpackReport(t *testing.T) { expected := "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000" assert.Equal(t, hexutil.Encode(res), expected) } - -func TestPacker_PackGetUpkeepPrivilegeConfig(t *testing.T) { - tests := []struct { - name string - upkeepId *big.Int - raw []byte - errored bool - }{ - { - name: "happy path", - upkeepId: func() *big.Int { - id, _ := new(big.Int).SetString("52236098515066839510538748191966098678939830769967377496848891145101407612976", 10) - - return id - }(), - raw: func() []byte { - b, _ := hexutil.Decode("0x19d97a94737c9583000000000000000000000001ea8ed6d0617dd5b3b87374020efaf030") - - return b - }(), - errored: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - packer := NewAbiPacker() - - b, err := packer.PackGetUpkeepPrivilegeConfig(test.upkeepId) - - if !test.errored { - require.NoError(t, err, "no error expected from packing") - - assert.Equal(t, test.raw, b, "raw bytes for output should match expected") - } else { - assert.NotNil(t, err, "error expected from packing function") - } - }) - } -} - -func TestPacker_UnpackGetUpkeepPrivilegeConfig(t *testing.T) { - tests := []struct { - name string - raw []byte - errored bool - }{ - { - name: "happy path", - raw: func() []byte { - b, _ := hexutil.Decode("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000177b226d657263757279456e61626c6564223a747275657d000000000000000000") - - return b - }(), - errored: false, - }, - { - name: "error empty config", - raw: func() []byte { - b, _ := hexutil.Decode("0x") - - return b - }(), - errored: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - packer := NewAbiPacker() - - b, err := packer.UnpackGetUpkeepPrivilegeConfig(test.raw) - - if !test.errored { - require.NoError(t, err, "should unpack bytes from abi encoded value") - - // the actual struct to unmarshal into is not available to this - // package so basic json encoding is the limit of the following test - var data map[string]interface{} - err = json.Unmarshal(b, &data) - - assert.NoError(t, err, "packed data should unmarshal using json encoding") - assert.Equal(t, []byte(`{"mercuryEnabled":true}`), b) - } else { - assert.NotNil(t, err, "error expected from unpack function") - } - }) - } -} - -func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { - tests := []struct { - name string - data []byte - expected *mercury.StreamsLookupError - state uint8 - err error - }{ - { - name: "success - decode to streams lookup", - data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - expected: &mercury.StreamsLookupError{ - FeedParamKey: "feedIdHex", - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: "blockNumber", - Time: big.NewInt(37969589), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - }, - { - name: "failure - unpack error", - data: []byte{1, 2, 3, 4}, - err: errors.New("unpack error: invalid data for unpacking"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - packer := NewAbiPacker() - fl, err := packer.DecodeStreamsLookupRequest(tt.data) - assert.Equal(t, tt.expected, fl) - if tt.err != nil { - assert.Equal(t, tt.err.Error(), err.Error()) - } - }) - } -} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go index f9a3c001c66..10e77bf50ce 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go @@ -10,9 +10,13 @@ import ( "net/http" "time" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) const ( @@ -68,13 +72,6 @@ type MercuryData struct { State MercuryUpkeepState } -type Packer interface { - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) - PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) - UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) - DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) -} - type MercuryConfigProvider interface { Credentials() *models.MercuryCredentials IsUpkeepAllowed(string) (interface{}, bool) @@ -113,6 +110,70 @@ func (l *StreamsLookup) IsMercuryV03() bool { return l.FeedParamKey == FeedIDs } +// IsMercuryV03UsingBlockNumber is used to distinguish the batch path. It is used for Mercury V03 only func (l *StreamsLookup) IsMercuryV03UsingBlockNumber() bool { return l.TimeParamKey == BlockNumber } + +type Packer interface { + UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) + PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) + UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) + DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) +} + +type abiPacker struct { + registryABI abi.ABI + streamsABI abi.ABI +} + +func NewAbiPacker() *abiPacker { + return &abiPacker{registryABI: core.RegistryABI, streamsABI: core.StreamsCompatibleABI} +} + +// DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) +func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) { + e := p.streamsABI.Errors["StreamsLookup"] + unpack, err := e.Unpack(data) + if err != nil { + return nil, fmt.Errorf("unpack error: %w", err) + } + errorParameters := unpack.([]interface{}) + + return &StreamsLookupError{ + FeedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), + Feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), + TimeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), + Time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), + ExtraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), + }, nil +} + +func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { + out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) + if err != nil { + return encoding.PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) + } + + upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) + rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) + failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) + gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return encoding.NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil +} + +func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) { + out, err := p.registryABI.Methods["getUpkeepPrivilegeConfig"].Outputs.UnpackValues(resp) + if err != nil { + return nil, fmt.Errorf("%w: unpack getUpkeepPrivilegeConfig return", err) + } + + bts := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return bts, nil +} + +func (p *abiPacker) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return p.registryABI.Pack("getUpkeepPrivilegeConfig", upkeepId) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go index baa939dbecc..6095e7b9466 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go @@ -1,7 +1,17 @@ package mercury import ( + "encoding/json" + "errors" + "math/big" "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) func TestGenerateHMACFn(t *testing.T) { @@ -47,3 +57,186 @@ func TestGenerateHMACFn(t *testing.T) { }) } } + +func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { + tests := []struct { + name string + data []byte + expected *StreamsLookupError + state uint8 + err error + }{ + { + name: "success - decode to streams lookup", + data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + expected: &StreamsLookupError{ + FeedParamKey: "feedIdHex", + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: "blockNumber", + Time: big.NewInt(37969589), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + }, + { + name: "failure - unpack error", + data: []byte{1, 2, 3, 4}, + err: errors.New("unpack error: invalid data for unpacking"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packer := NewAbiPacker() + fl, err := packer.DecodeStreamsLookupRequest(tt.data) + assert.Equal(t, tt.expected, fl) + if tt.err != nil { + assert.Equal(t, tt.err.Error(), err.Error()) + } + }) + } +} + +func TestPacker_UnpackGetUpkeepPrivilegeConfig(t *testing.T) { + tests := []struct { + name string + raw []byte + errored bool + }{ + { + name: "happy path", + raw: func() []byte { + b, _ := hexutil.Decode("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000177b226d657263757279456e61626c6564223a747275657d000000000000000000") + + return b + }(), + errored: false, + }, + { + name: "error empty config", + raw: func() []byte { + b, _ := hexutil.Decode("0x") + + return b + }(), + errored: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + packer := NewAbiPacker() + + b, err := packer.UnpackGetUpkeepPrivilegeConfig(test.raw) + + if !test.errored { + require.NoError(t, err, "should unpack bytes from abi encoded value") + + // the actual struct to unmarshal into is not available to this + // package so basic json encoding is the limit of the following test + var data map[string]interface{} + err = json.Unmarshal(b, &data) + + assert.NoError(t, err, "packed data should unmarshal using json encoding") + assert.Equal(t, []byte(`{"mercuryEnabled":true}`), b) + } else { + assert.NotNil(t, err, "error expected from unpack function") + } + }) + } +} + +func TestPacker_PackGetUpkeepPrivilegeConfig(t *testing.T) { + tests := []struct { + name string + upkeepId *big.Int + raw []byte + errored bool + }{ + { + name: "happy path", + upkeepId: func() *big.Int { + id, _ := new(big.Int).SetString("52236098515066839510538748191966098678939830769967377496848891145101407612976", 10) + + return id + }(), + raw: func() []byte { + b, _ := hexutil.Decode("0x19d97a94737c9583000000000000000000000001ea8ed6d0617dd5b3b87374020efaf030") + + return b + }(), + errored: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + packer := NewAbiPacker() + + b, err := packer.PackGetUpkeepPrivilegeConfig(test.upkeepId) + + if !test.errored { + require.NoError(t, err, "no error expected from packing") + + assert.Equal(t, test.raw, b, "raw bytes for output should match expected") + } else { + assert.NotNil(t, err, "error expected from packing function") + } + }) + } +} + +func TestPacker_UnpackCheckCallbackResult(t *testing.T) { + tests := []struct { + Name string + CallbackResp []byte + UpkeepNeeded bool + PerformData []byte + FailureReason uint8 + GasUsed *big.Int + ErrorString string + State uint8 + }{ + { + Name: "unpack upkeep needed", + CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: true, + PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + FailureReason: uint8(encoding.UpkeepFailureReasonNone), + GasUsed: big.NewInt(11796), + }, + { + Name: "unpack upkeep not needed", + CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: false, + PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + FailureReason: uint8(encoding.UpkeepFailureReasonUpkeepNotNeeded), + GasUsed: big.NewInt(13008), + }, + { + Name: "unpack malformed data", + CallbackResp: []byte{0, 0, 0, 23, 4, 163, 66, 91, 228, 102, 200, 84, 144, 233, 218, 44, 168, 192, 191, 253, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: false, + PerformData: nil, + ErrorString: "abi: improperly encoded boolean value: unpack checkUpkeep return: ", + State: encoding.PackUnpackDecodeFailed, + }, + } + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + packer := NewAbiPacker() + + state, needed, pd, failureReason, gasUsed, err := packer.UnpackCheckCallbackResult(test.CallbackResp) + + if test.ErrorString != "" { + assert.EqualError(t, err, test.ErrorString+hexutil.Encode(test.CallbackResp)) + } else { + assert.Nil(t, err) + } + assert.Equal(t, test.UpkeepNeeded, needed) + assert.Equal(t, test.PerformData, pd) + assert.Equal(t, test.FailureReason, failureReason) + assert.Equal(t, test.GasUsed, gasUsed) + assert.Equal(t, test.State, state) + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index d64a9e7ef83..e1bb69f33d8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -66,7 +66,6 @@ type UpkeepPrivilegeConfig struct { } func NewStreamsLookup( - packer mercury.Packer, mercuryConfig mercury.MercuryConfigProvider, blockSubscriber latestBlockProvider, client contextCaller, @@ -74,6 +73,8 @@ func NewStreamsLookup( lggr logger.Logger) *streams { httpClient := http.DefaultClient threadCtrl := utils.NewThreadControl() + packer := mercury.NewAbiPacker() + return &streams{ packer: packer, mercuryConfig: mercuryConfig, @@ -231,7 +232,7 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) checkResults[i].Eligible = true checkResults[i].PerformData = performData - s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) + s.lggr.Infof("at block %d upkeep %s requested time %s CheckCallback successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) return nil } @@ -288,6 +289,7 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s var upkeepPrivilegeConfigBytes []byte upkeepPrivilegeConfigBytes, err = s.packer.UnpackGetUpkeepPrivilegeConfig(resultBytes) + if err != nil { return mercury.PackUnpackDecodeFailed, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index caa3efb974b..54124b7c1a3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -102,14 +102,12 @@ func (r *mockRegistry) CheckCallback(opts *bind.CallOpts, id *big.Int, values [] // setups up a streams object for tests. func setupStreams(t *testing.T) *streams { lggr := logger.TestLogger(t) - packer := encoding.NewAbiPacker() mercuryConfig := new(MockMercuryConfigProvider) blockSubscriber := new(MockBlockSubscriber) registry := &mockRegistry{} client := evmClientMocks.NewClient(t) streams := NewStreamsLookup( - packer, mercuryConfig, blockSubscriber, client, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go index bb05b8029fe..39862bb7539 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go @@ -94,7 +94,6 @@ func NewEvmRegistry( AllowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), } - hc := http.DefaultClient return &EvmRegistry{ @@ -115,7 +114,7 @@ func NewEvmRegistry( logEventProvider: logEventProvider, bs: blockSub, finalityDepth: finalityDepth, - streams: streams.NewStreamsLookup(packer, mercuryConfig, blockSub, client.Client(), registry, lggr), + streams: streams.NewStreamsLookup(mercuryConfig, blockSub, client.Client(), registry, lggr), } } From c274c23eabc7fade1b1a5e7649187c56f218cc77 Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:45:38 +0000 Subject: [PATCH 055/234] Introduce job spec flag for custom reverted pipeline (#11529) * Introduce job spec flag for custom reverted pipeline * Disable the flag for V2+ * Rename file after merge conflict --- core/services/job/job_orm_test.go | 21 +++-- core/services/job/models.go | 3 + core/services/job/orm.go | 4 +- core/services/job/validate.go | 1 + core/services/vrf/delegate.go | 3 + .../0214_add_custom_reverts_vrf.sql | 5 ++ core/testdata/testspecs/v2_specs.go | 3 + core/web/presenters/job.go | 38 ++++---- core/web/presenters/job_test.go | 87 +++++++++++++++++++ core/web/resolver/spec.go | 5 ++ core/web/resolver/spec_test.go | 3 + core/web/schema/type/spec.graphql | 1 + 12 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index c0622ba8066..768d16ddeb7 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -454,6 +454,9 @@ func TestORM_CreateJob_VRFV2(t *testing.T) { var batchFulfillmentEnabled bool require.NoError(t, db.Get(&batchFulfillmentEnabled, `SELECT batch_fulfillment_enabled FROM vrf_specs LIMIT 1`)) require.False(t, batchFulfillmentEnabled) + var customRevertsPipelineEnabled bool + require.NoError(t, db.Get(&customRevertsPipelineEnabled, `SELECT custom_reverts_pipeline_enabled FROM vrf_specs LIMIT 1`)) + require.False(t, customRevertsPipelineEnabled) var batchFulfillmentGasMultiplier float64 require.NoError(t, db.Get(&batchFulfillmentGasMultiplier, `SELECT batch_fulfillment_gas_multiplier FROM vrf_specs LIMIT 1`)) require.Equal(t, float64(1.0), batchFulfillmentGasMultiplier) @@ -514,13 +517,14 @@ func TestORM_CreateJob_VRFV2Plus(t *testing.T) { fromAddresses := []string{cltest.NewEIP55Address().String(), cltest.NewEIP55Address().String()} jb, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec( testspecs.VRFSpecParams{ - VRFVersion: vrfcommon.V2Plus, - RequestedConfsDelay: 10, - FromAddresses: fromAddresses, - ChunkSize: 25, - BackoffInitialDelay: time.Minute, - BackoffMaxDelay: time.Hour, - GasLanePrice: assets.GWei(100), + VRFVersion: vrfcommon.V2Plus, + RequestedConfsDelay: 10, + FromAddresses: fromAddresses, + ChunkSize: 25, + BackoffInitialDelay: time.Minute, + BackoffMaxDelay: time.Hour, + GasLanePrice: assets.GWei(100), + CustomRevertsPipelineEnabled: true, }). Toml()) require.NoError(t, err) @@ -534,6 +538,9 @@ func TestORM_CreateJob_VRFV2Plus(t *testing.T) { var batchFulfillmentEnabled bool require.NoError(t, db.Get(&batchFulfillmentEnabled, `SELECT batch_fulfillment_enabled FROM vrf_specs LIMIT 1`)) require.False(t, batchFulfillmentEnabled) + var customRevertsPipelineEnabled bool + require.NoError(t, db.Get(&customRevertsPipelineEnabled, `SELECT custom_reverts_pipeline_enabled FROM vrf_specs LIMIT 1`)) + require.True(t, customRevertsPipelineEnabled) var batchFulfillmentGasMultiplier float64 require.NoError(t, db.Get(&batchFulfillmentGasMultiplier, `SELECT batch_fulfillment_gas_multiplier FROM vrf_specs LIMIT 1`)) require.Equal(t, float64(1.0), batchFulfillmentGasMultiplier) diff --git a/core/services/job/models.go b/core/services/job/models.go index 3c869f76056..f60e0a12981 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -503,6 +503,9 @@ type VRFSpec struct { // for fulfilling requests. If set to true, batchCoordinatorAddress must be set in // the job spec. BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` + // CustomRevertsPipelineEnabled indicates to the vrf job to run the + // custom reverted txns pipeline along with VRF listener + CustomRevertsPipelineEnabled bool `toml:"customRevertsPipelineEnabled"` // BatchFulfillmentGasMultiplier is used to determine the final gas estimate for the batch // fulfillment. BatchFulfillmentGasMultiplier tomlutils.Float64 `toml:"batchFulfillmentGasMultiplier"` diff --git a/core/services/job/orm.go b/core/services/job/orm.go index b2cf2b2af4b..6c5a879ebd0 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -326,14 +326,14 @@ func (o *orm) CreateJob(jb *Job, qopts ...pg.QOpt) error { evm_chain_id, from_addresses, poll_period, requested_confs_delay, request_timeout, chunk_size, batch_coordinator_address, batch_fulfillment_enabled, batch_fulfillment_gas_multiplier, backoff_initial_delay, backoff_max_delay, gas_lane_price, - vrf_owner_address, + vrf_owner_address, custom_reverts_pipeline_enabled, created_at, updated_at) VALUES ( :coordinator_address, :public_key, :min_incoming_confirmations, :evm_chain_id, :from_addresses, :poll_period, :requested_confs_delay, :request_timeout, :chunk_size, :batch_coordinator_address, :batch_fulfillment_enabled, :batch_fulfillment_gas_multiplier, :backoff_initial_delay, :backoff_max_delay, :gas_lane_price, - :vrf_owner_address, + :vrf_owner_address, :custom_reverts_pipeline_enabled, NOW(), NOW()) RETURNING id;` diff --git a/core/services/job/validate.go b/core/services/job/validate.go index 8f559fdb02d..b7a1dca3616 100644 --- a/core/services/job/validate.go +++ b/core/services/job/validate.go @@ -63,6 +63,7 @@ func ValidateSpec(ts string) (Type, error) { if jb.Pipeline.RequiresPreInsert() && !jb.Type.SupportsAsync() { return "", errors.Errorf("async=true tasks are not supported for %v", jb.Type) } + // spec.CustomRevertsPipelineEnabled == false, default is custom reverted txns pipeline disabled if strings.Contains(ts, "<{}>") { return "", errors.Errorf("'<{}>' syntax is not supported. Please use \"{}\" instead") diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index 03e40614a10..ba28e83bf3f 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -142,6 +142,9 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { if vrfOwner != nil { return nil, errors.New("VRF Owner is not supported for VRF V2 Plus") } + if jb.VRFSpec.CustomRevertsPipelineEnabled { + return nil, errors.New("Custom Reverted Txns Pipeline is not supported for VRF V2 Plus") + } // Get the LINKNATIVEFEED address with retries // This is needed because the RPC endpoint may be down so we need to diff --git a/core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql b/core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql new file mode 100644 index 00000000000..a2865fce816 --- /dev/null +++ b/core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql @@ -0,0 +1,5 @@ +-- +goose Up +ALTER TABLE vrf_specs ADD COLUMN custom_reverts_pipeline_enabled boolean DEFAULT FALSE NOT NULL; + +-- +goose Down +ALTER TABLE vrf_specs DROP COLUMN custom_reverts_pipeline_enabled; diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 00297ebdb12..e9dd25fa0f7 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -234,6 +234,7 @@ type VRFSpecParams struct { BatchCoordinatorAddress string VRFOwnerAddress string BatchFulfillmentEnabled bool + CustomRevertsPipelineEnabled bool BatchFulfillmentGasMultiplier float64 MinIncomingConfirmations int FromAddresses []string @@ -403,6 +404,7 @@ evmChainID = "%s" batchCoordinatorAddress = "%s" batchFulfillmentEnabled = %v batchFulfillmentGasMultiplier = %s +customRevertsPipelineEnabled = %v minIncomingConfirmations = %d requestedConfsDelay = %d requestTimeout = "%s" @@ -419,6 +421,7 @@ observationSource = """ toml := fmt.Sprintf(template, jobID, name, coordinatorAddress, params.EVMChainID, batchCoordinatorAddress, params.BatchFulfillmentEnabled, strconv.FormatFloat(batchFulfillmentGasMultiplier, 'f', 2, 64), + params.CustomRevertsPipelineEnabled, confirmations, params.RequestedConfsDelay, requestTimeout.String(), publicKey, chunkSize, params.BackoffInitialDelay.String(), params.BackoffMaxDelay.String(), gasLanePrice.String(), pollPeriod.String(), observationSource) diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index a2a9e70c793..e9b63c73615 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -267,6 +267,7 @@ func NewCronSpec(spec *job.CronSpec) *CronSpec { type VRFSpec struct { BatchCoordinatorAddress *ethkey.EIP55Address `json:"batchCoordinatorAddress"` BatchFulfillmentEnabled bool `json:"batchFulfillmentEnabled"` + CustomRevertsPipelineEnabled *bool `json:"customRevertsPipelineEnabled,omitempty"` BatchFulfillmentGasMultiplier float64 `json:"batchFulfillmentGasMultiplier"` CoordinatorAddress ethkey.EIP55Address `json:"coordinatorAddress"` PublicKey secp256k1.PublicKey `json:"publicKey"` @@ -281,26 +282,31 @@ type VRFSpec struct { BackoffInitialDelay models.Duration `json:"backoffInitialDelay"` BackoffMaxDelay models.Duration `json:"backoffMaxDelay"` GasLanePrice *assets.Wei `json:"gasLanePrice"` - VRFOwnerAddress *ethkey.EIP55Address `json:"vrfOwnerAddress"` + RequestedConfsDelay int64 `json:"requestedConfsDelay"` + VRFOwnerAddress *ethkey.EIP55Address `json:"vrfOwnerAddress,omitempty"` } func NewVRFSpec(spec *job.VRFSpec) *VRFSpec { return &VRFSpec{ - BatchCoordinatorAddress: spec.BatchCoordinatorAddress, - BatchFulfillmentEnabled: spec.BatchFulfillmentEnabled, - CoordinatorAddress: spec.CoordinatorAddress, - PublicKey: spec.PublicKey, - FromAddresses: spec.FromAddresses, - PollPeriod: models.MustMakeDuration(spec.PollPeriod), - MinIncomingConfirmations: spec.MinIncomingConfirmations, - CreatedAt: spec.CreatedAt, - UpdatedAt: spec.UpdatedAt, - EVMChainID: spec.EVMChainID, - ChunkSize: spec.ChunkSize, - RequestTimeout: models.MustMakeDuration(spec.RequestTimeout), - BackoffInitialDelay: models.MustMakeDuration(spec.BackoffInitialDelay), - BackoffMaxDelay: models.MustMakeDuration(spec.BackoffMaxDelay), - GasLanePrice: spec.GasLanePrice, + BatchCoordinatorAddress: spec.BatchCoordinatorAddress, + BatchFulfillmentEnabled: spec.BatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: float64(spec.BatchFulfillmentGasMultiplier), + CustomRevertsPipelineEnabled: &spec.CustomRevertsPipelineEnabled, + CoordinatorAddress: spec.CoordinatorAddress, + PublicKey: spec.PublicKey, + FromAddresses: spec.FromAddresses, + PollPeriod: models.MustMakeDuration(spec.PollPeriod), + MinIncomingConfirmations: spec.MinIncomingConfirmations, + CreatedAt: spec.CreatedAt, + UpdatedAt: spec.UpdatedAt, + EVMChainID: spec.EVMChainID, + ChunkSize: spec.ChunkSize, + RequestTimeout: models.MustMakeDuration(spec.RequestTimeout), + BackoffInitialDelay: models.MustMakeDuration(spec.BackoffInitialDelay), + BackoffMaxDelay: models.MustMakeDuration(spec.BackoffMaxDelay), + GasLanePrice: spec.GasLanePrice, + RequestedConfsDelay: spec.RequestedConfsDelay, + VRFOwnerAddress: spec.VRFOwnerAddress, } } diff --git a/core/web/presenters/job_test.go b/core/web/presenters/job_test.go index a5d6db0df18..b782452948b 100644 --- a/core/web/presenters/job_test.go +++ b/core/web/presenters/job_test.go @@ -14,11 +14,13 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/assets" + evmassets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -58,6 +60,7 @@ func TestJob(t *testing.T) { trustedBlockhashStoreBatchSize := int32(20) var specGasLimit uint32 = 1000 + vrfPubKey, _ := secp256k1.NewPublicKeyFromHex("0xede539e216e3a50e69d1c68aa9cc472085876c4002f6e1e6afee0ea63b50a78b00") testCases := []struct { name string @@ -469,6 +472,90 @@ func TestJob(t *testing.T) { } }`, }, + { + name: "vrf job spec", + job: job.Job{ + ID: 1, + Name: null.StringFrom("vrf_test"), + Type: job.VRF, + SchemaVersion: 1, + ExternalJobID: uuid.MustParse("0eec7e1d-d0d2-476c-a1a8-72dfb6633f47"), + VRFSpec: &job.VRFSpec{ + BatchCoordinatorAddress: &contractAddress, + BatchFulfillmentEnabled: true, + CustomRevertsPipelineEnabled: true, + MinIncomingConfirmations: 1, + CoordinatorAddress: contractAddress, + CreatedAt: timestamp, + UpdatedAt: timestamp, + EVMChainID: evmChainID, + FromAddresses: []ethkey.EIP55Address{fromAddress}, + PublicKey: vrfPubKey, + RequestedConfsDelay: 10, + ChunkSize: 25, + BatchFulfillmentGasMultiplier: 1, + GasLanePrice: evmassets.GWei(200), + VRFOwnerAddress: nil, + }, + PipelineSpec: &pipeline.Spec{ + ID: 1, + DotDagSource: "", + }, + }, + want: fmt.Sprintf(` + { + "data": { + "type": "jobs", + "id": "1", + "attributes": { + "name": "vrf_test", + "type": "vrf", + "schemaVersion": 1, + "maxTaskDuration": "0s", + "externalJobID": "0eec7e1d-d0d2-476c-a1a8-72dfb6633f47", + "directRequestSpec": null, + "fluxMonitorSpec": null, + "gasLimit": null, + "forwardingAllowed": false, + "cronSpec": null, + "offChainReportingOracleSpec": null, + "offChainReporting2OracleSpec": null, + "keeperSpec": null, + "vrfSpec": { + "batchCoordinatorAddress": "%s", + "batchFulfillmentEnabled": true, + "customRevertsPipelineEnabled": true, + "confirmations": 1, + "coordinatorAddress": "%s", + "createdAt": "2000-01-01T00:00:00Z", + "updatedAt": "2000-01-01T00:00:00Z", + "evmChainID": "42", + "fromAddresses": ["%s"], + "pollPeriod": "0s", + "publicKey": "%s", + "requestedConfsDelay": 10, + "requestTimeout": "0s", + "chunkSize": 25, + "batchFulfillmentGasMultiplier": 1, + "backoffInitialDelay": "0s", + "backoffMaxDelay": "0s", + "gasLanePrice": "200 gwei" + }, + "webhookSpec": null, + "blockhashStoreSpec": null, + "blockHeaderFeederSpec": null, + "bootstrapSpec": null, + "pipelineSpec": { + "id": 1, + "jobID": 0, + "dotDagSource": "" + }, + "gatewaySpec": null, + "errors": [] + } + } + }`, contractAddress, contractAddress, fromAddress, vrfPubKey.String()), + }, { name: "blockhash store spec", job: job.Job{ diff --git a/core/web/resolver/spec.go b/core/web/resolver/spec.go index 3e93ea38eab..5e8937dbb96 100644 --- a/core/web/resolver/spec.go +++ b/core/web/resolver/spec.go @@ -641,6 +641,11 @@ func (r *VRFSpecResolver) BatchFulfillmentGasMultiplier() float64 { return float64(r.spec.BatchFulfillmentGasMultiplier) } +// CustomRevertsPipelineEnabled resolves the spec's custom reverts pipeline enabled flag. +func (r *VRFSpecResolver) CustomRevertsPipelineEnabled() *bool { + return &r.spec.CustomRevertsPipelineEnabled +} + // ChunkSize resolves the spec's chunk size. func (r *VRFSpecResolver) ChunkSize() int32 { return int32(r.spec.ChunkSize) diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index 277aac851a6..a4fb9cdb338 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -581,6 +581,7 @@ func TestResolver_VRFSpec(t *testing.T) { VRFSpec: &job.VRFSpec{ BatchCoordinatorAddress: &batchCoordinatorAddress, BatchFulfillmentEnabled: true, + CustomRevertsPipelineEnabled: true, MinIncomingConfirmations: 1, CoordinatorAddress: coordinatorAddress, CreatedAt: f.Timestamp(), @@ -617,6 +618,7 @@ func TestResolver_VRFSpec(t *testing.T) { batchCoordinatorAddress batchFulfillmentEnabled batchFulfillmentGasMultiplier + customRevertsPipelineEnabled chunkSize backoffInitialDelay backoffMaxDelay @@ -644,6 +646,7 @@ func TestResolver_VRFSpec(t *testing.T) { "batchCoordinatorAddress": "0x0ad9FE7a58216242a8475ca92F222b0640E26B63", "batchFulfillmentEnabled": true, "batchFulfillmentGasMultiplier": 1, + "customRevertsPipelineEnabled": true, "chunkSize": 25, "backoffInitialDelay": "1m0s", "backoffMaxDelay": "1h0m0s", diff --git a/core/web/schema/type/spec.graphql b/core/web/schema/type/spec.graphql index 81244478637..5e24f7c3fa8 100644 --- a/core/web/schema/type/spec.graphql +++ b/core/web/schema/type/spec.graphql @@ -97,6 +97,7 @@ type VRFSpec { batchCoordinatorAddress: String batchFulfillmentEnabled: Boolean! batchFulfillmentGasMultiplier: Float! + customRevertsPipelineEnabled: Boolean chunkSize: Int! backoffInitialDelay: String! backoffMaxDelay: String! From 917b74f3a67395f4106f60f70949043f0d989bdf Mon Sep 17 00:00:00 2001 From: Anindita Ghosh <88458927+AnieeG@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:47:26 -0800 Subject: [PATCH 056/234] options to include customized pg and chainlink image (#11570) --- integration-tests/docker/test_env/cl_node.go | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index b79bb91c70e..d6ab22957a2 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" @@ -83,6 +84,28 @@ func WithLogStream(ls *logstream.LogStream) ClNodeOption { } } +func WithImage(image string) ClNodeOption { + return func(c *ClNode) { + c.ContainerImage = image + } +} + +func WithVersion(version string) ClNodeOption { + return func(c *ClNode) { + c.ContainerVersion = version + } +} + +func WithPgDBOptions(opts ...test_env.PostgresDbOption) ClNodeOption { + return func(c *ClNode) { + var err error + c.PostgresDb, err = test_env.NewPostgresDb(c.EnvComponent.Networks, opts...) + if err != nil { + c.t.Fatalf("failed to create postgres db: %v", err) + } + } +} + func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *chainlink.Config, opts ...ClNodeOption) (*ClNode, error) { nodeDefaultCName := fmt.Sprintf("%s-%s", "cl-node", uuid.NewString()[0:8]) pgDefaultCName := fmt.Sprintf("pg-%s", nodeDefaultCName) From 7a0704f436a4be1fa0d5c607b901265056c69f2d Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 17:45:54 -0800 Subject: [PATCH 057/234] add readme (#11535) --- core/scripts/chaincli/README.md | 91 ++++++++++++++++++++++++++ core/scripts/chaincli/handler/debug.go | 6 +- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/core/scripts/chaincli/README.md b/core/scripts/chaincli/README.md index da7aa7cc777..bcf407b684b 100644 --- a/core/scripts/chaincli/README.md +++ b/core/scripts/chaincli/README.md @@ -122,3 +122,94 @@ You can use the `grep` and `grepv` flags to filter log lines, e.g. to only show ```shell ./chaincli keeper logs --grep keepers-plugin ``` + +--- + +## ChainCLI Automation Debugging Script + +### Context + +The debugging script is a tool within ChainCLI designed to facilitate the debugging of upkeeps in Automation v21, covering both conditional and log-based scenarios. + +### Configuration + +#### Mandatory Fields + +Ensure the following fields are provided in your `.env` file: + +- `NODE_URL`: Archival node URL +- `KEEPER_REGISTRY_ADDRESS`: Address of the Keeper Registry contract. Refer to the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks#configurations) doc for addresses. + +#### Additional Fields (Streams Lookup) + +If your targeted upkeep involves streams lookup, include the following information: + +- `MERCURY_ID` +- `MERCURY_KEY` +- `MERCURY_LEGACY_URL` +- `MERCURY_URL` + +#### Tenderly Integration + +For detailed transaction simulation logs, set up Tenderly credentials. Refer to the [Tenderly Documentation](https://docs.tenderly.co/other/platform-access/how-to-generate-api-access-tokens) for creating an API key, account name, and project name. + +- `TENDERLY_KEY` +- `TENDERLY_ACCOUNT_NAME` +- `TENDERLY_PROJECT_NAME` + +### Usage + +Execute the following command based on your upkeep type: + +- For conditional upkeep: + + ```bash + go run main.go keeper debug UPKEEP_ID + ``` + +- For log trigger upkeep: + + ```bash + go run main.go keeper debug UPKEEP_ID TX_HASH LOG_INDEX + ``` + +### Checks Performed by the Debugging Script + +1. **Fetch and Sanity Check Upkeep:** + - Verify upkeep status: active, paused, or canceled + - Check upkeep balance + +2. **For Conditional Upkeep:** + - Check conditional upkeep + - Simulate `performUpkeep` + +3. **For Log Trigger Upkeep:** + - Check if the upkeep has already run for log-trigger-based upkeep + - Verify if log matches trigger configuration + - Check upkeep + - If check result indicates a streams lookup is required (TargetCheckReverted): + - Verify if the upkeep is allowed to use Mercury + - Execute Mercury request + - Execute check callback + + - Simulate `performUpkeep` + +### Examples +- Eligible and log trigger based and using mercury lookup v0.3: + + ```bash + go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xdc6d0e547a5aa85fefa5b0f3a37e3493eafb5aeba8b5f3071ce53c9e9a539e9c 0 + ``` + +- Ineligible and conditional upkeep: + + ```bash + go run main.go keeper debug 52635131310730056105456985154251306793887717546629785340977553840883117540096 + ``` + +- Ineligible and Log does not match trigger config: + + ```bash + go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xc0686ae85d2a7a976ef46df6c613517b9fd46f23340ac583be4e44f5c8b7a186 1 + ``` +--- \ No newline at end of file diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 3e8bd6d597e..288c7a68b80 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -111,8 +111,8 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if (upkeepInfo.Target == gethcommon.Address{}) { failCheckArgs("this upkeep does not exist on this registry", nil) } - addLink("upkeep", common.UpkeepLink(chainID, upkeepID)) - addLink("target", common.ContractExplorerLink(chainID, upkeepInfo.Target)) + addLink("upkeep link", common.UpkeepLink(chainID, upkeepID)) + addLink("upkeep contract address", common.ContractExplorerLink(chainID, upkeepInfo.Target)) if upkeepInfo.Paused { resolveIneligible("upkeep is paused") } @@ -180,7 +180,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failCheckArgs("failed to fetch tx receipt", err) } - addLink("trigger", common.ExplorerLink(chainID, txHash)) + addLink("trigger transaction", common.ExplorerLink(chainID, txHash)) blockNum = receipt.BlockNumber.Uint64() // find matching log event in tx var triggeringEvent *types.Log From f874ea608e835edd616937f852b193752cd48db8 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 14 Dec 2023 05:42:05 -0500 Subject: [PATCH 058/234] Little Docs Updates (#11569) * Little Docs Updates * Addition --- integration-tests/README.md | 6 ++++++ integration-tests/example.env | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/integration-tests/README.md b/integration-tests/README.md index c0b673fa92b..37a8fa19ed3 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -26,10 +26,16 @@ e.g. `make build_docker_image image=chainlink tag=test-tag` +You'll want to set the `CHAINLINK_IMAGE` and `CHAINLINK_VERSION` env values appropriately as well. See [example.env](./example.env) for more details. + ## Run `go test ./smoke/_test.go` +Most test files have a couple of tests, it's recommended to look into the file and focus on a specific one if possible. 90% of the time this will probably be the `Basic` test. See [ocr_test.go](./smoke/ocr_test.go) for example, which contains the `TestOCRBasic` test. + +`go test ./smoke/ocr_test.go -run TestOCRBasic` + It's generally recommended to run only one test at a time on a local machine as it needs a lot of docker containers and can peg your resources otherwise. You will see docker containers spin up on your machine for each component of the test where you can inspect logs. ## Analyze diff --git a/integration-tests/example.env b/integration-tests/example.env index 3e27ac2c7ab..bbc76bb0c09 100644 --- a/integration-tests/example.env +++ b/integration-tests/example.env @@ -30,15 +30,16 @@ export TEST_UPGRADE_VERSION="2.0.0" # Version of the Chainlink image to upgrade ########## Network Settings ########## +# If running on a live network, you must set the following values # Select a pre-defined network(s) export SELECTED_NETWORKS="SIMULATED" - # General private values that will be retrieved when running on non-simulated networks export EVM_URLS="wss://evm.url,wss://other.url" # Comma-sparated list of websocket URLs to use when running on live networks export EVM_HTTP_URLS="https://evm.url,https://other.url" # Comma-sparated list of HTTP URLs to use when running on live networks export EVM_KEYS="private,funding,keys" # Comma-separated list of private keys to use when running on live networks # Specific private values retrieved when running on specified chains +# Will override the general values above if the SELECTED_NETWORKS contains the network name # Goerli export GOERLI_URLS="wss://goerli.io/ws" export GOERLI_HTTP_URLS="http://goerli.io/ws" From 71e1a796ee3450cd1a0cb20fff8e5f1d46690972 Mon Sep 17 00:00:00 2001 From: Lei Date: Thu, 14 Dec 2023 03:12:22 -0800 Subject: [PATCH 059/234] add unit test to cover 2 check results (#11490) --- .../v21/mercury/streams/streams_test.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 54124b7c1a3..40f32751161 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -521,6 +521,69 @@ func TestStreams_StreamsLookup(t *testing.T) { }, }, }, + { + name: "two CheckResults: Mercury success E2E and No Mercury because of insufficient balance", + input: []ocr2keepers.CheckResult{ + { + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), + }, + { + PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted), + }, + }, + blobs: map[string]string{ + "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000": "0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa3", + "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000": "0x0006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d", + }, + cachedAdminCfg: false, + extraData: hexutil.MustDecode("0x0000000000000000000000000000000000000064"), + callbackNeeded: true, + checkCallbackResp: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063a400000000000000000000000000000000000000000000000000000000000006e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + values: [][]byte{hexutil.MustDecode("0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa3"), hexutil.MustDecode("0x0006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d")}, + expectedResults: []ocr2keepers.CheckResult{ + { + Eligible: false, + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), + }, + { + Eligible: true, + PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonNone), + }, + }, + hasPermission: true, + v3: false, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return []byte(`{"mercuryEnabled":true}`), nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{ + UpkeepNeeded: true, + PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + }, nil + }, + }, + }, { name: "success - happy path no cache - v0.3", input: []ocr2keepers.CheckResult{ From e427abbf4baf54e39281ee83495352c2de0ff02d Mon Sep 17 00:00:00 2001 From: frank zhu Date: Thu, 14 Dec 2023 06:41:44 -0800 Subject: [PATCH 060/234] add CI test for core/scripts (#11466) * add CI test for core/scripts * refactor * refactor golangci-lint build binary step * add ubig pkg * refactor based on suggestions --- .github/actions/golangci-lint/action.yml | 7 +--- .github/workflows/ci-chaincli.yml | 22 ------------ .github/workflows/ci-scripts.yml | 46 ++++++++++++++++++++++++ core/scripts/functions/src/fetching.go | 1 + 4 files changed, 48 insertions(+), 28 deletions(-) delete mode 100644 .github/workflows/ci-chaincli.yml create mode 100644 .github/workflows/ci-scripts.yml diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index 055960ff282..0047c6a54bd 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -42,14 +42,9 @@ runs: shell: bash run: mkdir -p core/web/assets && touch core/web/assets/index.html - name: Build binary - if: ${{ inputs.go-directory == '.' }} - shell: bash - run: go build ./... - - name: Build binary - if: ${{ inputs.go-directory != '.' }} working-directory: ${{ inputs.go-directory }} shell: bash - run: go build + run: go build ./... - name: golangci-lint uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 with: diff --git a/.github/workflows/ci-chaincli.yml b/.github/workflows/ci-chaincli.yml deleted file mode 100644 index 8a9ab03d766..00000000000 --- a/.github/workflows/ci-chaincli.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: chaincli CI - -on: - push: - pull_request: - -jobs: - golangci: - if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' }} - name: chaincli-lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Golang Lint - uses: ./.github/actions/golangci-lint - with: - name: chaincli-lint - go-directory: core/scripts/chaincli - go-version-file: core/scripts/go.mod - go-module-file: core/scripts/go.sum - gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/ci-scripts.yml b/.github/workflows/ci-scripts.yml new file mode 100644 index 00000000000..e83c22520cd --- /dev/null +++ b/.github/workflows/ci-scripts.yml @@ -0,0 +1,46 @@ +name: CI Scripts + +on: + push: + pull_request: + +jobs: + golangci: + if: ${{ github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Golang Lint + uses: ./.github/actions/golangci-lint + with: + name: scripts-lint + go-directory: core/scripts + go-version-file: core/scripts/go.mod + go-module-file: core/scripts/go.sum + gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} + + test: + if: ${{ github.event_name == 'pull_request' }} + name: scripts-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Setup Go + uses: ./.github/actions/setup-go + with: + go-version-file: core/scripts/go.mod + go-module-file: core/scripts/go.sum + - name: Run Tests + shell: bash + working-directory: core/scripts + run: go test ./... + - name: Collect Metrics + if: always() + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: scripts-test + continue-on-error: true diff --git a/core/scripts/functions/src/fetching.go b/core/scripts/functions/src/fetching.go index 9be624a40b6..cbe491fff5e 100644 --- a/core/scripts/functions/src/fetching.go +++ b/core/scripts/functions/src/fetching.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) From b2e163b4e5079e204489e29cd8587878233ab656 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 14 Dec 2023 18:14:31 +0100 Subject: [PATCH 061/234] bump npm packages (#11572) --- contracts/package.json | 26 +-- contracts/pnpm-lock.yaml | 404 +++++++++++++++++++-------------------- 2 files changed, 215 insertions(+), 215 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index abbf722140c..4dabbdb0509 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -43,49 +43,49 @@ "@nomiclabs/hardhat-etherscan": "^3.1.7", "@nomiclabs/hardhat-waffle": "2.0.6", "@openzeppelin/hardhat-upgrades": "1.28.0", - "@scroll-tech/contracts": "0.1.0", "@openzeppelin/test-helpers": "^0.5.16", "@typechain/ethers-v5": "^7.2.0", "@typechain/hardhat": "^7.0.0", "@types/cbor": "5.0.1", - "@types/chai": "^4.3.10", + "@types/chai": "^4.3.11", "@types/debug": "^4.1.12", "@types/deep-equal-in-any-order": "^1.0.3", - "@types/mocha": "^10.0.4", - "@types/node": "^16.18.61", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", + "@types/mocha": "^10.0.6", + "@types/node": "^16.18.68", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", "abi-to-sol": "^0.6.6", "cbor": "^5.2.0", "chai": "^4.3.10", "debug": "^4.3.4", - "eslint": "^8.53.0", - "eslint-config-prettier": "^9.0.0", + "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", "deep-equal-in-any-order": "^2.0.6", "eslint-plugin-prettier": "^5.0.1", "ethereum-waffle": "^3.4.4", "ethers": "~5.7.2", - "hardhat": "~2.19.1", + "hardhat": "~2.19.2", "hardhat-abi-exporter": "^2.10.1", "hardhat-contract-sizer": "^2.10.0", "hardhat-gas-reporter": "^1.0.9", "hardhat-ignore-warnings": "^0.2.6", "istanbul": "^0.4.5", "moment": "^2.29.4", - "prettier": "^3.1.0", + "prettier": "^3.1.1", "prettier-plugin-solidity": "1.2.0", "rlp": "^2.2.7", "solhint": "^4.0.0", "solhint-plugin-chainlink-solidity": "git+https://github.com/smartcontractkit/chainlink-solhint-rules.git#v1.2.0", "solhint-plugin-prettier": "^0.1.0", "solidity-coverage": "^0.8.5", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "tslib": "^2.6.2", "typechain": "^8.2.1", - "typescript": "^5.2.2" + "typescript": "^5.3.3" }, "dependencies": { - "@eth-optimism/contracts": "0.5.37", + "@eth-optimism/contracts": "0.6.0", + "@scroll-tech/contracts": "0.1.0", "@openzeppelin/contracts": "4.9.3", "@openzeppelin/contracts-upgradeable": "4.9.3" } diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index dffcb0a7c7c..1b18835dc2e 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -9,14 +9,17 @@ overrides: dependencies: '@eth-optimism/contracts': - specifier: 0.5.37 - version: 0.5.37(ethers@5.7.2) + specifier: 0.6.0 + version: 0.6.0(ethers@5.7.2) '@openzeppelin/contracts': specifier: 4.9.3 version: 4.9.3 '@openzeppelin/contracts-upgradeable': specifier: 4.9.3 version: 4.9.3 + '@scroll-tech/contracts': + specifier: 0.1.0 + version: 0.1.0 devDependencies: '@ethereum-waffle/mock-contract': @@ -39,37 +42,34 @@ devDependencies: version: 5.7.0 '@nomicfoundation/hardhat-network-helpers': specifier: ^1.0.9 - version: 1.0.9(hardhat@2.19.1) + version: 1.0.9(hardhat@2.19.2) '@nomiclabs/hardhat-ethers': specifier: ^2.2.3 - version: 2.2.3(ethers@5.7.2)(hardhat@2.19.1) + version: 2.2.3(ethers@5.7.2)(hardhat@2.19.2) '@nomiclabs/hardhat-etherscan': specifier: ^3.1.7 - version: 3.1.7(hardhat@2.19.1) + version: 3.1.7(hardhat@2.19.2) '@nomiclabs/hardhat-waffle': specifier: 2.0.6 - version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.1) + version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.2) '@openzeppelin/hardhat-upgrades': specifier: 1.28.0 - version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.1) + version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.2) '@openzeppelin/test-helpers': specifier: ^0.5.16 version: 0.5.16(bn.js@4.12.0) - '@scroll-tech/contracts': - specifier: 0.1.0 - version: 0.1.0 '@typechain/ethers-v5': specifier: ^7.2.0 - version: 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2) + version: 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.3.3) '@typechain/hardhat': specifier: ^7.0.0 - version: 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.1)(typechain@8.3.2) + version: 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.2)(typechain@8.3.2) '@types/cbor': specifier: 5.0.1 version: 5.0.1 '@types/chai': - specifier: ^4.3.10 - version: 4.3.10 + specifier: ^4.3.11 + version: 4.3.11 '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -77,17 +77,17 @@ devDependencies: specifier: ^1.0.3 version: 1.0.3 '@types/mocha': - specifier: ^10.0.4 - version: 10.0.4 + specifier: ^10.0.6 + version: 10.0.6 '@types/node': - specifier: ^16.18.61 - version: 16.18.61 + specifier: ^16.18.68 + version: 16.18.68 '@typescript-eslint/eslint-plugin': - specifier: ^6.11.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) + specifier: ^6.14.0 + version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: ^6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + specifier: ^6.14.0 + version: 6.14.0(eslint@8.55.0)(typescript@5.3.3) abi-to-sol: specifier: ^0.6.6 version: 0.6.6 @@ -104,32 +104,32 @@ devDependencies: specifier: ^2.0.6 version: 2.0.6 eslint: - specifier: ^8.53.0 - version: 8.53.0 + specifier: ^8.55.0 + version: 8.55.0 eslint-config-prettier: - specifier: ^9.0.0 - version: 9.0.0(eslint@8.53.0) + specifier: ^9.1.0 + version: 9.1.0(eslint@8.55.0) eslint-plugin-prettier: specifier: ^5.0.1 - version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.1.0) + version: 5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.1) ethereum-waffle: specifier: ^3.4.4 - version: 3.4.4(typescript@5.2.2) + version: 3.4.4(typescript@5.3.3) ethers: specifier: ~5.7.2 version: 5.7.2 hardhat: - specifier: ~2.19.1 - version: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + specifier: ~2.19.2 + version: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) hardhat-abi-exporter: specifier: ^2.10.1 - version: 2.10.1(hardhat@2.19.1) + version: 2.10.1(hardhat@2.19.2) hardhat-contract-sizer: specifier: ^2.10.0 - version: 2.10.0(hardhat@2.19.1) + version: 2.10.0(hardhat@2.19.2) hardhat-gas-reporter: specifier: ^1.0.9 - version: 1.0.9(debug@4.3.4)(hardhat@2.19.1) + version: 1.0.9(debug@4.3.4)(hardhat@2.19.2) hardhat-ignore-warnings: specifier: ^0.2.6 version: 0.2.9 @@ -140,11 +140,11 @@ devDependencies: specifier: ^2.29.4 version: 2.29.4 prettier: - specifier: ^3.1.0 - version: 3.1.0 + specifier: ^3.1.1 + version: 3.1.1 prettier-plugin-solidity: specifier: 1.2.0 - version: 1.2.0(prettier@3.1.0) + version: 1.2.0(prettier@3.1.1) rlp: specifier: ^2.2.7 version: 2.2.7 @@ -156,22 +156,22 @@ devDependencies: version: github.com/smartcontractkit/chainlink-solhint-rules/cfc50b32f95b730304a50deb2e27e88d87115874 solhint-plugin-prettier: specifier: ^0.1.0 - version: 0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.0) + version: 0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.1) solidity-coverage: specifier: ^0.8.5 - version: 0.8.5(hardhat@2.19.1) + version: 0.8.5(hardhat@2.19.2) ts-node: - specifier: ^10.9.1 - version: 10.9.1(@types/node@16.18.61)(typescript@5.2.2) + specifier: ^10.9.2 + version: 10.9.2(@types/node@16.18.68)(typescript@5.3.3) tslib: specifier: ^2.6.2 version: 2.6.2 typechain: specifier: ^8.2.1 - version: 8.3.2(typescript@5.2.2) + version: 8.3.2(typescript@5.3.3) typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.3.3 + version: 5.3.3 packages: @@ -327,13 +327,13 @@ packages: deprecated: Please use @ensdomains/ens-contracts dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.55.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.53.0 + eslint: 8.55.0 eslint-visitor-keys: 3.4.3 dev: true @@ -342,8 +342,8 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc@2.1.3: - resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 @@ -359,17 +359,17 @@ packages: - supports-color dev: true - /@eslint/js@8.53.0: - resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + /@eslint/js@8.55.0: + resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@eth-optimism/contracts@0.5.37(ethers@5.7.2): - resolution: {integrity: sha512-HbNUUDIM1dUAM0hWPfGp3l9/Zte40zi8QhVbUSIwdYRA7jG7cZgbteqavrjW8wwFqxkWX9IrtA0KAR7pNlSAIQ==} + /@eth-optimism/contracts@0.6.0(ethers@5.7.2): + resolution: {integrity: sha512-vQ04wfG9kMf1Fwy3FEMqH2QZbgS0gldKhcBeBUPfO8zu68L61VI97UDXmsMQXzTsEAxK8HnokW3/gosl4/NW3w==} peerDependencies: ethers: ^5 dependencies: - '@eth-optimism/core-utils': 0.10.1 + '@eth-optimism/core-utils': 0.12.0 '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 ethers: 5.7.2 @@ -378,8 +378,8 @@ packages: - utf-8-validate dev: false - /@eth-optimism/core-utils@0.10.1: - resolution: {integrity: sha512-IJvG5UtYvyz6An9QdohlCLoeB3NBFxx2lRJKlPzvYYlfugUNNCHsajRIWIwJTcPRRma0WPd46JUsKACLJDdNrA==} + /@eth-optimism/core-utils@0.12.0: + resolution: {integrity: sha512-qW+7LZYCz7i8dRa7SRlUKIo1VBU8lvN0HeXCxJR+z+xtMzMQpPds20XJNCMclszxYQHkXY00fOT6GvFw9ZL6nw==} dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/abstract-provider': 5.7.0 @@ -415,7 +415,7 @@ packages: - utf-8-validate dev: true - /@ethereum-waffle/compiler@3.4.4(typescript@5.2.2): + /@ethereum-waffle/compiler@3.4.4(typescript@5.3.3): resolution: {integrity: sha512-RUK3axJ8IkD5xpWjWoJgyHclOeEzDLQFga6gKpeGxiS/zBu+HB0W2FvsrrLalTFIaPw/CGYACRBSIxqiCqwqTQ==} engines: {node: '>=10.0'} dependencies: @@ -429,7 +429,7 @@ packages: node-fetch: 2.6.7 solc: 0.6.12 ts-generator: 0.1.1 - typechain: 3.0.0(typescript@5.2.2) + typechain: 3.0.0(typescript@5.3.3) transitivePeerDependencies: - bufferutil - encoding @@ -1040,13 +1040,13 @@ packages: - utf-8-validate dev: true - /@nomicfoundation/hardhat-network-helpers@1.0.9(hardhat@2.19.1): + /@nomicfoundation/hardhat-network-helpers@1.0.9(hardhat@2.19.2): resolution: {integrity: sha512-OXWCv0cHpwLUO2u7bFxBna6dQtCC2Gg/aN/KtJLO7gmuuA28vgmVKYFRCDUqrbjujzgfwQ2aKyZ9Y3vSmDqS7Q==} peerDependencies: hardhat: ^2.9.5 dependencies: ethereumjs-util: 7.1.5 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0: @@ -1155,17 +1155,17 @@ packages: '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.0 dev: true - /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.19.1): + /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.19.2): resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} peerDependencies: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true - /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.19.1): + /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.19.2): resolution: {integrity: sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==} peerDependencies: hardhat: ^2.0.4 @@ -1176,7 +1176,7 @@ packages: chalk: 2.4.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 7.0.1 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) lodash: 4.17.21 semver: 6.3.0 table: 6.8.1 @@ -1185,7 +1185,7 @@ packages: - supports-color dev: true - /@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.1): + /@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.2): resolution: {integrity: sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg==} peerDependencies: '@nomiclabs/hardhat-ethers': ^2.0.0 @@ -1194,11 +1194,11 @@ packages: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.2) '@types/sinon-chai': 3.2.8 - ethereum-waffle: 3.4.4(typescript@5.2.2) + ethereum-waffle: 3.4.4(typescript@5.3.3) ethers: 5.7.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true /@openzeppelin/contract-loader@0.6.3: @@ -1229,7 +1229,7 @@ packages: - encoding dev: true - /@openzeppelin/hardhat-upgrades@1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.1): + /@openzeppelin/hardhat-upgrades@1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.2): resolution: {integrity: sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ==} hasBin: true peerDependencies: @@ -1242,15 +1242,15 @@ packages: '@nomiclabs/harhdat-etherscan': optional: true dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.1) - '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.19.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.2) + '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.19.2) '@openzeppelin/defender-base-client': 1.49.0(debug@4.3.4) '@openzeppelin/platform-deploy-client': 0.8.0(debug@4.3.4) '@openzeppelin/upgrades-core': 1.30.1 chalk: 4.1.2 debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) proper-lockfile: 4.1.2 transitivePeerDependencies: - encoding @@ -1341,12 +1341,12 @@ packages: config-chain: 1.1.13 dev: true - /@prettier/sync@0.3.0(prettier@3.1.0): + /@prettier/sync@0.3.0(prettier@3.1.1): resolution: {integrity: sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==} peerDependencies: prettier: ^3.0.0 dependencies: - prettier: 3.1.0 + prettier: 3.1.1 dev: true /@resolver-engine/core@0.3.3: @@ -1392,7 +1392,7 @@ packages: /@scroll-tech/contracts@0.1.0: resolution: {integrity: sha512-aBbDOc3WB/WveZdpJYcrfvMYMz7ZTEiW8M9XMJLba8p9FAR5KGYB/cV+8+EUsq3MKt7C1BfR+WnXoTVdvwIY6w==} - dev: true + dev: false /@scure/base@1.1.1: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} @@ -1667,10 +1667,10 @@ packages: typechain: ^3.0.0 dependencies: ethers: 5.7.2 - typechain: 3.0.0(typescript@5.2.2) + typechain: 3.0.0(typescript@5.3.3) dev: true - /@typechain/ethers-v5@7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2): + /@typechain/ethers-v5@7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.3.3): resolution: {integrity: sha512-jfcmlTvaaJjng63QsT49MT6R1HFhtO/TBMWbyzPFSzMmVIqb2tL6prnKBs4ZJrSvmgIXWy+ttSjpaxCTq8D/Tw==} peerDependencies: '@ethersproject/abi': ^5.0.0 @@ -1685,12 +1685,12 @@ packages: '@ethersproject/providers': 5.7.2 ethers: 5.7.2 lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@5.2.2) - typechain: 8.3.2(typescript@5.2.2) - typescript: 5.2.2 + ts-essentials: 7.0.3(typescript@5.3.3) + typechain: 8.3.2(typescript@5.3.3) + typescript: 5.3.3 dev: true - /@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.1)(typechain@8.3.2): + /@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.2)(typechain@8.3.2): resolution: {integrity: sha512-XB79i5ewg9Met7gMVGfgVkmypicbnI25T5clJBEooMoW2161p4zvKFpoS2O+lBppQyMrPIZkdvl2M3LMDayVcA==} peerDependencies: '@ethersproject/abi': ^5.4.7 @@ -1702,23 +1702,23 @@ packages: dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/providers': 5.7.2 - '@typechain/ethers-v5': 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2) + '@typechain/ethers-v5': 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.3.3) ethers: 5.7.2 fs-extra: 9.1.0 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) - typechain: 8.3.2(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) + typechain: 8.3.2(typescript@5.3.3) dev: true /@types/bn.js@4.11.6: resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/cacheable-request@6.0.2: @@ -1726,24 +1726,24 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 16.18.61 + '@types/node': 16.18.68 '@types/responselike': 1.0.0 dev: true /@types/cbor@5.0.1: resolution: {integrity: sha512-zVqJy2KzusZPLOgyGJDnOIbu3DxIGGqxYbEwtEEe4Z+la8jwIhOyb+GMrlHafs5tvKruwf8f8qOYP6zTvse/pw==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true - /@types/chai@4.3.10: - resolution: {integrity: sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==} + /@types/chai@4.3.11: + resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==} dev: true /@types/concat-stream@1.6.1: resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/debug@4.1.12: @@ -1763,7 +1763,7 @@ packages: /@types/form-data@0.0.33: resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/glob@7.1.1: @@ -1771,7 +1771,7 @@ packages: dependencies: '@types/events': 3.0.0 '@types/minimatch': 3.0.3 - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/http-cache-semantics@4.0.1: @@ -1785,7 +1785,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/lru-cache@5.1.1: @@ -1799,11 +1799,11 @@ packages: /@types/mkdirp@0.5.2: resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true - /@types/mocha@10.0.4: - resolution: {integrity: sha512-xKU7bUjiFTIttpWaIZ9qvgg+22O1nmbA+HRxdlR+u6TWsGfmFdXrheJoK4fFxrHNVIOBDvDNKZG+LYBpMHpX3w==} + /@types/mocha@10.0.6: + resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} dev: true /@types/ms@0.7.31: @@ -1813,7 +1813,7 @@ packages: /@types/node-fetch@2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 form-data: 3.0.1 dev: true @@ -1825,8 +1825,8 @@ packages: resolution: {integrity: sha512-7xHmXm/QJ7cbK2laF+YYD7gb5MggHIIQwqyjin3bpEGiSuvScMQ5JZZXPvRipi1MwckTQbJZROMns/JxdnIL1Q==} dev: true - /@types/node@16.18.61: - resolution: {integrity: sha512-k0N7BqGhJoJzdh6MuQg1V1ragJiXTh8VUBAZTWjJ9cUq23SG0F0xavOwZbhiP4J3y20xd6jxKx+xNUhkMAi76Q==} + /@types/node@16.18.68: + resolution: {integrity: sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==} dev: true /@types/node@8.10.66: @@ -1836,7 +1836,7 @@ packages: /@types/pbkdf2@3.1.0: resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/prettier@2.7.1: @@ -1850,26 +1850,26 @@ packages: /@types/readable-stream@2.3.15: resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 safe-buffer: 5.1.2 dev: true /@types/resolve@0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/secp256k1@4.0.3: resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/semver@7.5.0: @@ -1879,7 +1879,7 @@ packages: /@types/sinon-chai@3.2.8: resolution: {integrity: sha512-d4ImIQbT/rKMG8+AXpmcan5T2/PNeSjrYhvkwet6z0p8kzYtfgA32xzOBlbU0yqJfq+/0Ml805iFoODO0LP5/g==} dependencies: - '@types/chai': 4.3.10 + '@types/chai': 4.3.11 '@types/sinon': 10.0.13 dev: true @@ -1893,8 +1893,8 @@ packages: resolution: {integrity: sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==} dev: true - /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} + /@typescript-eslint/eslint-plugin@6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -1905,25 +1905,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.8.0 - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.14.0 + '@typescript-eslint/type-utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.14.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 + eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==} + /@typescript-eslint/parser@6.14.0(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1932,27 +1932,27 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/scope-manager': 6.14.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.14.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - typescript: 5.2.2 + eslint: 8.55.0 + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.11.0: - resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==} + /@typescript-eslint/scope-manager@6.14.0: + resolution: {integrity: sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/visitor-keys': 6.14.0 dev: true - /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==} + /@typescript-eslint/type-utils@6.14.0(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1961,23 +1961,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 + eslint: 8.55.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.11.0: - resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==} + /@typescript-eslint/types@6.14.0: + resolution: {integrity: sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.11.0(typescript@5.2.2): - resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==} + /@typescript-eslint/typescript-estree@6.14.0(typescript@5.3.3): + resolution: {integrity: sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1985,42 +1985,42 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/visitor-keys': 6.14.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==} + /@typescript-eslint/utils@6.14.0(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - eslint: 8.53.0 + '@typescript-eslint/scope-manager': 6.14.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) + eslint: 8.55.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.11.0: - resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==} + /@typescript-eslint/visitor-keys@6.14.0: + resolution: {integrity: sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/types': 6.14.0 eslint-visitor-keys: 3.4.3 dev: true @@ -4714,16 +4714,16 @@ packages: source-map: 0.2.0 dev: true - /eslint-config-prettier@9.0.0(eslint@8.53.0): - resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + /eslint-config-prettier@9.1.0(eslint@8.55.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.53.0 + eslint: 8.55.0 dev: true - /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.1.0): + /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.1): resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4737,9 +4737,9 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.53.0 - eslint-config-prettier: 9.0.0(eslint@8.53.0) - prettier: 3.1.0 + eslint: 8.55.0 + eslint-config-prettier: 9.1.0(eslint@8.55.0) + prettier: 3.1.1 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 dev: true @@ -4757,15 +4757,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.53.0: - resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + /eslint@8.55.0: + resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) '@eslint-community/regexpp': 4.8.0 - '@eslint/eslintrc': 2.1.3 - '@eslint/js': 8.53.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.55.0 '@humanwhocodes/config-array': 0.11.13 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -5060,13 +5060,13 @@ packages: '@scure/bip39': 1.1.0 dev: true - /ethereum-waffle@3.4.4(typescript@5.2.2): + /ethereum-waffle@3.4.4(typescript@5.3.3): resolution: {integrity: sha512-PA9+jCjw4WC3Oc5ocSMBj5sXvueWQeAbvCA+hUlb6oFgwwKyq5ka3bWQ7QZcjzIX+TdFkxP4IbFmoY2D8Dkj9Q==} engines: {node: '>=10.0'} hasBin: true dependencies: '@ethereum-waffle/chai': 3.4.4 - '@ethereum-waffle/compiler': 3.4.4(typescript@5.2.2) + '@ethereum-waffle/compiler': 3.4.4(typescript@5.3.3) '@ethereum-waffle/mock-contract': 3.4.4 '@ethereum-waffle/provider': 3.4.4 ethers: 5.7.2 @@ -6218,7 +6218,7 @@ packages: har-schema: 2.0.0 dev: true - /hardhat-abi-exporter@2.10.1(hardhat@2.19.1): + /hardhat-abi-exporter@2.10.1(hardhat@2.19.2): resolution: {integrity: sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ==} engines: {node: '>=14.14.0'} peerDependencies: @@ -6226,28 +6226,28 @@ packages: dependencies: '@ethersproject/abi': 5.7.0 delete-empty: 3.0.0 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true - /hardhat-contract-sizer@2.10.0(hardhat@2.19.1): + /hardhat-contract-sizer@2.10.0(hardhat@2.19.2): resolution: {integrity: sha512-QiinUgBD5MqJZJh1hl1jc9dNnpJg7eE/w4/4GEnrcmZJJTDbVFNe3+/3Ep24XqISSkYxRz36czcPHKHd/a0dwA==} peerDependencies: hardhat: ^2.0.0 dependencies: chalk: 4.1.2 cli-table3: 0.6.3 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) strip-ansi: 6.0.1 dev: true - /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.19.1): + /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.19.2): resolution: {integrity: sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==} peerDependencies: hardhat: ^2.0.2 dependencies: array-uniq: 1.0.3 eth-gas-reporter: 0.2.27(debug@4.3.4) - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' @@ -6264,8 +6264,8 @@ packages: solidity-comments: 0.0.2 dev: true - /hardhat@2.19.1(ts-node@10.9.1)(typescript@5.2.2): - resolution: {integrity: sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw==} + /hardhat@2.19.2(ts-node@10.9.2)(typescript@5.3.3): + resolution: {integrity: sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ==} hasBin: true peerDependencies: ts-node: '*' @@ -6320,9 +6320,9 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@16.18.61)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@16.18.68)(typescript@5.3.3) tsort: 0.0.1 - typescript: 5.2.2 + typescript: 5.3.3 undici: 5.19.1 uuid: 8.3.2 ws: 7.5.9 @@ -8909,14 +8909,14 @@ packages: dev: true optional: true - /prettier-plugin-solidity@1.2.0(prettier@3.1.0): + /prettier-plugin-solidity@1.2.0(prettier@3.1.1): resolution: {integrity: sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==} engines: {node: '>=16'} peerDependencies: prettier: '>=2.3.0' dependencies: '@solidity-parser/parser': 0.16.2 - prettier: 3.1.0 + prettier: 3.1.1 semver: 7.5.4 solidity-comments-extractor: 0.0.7 dev: true @@ -8927,8 +8927,8 @@ packages: hasBin: true dev: true - /prettier@3.1.0: - resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==} + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} engines: {node: '>=14'} hasBin: true dev: true @@ -9960,16 +9960,16 @@ packages: - debug dev: true - /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.0): + /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.1): resolution: {integrity: sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw==} peerDependencies: prettier: ^3.0.0 prettier-plugin-solidity: ^1.0.0 dependencies: - '@prettier/sync': 0.3.0(prettier@3.1.0) - prettier: 3.1.0 + '@prettier/sync': 0.3.0(prettier@3.1.1) + prettier: 3.1.1 prettier-linter-helpers: 1.0.0 - prettier-plugin-solidity: 1.2.0(prettier@3.1.0) + prettier-plugin-solidity: 1.2.0(prettier@3.1.1) dev: true /solhint@4.0.0: @@ -10114,7 +10114,7 @@ packages: solidity-comments-win32-x64-msvc: 0.0.2 dev: true - /solidity-coverage@0.8.5(hardhat@2.19.1): + /solidity-coverage@0.8.5(hardhat@2.19.2): resolution: {integrity: sha512-6C6N6OV2O8FQA0FWA95FdzVH+L16HU94iFgg5wAFZ29UpLFkgNI/DRR2HotG1bC0F4gAc/OMs2BJI44Q/DYlKQ==} hasBin: true peerDependencies: @@ -10130,7 +10130,7 @@ packages: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) jsonschema: 1.4.0 lodash: 4.17.21 mocha: 10.2.0 @@ -10719,13 +10719,13 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ts-api-utils@1.0.3(typescript@5.2.2): + /ts-api-utils@1.0.3(typescript@5.3.3): resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: true /ts-command-line-args@2.5.1: @@ -10742,20 +10742,20 @@ packages: resolution: {integrity: sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==} dev: true - /ts-essentials@6.0.7(typescript@5.2.2): + /ts-essentials@6.0.7(typescript@5.3.3): resolution: {integrity: sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: true - /ts-essentials@7.0.3(typescript@5.2.2): + /ts-essentials@7.0.3(typescript@5.3.3): resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: true /ts-generator@0.1.1: @@ -10773,8 +10773,8 @@ packages: ts-essentials: 1.0.4 dev: true - /ts-node@10.9.1(@types/node@16.18.61)(typescript@5.2.2): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + /ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -10792,14 +10792,14 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 16.18.61 + '@types/node': 16.18.68 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.3.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -10884,7 +10884,7 @@ packages: resolution: {integrity: sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==} dev: true - /typechain@3.0.0(typescript@5.2.2): + /typechain@3.0.0(typescript@5.3.3): resolution: {integrity: sha512-ft4KVmiN3zH4JUFu2WJBrwfHeDf772Tt2d8bssDTo/YcckKW2D+OwFrHXRC6hJvO3mHjFQTihoMV6fJOi0Hngg==} hasBin: true dependencies: @@ -10893,14 +10893,14 @@ packages: fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 - ts-essentials: 6.0.7(typescript@5.2.2) + ts-essentials: 6.0.7(typescript@5.3.3) ts-generator: 0.1.1 transitivePeerDependencies: - supports-color - typescript dev: true - /typechain@8.3.2(typescript@5.2.2): + /typechain@8.3.2(typescript@5.3.3): resolution: {integrity: sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==} hasBin: true peerDependencies: @@ -10915,8 +10915,8 @@ packages: mkdirp: 1.0.4 prettier: 2.8.8 ts-command-line-args: 2.5.1 - ts-essentials: 7.0.3(typescript@5.2.2) - typescript: 5.2.2 + ts-essentials: 7.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true @@ -10969,8 +10969,8 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} hasBin: true dev: true From 7dd42eb8a8326c6c440e4346fe24c1aebc1cf076 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 14 Dec 2023 13:49:14 -0500 Subject: [PATCH 062/234] Fix Makefile Oversight (#11568) * Adds MAKE Command for Building Plugin Image * Makefile Oversight --- integration-tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 86a266a996a..5e34b059b68 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -196,7 +196,7 @@ build_docker_image: # tag: the tag for the chainlink image being built, example: tag=latest # example usage: make build_docker_image image=chainlink tag=latest .PHONY: build_plugin_docker_image -build_docker_image: +build_plugin_docker_image: docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ # image: the name for the chainlink image being built, example: image=chainlink From 3c299706fdfd76703853cab27db97b567a8a06cd Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Thu, 14 Dec 2023 16:14:00 -0500 Subject: [PATCH 063/234] Extract relay from evm (#11537) * Extract relay from evm * Replace type with string * Update helpers_test.go * ci --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/chains/evm/config/toml/config.go | 17 ++++++++--------- core/chains/evm/log/helpers_test.go | 9 +++++++-- core/chains/evm/types/types.go | 5 ++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 16ad74dbca2..ff6a9872840 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -21,7 +21,6 @@ import ( "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/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/store/models" configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) @@ -107,7 +106,7 @@ func (cs EVMConfigs) totalChains() int { } return total } -func (cs EVMConfigs) Chains(ids ...relay.ChainID) (r []commontypes.ChainStatus, total int, err error) { +func (cs EVMConfigs) Chains(ids ...string) (r []commontypes.ChainStatus, total int, err error) { total = cs.totalChains() for _, ch := range cs { if ch == nil { @@ -154,7 +153,7 @@ func (cs EVMConfigs) NodeStatus(name string) (commontypes.NodeStatus, error) { for i := range cs { for _, n := range cs[i].Nodes { if n.Name != nil && *n.Name == name { - return nodeStatus(n, relay.ChainID(cs[i].ChainID.String())) + return nodeStatus(n, cs[i].ChainID.String()) } } } @@ -179,7 +178,7 @@ func legacyNode(n *Node, chainID *big.Big) (v2 types.Node) { return } -func nodeStatus(n *Node, chainID relay.ChainID) (commontypes.NodeStatus, error) { +func nodeStatus(n *Node, chainID string) (commontypes.NodeStatus, error) { var s commontypes.NodeStatus s.ChainID = chainID s.Name = *n.Name @@ -191,7 +190,7 @@ func nodeStatus(n *Node, chainID relay.ChainID) (commontypes.NodeStatus, error) return s, nil } -func (cs EVMConfigs) nodes(id relay.ChainID) (ns EVMNodes) { +func (cs EVMConfigs) nodes(id string) (ns EVMNodes) { for _, c := range cs { if c.ChainID.String() == id { return c.Nodes @@ -200,7 +199,7 @@ func (cs EVMConfigs) nodes(id relay.ChainID) (ns EVMNodes) { return nil } -func (cs EVMConfigs) Nodes(chainID relay.ChainID) (ns []types.Node, err error) { +func (cs EVMConfigs) Nodes(chainID string) (ns []types.Node, err error) { evmID, err := ChainIDInt64(chainID) if err != nil { return nil, fmt.Errorf("invalid evm chain id %q : %w", chainID, err) @@ -220,14 +219,14 @@ func (cs EVMConfigs) Nodes(chainID relay.ChainID) (ns []types.Node, err error) { return } -func (cs EVMConfigs) NodeStatuses(chainIDs ...relay.ChainID) (ns []commontypes.NodeStatus, err error) { +func (cs EVMConfigs) NodeStatuses(chainIDs ...string) (ns []commontypes.NodeStatus, err error) { if len(chainIDs) == 0 { for i := range cs { for _, n := range cs[i].Nodes { if n == nil { continue } - n2, err := nodeStatus(n, relay.ChainID(cs[i].ChainID.String())) + n2, err := nodeStatus(n, cs[i].ChainID.String()) if err != nil { return nil, err } @@ -816,6 +815,6 @@ func (n *Node) SetFrom(f *Node) { } } -func ChainIDInt64(cid relay.ChainID) (int64, error) { +func ChainIDInt64(cid string) (int64, error) { return strconv.ParseInt(cid, 10, 64) } diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index cea2e361184..de8ff024b84 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" @@ -42,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) type broadcasterHelper struct { @@ -105,7 +105,12 @@ func newBroadcasterHelperWithEthClient(t *testing.T, ethClient evmclient.Client, LogBroadcaster: &log.NullBroadcaster{}, MailMon: mailMon, }) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(cc) + + m := make(map[string]legacyevm.Chain) + for _, r := range cc.Slice() { + m[r.Chain().ID().String()] = r.Chain() + } + legacyChains := legacyevm.NewLegacyChains(m, cc.AppConfig().EVMConfigs()) pipelineHelper := cltest.NewJobPipelineV2(t, config.WebServer(), config.JobPipeline(), config.Database(), legacyChains, db, kst, nil, nil) return &broadcasterHelper{ diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index aa43806da1f..ec1134de2b4 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -14,14 +14,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Configs interface { - Chains(ids ...relay.ChainID) ([]types.ChainStatus, int, error) + Chains(ids ...string) ([]types.ChainStatus, int, error) Node(name string) (Node, error) - Nodes(chainID relay.ChainID) (nodes []Node, err error) + Nodes(chainID string) (nodes []Node, err error) NodeStatus(name string) (types.NodeStatus, error) } From 4b0608a94c5b15f0168e4b87817564d16637e27d Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 14 Dec 2023 16:32:50 -0500 Subject: [PATCH 064/234] Lower All Runner Sizes (#11539) * Lower all runner sizes * Remove skips * Make Most of it Free * Return Solana and builds back to normal * Reset others --- .github/workflows/integration-tests.yml | 18 ++++++++--------- .../smoke/automation_test.go_test_list.json | 20 +++++++++---------- .../smoke/keeper_test.go_test_list.json | 20 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 6ffd1a14ff2..0c754807f31 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -207,8 +207,8 @@ jobs: id: build-test-matrix-list run: | cd ./integration-tests - MATRIX_JSON_AUTOMATION=$(./scripts/buildTestMatrixList.sh ./smoke/automation_test.go automation ubuntu20.04-8cores-32GB 1) - MATRIX_JSON_KEEPER=$(./scripts/buildTestMatrixList.sh ./smoke/keeper_test.go keeper ubuntu20.04-8cores-32GB 1) + MATRIX_JSON_AUTOMATION=$(./scripts/buildTestMatrixList.sh ./smoke/automation_test.go automation ubuntu-latest 1) + MATRIX_JSON_KEEPER=$(./scripts/buildTestMatrixList.sh ./smoke/keeper_test.go keeper ubuntu-latest 1) COMBINED_ARRAY=$(jq -c -n "$MATRIX_JSON_AUTOMATION + $MATRIX_JSON_KEEPER") # if we running a PR against the develop branch we should only run the automation tests unless we are in the merge group event @@ -324,13 +324,13 @@ jobs: pyroscope_env: "" - name: ocr nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest run: -run TestOCRJobReplacement file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr-geth nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest run: -run TestOCRBasic file: ocr client: geth @@ -403,25 +403,25 @@ jobs: pyroscope_env: "" - name: vrf nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-vrf-evm-simulated - name: vrfv2 nodes: 1 run: -run TestVRFv2MultipleSendingKeys file: vrfv2 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-forwarder-ocr-evm-simulated - name: forwarders_ocr2 nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-forwarder-ocr-evm-simulated runs-on: ${{ matrix.product.os }} name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} diff --git a/integration-tests/smoke/automation_test.go_test_list.json b/integration-tests/smoke/automation_test.go_test_list.json index ad6de592ede..da214a0d282 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -2,7 +2,7 @@ "tests": [ { "name": "TestAutomationBasic", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 6 }, { @@ -10,43 +10,43 @@ }, { "name": "TestAutomationAddFunds", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationPauseUnPause", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationRegisterUpkeep", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationPauseRegistry", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationKeeperNodesDown", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationPerformSimulation", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationCheckPerformGasLimit", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestUpdateCheckData", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 2 } ] -} +} \ No newline at end of file diff --git a/integration-tests/smoke/keeper_test.go_test_list.json b/integration-tests/smoke/keeper_test.go_test_list.json index b874bc65ee0..b2f4aa00659 100644 --- a/integration-tests/smoke/keeper_test.go_test_list.json +++ b/integration-tests/smoke/keeper_test.go_test_list.json @@ -2,42 +2,42 @@ "tests": [ { "name": "TestKeeperBasicSmoke", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperBlockCountPerTurn", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperSimulation", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestKeeperCheckPerformGasLimit", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperRegisterUpkeep", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperAddFunds", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperRemove", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperPauseRegistry", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { @@ -45,7 +45,7 @@ }, { "name": "TestKeeperNodeDown", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { @@ -58,4 +58,4 @@ "name": "TestKeeperJobReplacement" } ] -} +} \ No newline at end of file From 7773d031434095f21c81eb790efbb873ba95b4f7 Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:18:31 -0600 Subject: [PATCH 065/234] Remove direct references to TXM DB from external component tests (#11538) * Removed direct references to TXM DB from external component tests * Fixed linting --- core/chains/evm/txmgr/evm_tx_store.go | 65 ++++++++++++++++++ core/chains/evm/txmgr/evm_tx_store_test.go | 13 +++- core/cmd/evm_transaction_commands_test.go | 68 +++++++++++-------- core/cmd/shell_local_test.go | 2 +- core/internal/cltest/cltest.go | 10 ++- core/internal/testutils/testutils.go | 9 --- core/services/keeper/upkeep_executer_test.go | 7 +- .../promreporter/prom_reporter_test.go | 3 +- core/services/vrf/delegate_test.go | 10 +-- core/web/eth_keys_controller_test.go | 17 +++-- core/web/evm_transfer_controller_test.go | 21 ++++-- 11 files changed, 162 insertions(+), 63 deletions(-) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index f9014163675..9b46ab7a80c 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -71,6 +71,11 @@ type TestEvmTxStore interface { InsertTxAttempt(attempt *TxAttempt) error LoadTxesAttempts(etxs []*Tx, qopts ...pg.QOpt) error GetFatalTransactions(ctx context.Context) (txes []*Tx, err error) + GetAllTxes(ctx context.Context) (txes []*Tx, err error) + GetAllTxAttempts(ctx context.Context) (attempts []TxAttempt, err error) + CountTxesByStateAndSubject(ctx context.Context, state txmgrtypes.TxState, subject uuid.UUID) (count int, err error) + FindTxesByFromAddressAndState(ctx context.Context, fromAddress common.Address, state string) (txes []*Tx, err error) + UpdateTxAttemptBroadcastBeforeBlockNum(ctx context.Context, id int64, blockNum uint) error } type evmTxStore struct { @@ -2011,6 +2016,66 @@ func (o *evmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Co return txes, pkgerrors.Wrap(err, "FindTxesWithAttemptsAndReceiptsByIdsAndState failed") } +// For testing only, get all txes in the DB +func (o *evmTxStore) GetAllTxes(ctx context.Context) (txes []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + var dbEtxs []DbEthTx + sql := "SELECT * FROM evm.txes" + err = qq.Select(&dbEtxs, sql) + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, err +} + +// For testing only, get all tx attempts in the DB +func (o *evmTxStore) GetAllTxAttempts(ctx context.Context) (attempts []TxAttempt, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + var dbAttempts []DbEthTxAttempt + sql := "SELECT * FROM evm.tx_attempts" + err = qq.Select(&dbAttempts, sql) + attempts = dbEthTxAttemptsToEthTxAttempts(dbAttempts) + return attempts, err +} + +func (o *evmTxStore) CountTxesByStateAndSubject(ctx context.Context, state txmgrtypes.TxState, subject uuid.UUID) (count int, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + sql := "SELECT COUNT(*) FROM evm.txes WHERE state = $1 AND subject = $2" + err = qq.Get(&count, sql, state, subject) + return count, err +} + +func (o *evmTxStore) FindTxesByFromAddressAndState(ctx context.Context, fromAddress common.Address, state string) (txes []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + sql := "SELECT * FROM evm.txes WHERE from_address = $1 AND state = $2" + var dbEtxs []DbEthTx + err = qq.Select(&dbEtxs, sql, fromAddress, state) + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, err +} + +func (o *evmTxStore) UpdateTxAttemptBroadcastBeforeBlockNum(ctx context.Context, id int64, blockNum uint) error { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + sql := "UPDATE evm.tx_attempts SET broadcast_before_block_num = $1 WHERE eth_tx_id = $2" + _, err := qq.Exec(sql, blockNum, id) + return err +} + // Returns a context that contains the values of the provided context, // and which is canceled when either the provided contextg or TxStore parent context is canceled. func (o *evmTxStore) mergeContexts(ctx context.Context) (context.Context, context.CancelFunc) { diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 0b4287b6f6c..6c19026c1da 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -1811,7 +1811,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := newTestChainScopedConfig(t) - txStore := newTxStore(t, db, cfg.Database()) + txStore := txmgr.NewTxStore(db, logger.Test(t), cfg.Database()) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() evmtest.NewEthClientMockWithDefaultChain(t) _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) @@ -1822,7 +1822,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { for i := 0; i < 5; i++ { mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithStrategy(strategy1)) } - testutils.AssertCountPerSubject(t, db, int64(5), subject1) + AssertCountPerSubject(t, txStore, int64(5), subject1) }) t.Run("prunes if queue has exceeded capacity", func(t *testing.T) { @@ -1831,6 +1831,13 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { for i := 0; i < 5; i++ { mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithStrategy(strategy2)) } - testutils.AssertCountPerSubject(t, db, int64(3), subject2) + AssertCountPerSubject(t, txStore, int64(3), subject2) }) } + +func AssertCountPerSubject(t *testing.T, txStore txmgr.TestEvmTxStore, expected int64, subject uuid.UUID) { + t.Helper() + count, err := txStore.CountTxesByStateAndSubject(testutils.Context(t), "unstarted", subject) + require.NoError(t, err) + require.Equal(t, int(expected), count) +} diff --git a/core/cmd/evm_transaction_commands_test.go b/core/cmd/evm_transaction_commands_test.go index 6c079a12495..5c80a7d74a0 100644 --- a/core/cmd/evm_transaction_commands_test.go +++ b/core/cmd/evm_transaction_commands_test.go @@ -19,6 +19,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -156,7 +158,8 @@ func TestShell_SendEther_From_Txm(t *testing.T) { ) client, r := app.NewShellAndRenderer() db := app.GetSqlxDB() - + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) set := flag.NewFlagSet("sendether", 0) flagSetApplyFromAction(client.SendEther, set, "") @@ -170,21 +173,26 @@ func TestShell_SendEther_From_Txm(t *testing.T) { assert.NoError(t, client.SendEther(c)) - dbEvmTx := txmgr.DbEthTx{} - require.NoError(t, db.Get(&dbEvmTx, `SELECT * FROM evm.txes`)) - require.Equal(t, "100.500000000000000000", dbEvmTx.Value.String()) - require.Equal(t, fromAddress, dbEvmTx.FromAddress) - require.Equal(t, to, dbEvmTx.ToAddress.String()) + evmTxes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, evmTxes, 1) + evmTx := evmTxes[0] + value := assets.Eth(evmTx.Value) + require.Equal(t, "100.500000000000000000", value.String()) + require.Equal(t, fromAddress, evmTx.FromAddress) + require.Equal(t, to, evmTx.ToAddress.String()) output := *r.Renders[0].(*cmd.EthTxPresenter) - assert.Equal(t, &dbEvmTx.FromAddress, output.From) - assert.Equal(t, &dbEvmTx.ToAddress, output.To) - assert.Equal(t, dbEvmTx.Value.String(), output.Value) - assert.Equal(t, fmt.Sprintf("%d", *dbEvmTx.Nonce), output.Nonce) - - var dbEvmTxAttempt txmgr.DbEthTxAttempt - require.NoError(t, db.Get(&dbEvmTxAttempt, `SELECT * FROM evm.tx_attempts`)) - assert.Equal(t, dbEvmTxAttempt.Hash, output.Hash) + assert.Equal(t, &evmTx.FromAddress, output.From) + assert.Equal(t, &evmTx.ToAddress, output.To) + assert.Equal(t, value.String(), output.Value) + assert.Equal(t, fmt.Sprintf("%d", *evmTx.Sequence), output.Nonce) + + attempts, err := txStore.GetAllTxAttempts(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, attempts, 1) + assert.Equal(t, attempts[0].Hash, output.Hash) + } func TestShell_SendEther_From_Txm_WEI(t *testing.T) { @@ -216,6 +224,8 @@ func TestShell_SendEther_From_Txm_WEI(t *testing.T) { ) client, r := app.NewShellAndRenderer() db := app.GetSqlxDB() + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) set := flag.NewFlagSet("sendether", 0) flagSetApplyFromAction(client.SendEther, set, "") @@ -236,19 +246,23 @@ func TestShell_SendEther_From_Txm_WEI(t *testing.T) { assert.NoError(t, client.SendEther(c)) - dbEvmTx := txmgr.DbEthTx{} - require.NoError(t, db.Get(&dbEvmTx, `SELECT * FROM evm.txes`)) - require.Equal(t, "1.000000000000000000", dbEvmTx.Value.String()) - require.Equal(t, fromAddress, dbEvmTx.FromAddress) - require.Equal(t, to, dbEvmTx.ToAddress.String()) + evmTxes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, evmTxes, 1) + evmTx := evmTxes[0] + value := assets.Eth(evmTx.Value) + require.Equal(t, "1.000000000000000000", value.String()) + require.Equal(t, fromAddress, evmTx.FromAddress) + require.Equal(t, to, evmTx.ToAddress.String()) output := *r.Renders[0].(*cmd.EthTxPresenter) - assert.Equal(t, &dbEvmTx.FromAddress, output.From) - assert.Equal(t, &dbEvmTx.ToAddress, output.To) - assert.Equal(t, dbEvmTx.Value.String(), output.Value) - assert.Equal(t, fmt.Sprintf("%d", *dbEvmTx.Nonce), output.Nonce) - - var dbEvmTxAttempt txmgr.DbEthTxAttempt - require.NoError(t, db.Get(&dbEvmTxAttempt, `SELECT * FROM evm.tx_attempts`)) - assert.Equal(t, dbEvmTxAttempt.Hash, output.Hash) + assert.Equal(t, &evmTx.FromAddress, output.From) + assert.Equal(t, &evmTx.ToAddress, output.To) + assert.Equal(t, value.String(), output.Value) + assert.Equal(t, fmt.Sprintf("%d", *evmTx.Sequence), output.Nonce) + + attempts, err := txStore.GetAllTxAttempts(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, attempts, 1) + assert.Equal(t, attempts[0].Hash, output.Hash) } diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 56da90d811c..400a8e3e75a 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -420,7 +420,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { assert.NoError(t, c.RebroadcastTransactions(ctx)) - cltest.AssertEthTxAttemptCountStays(t, app.GetSqlxDB(), 1) + cltest.AssertEthTxAttemptCountStays(t, txStore, 1) }) } } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index c5e1fc5e4d0..8ebf4b84b46 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -974,16 +974,14 @@ func AssertPipelineRunsStays(t testing.TB, pipelineSpecID int32, db *sqlx.DB, wa } // AssertEthTxAttemptCountStays asserts that the number of tx attempts remains at the provided value -func AssertEthTxAttemptCountStays(t testing.TB, db *sqlx.DB, want int) []int64 { +func AssertEthTxAttemptCountStays(t testing.TB, txStore txmgr.TestEvmTxStore, want int) []int64 { g := gomega.NewWithT(t) var txaIds []int64 - var err error - g.Consistently(func() []int64 { - txaIds = make([]int64, 0) - err = db.Select(&txaIds, `SELECT ID FROM evm.tx_attempts ORDER BY id ASC`) + g.Consistently(func() []txmgr.TxAttempt { + attempts, err := txStore.GetAllTxAttempts(testutils.Context(t)) assert.NoError(t, err) - return txaIds + return attempts }, AssertNoActionTimeout, DBPollingInterval).Should(gomega.HaveLen(want)) return txaIds } diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index 6ffd873d092..81c09b1c362 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -425,15 +425,6 @@ func AssertCount(t *testing.T, db *sqlx.DB, tableName string, expected int64) { require.Equal(t, expected, count) } -func AssertCountPerSubject(t *testing.T, db *sqlx.DB, expected int64, subject uuid.UUID) { - t.Helper() - var count int64 - err := db.Get(&count, `SELECT COUNT(*) FROM evm.txes - WHERE state = 'unstarted' AND subject = $1;`, subject) - require.NoError(t, err) - require.Equal(t, expected, count) -} - func NewTestFlagSet() *flag.FlagSet { return flag.NewFlagSet("test", flag.PanicOnError) } diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 61ccca956f4..42e79425cff 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -335,7 +335,12 @@ func Test_UpkeepExecuter_PerformsUpkeep_Error(t *testing.T) { executer.OnNewLongestChain(testutils.Context(t), &head) g.Eventually(wasCalled.Load).Should(gomega.Equal(true)) - cltest.AssertCountStays(t, db, "evm.txes", 0) + + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + txes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, txes, 0) } func ptr[T any](t T) *T { return &t } diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 7b9930e4daa..60d6d9388fa 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -26,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func newHead() evmtypes.Head { @@ -113,7 +112,7 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) - require.NoError(t, utils.JustError(db.Exec(`UPDATE evm.tx_attempts SET broadcast_before_block_num = 7 WHERE eth_tx_id = $1`, etx.ID))) + require.NoError(t, txStore.UpdateTxAttemptBroadcastBeforeBlockNum(testutils.Context(t), etx.ID, 7)) head := newHead() reporter.OnNewLongestChain(testutils.Context(t), &head) diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 663080c86a0..1d9f97d136d 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -404,11 +404,13 @@ func TestDelegate_InvalidLog(t *testing.T) { } } - // Ensure we have NOT queued up an eth transaction - var ethTxes []txmgr.DbEthTx - err = vuni.prm.GetQ().Select(ðTxes, `SELECT * FROM evm.txes;`) + db := pgtest.NewSqlxDB(t) + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + + txes, err := txStore.GetAllTxes(testutils.Context(t)) require.NoError(t, err) - require.Len(t, ethTxes, 0) + require.Len(t, txes, 0) } func TestFulfilledCheck(t *testing.T) { diff --git a/core/web/eth_keys_controller_test.go b/core/web/eth_keys_controller_test.go index d0ad27262f2..739af5820c9 100644 --- a/core/web/eth_keys_controller_test.go +++ b/core/web/eth_keys_controller_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" webpresenters "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -411,10 +412,12 @@ func TestETHKeysController_ChainSuccess_ResetWithAbandon(t *testing.T) { }) assert.NoError(t, err) - var count int - err = app.GetSqlxDB().Get(&count, `SELECT count(*) FROM evm.txes WHERE from_address = $1 AND state = 'fatal_error'`, addr) + db := app.GetSqlxDB() + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg.Database()) + + txes, err := txStore.FindTxesByFromAddressAndState(testutils.Context(t), addr, "fatal_error") require.NoError(t, err) - assert.Equal(t, 0, count) + require.Len(t, txes, 0) client := app.NewHTTPClient(nil) chainURL := url.URL{Path: "/v2/keys/evm/chain"} @@ -437,10 +440,12 @@ func TestETHKeysController_ChainSuccess_ResetWithAbandon(t *testing.T) { assert.Equal(t, cltest.FixtureChainID.String(), updatedKey.EVMChainID.String()) assert.Equal(t, false, updatedKey.Disabled) - var s string - err = app.GetSqlxDB().Get(&s, `SELECT error FROM evm.txes WHERE from_address = $1 AND state = 'fatal_error'`, addr) + txes, err = txStore.FindTxesByFromAddressAndState(testutils.Context(t), addr, "fatal_error") require.NoError(t, err) - assert.Equal(t, "abandoned", s) + require.Len(t, txes, 1) + + tx := txes[0] + assert.Equal(t, "abandoned", tx.Error.String) } func TestETHKeysController_ChainFailure_InvalidAbandon(t *testing.T) { diff --git a/core/web/evm_transfer_controller_test.go b/core/web/evm_transfer_controller_test.go index dd083a8cd63..cff996dd21c 100644 --- a/core/web/evm_transfer_controller_test.go +++ b/core/web/evm_transfer_controller_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -18,6 +20,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/web" @@ -68,7 +72,7 @@ func TestTransfersController_CreateSuccess_From(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Len(t, errors.Errors, 0) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) } func TestTransfersController_CreateSuccess_From_WEI(t *testing.T) { @@ -109,7 +113,7 @@ func TestTransfersController_CreateSuccess_From_WEI(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Len(t, errors.Errors, 0) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) } func TestTransfersController_CreateSuccess_From_BalanceMonitorDisabled(t *testing.T) { @@ -155,7 +159,7 @@ func TestTransfersController_CreateSuccess_From_BalanceMonitorDisabled(t *testin assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Len(t, errors.Errors, 0) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) } func TestTransfersController_TransferZeroAddressError(t *testing.T) { @@ -323,7 +327,7 @@ func TestTransfersController_CreateSuccess_eip1559(t *testing.T) { err = web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, resp), &resource) assert.NoError(t, err) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) // check returned data assert.NotEmpty(t, resource.Hash) @@ -393,3 +397,12 @@ func TestTransfersController_FindTxAttempt(t *testing.T) { assert.ErrorContains(t, err, "context canceled") }) } + +func validateTxCount(t *testing.T, db *sqlx.DB, count int) { + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + + txes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, txes, count) +} From e12a38c13fea3a0c60bb77a872b4eb71ac223741 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 14 Dec 2023 17:34:14 -0600 Subject: [PATCH 066/234] core/chains/evm/txmgr: fix race by waiting for goroutines to complete (#11580) --- core/chains/evm/txmgr/confirmer_test.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 30b2a391a7f..f1063ba0a79 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -2984,13 +2984,19 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + done := make(chan struct{}) + t.Cleanup(func() { <-done }) go func() { + defer close(done) err2 := ec.ResumePendingTaskRuns(testutils.Context(t), &head) - require.NoError(t, err2) + if !assert.NoError(t, err2) { + return + } // Retrieve Tx to check if callback completed flag was set to true updateTx, err3 := txStore.FindTxWithSequence(testutils.Context(t), fromAddress, nonce) - require.NoError(t, err3) - require.Equal(t, true, updateTx.CallbackCompleted) + if assert.NoError(t, err3) { + assert.Equal(t, true, updateTx.CallbackCompleted) + } }() select { @@ -3032,13 +3038,19 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + done := make(chan struct{}) + t.Cleanup(func() { <-done }) go func() { + defer close(done) err2 := ec.ResumePendingTaskRuns(testutils.Context(t), &head) - require.NoError(t, err2) + if !assert.NoError(t, err2) { + return + } // Retrieve Tx to check if callback completed flag was set to true updateTx, err3 := txStore.FindTxWithSequence(testutils.Context(t), fromAddress, nonce) - require.NoError(t, err3) - require.Equal(t, true, updateTx.CallbackCompleted) + if assert.NoError(t, err3) { + assert.Equal(t, true, updateTx.CallbackCompleted) + } }() select { From 413ebe25d43eb0ac5c6ca7c6d65bd6429b473f9d Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 15 Dec 2023 07:56:15 -0600 Subject: [PATCH 067/234] core/scripts: golangci-lint cleanup (#11583) --- .../scripts/chaincli/command/keeper/upkeep.go | 13 +-- core/scripts/chaincli/handler/handler.go | 32 ++--- core/scripts/chaincli/handler/keeper.go | 109 ++++++++---------- .../scripts/chaincli/handler/keeper_launch.go | 26 ++--- core/scripts/chaincli/handler/ocr2_config.go | 2 +- core/scripts/chaincli/handler/report.go | 14 ++- core/scripts/common/avalanche.go | 8 +- core/scripts/common/helpers.go | 30 ++--- core/scripts/common/vrf/model/model.go | 1 - core/scripts/common/vrf/setup-envs/main.go | 26 ++--- core/scripts/functions/src/delete_jobs.go | 5 +- .../functions/src/deploy_jobspecs_cmd.go | 17 +-- core/scripts/functions/src/fetching.go | 16 +-- .../functions/src/generate_ocr2_config_cmd.go | 3 +- core/scripts/gateway/client/send_request.go | 37 +++--- .../gateway/connector/run_connector.go | 17 ++- core/scripts/gateway/run_gateway.go | 12 +- core/scripts/ocr2vrf/setup_ocr2vrf.go | 4 +- core/scripts/ocr2vrf/util.go | 36 ------ core/scripts/vrfv2/testnet/main.go | 3 +- .../vrfv2/testnet/v2scripts/super_scripts.go | 9 +- core/scripts/vrfv2plus/testnet/main.go | 3 +- core/scripts/vrfv2plus/testnet/proofs.go | 5 +- .../testnet/v2plusscripts/super_scripts.go | 53 ++++----- .../vrfv2plus/testnet/v2plusscripts/util.go | 2 +- 25 files changed, 213 insertions(+), 270 deletions(-) diff --git a/core/scripts/chaincli/command/keeper/upkeep.go b/core/scripts/chaincli/command/keeper/upkeep.go index 8e2bc8bd993..c615bbf1aec 100644 --- a/core/scripts/chaincli/command/keeper/upkeep.go +++ b/core/scripts/chaincli/command/keeper/upkeep.go @@ -82,14 +82,11 @@ var ocr2UpkeepReportHistoryCmd = &cobra.Command{ hdlr := handler.NewBaseHandler(cfg) var hashes []string - var err error - var path string - - path, err = cmd.Flags().GetString("csv") + path, err := cmd.Flags().GetString("csv") if err == nil && len(path) != 0 { - rec, err := readCsvFile(path) - if err != nil { - log.Fatal(err) + rec, err2 := readCsvFile(path) + if err2 != nil { + log.Fatal(err2) } if len(rec) < 1 { @@ -107,7 +104,7 @@ var ocr2UpkeepReportHistoryCmd = &cobra.Command{ } } - if err := handler.OCR2AutomationReports(hdlr, hashes); err != nil { + if err = handler.OCR2AutomationReports(hdlr, hashes); err != nil { log.Fatalf("failed to collect transaction data: %s", err) } }, diff --git a/core/scripts/chaincli/handler/handler.go b/core/scripts/chaincli/handler/handler.go index f72e94605d4..6423df3aa23 100644 --- a/core/scripts/chaincli/handler/handler.go +++ b/core/scripts/chaincli/handler/handler.go @@ -411,44 +411,44 @@ func (h *baseHandler) launchChainlinkNode(ctx context.Context, port int, contain if writeLogs { var rdr io.ReadCloser - rdr, err := dockerClient.ContainerLogs(ctx, nodeContainerResp.ID, types.ContainerLogsOptions{ + rdr, err2 := dockerClient.ContainerLogs(ctx, nodeContainerResp.ID, types.ContainerLogsOptions{ ShowStderr: true, Timestamps: true, }) - if err != nil { + if err2 != nil { rdr.Close() - log.Fatal("Failed to collect logs from container: ", err) + log.Fatal("Failed to collect logs from container: ", err2) } - stdErr, err := os.OpenFile(fmt.Sprintf("./%s-stderr.log", nodeContainerResp.ID), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { + stdErr, err2 := os.OpenFile(fmt.Sprintf("./%s-stderr.log", nodeContainerResp.ID), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err2 != nil { rdr.Close() stdErr.Close() - log.Fatal("Failed to open file: ", err) + log.Fatal("Failed to open file: ", err2) } - if _, err := stdcopy.StdCopy(io.Discard, stdErr, rdr); err != nil { + if _, err2 := stdcopy.StdCopy(io.Discard, stdErr, rdr); err2 != nil { rdr.Close() stdErr.Close() - log.Fatal("Failed to write logs to file: ", err) + log.Fatal("Failed to write logs to file: ", err2) } rdr.Close() stdErr.Close() } - if err = dockerClient.ContainerStop(ctx, nodeContainerResp.ID, container.StopOptions{}); err != nil { - log.Fatal("Failed to stop node container: ", err) + if err2 := dockerClient.ContainerStop(ctx, nodeContainerResp.ID, container.StopOptions{}); err2 != nil { + log.Fatal("Failed to stop node container: ", err2) } - if err = dockerClient.ContainerRemove(ctx, nodeContainerResp.ID, types.ContainerRemoveOptions{}); err != nil { - log.Fatal("Failed to remove node container: ", err) + if err2 := dockerClient.ContainerRemove(ctx, nodeContainerResp.ID, types.ContainerRemoveOptions{}); err2 != nil { + log.Fatal("Failed to remove node container: ", err2) } - if err = dockerClient.ContainerStop(ctx, dbContainerResp.ID, container.StopOptions{}); err != nil { - log.Fatal("Failed to stop DB container: ", err) + if err2 := dockerClient.ContainerStop(ctx, dbContainerResp.ID, container.StopOptions{}); err2 != nil { + log.Fatal("Failed to stop DB container: ", err2) } - if err = dockerClient.ContainerRemove(ctx, dbContainerResp.ID, types.ContainerRemoveOptions{}); err != nil { - log.Fatal("Failed to remove DB container: ", err) + if err2 := dockerClient.ContainerRemove(ctx, dbContainerResp.ID, types.ContainerRemoveOptions{}); err2 != nil { + log.Fatal("Failed to remove DB container: ", err2) } }, nil } diff --git a/core/scripts/chaincli/handler/keeper.go b/core/scripts/chaincli/handler/keeper.go index ad8bd93649c..a5fb505adb4 100644 --- a/core/scripts/chaincli/handler/keeper.go +++ b/core/scripts/chaincli/handler/keeper.go @@ -528,13 +528,11 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, var registerUpkeepTx *types.Transaction var logUpkeepCounter *log_upkeep_counter_wrapper.LogUpkeepCounter var checkData []byte - var err error + switch k.cfg.UpkeepType { case config.Conditional: checkData = []byte(k.cfg.UpkeepCheckData) - if err != nil { - log.Fatal(err) - } + var err error if k.cfg.UpkeepAverageEligibilityCadence > 0 { upkeepAddr, deployUpkeepTx, _, err = upkeep.DeployUpkeepPerformCounterRestrictive( k.buildTxOpts(ctx), @@ -570,9 +568,7 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, } case config.Mercury: checkData = []byte(k.cfg.UpkeepCheckData) - if err != nil { - log.Fatal(err) - } + var err error if k.cfg.VerifiableLoadTest { upkeepAddr, deployUpkeepTx, _, err = verifiable_load_streams_lookup_upkeep_wrapper.DeployVerifiableLoadStreamsLookupUpkeep( k.buildTxOpts(ctx), @@ -603,6 +599,7 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, log.Fatal(i, upkeepAddr.Hex(), ": RegisterUpkeep failed - ", err) } case config.LogTrigger: + var err error upkeepAddr, deployUpkeepTx, logUpkeepCounter, err = log_upkeep_counter_wrapper.DeployLogUpkeepCounter( k.buildTxOpts(ctx), k.client, @@ -637,7 +634,7 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, if err != nil { log.Fatal("failed to start log upkeep counter", err) } - if err := k.waitTx(ctx, logUpkeepStartTx); err != nil { + if err = k.waitTx(ctx, logUpkeepStartTx); err != nil { log.Fatalf("Log upkeep Start() failed for upkeepId: %s, error is %s", upkeepAddr.Hex(), err.Error()) } log.Println(i, upkeepAddr.Hex(), ": Log upkeep successfully started - ", helpers.ExplorerLink(k.cfg.ChainID, logUpkeepStartTx.Hash())) @@ -653,32 +650,34 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, upkeepAddrs = append(upkeepAddrs, upkeepAddr) } - var err error var upkeepGetter activeUpkeepGetter upkeepCount := big.NewInt(k.cfg.UpkeepCount) // second arg in GetActiveUpkeepIds (on registry) - switch k.cfg.RegistryVersion { - case keeper.RegistryVersion_1_1: - panic("not supported 1.1 registry") - case keeper.RegistryVersion_1_2: - upkeepGetter, err = registry12.NewKeeperRegistry( - registryAddr, - k.client, - ) - case keeper.RegistryVersion_2_0: - upkeepGetter, err = registry20.NewKeeperRegistry( - registryAddr, - k.client, - ) - case keeper.RegistryVersion_2_1: - upkeepGetter, err = iregistry21.NewIKeeperRegistryMaster( - registryAddr, - k.client, - ) - default: - panic("unexpected registry address") - } - if err != nil { - log.Fatal("Registry failed: ", err) + { + var err error + switch k.cfg.RegistryVersion { + case keeper.RegistryVersion_1_1: + panic("not supported 1.1 registry") + case keeper.RegistryVersion_1_2: + upkeepGetter, err = registry12.NewKeeperRegistry( + registryAddr, + k.client, + ) + case keeper.RegistryVersion_2_0: + upkeepGetter, err = registry20.NewKeeperRegistry( + registryAddr, + k.client, + ) + case keeper.RegistryVersion_2_1: + upkeepGetter, err = iregistry21.NewIKeeperRegistryMaster( + registryAddr, + k.client, + ) + default: + panic("unexpected registry address") + } + if err != nil { + log.Fatal("Registry failed: ", err) + } } activeUpkeepIds := k.getActiveUpkeepIds(ctx, upkeepGetter, big.NewInt(existingCount), upkeepCount) @@ -724,22 +723,24 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, } for _, id := range activeUpkeepIds { - tx, err := reg21.SetUpkeepPrivilegeConfig(k.buildTxOpts(ctx), id, adminBytes) - if err != nil { - log.Fatalf("failed to upkeep privilege config: %v", err) + tx, err2 := reg21.SetUpkeepPrivilegeConfig(k.buildTxOpts(ctx), id, adminBytes) + if err2 != nil { + log.Fatalf("failed to upkeep privilege config: %v", err2) } - err = k.waitTx(ctx, tx) - if err != nil { - log.Fatalf("failed to wait for tx: %v", err) - } else { - log.Printf("upkeep privilege config is set for %s", id.String()) + err2 = k.waitTx(ctx, tx) + if err2 != nil { + log.Fatalf("failed to wait for tx: %v", err2) } + log.Printf("upkeep privilege config is set for %s", id.String()) - info, err := reg21.GetUpkeep(nil, id) - if err != nil { - log.Fatalf("failed to fetch upkeep id %s from registry 2.1: %v", id, err) + info, err2 := reg21.GetUpkeep(nil, id) + if err2 != nil { + log.Fatalf("failed to fetch upkeep id %s from registry 2.1: %v", id, err2) + } + min, err2 := reg21.GetMinBalanceForUpkeep(nil, id) + if err2 != nil { + log.Fatalf("failed to fetch upkeep id %s from registry 2.1: %v", id, err2) } - min, err := reg21.GetMinBalanceForUpkeep(nil, id) log.Printf(" Balance: %s", info.Balance) log.Printf("Min Balance: %s", min.String()) } @@ -757,7 +758,7 @@ func (k *Keeper) setKeepers(ctx context.Context, cls []cmd.HTTPClient, deployer log.Fatal("SetKeepers failed: ", err) } - if err := k.waitTx(ctx, setKeepersTx); err != nil { + if err = k.waitTx(ctx, setKeepersTx); err != nil { log.Fatalf("SetKeepers failed, error is: %s", err.Error()) } @@ -777,24 +778,6 @@ func (k *Keeper) keepers() ([]common.Address, []common.Address) { return addrs, fromAddrs } -// createKeeperJobOnExistingNode connect to existing node to create keeper job -func (k *Keeper) createKeeperJobOnExistingNode(urlStr, email, password, registryAddr, nodeAddr string) error { - lggr, closeLggr := logger.NewLogger() - logger.Sugared(lggr).ErrorIfFn(closeLggr, "Failed to close logger") - - cl, err := authenticate(urlStr, email, password, lggr) - if err != nil { - return err - } - - if err = k.createKeeperJob(cl, registryAddr, nodeAddr); err != nil { - log.Println("Failed to create keeper job: ", err) - return err - } - - return nil -} - type activeUpkeepGetter interface { Address() common.Address GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) diff --git a/core/scripts/chaincli/handler/keeper_launch.go b/core/scripts/chaincli/handler/keeper_launch.go index 22b67763945..ca0d5c403f1 100644 --- a/core/scripts/chaincli/handler/keeper_launch.go +++ b/core/scripts/chaincli/handler/keeper_launch.go @@ -100,14 +100,12 @@ func (k *Keeper) LaunchAndTest(ctx context.Context, withdraw, printLogs, force, if len(k.cfg.KeeperKeys) > 0 { // import key if exists - var err error nodeAddrHex, err = k.addKeyToKeeper(cl, k.cfg.KeeperKeys[i]) if err != nil { log.Fatal("could not add key to keeper", err) } } else { // get node's default wallet address - var err error nodeAddrHex, err = getNodeAddress(cl) if err != nil { log.Println("Failed to get node addr: ", err) @@ -220,35 +218,35 @@ func (k *Keeper) LaunchAndTest(ctx context.Context, withdraw, printLogs, force, // cancelAndWithdrawActiveUpkeeps cancels all active upkeeps and withdraws funds for registry 1.2 func (k *Keeper) cancelAndWithdrawActiveUpkeeps(ctx context.Context, activeUpkeepIds []*big.Int, canceller canceller) error { - var err error for i := 0; i < len(activeUpkeepIds); i++ { - var tx *ethtypes.Transaction upkeepId := activeUpkeepIds[i] - if tx, err = canceller.CancelUpkeep(k.buildTxOpts(ctx), upkeepId); err != nil { + tx, err := canceller.CancelUpkeep(k.buildTxOpts(ctx), upkeepId) + if err != nil { return fmt.Errorf("failed to cancel upkeep %s: %s", upkeepId.String(), err) } - if err := k.waitTx(ctx, tx); err != nil { + if err = k.waitTx(ctx, tx); err != nil { log.Fatalf("failed to cancel upkeep for upkeepId: %s, error is: %s", upkeepId.String(), err.Error()) } - if tx, err = canceller.WithdrawFunds(k.buildTxOpts(ctx), upkeepId, k.fromAddr); err != nil { + tx, err = canceller.WithdrawFunds(k.buildTxOpts(ctx), upkeepId, k.fromAddr) + if err != nil { return fmt.Errorf("failed to withdraw upkeep %s: %s", upkeepId.String(), err) } - if err := k.waitTx(ctx, tx); err != nil { + if err = k.waitTx(ctx, tx); err != nil { log.Fatalf("failed to withdraw upkeep for upkeepId: %s, error is: %s", upkeepId.String(), err.Error()) } log.Printf("Upkeep %s successfully canceled and refunded: ", upkeepId.String()) } - var tx *ethtypes.Transaction - if tx, err = canceller.RecoverFunds(k.buildTxOpts(ctx)); err != nil { + tx, err := canceller.RecoverFunds(k.buildTxOpts(ctx)) + if err != nil { return fmt.Errorf("failed to recover funds: %s", err) } - if err := k.waitTx(ctx, tx); err != nil { + if err = k.waitTx(ctx, tx); err != nil { log.Fatalf("failed to recover funds, error is: %s", err.Error()) } @@ -264,7 +262,7 @@ func (k *Keeper) cancelAndWithdrawUpkeeps(ctx context.Context, upkeepCount *big. return fmt.Errorf("failed to cancel upkeep %d: %s", i, err) } - if err := k.waitTx(ctx, tx); err != nil { + if err = k.waitTx(ctx, tx); err != nil { log.Fatalf("failed to cancel upkeep, error is: %s", err.Error()) } @@ -272,7 +270,7 @@ func (k *Keeper) cancelAndWithdrawUpkeeps(ctx context.Context, upkeepCount *big. return fmt.Errorf("failed to withdraw upkeep %d: %s", i, err) } - if err := k.waitTx(ctx, tx); err != nil { + if err = k.waitTx(ctx, tx); err != nil { log.Fatalf("failed to withdraw upkeep, error is: %s", err.Error()) } @@ -284,7 +282,7 @@ func (k *Keeper) cancelAndWithdrawUpkeeps(ctx context.Context, upkeepCount *big. return fmt.Errorf("failed to recover funds: %s", err) } - if err := k.waitTx(ctx, tx); err != nil { + if err = k.waitTx(ctx, tx); err != nil { log.Fatalf("failed to recover funds, error is: %s", err.Error()) } diff --git a/core/scripts/chaincli/handler/ocr2_config.go b/core/scripts/chaincli/handler/ocr2_config.go index 21b2ad414a3..caa96112ea8 100644 --- a/core/scripts/chaincli/handler/ocr2_config.go +++ b/core/scripts/chaincli/handler/ocr2_config.go @@ -54,7 +54,7 @@ func OCR2GetConfig(hdlr *baseHandler, registry_addr string) error { func configFromBlock(bl *types.Block, addr common.Address, detail keeper_registry_wrapper2_0.LatestConfigDetails) (*confighelper.PublicConfig, error) { for _, tx := range bl.Transactions() { - if tx.To() != nil && bytes.Compare(tx.To()[:], addr[:]) == 0 { + if tx.To() != nil && bytes.Equal(tx.To()[:], addr[:]) { // this is our transaction // txRes, txErr, err := getTransactionDetailForHashes(hdlr, []string{tx}) ocr2Tx, err := NewBaseOCR2Tx(tx) diff --git a/core/scripts/chaincli/handler/report.go b/core/scripts/chaincli/handler/report.go index 10970b5f03b..1dcbb21ee83 100644 --- a/core/scripts/chaincli/handler/report.go +++ b/core/scripts/chaincli/handler/report.go @@ -85,24 +85,26 @@ func OCR2AutomationReports(hdlr *baseHandler, txs []string) error { } txRes, txErr, err = getSimulationsForTxs(hdlr, simBatch) + if err != nil { + return err + } for i := range txRes { if txErr[i] == nil { continue } - err, ok := txErr[i].(JsonError) + err2, ok := txErr[i].(JsonError) //nolint:errorlint if ok { - decoded, err := hexutil.Decode(err.ErrorData().(string)) + decoded, err := hexutil.Decode(err2.ErrorData().(string)) if err != nil { elements[i].Err = err.Error() continue } elements[i].Err = ocr2Txs[i].DecodeError(decoded) - } else if err != nil { - elements[i].Err = err.Error() + } else if err2 != nil { + elements[i].Err = err2.Error() } - } data := make([][]string, len(elements)) @@ -275,7 +277,7 @@ func (t *OCR2Transaction) DecodeError(b []byte) string { } } - return fmt.Sprintf("%s", j) + return j } func NewOCR2TransmitTx(raw map[string]interface{}) (*OCR2TransmitTx, error) { diff --git a/core/scripts/common/avalanche.go b/core/scripts/common/avalanche.go index c9c53779905..8699463c365 100644 --- a/core/scripts/common/avalanche.go +++ b/core/scripts/common/avalanche.go @@ -79,8 +79,8 @@ func (b *AvaBloom) UnmarshalText(input []byte) error { // bloomValues returns the bytes (index-value pairs) to set for the given data func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) { sha := crypto.NewKeccakState() - sha.Write(data) - sha.Read(hashbuf) + sha.Write(data) //nolint:errcheck + sha.Read(hashbuf) //nolint:errcheck // The actual bits to flip v1 := byte(1 << (hashbuf[1] & 0x7)) v2 := byte(1 << (hashbuf[3] & 0x7)) @@ -259,7 +259,7 @@ func (h *AvaHeader) Hash() common.Hash { func rlpHash(x interface{}) (h common.Hash) { sha := crypto.NewKeccakState() sha.Reset() - rlp.Encode(sha, x) - sha.Read(h[:]) + rlp.Encode(sha, x) //nolint:errcheck + sha.Read(h[:]) //nolint:errcheck return h } diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go index b5a3b1ed943..9d2d06c89d0 100644 --- a/core/scripts/common/helpers.go +++ b/core/scripts/common/helpers.go @@ -395,12 +395,12 @@ func FundNode(e Environment, address string, fundingAmount *big.Int) { var gasLimit uint64 if IsArbitrumChainID(e.ChainID) { to := common.HexToAddress(address) - estimated, err := e.Ec.EstimateGas(context.Background(), ethereum.CallMsg{ + estimated, err2 := e.Ec.EstimateGas(context.Background(), ethereum.CallMsg{ From: e.Owner.From, To: &to, Value: fundingAmount, }) - PanicErr(err) + PanicErr(err2) gasLimit = estimated } else { gasLimit = uint64(21_000) @@ -461,7 +461,7 @@ func GetRlpHeaders(env Environment, blockNumbers []*big.Int, getParentBlocks boo hashes = make([]string, 0) - var offset *big.Int = big.NewInt(0) + offset := big.NewInt(0) if getParentBlocks { offset = big.NewInt(1) } @@ -475,15 +475,15 @@ func GetRlpHeaders(env Environment, blockNumbers []*big.Int, getParentBlocks boo var h AvaHeader // Get child block since it's the one that has the parent hash in its header. nextBlockNum := new(big.Int).Set(blockNum).Add(blockNum, offset) - err := env.Jc.CallContext(context.Background(), &h, "eth_getBlockByNumber", hexutil.EncodeBig(nextBlockNum), false) - if err != nil { - return nil, hashes, fmt.Errorf("failed to get header: %+v", err) + err2 := env.Jc.CallContext(context.Background(), &h, "eth_getBlockByNumber", hexutil.EncodeBig(nextBlockNum), false) + if err2 != nil { + return nil, hashes, fmt.Errorf("failed to get header: %+v", err2) } // We can still use vanilla go-ethereum rlp.EncodeToBytes, see e.g // https://github.com/ava-labs/coreth/blob/e3ca41bf5295a9a7ca1aeaf29d541fcbb94f79b1/core/types/hashing.go#L49-L57. - rlpHeader, err = rlp.EncodeToBytes(h) - if err != nil { - return nil, hashes, fmt.Errorf("failed to encode rlp: %+v", err) + rlpHeader, err2 = rlp.EncodeToBytes(h) + if err2 != nil { + return nil, hashes, fmt.Errorf("failed to encode rlp: %+v", err2) } hashes = append(hashes, h.Hash().String()) @@ -509,16 +509,16 @@ func GetRlpHeaders(env Environment, blockNumbers []*big.Int, getParentBlocks boo } else { // Get child block since it's the one that has the parent hash in its header. - h, err := env.Ec.HeaderByNumber( + h, err2 := env.Ec.HeaderByNumber( context.Background(), new(big.Int).Set(blockNum).Add(blockNum, offset), ) - if err != nil { - return nil, hashes, fmt.Errorf("failed to get header: %+v", err) + if err2 != nil { + return nil, hashes, fmt.Errorf("failed to get header: %+v", err2) } - rlpHeader, err = rlp.EncodeToBytes(h) - if err != nil { - return nil, hashes, fmt.Errorf("failed to encode rlp: %+v", err) + rlpHeader, err2 = rlp.EncodeToBytes(h) + if err2 != nil { + return nil, hashes, fmt.Errorf("failed to encode rlp: %+v", err2) } hashes = append(hashes, h.Hash().String()) diff --git a/core/scripts/common/vrf/model/model.go b/core/scripts/common/vrf/model/model.go index 0972c47e618..1b4e59fe4f3 100644 --- a/core/scripts/common/vrf/model/model.go +++ b/core/scripts/common/vrf/model/model.go @@ -21,7 +21,6 @@ type Node struct { NumberOfSendingKeysToCreate int SendingKeyFundingAmount *big.Int VrfKeys []string - jobSpec string } type SendingKey struct { diff --git a/core/scripts/common/vrf/setup-envs/main.go b/core/scripts/common/vrf/setup-envs/main.go index e9ea252e4c4..598238bebef 100644 --- a/core/scripts/common/vrf/setup-envs/main.go +++ b/core/scripts/common/vrf/setup-envs/main.go @@ -50,7 +50,6 @@ func newApp(remoteNodeURL string, writer io.Writer) (*clcmd.Shell, *cli.App) { var ( checkMarkEmoji = "✅" xEmoji = "❌" - infoEmoji = "ℹ️" ) func main() { @@ -142,7 +141,7 @@ func main() { output := &bytes.Buffer{} for key, node := range nodesMap { - + node := node client, app := connectToNode(&node.URL, output, node.CredsFile) ethKeys := createETHKeysIfNeeded(client, app, output, numEthKeys, &node.URL, maxGasPriceGwei) if key == model.VRFPrimaryNodeName { @@ -242,6 +241,7 @@ func main() { } for key, node := range nodesMap { + node := node client, app := connectToNode(&node.URL, output, node.CredsFile) //GET ALL JOBS @@ -318,7 +318,7 @@ func getVRFKeys(client *clcmd.Shell, app *cli.App, output *bytes.Buffer) []prese } func createJob(jobSpec string, client *clcmd.Shell, app *cli.App, output *bytes.Buffer) { - if err := os.WriteFile("job-spec.toml", []byte(jobSpec), 0666); err != nil { + if err := os.WriteFile("job-spec.toml", []byte(jobSpec), 0666); err != nil { //nolint:gosec helpers.PanicErr(err) } job := presenters.JobResource{} @@ -332,7 +332,7 @@ func createJob(jobSpec string, client *clcmd.Shell, app *cli.App, output *bytes. } func exportVRFKey(client *clcmd.Shell, app *cli.App, vrfKey presenters.VRFKeyResource, output *bytes.Buffer) { - if err := os.WriteFile("vrf-key-password.txt", []byte("twochains"), 0666); err != nil { + if err := os.WriteFile("vrf-key-password.txt", []byte("twochains"), 0666); err != nil { //nolint:gosec helpers.PanicErr(err) } flagSet := flag.NewFlagSet("blah", flag.ExitOnError) @@ -346,7 +346,7 @@ func exportVRFKey(client *clcmd.Shell, app *cli.App, vrfKey presenters.VRFKeyRes } func importVRFKey(client *clcmd.Shell, app *cli.App, output *bytes.Buffer) { - if err := os.WriteFile("vrf-key-password.txt", []byte("twochains"), 0666); err != nil { + if err := os.WriteFile("vrf-key-password.txt", []byte("twochains"), 0666); err != nil { //nolint:gosec helpers.PanicErr(err) } flagSet := flag.NewFlagSet("blah", flag.ExitOnError) @@ -465,12 +465,8 @@ func createVRFKeyIfNeeded(client *clcmd.Shell, app *cli.App, output *bytes.Buffe }(), ", ")) } fmt.Println() - for _, vrfKey := range vrfKeys { - allVRFKeys = append(allVRFKeys, vrfKey) - } - for _, nk := range newKeys { - allVRFKeys = append(allVRFKeys, nk) - } + allVRFKeys = append(allVRFKeys, vrfKeys...) + allVRFKeys = append(allVRFKeys, newKeys...) return allVRFKeys } @@ -526,11 +522,7 @@ func createETHKeysIfNeeded(client *clcmd.Shell, app *cli.App, output *bytes.Buff } output.Reset() fmt.Println() - for _, ethKey := range ethKeys { - allETHKeysNode = append(allETHKeysNode, ethKey) - } - for _, nk := range newKeys { - allETHKeysNode = append(allETHKeysNode, nk) - } + allETHKeysNode = append(allETHKeysNode, ethKeys...) + allETHKeysNode = append(allETHKeysNode, newKeys...) return allETHKeysNode } diff --git a/core/scripts/functions/src/delete_jobs.go b/core/scripts/functions/src/delete_jobs.go index 632a239a62b..4fac1a4a772 100644 --- a/core/scripts/functions/src/delete_jobs.go +++ b/core/scripts/functions/src/delete_jobs.go @@ -62,7 +62,7 @@ func (g *deleteJobs) Run(args []string) { output.Reset() fileFs := flag.NewFlagSet("test", flag.ExitOnError) - client.ListJobs(cli.NewContext(app, fileFs, nil)) + err = client.ListJobs(cli.NewContext(app, fileFs, nil)) helpers.PanicErr(err) var parsed []JobSpec @@ -73,7 +73,8 @@ func (g *deleteJobs) Run(args []string) { if jobSpec.BootstrapSpec.ContractID == *contractAddress || jobSpec.OffChainReporting2OracleSpec.ContractID == *contractAddress { fmt.Println("Deleting job ID:", jobSpec.Id, "name:", jobSpec.Name) set := flag.NewFlagSet("test", flag.ExitOnError) - set.Parse([]string{jobSpec.Id}) + err = set.Parse([]string{jobSpec.Id}) + helpers.PanicErr(err) err = client.DeleteJob(cli.NewContext(app, set, nil)) helpers.PanicErr(err) } diff --git a/core/scripts/functions/src/deploy_jobspecs_cmd.go b/core/scripts/functions/src/deploy_jobspecs_cmd.go index 0039aaa4601..e892370cc22 100644 --- a/core/scripts/functions/src/deploy_jobspecs_cmd.go +++ b/core/scripts/functions/src/deploy_jobspecs_cmd.go @@ -27,18 +27,18 @@ func (g *deployJobSpecs) Name() string { func (g *deployJobSpecs) Run(args []string) { fs := flag.NewFlagSet(g.Name(), flag.ContinueOnError) nodesFile := fs.String("nodes", "", "a file containing nodes urls, logins and passwords") - err := fs.Parse(args) - if err != nil || nodesFile == nil || *nodesFile == "" { + + if err := fs.Parse(args); err != nil || nodesFile == nil || *nodesFile == "" { fs.Usage() os.Exit(1) } nodes := mustReadNodesList(*nodesFile) - for _, node := range nodes { + for _, n := range nodes { output := &bytes.Buffer{} - client, app := newApp(node, output) + client, app := newApp(n, output) - fmt.Println("Logging in:", node.url) + fmt.Println("Logging in:", n.url) loginFs := flag.NewFlagSet("test", flag.ContinueOnError) loginFs.Bool("bypass-version-check", true, "") loginCtx := cli.NewContext(app, loginFs, nil) @@ -46,18 +46,19 @@ func (g *deployJobSpecs) Run(args []string) { helpers.PanicErr(err) output.Reset() - tomlPath := filepath.Join(artefactsDir, node.url.Host+".toml") + tomlPath := filepath.Join(artefactsDir, n.url.Host+".toml") tomlPath, err = filepath.Abs(tomlPath) if err != nil { helpers.PanicErr(err) } fmt.Println("Deploying jobspec:", tomlPath) - if _, err := os.Stat(tomlPath); err != nil { + if _, err = os.Stat(tomlPath); err != nil { helpers.PanicErr(errors.New("toml file does not exist")) } fileFs := flag.NewFlagSet("test", flag.ExitOnError) - fileFs.Parse([]string{tomlPath}) + err = fileFs.Parse([]string{tomlPath}) + helpers.PanicErr(err) err = client.CreateJob(cli.NewContext(app, fileFs, nil)) helpers.PanicErr(err) output.Reset() diff --git a/core/scripts/functions/src/fetching.go b/core/scripts/functions/src/fetching.go index cbe491fff5e..2e0841b49de 100644 --- a/core/scripts/functions/src/fetching.go +++ b/core/scripts/functions/src/fetching.go @@ -24,11 +24,11 @@ type ocr2Bundle struct { } func mustFetchNodesKeys(chainID int64, nodes []*node) (nca []NodeKeys) { - for _, node := range nodes { + for _, n := range nodes { output := &bytes.Buffer{} - client, app := newApp(node, output) + client, app := newApp(n, output) - fmt.Println("Logging in:", node.url) + fmt.Println("Logging in:", n.url) loginFs := flag.NewFlagSet("test", flag.ContinueOnError) loginFs.Bool("bypass-version-check", true, "") loginCtx := cli.NewContext(app, loginFs, nil) @@ -68,7 +68,7 @@ func mustFetchNodesKeys(chainID int64, nodes []*node) (nca []NodeKeys) { if ocr2BundleIndex == -1 { helpers.PanicErr(errors.New("node must have EVM OCR2 bundle")) } - ocr2Bundle := ocr2Bundles[ocr2BundleIndex] + ocr2Bndl := ocr2Bundles[ocr2BundleIndex] output.Reset() err = client.ListCSAKeys(&cli.Context{ @@ -84,10 +84,10 @@ func mustFetchNodesKeys(chainID int64, nodes []*node) (nca []NodeKeys) { nc := NodeKeys{ EthAddress: ethAddress, P2PPeerID: peerID, - OCR2BundleID: ocr2Bundle.ID, - OCR2ConfigPublicKey: strings.TrimPrefix(ocr2Bundle.ConfigPublicKey, "ocr2cfg_evm_"), - OCR2OnchainPublicKey: strings.TrimPrefix(ocr2Bundle.OnchainPublicKey, "ocr2on_evm_"), - OCR2OffchainPublicKey: strings.TrimPrefix(ocr2Bundle.OffchainPublicKey, "ocr2off_evm_"), + OCR2BundleID: ocr2Bndl.ID, + OCR2ConfigPublicKey: strings.TrimPrefix(ocr2Bndl.ConfigPublicKey, "ocr2cfg_evm_"), + OCR2OnchainPublicKey: strings.TrimPrefix(ocr2Bndl.OnchainPublicKey, "ocr2on_evm_"), + OCR2OffchainPublicKey: strings.TrimPrefix(ocr2Bndl.OffchainPublicKey, "ocr2off_evm_"), CSAPublicKey: csaPubKey, } diff --git a/core/scripts/functions/src/generate_ocr2_config_cmd.go b/core/scripts/functions/src/generate_ocr2_config_cmd.go index 1cfddf4413c..7ac3b68d11d 100644 --- a/core/scripts/functions/src/generate_ocr2_config_cmd.go +++ b/core/scripts/functions/src/generate_ocr2_config_cmd.go @@ -134,8 +134,7 @@ func (g *generateOCR2Config) Run(args []string) { keysFile := fs.String("keys", "", "a file containing nodes public keys") configFile := fs.String("config", "", "a file containing JSON config") chainID := fs.Int64("chainid", 80001, "chain id") - err := fs.Parse(args) - if err != nil || (*nodesFile == "" && *keysFile == "") || *configFile == "" || chainID == nil { + if err := fs.Parse(args); err != nil || (*nodesFile == "" && *keysFile == "") || *configFile == "" || chainID == nil { fs.Usage() os.Exit(1) } diff --git a/core/scripts/gateway/client/send_request.go b/core/scripts/gateway/client/send_request.go index 8ab4e8bce79..eeb85ff0dcb 100644 --- a/core/scripts/gateway/client/send_request.go +++ b/core/scripts/gateway/client/send_request.go @@ -52,7 +52,6 @@ func main() { var s4SetPayload []byte if *methodName == functions.MethodSecretsSet { - var err error s4SetPayload, err = os.ReadFile(*s4SetPayloadFile) if err != nil { fmt.Println("error reading S4 payload file", err) @@ -70,23 +69,21 @@ func main() { Payload: s4SetPayload, Expiration: time.Now().UnixMilli() + *s4SetExpirationPeriod, } - signature, err := envelope.Sign(key) - if err != nil { - fmt.Println("error signing S4 envelope", err) + signature, err2 := envelope.Sign(key) + if err2 != nil { + fmt.Println("error signing S4 envelope", err2) return } - s4SetPayload := functions.SecretsSetRequest{ + payloadJSON, err2 = json.Marshal(functions.SecretsSetRequest{ SlotID: envelope.SlotID, Version: envelope.Version, Expiration: envelope.Expiration, Payload: s4SetPayload, Signature: signature, - } - - payloadJSON, err = json.Marshal(s4SetPayload) - if err != nil { - fmt.Println("error marshaling S4 payload", err) + }) + if err2 != nil { + fmt.Println("error marshaling S4 payload", err2) return } } @@ -122,27 +119,27 @@ func main() { client := &http.Client{} sendRequest := func() { - req, err := createRequest() - if err != nil { - fmt.Println("error creating a request", err) + req, err2 := createRequest() + if err2 != nil { + fmt.Println("error creating a request", err2) return } - resp, err := client.Do(req) - if err != nil { - fmt.Println("error sending a request", err) + resp, err2 := client.Do(req) + if err2 != nil { + fmt.Println("error sending a request", err2) return } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - fmt.Println("error sending a request", err) + body, err2 := io.ReadAll(resp.Body) + if err2 != nil { + fmt.Println("error sending a request", err2) return } var prettyJSON bytes.Buffer - if err := json.Indent(&prettyJSON, body, "", " "); err != nil { + if err2 = json.Indent(&prettyJSON, body, "", " "); err2 != nil { fmt.Println(string(body)) } else { fmt.Println(prettyJSON.String()) diff --git a/core/scripts/gateway/connector/run_connector.go b/core/scripts/gateway/connector/run_connector.go index 248162ef822..c6ad187461c 100644 --- a/core/scripts/gateway/connector/run_connector.go +++ b/core/scripts/gateway/connector/run_connector.go @@ -31,7 +31,10 @@ type client struct { func (h *client) HandleGatewayMessage(ctx context.Context, gatewayId string, msg *api.Message) { h.lggr.Infof("received message from gateway %s. Echoing back.", gatewayId) - h.connector.SendToGateway(context.Background(), gatewayId, msg) + err := h.connector.SendToGateway(ctx, gatewayId, msg) + if err != nil { + h.lggr.Errorw("failed to send to gateway", "id", gatewayId, "err", err) + } } func (h *client) Sign(data ...[]byte) ([]byte, error) { @@ -70,8 +73,16 @@ func main() { client.connector = connector ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt) - connector.Start(ctx) + err = connector.Start(ctx) + if err != nil { + fmt.Println("error staring connector:", err) + return + } <-ctx.Done() - connector.Close() + err = connector.Close() + if err != nil { + fmt.Println("error closing connector:", err) + return + } } diff --git a/core/scripts/gateway/run_gateway.go b/core/scripts/gateway/run_gateway.go index 1f0230bb8ad..e30c43bb8af 100644 --- a/core/scripts/gateway/run_gateway.go +++ b/core/scripts/gateway/run_gateway.go @@ -56,8 +56,16 @@ func main() { } ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt) - gw.Start(ctx) + err = gw.Start(ctx) + if err != nil { + fmt.Println("error staring gateway:", err) + return + } <-ctx.Done() - gw.Close() + err = gw.Close() + if err != nil { + fmt.Println("error closing gateway:", err) + return + } } diff --git a/core/scripts/ocr2vrf/setup_ocr2vrf.go b/core/scripts/ocr2vrf/setup_ocr2vrf.go index ab43dc0180f..1094b823b4e 100644 --- a/core/scripts/ocr2vrf/setup_ocr2vrf.go +++ b/core/scripts/ocr2vrf/setup_ocr2vrf.go @@ -232,9 +232,7 @@ func setupOCR2VRFNodes(e helpers.Environment) { transmitters[i+1] = f.String() } } else { - for _, t := range transmitters[1:] { - nodesToFund = append(nodesToFund, t) - } + nodesToFund = append(nodesToFund, transmitters[1:]...) } var payees []common.Address diff --git a/core/scripts/ocr2vrf/util.go b/core/scripts/ocr2vrf/util.go index 4ec78472e50..ef71327b52b 100644 --- a/core/scripts/ocr2vrf/util.go +++ b/core/scripts/ocr2vrf/util.go @@ -306,14 +306,6 @@ func findSubscriptionID(e helpers.Environment, vrfCoordinatorAddr string) *big.I return subscriptionIterator.Event.SubId } -func registerMigratableCoordinator(e helpers.Environment, coordinatorAddress, migratableCoordinatorAddress string) { - coordinator := newVRFCoordinator(common.HexToAddress(coordinatorAddress), e.Ec) - - tx, err := coordinator.RegisterMigratableCoordinator(e.Owner, common.HexToAddress(migratableCoordinatorAddress)) - helpers.PanicErr(err) - helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID) -} - func addConsumer(e helpers.Environment, vrfCoordinatorAddr, consumerAddr string, subId *big.Int) { coordinator := newVRFCoordinator(common.HexToAddress(vrfCoordinatorAddr), e.Ec) @@ -330,34 +322,6 @@ func setPayees(e helpers.Environment, vrfBeaconAddr string, transmitters, payees helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID) } -func setBeaconBilling(e helpers.Environment, vrfBeaconAddr string, maximumGasPrice, reasonableGasPrice, observationPayment, - transmissionPayment, accountingGas uint64) { - beacon := newVRFBeacon(common.HexToAddress(vrfBeaconAddr), e.Ec) - - tx, err := beacon.SetBilling(e.Owner, maximumGasPrice, reasonableGasPrice, observationPayment, transmissionPayment, big.NewInt(0).SetUint64(accountingGas)) - helpers.PanicErr(err) - helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID) -} - -func setCoordinatorBilling(e helpers.Environment, vrfCoordinatorAddr string, useReasonableGasPrice bool, unusedGasPenaltyPercent uint8, - stalenessSeconds, redeemableRequestGasOverhead, callbackRequestGasOverhead, premiumPercentage, reasonableGasPriceStalenessBlocks uint32, - fallbackWeiPerUnitLink *big.Int) { - coordinator := newVRFCoordinator(common.HexToAddress(vrfCoordinatorAddr), e.Ec) - - tx, err := coordinator.SetCoordinatorConfig(e.Owner, vrf_coordinator.VRFBeaconTypesCoordinatorConfig{ - UseReasonableGasPrice: useReasonableGasPrice, - UnusedGasPenaltyPercent: unusedGasPenaltyPercent, - StalenessSeconds: stalenessSeconds, - RedeemableRequestGasOverhead: redeemableRequestGasOverhead, - CallbackRequestGasOverhead: callbackRequestGasOverhead, - PremiumPercentage: uint8(premiumPercentage), - ReasonableGasPriceStalenessBlocks: reasonableGasPriceStalenessBlocks, - FallbackWeiPerUnitLink: fallbackWeiPerUnitLink, - }) - helpers.PanicErr(err) - helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID) -} - func eoaFundSubscription(e helpers.Environment, coordinatorAddress, linkAddress string, amount, subID *big.Int) { linkToken, err := link_token_interface.NewLinkToken(common.HexToAddress(linkAddress), e.Ec) helpers.PanicErr(err) diff --git a/core/scripts/vrfv2/testnet/main.go b/core/scripts/vrfv2/testnet/main.go index 5856256504b..4607c09e6e0 100644 --- a/core/scripts/vrfv2/testnet/main.go +++ b/core/scripts/vrfv2/testnet/main.go @@ -1088,9 +1088,8 @@ func main() { return true } else if strings.Contains(err.Error(), "execution reverted") { return false - } else { - panic(err) } + panic(err) } result := helpers.BinarySearch(assets.Ether(int64(*start*2)).ToInt(), big.NewInt(0), isWithdrawable) diff --git a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go index 4a1c1fcec41..2608873fcfd 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go +++ b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/util" evmtypes "github.com/ethereum/go-ethereum/core/types" + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" @@ -297,7 +298,7 @@ func VRFV2DeployUniverse( tx, err := vrfOwner.SetAuthorizedSenders(e.Owner, authorizedSendersSlice) helpers.PanicErr(err) helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "vrf owner set authorized senders") - fmt.Printf("\nTransfering ownership of coordinator: %v, VRF Owner %v\n", contractAddresses.CoordinatorAddress, vrfOwnerAddress.String()) + fmt.Printf("\nTransferring ownership of coordinator: %v, VRF Owner %v\n", contractAddresses.CoordinatorAddress, vrfOwnerAddress.String()) tx, err = coordinator.TransferOwnership(e.Owner, vrfOwnerAddress) helpers.PanicErr(err) helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "transfer ownership to", vrfOwnerAddress.String()) @@ -320,9 +321,8 @@ func VRFV2DeployUniverse( func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address - } else { - return common.HexToAddress("0x0").String() } + return common.HexToAddress("0x0").String() }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, @@ -347,9 +347,8 @@ func VRFV2DeployUniverse( func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address - } else { - return common.HexToAddress("0x0").String() } + return common.HexToAddress("0x0").String() }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index 6b1ef585a5a..f9c6ea7e2dc 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -1046,9 +1046,8 @@ func main() { return true } else if strings.Contains(err.Error(), "execution reverted") { return false - } else { - panic(err) } + panic(err) } result := helpers.BinarySearch(assets.Ether(int64(*start*2)).ToInt(), big.NewInt(0), isWithdrawable) diff --git a/core/scripts/vrfv2plus/testnet/proofs.go b/core/scripts/vrfv2plus/testnet/proofs.go index 126ac2d6c5a..306d237caf4 100644 --- a/core/scripts/vrfv2plus/testnet/proofs.go +++ b/core/scripts/vrfv2plus/testnet/proofs.go @@ -11,13 +11,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/shopspring/decimal" + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/extraargs" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/proof" ) -var vrfProofTemplate string = `{ +var vrfProofTemplate = `{ pk: [ %s, %s @@ -42,7 +43,7 @@ var vrfProofTemplate string = `{ } ` -var rcTemplate string = `{ +var rcTemplate = `{ blockNum: %d, subId: %d, callbackGasLimit: %d, diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go index 50584d885a2..21d76c42048 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go @@ -202,9 +202,9 @@ func SmokeTestVRF(e helpers.Environment) { var provingKeyRegisteredLog *vrf_coordinator_v2_5.VRFCoordinatorV25ProvingKeyRegistered for _, log := range registerReceipt.Logs { if log.Address == coordinatorAddress { - var err error - provingKeyRegisteredLog, err = coordinator.ParseProvingKeyRegistered(*log) - if err != nil { + var err2 error + provingKeyRegisteredLog, err2 = coordinator.ParseProvingKeyRegistered(*log) + if err2 != nil { continue } } @@ -214,9 +214,8 @@ func SmokeTestVRF(e helpers.Environment) { } if !bytes.Equal(provingKeyRegisteredLog.KeyHash[:], keyHash[:]) { panic(fmt.Sprintf("unexpected key hash registered %s, expected %s", hexutil.Encode(provingKeyRegisteredLog.KeyHash[:]), hexutil.Encode(keyHash[:]))) - } else { - fmt.Println("key hash registered:", hexutil.Encode(provingKeyRegisteredLog.KeyHash[:])) } + fmt.Println("key hash registered:", hexutil.Encode(provingKeyRegisteredLog.KeyHash[:])) fmt.Println("\nProving key registered, getting proving key hashes from deployed contract...") _, _, provingKeyHashes, configErr := coordinator.GetRequestConfig(nil) @@ -268,6 +267,7 @@ func SmokeTestVRF(e helpers.Environment) { consumer, err := vrf_v2plus_sub_owner.NewVRFV2PlusExternalSubOwnerExample(consumerAddress, e.Ec) helpers.PanicErr(err) tx, err = consumer.RequestRandomWords(e.Owner, subID, 100_000, 3, 3, provingKeyRegisteredLog.KeyHash, false) + helpers.PanicErr(err) receipt := helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "request random words from", consumerAddress.String()) fmt.Println("request blockhash:", receipt.BlockHash) @@ -275,9 +275,9 @@ func SmokeTestVRF(e helpers.Environment) { var rwrLog *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested for _, log := range receipt.Logs { if log.Address == coordinatorAddress { - var err error - rwrLog, err = coordinator.ParseRandomWordsRequested(*log) - if err != nil { + var err2 error + rwrLog, err2 = coordinator.ParseRandomWordsRequested(*log) + if err2 != nil { continue } } @@ -396,13 +396,13 @@ func SmokeTestBHS(e helpers.Environment) { if seReceipt.Status != 1 { fmt.Println("storeEarliest failed") os.Exit(1) - } else { - fmt.Println("storeEarliest succeeded, checking BH is there") - bh, err := bhs.GetBlockhash(nil, seReceipt.BlockNumber.Sub(seReceipt.BlockNumber, big.NewInt(256))) - helpers.PanicErr(err) - fmt.Println("blockhash stored by storeEarliest:", hexutil.Encode(bh[:])) - anchorBlockNumber = seReceipt.BlockNumber } + fmt.Println("storeEarliest succeeded, checking BH is there") + bh, err := bhs.GetBlockhash(nil, seReceipt.BlockNumber.Sub(seReceipt.BlockNumber, big.NewInt(256))) + helpers.PanicErr(err) + fmt.Println("blockhash stored by storeEarliest:", hexutil.Encode(bh[:])) + anchorBlockNumber = seReceipt.BlockNumber + if anchorBlockNumber == nil { panic("no anchor block number") } @@ -417,12 +417,11 @@ func SmokeTestBHS(e helpers.Environment) { if sReceipt.Status != 1 { fmt.Println("store failed") os.Exit(1) - } else { - fmt.Println("store succeeded, checking BH is there") - bh, err := bhs.GetBlockhash(nil, toStore) - helpers.PanicErr(err) - fmt.Println("blockhash stored by store:", hexutil.Encode(bh[:])) } + fmt.Println("store succeeded, checking BH is there") + bh, err = bhs.GetBlockhash(nil, toStore) + helpers.PanicErr(err) + fmt.Println("blockhash stored by store:", hexutil.Encode(bh[:])) fmt.Println("\nexecuting storeVerifyHeader") headers, _, err := helpers.GetRlpHeaders(e, []*big.Int{anchorBlockNumber}, false) @@ -435,12 +434,11 @@ func SmokeTestBHS(e helpers.Environment) { if svhReceipt.Status != 1 { fmt.Println("storeVerifyHeader failed") os.Exit(1) - } else { - fmt.Println("storeVerifyHeader succeeded, checking BH is there") - bh, err := bhs.GetBlockhash(nil, toStore) - helpers.PanicErr(err) - fmt.Println("blockhash stored by storeVerifyHeader:", hexutil.Encode(bh[:])) } + fmt.Println("storeVerifyHeader succeeded, checking BH is there") + bh, err = bhs.GetBlockhash(nil, toStore) + helpers.PanicErr(err) + fmt.Println("blockhash stored by storeVerifyHeader:", hexutil.Encode(bh[:])) } func sendTx(e helpers.Environment, to common.Address, data []byte) (*types.Receipt, common.Hash) { @@ -501,7 +499,6 @@ func DeployUniverseViaCLI(e helpers.Environment) { fallbackWeiPerUnitLink := decimal.RequireFromString(*fallbackWeiPerUnitLinkString).BigInt() subscriptionBalanceJuels := decimal.RequireFromString(*subscriptionBalanceJuelsString).BigInt() subscriptionBalanceNativeWei := decimal.RequireFromString(*subscriptionBalanceNativeWeiString).BigInt() - fundingAmount := decimal.RequireFromString(*nodeSendingKeyFundingAmount).BigInt() feeConfig := vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig{ FulfillmentFlatFeeLinkPPM: uint32(*flatFeeLinkPPM), @@ -712,9 +709,8 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address - } else { - return common.HexToAddress("0x0").String() } + return common.HexToAddress("0x0").String() }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, @@ -733,9 +729,8 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address - } else { - return common.HexToAddress("0x0").String() } + return common.HexToAddress("0x0").String() }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go index ebe881a9951..7598c6f398f 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go @@ -4,7 +4,6 @@ import ( "context" "encoding/hex" "fmt" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -17,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_sub_owner" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example" From 463d4336c9069e6adc764e39c7f4ed51ada821de Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 15 Dec 2023 10:22:59 -0500 Subject: [PATCH 068/234] Update job names for scripts CI (#11585) --- .github/workflows/ci-scripts.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-scripts.yml b/.github/workflows/ci-scripts.yml index e83c22520cd..89f11476f6a 100644 --- a/.github/workflows/ci-scripts.yml +++ b/.github/workflows/ci-scripts.yml @@ -5,7 +5,7 @@ on: pull_request: jobs: - golangci: + lint-scripts: if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest steps: @@ -13,14 +13,14 @@ jobs: - name: Golang Lint uses: ./.github/actions/golangci-lint with: - name: scripts-lint + name: lint-scripts go-directory: core/scripts go-version-file: core/scripts/go.mod go-module-file: core/scripts/go.sum gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} - test: + test-scripts: if: ${{ github.event_name == 'pull_request' }} name: scripts-test runs-on: ubuntu-latest @@ -42,5 +42,5 @@ jobs: with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: scripts-test + this-job-name: test-scripts continue-on-error: true From 7665e26fda1a22eec702b19da84a02f6a0a8f2d8 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:00:17 -0500 Subject: [PATCH 069/234] Fix CI job name by using default (#11586) --- .github/workflows/ci-scripts.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci-scripts.yml b/.github/workflows/ci-scripts.yml index 89f11476f6a..dd46dfd7799 100644 --- a/.github/workflows/ci-scripts.yml +++ b/.github/workflows/ci-scripts.yml @@ -22,10 +22,9 @@ jobs: test-scripts: if: ${{ github.event_name == 'pull_request' }} - name: scripts-test runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Go uses: ./.github/actions/setup-go with: From 0b99f3a8195d4e103891393947ff9df801d3bed3 Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:31:51 -0600 Subject: [PATCH 070/234] Fix batch tx send encoding (#11500) * Changed signed tx encoding to use MarshalBinary * Added tests and CHANGELOG entry * Fixed linting * Cleaned up ClassifySendError method signature * Fixed linting * Consolidated error logs to avoid duplicate logging by caller * Isolated encoding change for batch resend logic * Reverted models test changes * Added comments --- common/txmgr/broadcaster.go | 4 +- common/txmgr/confirmer.go | 5 +- core/chains/evm/client/chain_client.go | 3 +- core/chains/evm/client/client.go | 3 +- core/chains/evm/client/client_test.go | 6 +-- core/chains/evm/client/errors.go | 47 ++++++++++--------- core/chains/evm/client/send_only_node_test.go | 3 +- core/chains/evm/txmgr/attempts.go | 15 ++++-- core/chains/evm/txmgr/attempts_test.go | 39 +++++++++++++++ core/chains/evm/txmgr/client.go | 8 +++- core/chains/evm/txmgr/common.go | 17 +++++-- core/chains/evm/txmgr/txmgr_test.go | 18 ++++--- core/chains/evm/types/models_test.go | 4 +- core/internal/cltest/factories.go | 14 +++++- core/internal/features/features_test.go | 9 ++-- .../features/ocr2/features_ocr2_test.go | 2 +- core/services/keystore/eth_test.go | 3 +- .../v1/internal/testutils.go | 3 +- .../internal/ocr2vrf_integration_test.go | 5 +- docs/CHANGELOG.md | 4 +- 20 files changed, 147 insertions(+), 65 deletions(-) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index a62b3df8699..7323fba7a81 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -647,7 +647,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // If there is only one RPC node, or all RPC nodes have the same // configured cap, this transaction will get stuck and keep repeating // forever until the issue is resolved. - lgr.Criticalw(`RPC node rejected this tx as outside Fee Cap`) + lgr.Criticalw(`RPC node rejected this tx as outside Fee Cap`, "attempt", attempt) fallthrough default: // Every error that doesn't fall under one of the above categories will be treated as Unknown. @@ -655,7 +655,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand case client.Unknown: eb.SvcErrBuffer.Append(err) lgr.Criticalw(`Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+ - `Urgent resolution required, Chainlink is currently operating in a degraded state and may miss transactions`, "err", err, "etx", etx, "attempt", attempt) + `Urgent resolution required, Chainlink is currently operating in a degraded state and may miss transactions`, "attempt", attempt) nextSequence, e := eb.client.PendingSequenceAt(ctx, etx.FromAddress) if e != nil { err = multierr.Combine(e, err) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index fbc6ea8a108..112c19edf63 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -824,7 +824,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han case client.Underpriced: // This should really not ever happen in normal operation since we // already bumped above the required minimum in broadcaster. - ec.lggr.Warnw("Got terminally underpriced error for gas bump, this should never happen unless the remote RPC node changed its configuration on the fly, or you are using multiple RPC nodes with different minimum gas price requirements. This is not recommended", "err", sendError, "attempt", attempt) + ec.lggr.Warnw("Got terminally underpriced error for gas bump, this should never happen unless the remote RPC node changed its configuration on the fly, or you are using multiple RPC nodes with different minimum gas price requirements. This is not recommended", "attempt", attempt) // "Lazily" load attempts here since the overwhelmingly common case is // that we don't need them unless we enter this path if err := ec.txStore.LoadTxAttempts(ctx, &etx); err != nil { @@ -867,7 +867,6 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han // Broadcaster can never create a TxAttempt that will // fatally error. lggr.Criticalw("Invariant violation: fatal error while re-attempting transaction", - "err", sendError, "fee", attempt.TxFee, "feeLimit", etx.FeeLimit, "signedRawTx", utils.EnsureHexPrefix(hex.EncodeToString(attempt.SignedRawTx)), @@ -879,7 +878,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han case client.TransactionAlreadyKnown: // Sequence too low indicated that a transaction at this sequence was confirmed already. // Mark confirmed_missing_receipt and wait for the next cycle to try to get a receipt - lggr.Debugw("Sequence already used", "txAttemptID", attempt.ID, "txHash", attempt.Hash.String(), "err", sendError) + lggr.Debugw("Sequence already used", "txAttemptID", attempt.ID, "txHash", attempt.Hash.String()) timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveConfirmedMissingReceiptAttempt(ctx, timeout, &attempt, now) case client.InsufficientFunds: diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 255d802720a..5dd70992382 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -216,7 +216,8 @@ func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { err := c.SendTransaction(ctx, tx) - return ClassifySendError(err, c.logger, tx, fromAddress, c.IsL2()) + returnCode := ClassifySendError(err, c.logger, tx, fromAddress, c.IsL2()) + return returnCode, err } func (c *chainClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (evmtypes.Nonce, error) { diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index f32ec011445..bddfdb7e7bf 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -220,7 +220,8 @@ func (client *client) HeaderByHash(ctx context.Context, h common.Hash) (*types.H func (client *client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { err := client.SendTransaction(ctx, tx) - return ClassifySendError(err, client.logger, tx, fromAddress, client.pool.ChainType().IsL2()) + returnCode := ClassifySendError(err, client.logger, tx, fromAddress, client.pool.ChainType().IsL2()) + return returnCode, err } // SendTransaction also uses the sendonly HTTP RPC URLs if set diff --git a/core/chains/evm/client/client_test.go b/core/chains/evm/client/client_test.go index 631b5722dec..bc3ae958461 100644 --- a/core/chains/evm/client/client_test.go +++ b/core/chains/evm/client/client_test.go @@ -417,7 +417,7 @@ func TestEthClient_HeaderByNumber(t *testing.T) { func TestEthClient_SendTransaction_NoSecondaryURL(t *testing.T) { t.Parallel() - tx := types.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { @@ -449,7 +449,7 @@ func TestEthClient_SendTransaction_NoSecondaryURL(t *testing.T) { func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { t.Parallel() - tx := types.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { @@ -494,7 +494,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { t.Parallel() fromAddress := testutils.NewAddress() - tx := types.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) t.Run("returns Fatal error type when error message is fatal", func(t *testing.T) { wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 66cc30f74b4..bb748cb52fb 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -413,63 +413,67 @@ func ExtractRPCError(baseErr error) (*JsonError, error) { return &jErr, nil } -func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (commonclient.SendTxReturnCode, error) { +func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) commonclient.SendTxReturnCode { sendError := NewSendError(err) if sendError == nil { - return commonclient.Successful, err + return commonclient.Successful } if sendError.Fatal() { lggr.Criticalw("Fatal error sending transaction", "err", sendError, "etx", tx) // Attempt is thrown away in this case; we don't need it since it never got accepted by a node - return commonclient.Fatal, err + return commonclient.Fatal } if sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() { + lggr.Debugw("Transaction already confirmed for this nonce: %d", tx.Nonce(), "err", sendError, "etx", tx) // Nonce too low indicated that a transaction at this nonce was confirmed already. // Mark it as TransactionAlreadyKnown. - return commonclient.TransactionAlreadyKnown, err + return commonclient.TransactionAlreadyKnown } if sendError.IsReplacementUnderpriced() { lggr.Errorw(fmt.Sprintf("Replacement transaction underpriced for eth_tx %x. "+ - "Eth node returned error: '%s'. "+ "Please note that using your node's private keys outside of the chainlink node is NOT SUPPORTED and can lead to missed transactions.", - tx.Hash(), err), "gasPrice", tx.GasPrice, "gasTipCap", tx.GasTipCap, "gasFeeCap", tx.GasFeeCap) + tx.Hash()), "gasPrice", tx.GasPrice, "gasTipCap", tx.GasTipCap, "gasFeeCap", tx.GasFeeCap, "err", sendError, "etx", tx) // Assume success and hand off to the next cycle. - return commonclient.Successful, err + return commonclient.Successful } if sendError.IsTransactionAlreadyInMempool() { - lggr.Debugw("Transaction already in mempool", "txHash", tx.Hash, "nodeErr", sendError.Error()) - return commonclient.Successful, err + lggr.Debugw("Transaction already in mempool", "etx", tx, "err", sendError) + return commonclient.Successful } if sendError.IsTemporarilyUnderpriced() { - lggr.Infow("Transaction temporarily underpriced", "err", sendError.Error()) - return commonclient.Successful, err + lggr.Infow("Transaction temporarily underpriced", "err", sendError) + return commonclient.Successful } if sendError.IsTerminallyUnderpriced() { - return commonclient.Underpriced, err + lggr.Errorw("Transaction terminally underpriced", "etx", tx, "err", sendError) + return commonclient.Underpriced } if sendError.L2FeeTooLow() || sendError.IsL2FeeTooHigh() || sendError.IsL2Full() { if isL2 { - return commonclient.FeeOutOfValidRange, err + lggr.Errorw("Transaction fee out of range", "err", sendError, "etx", tx) + return commonclient.FeeOutOfValidRange } - return commonclient.Unsupported, errors.Wrap(sendError, "this error type only handled for L2s") + lggr.Errorw("this error type only handled for L2s", "err", sendError, "etx", tx) + return commonclient.Unsupported } if sendError.IsNonceTooHighError() { // This error occurs when the tx nonce is greater than current_nonce + tx_count_in_mempool, // instead of keeping the tx in mempool. This can happen if previous transactions haven't // reached the client yet. The correct thing to do is to mark it as retryable. - lggr.Warnw("Transaction has a nonce gap.", "err", err) - return commonclient.Retryable, err + lggr.Warnw("Transaction has a nonce gap.", "err", sendError, "etx", tx) + return commonclient.Retryable } if sendError.IsInsufficientEth() { lggr.Criticalw(fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+ "ACTION REQUIRED: Chainlink wallet with address 0x%x is OUT OF FUNDS", tx.Hash(), tx.Type(), sendError.Error(), fromAddress, - ), "err", sendError) - return commonclient.InsufficientFunds, err + ), "err", sendError, "etx", tx) + return commonclient.InsufficientFunds } if sendError.IsTimeout() { - return commonclient.Retryable, errors.Wrapf(sendError, "timeout while sending transaction %s", tx.Hash().Hex()) + lggr.Errorw("timeout while sending transaction %x", tx.Hash(), "err", sendError, "etx", tx) + return commonclient.Retryable } if sendError.IsTxFeeExceedsCap() { lggr.Criticalw(fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), @@ -477,9 +481,10 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti "err", sendError, "id", "RPCTxFeeCapExceeded", ) - return commonclient.ExceedsMaxFee, err + return commonclient.ExceedsMaxFee } - return commonclient.Unknown, err + lggr.Errorw("Unknown error encountered when sending transaction", "err", err, "etx", tx) + return commonclient.Unknown } // ClassifySendOnlyError handles SendOnly nodes error codes. In that case, we don't assume there is another transaction that will be correctly diff --git a/core/chains/evm/client/send_only_node_test.go b/core/chains/evm/client/send_only_node_test.go index 760f7f4d3eb..c2fdad06ec1 100644 --- a/core/chains/evm/client/send_only_node_test.go +++ b/core/chains/evm/client/send_only_node_test.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "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/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) @@ -95,7 +96,7 @@ func createSignedTx(t *testing.T, chainID *big.Int, nonce uint64, data []byte) * require.NoError(t, err) sender, err := bind.NewKeyedTransactorWithChainID(key, chainID) require.NoError(t, err) - tx := types.NewTransaction( + tx := cltest.NewLegacyTransaction( nonce, sender.From, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), data, diff --git a/core/chains/evm/txmgr/attempts.go b/core/chains/evm/txmgr/attempts.go index 37626f4550e..ab143a0c963 100644 --- a/core/chains/evm/txmgr/attempts.go +++ b/core/chains/evm/txmgr/attempts.go @@ -3,6 +3,7 @@ package txmgr import ( "bytes" "context" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -119,9 +120,17 @@ func (c *evmTxAttemptBuilder) NewEmptyTxAttempt(nonce evmtypes.Nonce, feeLimit u return attempt, errors.New("NewEmptyTranscation: legacy fee cannot be nil") } - tx := types.NewTransaction(uint64(nonce), fromAddress, value, uint64(feeLimit), fee.Legacy.ToInt(), payload) + tx := newLegacyTransaction( + uint64(nonce), + fromAddress, + value, + uint32(feeLimit), + fee.Legacy, + payload, + ) - hash, signedTxBytes, err := c.SignTx(fromAddress, tx) + transaction := types.NewTx(&tx) + hash, signedTxBytes, err := c.SignTx(fromAddress, transaction) if err != nil { return attempt, errors.Wrapf(err, "error using account %s to sign empty transaction", fromAddress.String()) } @@ -295,7 +304,7 @@ func newLegacyTransaction(nonce uint64, to common.Address, value *big.Int, gasLi func (c *evmTxAttemptBuilder) SignTx(address common.Address, tx *types.Transaction) (common.Hash, []byte, error) { signedTx, err := c.keystore.SignTx(address, tx, &c.chainID) if err != nil { - return common.Hash{}, nil, errors.Wrap(err, "SignTx failed") + return common.Hash{}, nil, fmt.Errorf("failed to sign tx: %w", err) } rlp := new(bytes.Buffer) if err := signedTx.EncodeRLP(rlp); err != nil { diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index 131115e6fae..5635972d7ae 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -6,6 +6,7 @@ import ( "testing" gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" @@ -85,6 +86,44 @@ func TestTxm_SignTx(t *testing.T) { require.NotNil(t, rawBytes) require.Equal(t, "0xdd68f554373fdea7ec6713a6e437e7646465d553a6aa0b43233093366cc87ef0", hash.String()) }) + t.Run("can properly encoded and decode raw transaction for LegacyTx", func(t *testing.T) { + chainID := big.NewInt(1) + kst := ksmocks.NewEth(t) + kst.On("SignTx", to, tx, chainID).Return(tx, nil).Once() + cks := txmgr.NewEvmTxAttemptBuilder(*chainID, newFeeConfig(), kst, nil) + + _, rawBytes, err := cks.SignTx(addr, tx) + require.NoError(t, err) + require.NotNil(t, rawBytes) + require.Equal(t, "0xe42a82015681f294b921f7763960b296b9cbad586ff066a18d749724818e83010203808080", hexutil.Encode(rawBytes)) + + var decodedTx *gethtypes.Transaction + decodedTx, err = txmgr.GetGethSignedTx(rawBytes) + require.NoError(t, err) + require.Equal(t, tx.Hash(), decodedTx.Hash()) + }) + t.Run("can properly encoded and decode raw transaction for DynamicFeeTx", func(t *testing.T) { + chainID := big.NewInt(1) + kst := ksmocks.NewEth(t) + typedTx := gethtypes.NewTx(&gethtypes.DynamicFeeTx{ + Nonce: 42, + To: &to, + Value: big.NewInt(142), + Gas: 242, + Data: []byte{1, 2, 3}, + }) + kst.On("SignTx", to, typedTx, chainID).Return(typedTx, nil).Once() + cks := txmgr.NewEvmTxAttemptBuilder(*chainID, newFeeConfig(), kst, nil) + _, rawBytes, err := cks.SignTx(addr, typedTx) + require.NoError(t, err) + require.NotNil(t, rawBytes) + require.Equal(t, "0xa702e5802a808081f294b921f7763960b296b9cbad586ff066a18d749724818e83010203c0808080", hexutil.Encode(rawBytes)) + + var decodedTx *gethtypes.Transaction + decodedTx, err = txmgr.GetGethSignedTx(rawBytes) + require.NoError(t, err) + require.Equal(t, typedTx.Hash(), decodedTx.Hash()) + }) } func TestTxm_NewDynamicFeeTx(t *testing.T) { diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index dc7b62647c0..9ff6f8d5d74 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -77,10 +77,14 @@ func (c *evmTxmClient) BatchSendTransactions( // convert to tx for logging purposes - exits early if error occurs tx, signedErr := GetGethSignedTx(attempts[i].SignedRawTx) if signedErr != nil { - processingErr[i] = fmt.Errorf("failed to process tx (index %d): %w", i, signedErr) + signedErrMsg := fmt.Sprintf("failed to process tx (index %d)", i) + lggr.Errorw(signedErrMsg, "err", signedErr) + processingErr[i] = fmt.Errorf("%s: %w", signedErrMsg, signedErr) return } - codes[i], txErrs[i] = client.ClassifySendError(reqs[i].Error, lggr, tx, attempts[i].Tx.FromAddress, c.client.IsL2()) + sendErr := reqs[i].Error + codes[i] = client.ClassifySendError(sendErr, lggr, tx, attempts[i].Tx.FromAddress, c.client.IsL2()) + txErrs[i] = sendErr }(index) } wg.Wait() diff --git a/core/chains/evm/txmgr/common.go b/core/chains/evm/txmgr/common.go index 1956476f8dd..d1e851f0c21 100644 --- a/core/chains/evm/txmgr/common.go +++ b/core/chains/evm/txmgr/common.go @@ -35,12 +35,25 @@ func batchSendTransactions( reqs := make([]rpc.BatchElem, len(attempts)) ethTxIDs := make([]int64, len(attempts)) hashes := make([]string, len(attempts)) + now := time.Now() + successfulBroadcast := []int64{} for i, attempt := range attempts { ethTxIDs[i] = attempt.TxID hashes[i] = attempt.Hash.String() + // Decode the signed raw tx back into a Transaction object + signedTx, decodeErr := GetGethSignedTx(attempt.SignedRawTx) + if decodeErr != nil { + return reqs, now, successfulBroadcast, fmt.Errorf("failed to decode signed raw tx into Transaction object: %w", decodeErr) + } + // Get the canonical encoding of the Transaction object needed for the eth_sendRawTransaction request + // The signed raw tx cannot be used directly because it uses a different encoding + txBytes, marshalErr := signedTx.MarshalBinary() + if marshalErr != nil { + return reqs, now, successfulBroadcast, fmt.Errorf("failed to marshal tx into canonical encoding: %w", marshalErr) + } req := rpc.BatchElem{ Method: "eth_sendRawTransaction", - Args: []interface{}{hexutil.Encode(attempt.SignedRawTx)}, + Args: []interface{}{hexutil.Encode(txBytes)}, Result: &common.Hash{}, } reqs[i] = req @@ -48,12 +61,10 @@ func batchSendTransactions( logger.Debugw(fmt.Sprintf("Batch sending %d unconfirmed transactions.", len(attempts)), "n", len(attempts), "ethTxIDs", ethTxIDs, "hashes", hashes) - now := time.Now() if batchSize == 0 { batchSize = len(reqs) } - successfulBroadcast := []int64{} for i := 0; i < len(reqs); i += batchSize { j := i + batchSize if j > len(reqs) { diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index df913a41905..13b81bcef3c 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -652,14 +652,15 @@ func mustInsertUnconfirmedEthTxWithAttemptState(t *testing.T, txStore txmgr.Test etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress, opts...) attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) rlp := new(bytes.Buffer) require.NoError(t, tx.EncodeRLP(rlp)) attempt.SignedRawTx = rlp.Bytes() attempt.State = txAttemptState require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) + var err error + etx, err = txStore.FindTxWithAttempts(etx.ID) require.NoError(t, err) return etx } @@ -686,7 +687,8 @@ func mustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t *testing.T, txSt attempt.State = txmgrtypes.TxAttemptBroadcast require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) + var err error + etx, err = txStore.FindTxWithAttempts(etx.ID) require.NoError(t, err) return etx } @@ -703,14 +705,15 @@ func mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t *testing.T, txStore require.NoError(t, txStore.InsertTx(&etx)) attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) rlp := new(bytes.Buffer) require.NoError(t, tx.EncodeRLP(rlp)) attempt.SignedRawTx = rlp.Bytes() attempt.State = txmgrtypes.TxAttemptInsufficientFunds require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) + var err error + etx, err = txStore.FindTxWithAttempts(etx.ID) require.NoError(t, err) return etx } @@ -741,13 +744,14 @@ func mustInsertInProgressEthTxWithAttempt(t *testing.T, txStore txmgr.TestEvmTxS etx.State = txmgrcommon.TxInProgress require.NoError(t, txStore.InsertTx(&etx)) attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) rlp := new(bytes.Buffer) require.NoError(t, tx.EncodeRLP(rlp)) attempt.SignedRawTx = rlp.Bytes() attempt.State = txmgrtypes.TxAttemptInProgress require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) + var err error + etx, err = txStore.FindTxWithAttempts(etx.ID) require.NoError(t, err) return etx } diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index 3507b6a1318..6d367b44a75 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -12,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -95,13 +94,12 @@ func TestEthTxAttempt_GetSignedTx(t *testing.T) { cfg := configtest.NewGeneralConfig(t, nil) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - tx := gethTypes.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := cltest.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) chainID := big.NewInt(3) signedTx, err := ethKeyStore.SignTx(fromAddress, tx, chainID) require.NoError(t, err) - signedTx.Size() // Needed to write the size for equality checking rlp := new(bytes.Buffer) require.NoError(t, signedTx.EncodeRLP(rlp)) diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index bece916ecc5..b96e97103e5 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -146,6 +146,18 @@ func NewEthTx(fromAddress common.Address) txmgr.Tx { } } +func NewLegacyTransaction(nonce uint64, to common.Address, value *big.Int, gasLimit uint32, gasPrice *big.Int, data []byte) *types.Transaction { + tx := types.LegacyTx{ + Nonce: nonce, + To: &to, + Value: value, + Gas: uint64(gasLimit), + GasPrice: gasPrice, + Data: data, + } + return types.NewTx(&tx) +} + func MustInsertUnconfirmedEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, opts ...interface{}) txmgr.Tx { broadcastAt := time.Now() chainID := &FixtureChainID @@ -173,7 +185,7 @@ func MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t *testing.T, txStore etx := MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress, opts...) attempt := NewLegacyEthTxAttempt(t, etx.ID) - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + tx := NewLegacyTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) rlp := new(bytes.Buffer) require.NoError(t, tx.EncodeRLP(rlp)) attempt.SignedRawTx = rlp.Bytes() diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index f115a3f8858..e8fcd0a6d37 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -21,7 +21,6 @@ import ( "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/core/types" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/rpc" "github.com/google/uuid" @@ -380,7 +379,7 @@ func TestIntegration_DirectRequest(t *testing.T) { // Fund node account with ETH. n, err := b.NonceAt(testutils.Context(t), operatorContracts.user.From, nil) require.NoError(t, err) - tx = types.NewTransaction(n, sendingKeys[0].Address, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) + tx = cltest.NewLegacyTransaction(n, sendingKeys[0].Address, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) signedTx, err := operatorContracts.user.Signer(operatorContracts.user.From, tx) require.NoError(t, err) err = b.SendTransaction(testutils.Context(t), signedTx) @@ -479,7 +478,7 @@ func setupAppForEthTx(t *testing.T, operatorContracts OperatorContracts) (app *c // Fund node account with ETH. n, err := b.NonceAt(testutils.Context(t), operatorContracts.user.From, nil) require.NoError(t, err) - tx := types.NewTransaction(n, sendingKeys[0].Address, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) + tx := cltest.NewLegacyTransaction(n, sendingKeys[0].Address, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) signedTx, err := operatorContracts.user.Signer(operatorContracts.user.From, tx) require.NoError(t, err) err = b.SendTransaction(testutils.Context(t), signedTx) @@ -709,7 +708,7 @@ func setupNode(t *testing.T, owner *bind.TransactOpts, portV2 int, n, err := b.NonceAt(testutils.Context(t), owner.From, nil) require.NoError(t, err) - tx := types.NewTransaction(n, transmitter, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) + tx := cltest.NewLegacyTransaction(n, transmitter, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) signedTx, err := owner.Signer(owner.From, tx) require.NoError(t, err) err = b.SendTransaction(testutils.Context(t), signedTx) @@ -751,7 +750,7 @@ func setupForwarderEnabledNode(t *testing.T, owner *bind.TransactOpts, portV2 in n, err := b.NonceAt(testutils.Context(t), owner.From, nil) require.NoError(t, err) - tx := types.NewTransaction(n, transmitter, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) + tx := cltest.NewLegacyTransaction(n, transmitter, assets.Ether(100).ToInt(), 21000, big.NewInt(1000000000), nil) signedTx, err := owner.Signer(owner.From, tx) require.NoError(t, err) err = b.SendTransaction(testutils.Context(t), signedTx) diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 70d4b0d79fd..e3b6c1a24d1 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -143,7 +143,7 @@ func setupNodeOCR2( n, err := b.NonceAt(testutils.Context(t), owner.From, nil) require.NoError(t, err) - tx := types.NewTransaction( + tx := cltest.NewLegacyTransaction( n, transmitter, assets.Ether(1).ToInt(), 21000, diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index 3935a44558b..54b19145212 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -337,7 +336,7 @@ func Test_EthKeyStore_SignTx(t *testing.T) { k, _ := cltest.MustInsertRandomKey(t, ethKeyStore) chainID := big.NewInt(evmclient.NullClientChainID) - tx := types.NewTransaction(0, testutils.NewAddress(), big.NewInt(53), 21000, big.NewInt(1000000000), []byte{1, 2, 3, 4}) + tx := cltest.NewLegacyTransaction(0, testutils.NewAddress(), big.NewInt(53), 21000, big.NewInt(1000000000), []byte{1, 2, 3, 4}) randomAddress := testutils.NewAddress() _, err := ethKeyStore.SignTx(randomAddress, tx, chainID) diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index 22a59feb3ae..3e6e728f57a 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -18,7 +18,6 @@ import ( "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/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/hashicorp/consul/sdk/freeport" @@ -348,7 +347,7 @@ func StartNewNode( n, err := b.NonceAt(testutils.Context(t), owner.From, nil) require.NoError(t, err) - tx := types.NewTransaction( + tx := cltest.NewLegacyTransaction( n, transmitter, assets.Ether(1).ToInt(), 21000, diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index c559fb27fb7..e9326d07d29 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -14,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/hashicorp/consul/sdk/freeport" @@ -299,7 +298,7 @@ func setupNodeOCR2( n, err := b.NonceAt(testutils.Context(t), owner.From, nil) require.NoError(t, err) - tx := types.NewTransaction( + tx := cltest.NewLegacyTransaction( n, k.Address, assets.Ether(1).ToInt(), 21000, @@ -663,7 +662,7 @@ linkEthFeedAddress = "%s" // Fund the payee with some ETH. n, err2 := uni.backend.NonceAt(testutils.Context(t), uni.owner.From, nil) require.NoError(t, err2) - tx := types.NewTransaction( + tx := cltest.NewLegacyTransaction( n, payeeTransactor.From, assets.Ether(1).ToInt(), 21000, diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 62236558c4c..f18fe5369af 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] -... +### Fixed + +- Fixed the encoding used for transactions when resending in batches ## 2.8.0 - UNRELEASED From b12329e7092e412ec4c6a4c4d163c6615aa96378 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 15 Dec 2023 16:53:30 -0600 Subject: [PATCH 071/234] core/web: /health - and support for HTML & Plaintext (#11552) --- core/web/health_controller.go | 194 +++++++++++++++++++++++++++-- core/web/health_controller_test.go | 52 +++++--- core/web/health_template_test.go | 53 ++++++++ core/web/presenters/check.go | 6 + core/web/router.go | 3 + core/web/testdata/body/health.html | 95 ++++++++++++++ core/web/testdata/body/health.txt | 19 +++ core/web/testdata/health.html | 67 ++++++++++ core/web/testdata/health.txt | 15 +++ 9 files changed, 480 insertions(+), 24 deletions(-) create mode 100644 core/web/health_template_test.go create mode 100644 core/web/testdata/body/health.html create mode 100644 core/web/testdata/body/health.txt create mode 100644 core/web/testdata/health.html create mode 100644 core/web/testdata/health.txt diff --git a/core/web/health_controller.go b/core/web/health_controller.go index d6490e5542a..c8489fd6325 100644 --- a/core/web/health_controller.go +++ b/core/web/health_controller.go @@ -1,12 +1,15 @@ package web import ( - "cmp" + "bytes" + "fmt" + "io" "net/http" "slices" - "testing" + "strings" "github.com/gin-gonic/gin" + "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -79,7 +82,6 @@ func (hc *HealthController) Health(c *gin.Context) { c.Status(status) checks := make([]presenters.Check, 0, len(errors)) - for name, err := range errors { status := HealthStatusPassing var output string @@ -97,12 +99,188 @@ func (hc *HealthController) Health(c *gin.Context) { }) } - if testing.Testing() { - slices.SortFunc(checks, func(a, b presenters.Check) int { - return cmp.Compare(a.Name, b.Name) - }) + switch c.NegotiateFormat(gin.MIMEJSON, gin.MIMEHTML, gin.MIMEPlain) { + case gin.MIMEJSON: + break // default + + case gin.MIMEHTML: + if err := newCheckTree(checks).WriteHTMLTo(c.Writer); err != nil { + hc.App.GetLogger().Errorw("Failed to write HTML health report", "err", err) + c.AbortWithStatus(http.StatusInternalServerError) + } + return + + case gin.MIMEPlain: + if err := writeTextTo(c.Writer, checks); err != nil { + hc.App.GetLogger().Errorw("Failed to write plaintext health report", "err", err) + c.AbortWithStatus(http.StatusInternalServerError) + } + return } - // return a json description of all the checks + slices.SortFunc(checks, presenters.CmpCheckName) jsonAPIResponseWithStatus(c, checks, "checks", status) } + +func writeTextTo(w io.Writer, checks []presenters.Check) error { + slices.SortFunc(checks, presenters.CmpCheckName) + for _, ch := range checks { + status := "?" + switch ch.Status { + case HealthStatusPassing: + status = "-" + case HealthStatusFailing: + status = "!" + } + if _, err := fmt.Fprintf(w, "%s%s\n", status, ch.Name); err != nil { + return err + } + if ch.Output != "" { + if _, err := fmt.Fprintf(newLinePrefixWriter(w, "\t"), "\t%s", ch.Output); err != nil { + return err + } + if _, err := fmt.Fprintln(w); err != nil { + return err + } + } + } + return nil +} + +type checkNode struct { + Name string // full + Status string + Output string + + Subs checkTree +} + +type checkTree map[string]checkNode + +func newCheckTree(checks []presenters.Check) checkTree { + slices.SortFunc(checks, presenters.CmpCheckName) + root := make(checkTree) + for _, c := range checks { + parts := strings.Split(c.Name, ".") + node := root + for _, short := range parts[:len(parts)-1] { + n, ok := node[short] + if !ok { + n = checkNode{Subs: make(checkTree)} + node[short] = n + } + node = n.Subs + } + p := parts[len(parts)-1] + node[p] = checkNode{ + Name: c.Name, + Status: c.Status, + Output: c.Output, + Subs: make(checkTree), + } + } + return root +} + +func (t checkTree) WriteHTMLTo(w io.Writer) error { + if _, err := io.WriteString(w, ``); err != nil { + return err + } + return t.writeHTMLTo(newLinePrefixWriter(w, "")) +} + +func (t checkTree) writeHTMLTo(w *linePrefixWriter) error { + keys := maps.Keys(t) + slices.Sort(keys) + for _, short := range keys { + node := t[short] + if _, err := io.WriteString(w, ` +

`); err != nil { + return err + } + var expand string + if node.Output == "" && len(node.Subs) == 0 { + expand = ` class="noexpand"` + } + if _, err := fmt.Fprintf(w, ` + %s`, node.Name, expand, node.Status, short); err != nil { + return err + } + if node.Output != "" { + if _, err := w.WriteRawLinef("
%s
", node.Output); err != nil { + return err + } + } + if len(node.Subs) > 0 { + if err := node.Subs.writeHTMLTo(w.new(" ")); err != nil { + return err + } + } + if _, err := io.WriteString(w, "\n
"); err != nil { + return err + } + } + return nil +} + +type linePrefixWriter struct { + w io.Writer + prefix string + prefixB []byte +} + +func newLinePrefixWriter(w io.Writer, prefix string) *linePrefixWriter { + prefix = "\n" + prefix + return &linePrefixWriter{w: w, prefix: prefix, prefixB: []byte(prefix)} +} + +func (w *linePrefixWriter) new(prefix string) *linePrefixWriter { + prefix = w.prefix + prefix + return &linePrefixWriter{w: w.w, prefix: prefix, prefixB: []byte(prefix)} +} + +func (w *linePrefixWriter) Write(b []byte) (int, error) { + return w.w.Write(bytes.ReplaceAll(b, []byte("\n"), w.prefixB)) +} + +func (w *linePrefixWriter) WriteString(s string) (n int, err error) { + return io.WriteString(w.w, strings.ReplaceAll(s, "\n", w.prefix)) +} + +// WriteRawLinef writes a new newline with prefix, followed by s without modification. +func (w *linePrefixWriter) WriteRawLinef(s string, args ...any) (n int, err error) { + return fmt.Fprintf(w.w, w.prefix+s, args...) +} diff --git a/core/web/health_controller_test.go b/core/web/health_controller_test.go index 7e7c42141ca..ae40a66bca9 100644 --- a/core/web/health_controller_test.go +++ b/core/web/health_controller_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -90,24 +91,43 @@ func TestHealthController_Health_status(t *testing.T) { var ( //go:embed testdata/body/health.json - healthJSON string + bodyJSON string + //go:embed testdata/body/health.html + bodyHTML string + //go:embed testdata/body/health.txt + bodyTXT string ) func TestHealthController_Health_body(t *testing.T) { - app := cltest.NewApplicationWithKey(t) - require.NoError(t, app.Start(testutils.Context(t))) - - client := app.NewHTTPClient(nil) - resp, cleanup := client.Get("/health") - t.Cleanup(cleanup) - assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - - // pretty print for comparison - var b bytes.Buffer - require.NoError(t, json.Indent(&b, body, "", " ")) - body = b.Bytes() + for _, tc := range []struct { + name string + path string + headers map[string]string + expBody string + }{ + {"default", "/health", nil, bodyJSON}, + {"json", "/health", map[string]string{"Accept": gin.MIMEJSON}, bodyJSON}, + {"html", "/health", map[string]string{"Accept": gin.MIMEHTML}, bodyHTML}, + {"text", "/health", map[string]string{"Accept": gin.MIMEPlain}, bodyTXT}, + {".txt", "/health.txt", nil, bodyTXT}, + } { + t.Run(tc.name, func(t *testing.T) { + app := cltest.NewApplicationWithKey(t) + require.NoError(t, app.Start(testutils.Context(t))) - assert.Equal(t, healthJSON, string(body)) + client := app.NewHTTPClient(nil) + resp, cleanup := client.Get(tc.path, tc.headers) + t.Cleanup(cleanup) + assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + if tc.expBody == bodyJSON { + // pretty print for comparison + var b bytes.Buffer + require.NoError(t, json.Indent(&b, body, "", " ")) + body = b.Bytes() + } + assert.Equal(t, tc.expBody, string(body)) + }) + } } diff --git a/core/web/health_template_test.go b/core/web/health_template_test.go new file mode 100644 index 00000000000..fa9750fed22 --- /dev/null +++ b/core/web/health_template_test.go @@ -0,0 +1,53 @@ +package web + +import ( + "bytes" + _ "embed" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/web/presenters" +) + +var ( + //go:embed testdata/health.html + healthHTML string + + //go:embed testdata/health.txt + healthTXT string +) + +func checks() []presenters.Check { + const passing, failing = HealthStatusPassing, HealthStatusFailing + return []presenters.Check{ + {Name: "foo", Status: passing}, + {Name: "foo.bar", Status: failing, Output: "example error message"}, + {Name: "foo.bar.1", Status: passing}, + {Name: "foo.bar.1.A", Status: passing}, + {Name: "foo.bar.1.B", Status: passing}, + {Name: "foo.bar.2", Status: failing, Output: `error: +this is a multi-line error: +new line: +original error`}, + {Name: "foo.bar.2.A", Status: failing, Output: "failure!"}, + {Name: "foo.bar.2.B", Status: passing}, + {Name: "foo.baz", Status: passing}, + } + //TODO truncated error +} + +func Test_checkTree_WriteHTMLTo(t *testing.T) { + ct := newCheckTree(checks()) + var b bytes.Buffer + require.NoError(t, ct.WriteHTMLTo(&b)) + got := b.String() + require.Equalf(t, healthHTML, got, "got: %s", got) +} + +func Test_writeTextTo(t *testing.T) { + var b bytes.Buffer + require.NoError(t, writeTextTo(&b, checks())) + got := b.String() + require.Equalf(t, healthTXT, got, "got: %s", got) +} diff --git a/core/web/presenters/check.go b/core/web/presenters/check.go index 52e4aa68005..4e1a2147a8c 100644 --- a/core/web/presenters/check.go +++ b/core/web/presenters/check.go @@ -1,5 +1,7 @@ package presenters +import "cmp" + type Check struct { JAID Name string `json:"name"` @@ -10,3 +12,7 @@ type Check struct { func (c Check) GetName() string { return "checks" } + +func CmpCheckName(a, b Check) int { + return cmp.Compare(a.Name, b.Name) +} diff --git a/core/web/router.go b/core/web/router.go index 28bd4f2170c..5c5e86a12b6 100644 --- a/core/web/router.go +++ b/core/web/router.go @@ -215,6 +215,9 @@ func healthRoutes(app chainlink.Application, r *gin.RouterGroup) { hc := HealthController{app} r.GET("/readyz", hc.Readyz) r.GET("/health", hc.Health) + r.GET("/health.txt", func(context *gin.Context) { + context.Request.Header.Set("Accept", gin.MIMEPlain) + }, hc.Health) } func loopRoutes(app chainlink.Application, r *gin.RouterGroup) { diff --git a/core/web/testdata/body/health.html b/core/web/testdata/body/health.html new file mode 100644 index 00000000000..5999891a0f6 --- /dev/null +++ b/core/web/testdata/body/health.html @@ -0,0 +1,95 @@ + +
+ EVM +
+ 0 +
+ BalanceMonitor +
+
+ HeadBroadcaster +
+
+ HeadTracker +
+ HeadListener +
Listener is not connected
+
+
+
+ LogBroadcaster +
+
+ Txm +
+ BlockHistoryEstimator +
+
+ Broadcaster +
+
+ Confirmer +
+
+ WrappedEvmEstimator +
+
+
+
+
+ JobSpawner +
+
+ Mercury +
+ WSRPCPool +
+
+
+ Monitor +
+
+ PipelineORM +
+
+ PipelineRunner +
+
+ PromReporter +
+
+ TelemetryManager +
\ No newline at end of file diff --git a/core/web/testdata/body/health.txt b/core/web/testdata/body/health.txt new file mode 100644 index 00000000000..5b636829587 --- /dev/null +++ b/core/web/testdata/body/health.txt @@ -0,0 +1,19 @@ +-EVM.0 +-EVM.0.BalanceMonitor +-EVM.0.HeadBroadcaster +-EVM.0.HeadTracker +!EVM.0.HeadTracker.HeadListener + Listener is not connected +-EVM.0.LogBroadcaster +-EVM.0.Txm +-EVM.0.Txm.BlockHistoryEstimator +-EVM.0.Txm.Broadcaster +-EVM.0.Txm.Confirmer +-EVM.0.Txm.WrappedEvmEstimator +-JobSpawner +-Mercury.WSRPCPool +-Monitor +-PipelineORM +-PipelineRunner +-PromReporter +-TelemetryManager diff --git a/core/web/testdata/health.html b/core/web/testdata/health.html new file mode 100644 index 00000000000..3c007bef96f --- /dev/null +++ b/core/web/testdata/health.html @@ -0,0 +1,67 @@ + +
+ foo +
+ bar +
example error message
+
+ 1 +
+ A +
+
+ B +
+
+
+ 2 +
error:
+this is a multi-line error:
+new line:
+original error
+
+ A +
failure!
+
+
+ B +
+
+
+
+ baz +
+
\ No newline at end of file diff --git a/core/web/testdata/health.txt b/core/web/testdata/health.txt new file mode 100644 index 00000000000..f155d6c0212 --- /dev/null +++ b/core/web/testdata/health.txt @@ -0,0 +1,15 @@ +-foo +!foo.bar + example error message +-foo.bar.1 +-foo.bar.1.A +-foo.bar.1.B +!foo.bar.2 + error: + this is a multi-line error: + new line: + original error +!foo.bar.2.A + failure! +-foo.bar.2.B +-foo.baz From 5cfc86603b017e0951ce928a0791dfe30d2b4e03 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 15 Dec 2023 17:27:37 -0600 Subject: [PATCH 072/234] core/services/pipeline: pad deadline for late return (#11555) --- core/config/env/env.go | 2 ++ core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/pipeline/runner.go | 29 ++++++++++++++++++++++++++--- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 8 files changed, 37 insertions(+), 12 deletions(-) diff --git a/core/config/env/env.go b/core/config/env/env.go index ea84a50b754..37ae131ddf1 100644 --- a/core/config/env/env.go +++ b/core/config/env/env.go @@ -27,6 +27,8 @@ var ( LOOPPHostName = Var("CL_LOOPP_HOSTNAME") // Work around for Solana LOOPPs configured with zero values. MinOCR2MaxDurationQuery = Var("CL_MIN_OCR2_MAX_DURATION_QUERY") + // PipelineOvertime is an undocumented escape hatch for overriding the default padding in pipeline executions. + PipelineOvertime = Var("CL_PIPELINE_OVERTIME") DatabaseAllowSimplePasswords = Var("CL_DATABASE_ALLOW_SIMPLE_PASSWORDS") DatabaseURL = Secret("CL_DATABASE_URL") diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 895363a53f8..87d4fd8106a 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -236,7 +236,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index dcf47d392cc..b4a60bd3da6 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1146,8 +1146,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 h1:lSYiaiIfAA+5ac45/UD8ciytlNw/S6fnhK7bxFHYI88= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 h1:7OP1znQwQP3ha1KL5sDjHeKobOfe//YTYdUQH+klkhk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 768f163f9b1..e4d60db881b 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -15,6 +15,8 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/services" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -211,18 +213,39 @@ func (r *runner) OnRunFinished(fn func(*Run)) { r.runFinished = fn } -// Be careful with the ctx passed in here: it applies to requests in individual -// tasks but should _not_ apply to the scheduler or run itself +// github.com/smartcontractkit/libocr/offchainreporting2plus/internal/protocol.ReportingPluginTimeoutWarningGracePeriod +var overtime = 100 * time.Millisecond + +func init() { + // undocumented escape hatch + if v := env.PipelineOvertime.Get(); v != "" { + d, err := time.ParseDuration(v) + if err == nil { + overtime = d + } + } +} + func (r *runner) ExecuteRun( ctx context.Context, spec Spec, vars Vars, l logger.Logger, ) (*Run, TaskRunResults, error) { + // Pipeline runs may return results after the context is cancelled, so we modify the + // deadline to give them time to return before the parent context deadline. + var cancel func() + ctx, cancel = commonutils.ContextWithDeadlineFn(ctx, func(orig time.Time) time.Time { + if tenPct := time.Until(orig) / 10; overtime > tenPct { + return orig.Add(-tenPct) + } + return orig.Add(-overtime) + }) + defer cancel() + run := NewRun(spec, vars) pipeline, err := r.initializePipeline(run) - if err != nil { return run, nil, err } diff --git a/go.mod b/go.mod index c71705d9c92..e013a192c08 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index b38b7ac2632..35b136e2c9b 100644 --- a/go.sum +++ b/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 h1:lSYiaiIfAA+5ac45/UD8ciytlNw/S6fnhK7bxFHYI88= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 h1:7OP1znQwQP3ha1KL5sDjHeKobOfe//YTYdUQH+klkhk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 9f0ff1b384c..665fb393052 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f8a9529a4cc..e3688f68a7f 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1432,8 +1432,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490 h1:lSYiaiIfAA+5ac45/UD8ciytlNw/S6fnhK7bxFHYI88= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231213134506-b6c433e6c490/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 h1:7OP1znQwQP3ha1KL5sDjHeKobOfe//YTYdUQH+klkhk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From 3cea51d603e6e49f60ff834c91a4dca3060db549 Mon Sep 17 00:00:00 2001 From: Tate Date: Fri, 15 Dec 2023 16:28:27 -0700 Subject: [PATCH 073/234] Bump wasp version which has a loki bump (#11590) --- integration-tests/go.mod | 16 +++++- integration-tests/go.sum | 52 +++++++++++++++++-- integration-tests/load/automationv2_1/gun.go | 6 +-- .../load/functions/gateway_gun.go | 18 +++---- .../load/functions/request_gun.go | 20 +++---- integration-tests/load/ocr/gun.go | 8 +-- integration-tests/load/ocr/vu.go | 6 +-- integration-tests/load/vrfv2/gun.go | 6 +-- integration-tests/load/vrfv2plus/gun.go | 6 +-- integration-tests/universal/log_poller/gun.go | 8 +-- 10 files changed, 101 insertions(+), 45 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 665fb393052..73e353c5c1b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -30,7 +30,7 @@ require ( github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 - github.com/smartcontractkit/wasp v0.3.7 + github.com/smartcontractkit/wasp v0.4.0 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.23.0 @@ -75,7 +75,9 @@ require ( github.com/DataDog/zstd v1.5.2 // indirect github.com/K-Phoen/sdk v0.12.2 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/VictoriaMetrics/fastcache v1.10.0 // indirect @@ -94,6 +96,7 @@ require ( github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect @@ -138,6 +141,7 @@ require ( github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -150,6 +154,7 @@ require ( github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect + github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect @@ -187,6 +192,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-webauthn/webauthn v0.9.4 // indirect github.com/go-webauthn/x v0.1.5 // indirect @@ -216,7 +222,8 @@ require ( github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f // indirect - github.com/grafana/loki v1.6.2-0.20231201111602-11ef833ed3e4 // indirect + github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 // indirect + github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4 // indirect github.com/grafana/pyroscope-go v1.0.4 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.4 // indirect @@ -249,6 +256,7 @@ require ( github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/huandu/skiplist v1.2.0 // indirect + github.com/huandu/xstrings v1.4.0 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -286,10 +294,12 @@ require ( github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.56 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect @@ -353,6 +363,7 @@ require ( github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect + github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -406,6 +417,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.6.0 // indirect golang.org/x/crypto v0.16.0 // indirect golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index e3688f68a7f..cc08fb43dcc 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -124,9 +124,14 @@ github.com/K-Phoen/sdk v0.12.2 h1:0QofDlKE+lloyBOzhjEEMW21061zts/WIpfpQ5NLLAs= github.com/K-Phoen/sdk v0.12.2/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= @@ -145,6 +150,8 @@ github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40wo github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd5wAKUHEO/k= +github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= @@ -158,6 +165,11 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= +github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo= +github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -214,6 +226,7 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOF github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= @@ -391,6 +404,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digitalocean/godo v1.104.1 h1:SZNxjAsskM/su0YW9P8Wx3gU0W1Z13b6tZlYNpl5BnA= github.com/digitalocean/godo v1.104.1/go.mod h1:VAI/L5YDzMuPRU01lEEUSQ/sp5Z//1HnnFv/RBTEdbg= @@ -445,6 +460,8 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= @@ -591,6 +608,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= @@ -801,8 +820,10 @@ github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6 github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4= github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f/go.mod h1:8dsy5tQOkeNQyjXpm5mQsbCu3H5uzeBD35MzRQFznKU= -github.com/grafana/loki v1.6.2-0.20231201111602-11ef833ed3e4 h1:YbfISHhpbiDvoFNxDPCicPOU/+9s4rdv9Fq/V3iRvTo= -github.com/grafana/loki v1.6.2-0.20231201111602-11ef833ed3e4/go.mod h1:Tx2uhmS+H0pGmtqX94/KaDkRHWhIowt4iYtJLctAbEY= +github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= +github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= +github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 h1:gdrsYbmk8822v6qvPwZO5DC6QjnAW7uKJ9YXnoUmV8c= +github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503/go.mod h1:d8seWXCEXkL42mhuIJYcGi6DxfehzoIpLrMQWJojvOo= github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4 h1:wQ0FnSeebhJIBkgYOD06Mxk9HV2KhtEG0hp/7R+5RUQ= github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0= github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= @@ -924,11 +945,15 @@ github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3 github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= @@ -1159,6 +1184,9 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1178,6 +1206,9 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -1285,6 +1316,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= @@ -1458,8 +1491,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wasp v0.3.7 h1:3toT+iMSHJ1EKQXE+jGnxfmtLlT0gwEl1A7xGyw0NZY= -github.com/smartcontractkit/wasp v0.3.7/go.mod h1:L/cyUGfpaWxy/2twOVJLRt2mySJEIqGrFj9nyvRLpSo= +github.com/smartcontractkit/wasp v0.4.0 h1:N8yPxlBvoJiyE6HaDkTkwRbuOHkGgQFGEHbw36oh4jA= +github.com/smartcontractkit/wasp v0.4.0/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4= github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb6vPbtRQ= github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1467,6 +1500,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1474,6 +1509,7 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -1615,6 +1651,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= +github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= @@ -1707,6 +1745,8 @@ go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= @@ -1738,6 +1778,7 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= @@ -1845,6 +1886,7 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= @@ -1974,6 +2016,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1986,6 +2029,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= diff --git a/integration-tests/load/automationv2_1/gun.go b/integration-tests/load/automationv2_1/gun.go index a2863a6064b..2bbb654e00b 100644 --- a/integration-tests/load/automationv2_1/gun.go +++ b/integration-tests/load/automationv2_1/gun.go @@ -28,7 +28,7 @@ func NewLogTriggerUser( } } -func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.CallResult { +func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.Response { m.logger.Debug().Str("Trigger address", m.triggerContract.Address().String()).Msg("Triggering upkeep") payload := make([]int, 0) for i := 0; i < m.numberOfEvents; i++ { @@ -36,8 +36,8 @@ func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.CallResult { } _, err := m.triggerContract.EmitLogInts(payload) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } diff --git a/integration-tests/load/functions/gateway_gun.go b/integration-tests/load/functions/gateway_gun.go index 3dafb458a50..62fe80ed02c 100644 --- a/integration-tests/load/functions/gateway_gun.go +++ b/integration-tests/load/functions/gateway_gun.go @@ -37,7 +37,7 @@ func NewGatewaySecretsSetGun(cfg *PerformanceConfig, method string, pKey *ecdsa. } } -func callSecretsSet(m *GatewaySecretsSetGun) *wasp.CallResult { +func callSecretsSet(m *GatewaySecretsSetGun) *wasp.Response { randNum := strconv.Itoa(rand.Intn(100000)) randSlot := uint(rand.Intn(5)) version := uint64(time.Now().UnixNano()) @@ -57,7 +57,7 @@ func callSecretsSet(m *GatewaySecretsSetGun) *wasp.CallResult { secret, ) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } _, _, err = UploadS4Secrets(m.Resty, &S4SecretsCfg{ GatewayURL: m.Cfg.Common.GatewayURL, @@ -71,12 +71,12 @@ func callSecretsSet(m *GatewaySecretsSetGun) *wasp.CallResult { S4SetPayload: secrets, }) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } -func callSecretsList(m *GatewaySecretsSetGun) *wasp.CallResult { +func callSecretsList(m *GatewaySecretsSetGun) *wasp.Response { randNum := strconv.Itoa(rand.Intn(100000)) randSlot := uint(rand.Intn(5)) version := uint64(time.Now().UnixNano()) @@ -92,14 +92,14 @@ func callSecretsList(m *GatewaySecretsSetGun) *wasp.CallResult { S4SetVersion: version, S4SetExpirationPeriod: expiration, }); err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } // Call implements example gun call, assertions on response bodies should be done here -func (m *GatewaySecretsSetGun) Call(_ *wasp.Generator) *wasp.CallResult { - var res *wasp.CallResult +func (m *GatewaySecretsSetGun) Call(_ *wasp.Generator) *wasp.Response { + var res *wasp.Response switch m.Method { case "secrets_set": res = callSecretsSet(m) diff --git a/integration-tests/load/functions/request_gun.go b/integration-tests/load/functions/request_gun.go index bd4cf5f35aa..6b79a2f19ee 100644 --- a/integration-tests/load/functions/request_gun.go +++ b/integration-tests/load/functions/request_gun.go @@ -48,7 +48,7 @@ func NewSingleFunctionCallGun( } } -func (m *SingleFunctionCallGun) callReal() *wasp.CallResult { +func (m *SingleFunctionCallGun) callReal() *wasp.Response { err := m.ft.LoadTestClient.SendRequestWithDONHostedSecrets( m.times, m.source, @@ -59,12 +59,12 @@ func (m *SingleFunctionCallGun) callReal() *wasp.CallResult { m.jobId, ) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } -func (m *SingleFunctionCallGun) callWithSecrets() *wasp.CallResult { +func (m *SingleFunctionCallGun) callWithSecrets() *wasp.Response { err := m.ft.LoadTestClient.SendRequestWithDONHostedSecrets( m.times, m.source, @@ -75,12 +75,12 @@ func (m *SingleFunctionCallGun) callWithSecrets() *wasp.CallResult { m.jobId, ) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } -func (m *SingleFunctionCallGun) callWithHttp() *wasp.CallResult { +func (m *SingleFunctionCallGun) callWithHttp() *wasp.Response { err := m.ft.LoadTestClient.SendRequest( m.times, m.source, @@ -90,13 +90,13 @@ func (m *SingleFunctionCallGun) callWithHttp() *wasp.CallResult { m.jobId, ) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } // Call implements example gun call, assertions on response bodies should be done here -func (m *SingleFunctionCallGun) Call(_ *wasp.Generator) *wasp.CallResult { +func (m *SingleFunctionCallGun) Call(_ *wasp.Generator) *wasp.Response { switch m.mode { case ModeSecretsOnlyPayload: return m.callWithSecrets() diff --git a/integration-tests/load/ocr/gun.go b/integration-tests/load/ocr/gun.go index a2eb1ff2200..ed92e328024 100644 --- a/integration-tests/load/ocr/gun.go +++ b/integration-tests/load/ocr/gun.go @@ -30,7 +30,7 @@ func NewGun(l zerolog.Logger, cc blockchain.EVMClient, ocrInstances []contracts. } } -func (m *Gun) Call(_ *wasp.Generator) *wasp.CallResult { +func (m *Gun) Call(_ *wasp.Generator) *wasp.Response { m.roundNum.Add(1) requestedRound := m.roundNum.Load() m.l.Info(). @@ -39,17 +39,17 @@ func (m *Gun) Call(_ *wasp.Generator) *wasp.CallResult { Msg("starting new round") err := m.ocrInstances[0].RequestNewRound() if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } for { time.Sleep(5 * time.Second) lr, err := m.ocrInstances[0].GetLatestRound(context.Background()) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } m.l.Info().Interface("LatestRound", lr).Msg("latest round") if lr.RoundId.Int64() >= requestedRound { - return &wasp.CallResult{} + return &wasp.Response{} } } } diff --git a/integration-tests/load/ocr/vu.go b/integration-tests/load/ocr/vu.go index 96be77c701a..d113f7eb3f9 100644 --- a/integration-tests/load/ocr/vu.go +++ b/integration-tests/load/ocr/vu.go @@ -105,17 +105,17 @@ func (m *VU) Call(l *wasp.Generator) { Msg("starting new round") err := m.ocrInstances[0].RequestNewRound() if err != nil { - l.ResponsesChan <- &wasp.CallResult{Error: err.Error(), Failed: true} + l.ResponsesChan <- &wasp.Response{Error: err.Error(), Failed: true} } for { time.Sleep(5 * time.Second) lr, err := m.ocrInstances[0].GetLatestRound(context.Background()) if err != nil { - l.ResponsesChan <- &wasp.CallResult{Error: err.Error(), Failed: true} + l.ResponsesChan <- &wasp.Response{Error: err.Error(), Failed: true} } m.l.Info().Interface("LatestRound", lr).Msg("latest round") if lr.RoundId.Int64() >= requestedRound { - l.ResponsesChan <- &wasp.CallResult{} + l.ResponsesChan <- &wasp.Response{} } } } diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index 8a5eb3c66de..fd8cb6195e8 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -38,7 +38,7 @@ func NewSingleHashGun( } // Call implements example gun call, assertions on response bodies should be done here -func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { +func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets //randomly increase/decrease randomness request count per TX @@ -56,9 +56,9 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { m.logger, ) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } func deviateValue(requestCountPerTX uint16, deviation uint16) uint16 { diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index 8ab278b73e9..77b60e30002 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -38,7 +38,7 @@ func NewSingleHashGun( } // Call implements example gun call, assertions on response bodies should be done here -func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { +func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets //randomly increase/decrease randomness request count per TX @@ -58,9 +58,9 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { m.logger, ) if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } - return &wasp.CallResult{} + return &wasp.Response{} } func deviateValue(requestCountPerTX uint16, deviation uint16) uint16 { diff --git a/integration-tests/universal/log_poller/gun.go b/integration-tests/universal/log_poller/gun.go index 39286f1b53e..a75209aa101 100644 --- a/integration-tests/universal/log_poller/gun.go +++ b/integration-tests/universal/log_poller/gun.go @@ -39,7 +39,7 @@ func NewLogEmitterGun( } } -func (m *LogEmitterGun) Call(l *wasp.Generator) *wasp.CallResult { +func (m *LogEmitterGun) Call(l *wasp.Generator) *wasp.Response { localCounter := 0 logEmitter := (*m.contract) address := logEmitter.Address() @@ -58,7 +58,7 @@ func (m *LogEmitterGun) Call(l *wasp.Generator) *wasp.CallResult { } if err != nil { - return &wasp.CallResult{Error: err.Error(), Failed: true} + return &wasp.Response{Error: err.Error(), Failed: true} } localCounter++ } @@ -69,11 +69,11 @@ func (m *LogEmitterGun) Call(l *wasp.Generator) *wasp.CallResult { defer counter.mu.Unlock() counter.value += localCounter } else { - return &wasp.CallResult{ + return &wasp.Response{ Error: "SharedData did not contain a Counter", Failed: true, } } - return &wasp.CallResult{} + return &wasp.Response{} } From 6ce226c4962c8874bb1e8dd5d9f1557e52a43465 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Mon, 18 Dec 2023 11:21:02 -0500 Subject: [PATCH 074/234] [TT-758] Fixes Upgrade Test (#11589) * Fixes Upgrade Test * Comment * Ani's comments * Mod tidy * Mod tidy again --- integration-tests/docker/test_env/cl_node.go | 9 ++++++++- integration-tests/go.mod | 3 ++- integration-tests/migration/upgrade_version_test.go | 12 ++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index d6ab22957a2..95a0d4c7d84 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -153,8 +153,15 @@ func (n *ClNode) UpgradeVersion(newImage, newVersion string) error { return fmt.Errorf("new version is empty") } if newImage == "" { - return fmt.Errorf("new image name is empty") + newImage = os.Getenv("CHAINLINK_IMAGE") } + n.l.Info(). + Str("Name", n.ContainerName). + Str("Old Image", os.Getenv("CHAINLINK_IMAGE")). + Str("Old Version", os.Getenv("CHAINLINK_VERSION")). + Str("New Image", newImage). + Str("New Version", newVersion). + Msg("Upgrading Chainlink Node") n.ContainerImage = newImage n.ContainerVersion = newVersion return n.Restart(n.NodeConfig) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 73e353c5c1b..f981f303402 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -45,9 +45,10 @@ require ( // avoids ambigious imports of indirect dependencies exclude github.com/hashicorp/consul v1.2.1 -// Pin K8s versions as their updates are highly disruptive and go mod keeps wanting to update them replace ( + // Required until https://github.com/testcontainers/testcontainers-go/pull/1971 is merged github.com/testcontainers/testcontainers-go => github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56 + // Pin K8s versions as their updates are highly disruptive and go mod keeps wanting to update them k8s.io/api => k8s.io/api v0.25.11 k8s.io/client-go => k8s.io/client-go v0.25.11 k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index 97db2374bf3..6603fc1685d 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -1,12 +1,12 @@ package migration import ( - "os" "testing" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -14,6 +14,7 @@ func TestVersionUpgrade(t *testing.T) { t.Parallel() env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithStandardCleanup(). WithGeth(). WithCLNodes(1). Build() @@ -23,16 +24,11 @@ func TestVersionUpgrade(t *testing.T) { require.NoError(t, err, "Error getting upgrade image") upgradeVersion, err := osutil.GetEnv("UPGRADE_VERSION") require.NoError(t, err, "Error getting upgrade version") - - _ = os.Setenv("CHAINLINK_IMAGE", upgradeImage) - _ = os.Setenv("CHAINLINK_VERSION", upgradeVersion) - - // just restarting CL container with the same name, DB is still the same - // // [Database] // MigrateOnStartup = true // // by default - err = env.ClCluster.Nodes[0].Restart(env.ClCluster.Nodes[0].NodeConfig) + err = env.ClCluster.Nodes[0].UpgradeVersion(upgradeImage, upgradeVersion) require.NoError(t, err) + } From cfbc0b06ec02da87f31f7c952b5847d734e76f39 Mon Sep 17 00:00:00 2001 From: Makram Date: Mon, 18 Dec 2023 19:25:07 +0200 Subject: [PATCH 075/234] check mercury opts nil-ness prior to svcs append (#11603) If we append a nil MercuryOpts the application startup will panic. --- core/services/chainlink/application.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index ed043086586..ccf61d17943 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -241,7 +241,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { } // pool must be started before all relayers and stopped after them - srvcs = append(srvcs, opts.MercuryPool) + if opts.MercuryPool != nil { + srvcs = append(srvcs, opts.MercuryPool) + } // EVM chains are used all over the place. This will need to change for fully EVM extraction // TODO: BCF-2510, BCF-2511 From f177b32996b3a49fa236ff37f11b8b2cabcf3472 Mon Sep 17 00:00:00 2001 From: Lei Date: Mon, 18 Dec 2023 11:54:05 -0800 Subject: [PATCH 076/234] use a centralized place for failure reasons and states (#11523) --- core/scripts/chaincli/handler/debug.go | 4 +- .../evmregistry/v21/encoding/interface.go | 59 +++++++++---------- .../evmregistry/v21/encoding/packer.go | 8 +-- .../evmregistry/v21/encoding/packer_test.go | 2 +- .../evmregistry/v21/mercury/mercury.go | 10 ++-- .../evmregistry/v21/mercury/mercury_test.go | 8 +-- .../v21/mercury/streams/streams.go | 42 ++++++------- .../v21/mercury/streams/streams_test.go | 20 +++---- .../v21/mercury/upkeep_failure_reasons.go | 17 ------ .../evmregistry/v21/mercury/upkeep_states.go | 16 ----- .../evmregistry/v21/mercury/v02/request.go | 31 +++++----- .../v21/mercury/v02/v02_request_test.go | 17 +++--- .../evmregistry/v21/mercury/v03/request.go | 35 +++++------ .../v21/mercury/v03/v03_request_test.go | 5 +- .../v21/registry_check_pipeline.go | 4 +- .../v21/registry_check_pipeline_test.go | 6 +- 16 files changed, 127 insertions(+), 157 deletions(-) delete mode 100644 core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go delete mode 100644 core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 288c7a68b80..5ad3d3561f9 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -308,10 +308,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var values [][]byte values, err = streams.DoMercuryRequest(ctx, streamsLookup, checkResults, 0) - if checkResults[0].IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { + if checkResults[0].IneligibilityReason == uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput) { resolveIneligible("upkeep used invalid revert data") } - if checkResults[0].PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { + if checkResults[0].PipelineExecutionState == uint8(encoding.InvalidMercuryRequest) { resolveIneligible("the mercury request data is invalid") } if err != nil { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go index ed3218ea405..4555a5b0b9b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -7,48 +7,47 @@ import ( iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" ) +type UpkeepFailureReason uint8 +type PipelineExecutionState uint8 + const ( - // NOTE: This enum should be kept in sync with evmregistry/v21/mercury/upkeep_failure_reasons.go - // TODO (AUTO-7928) Remove this duplication // upkeep failure onchain reasons - UpkeepFailureReasonNone uint8 = 0 - UpkeepFailureReasonUpkeepCancelled uint8 = 1 - UpkeepFailureReasonUpkeepPaused uint8 = 2 - UpkeepFailureReasonTargetCheckReverted uint8 = 3 - UpkeepFailureReasonUpkeepNotNeeded uint8 = 4 - UpkeepFailureReasonPerformDataExceedsLimit uint8 = 5 - UpkeepFailureReasonInsufficientBalance uint8 = 6 - UpkeepFailureReasonMercuryCallbackReverted uint8 = 7 - UpkeepFailureReasonRevertDataExceedsLimit uint8 = 8 - UpkeepFailureReasonRegistryPaused uint8 = 9 + UpkeepFailureReasonNone UpkeepFailureReason = 0 + UpkeepFailureReasonUpkeepCancelled UpkeepFailureReason = 1 + UpkeepFailureReasonUpkeepPaused UpkeepFailureReason = 2 + UpkeepFailureReasonTargetCheckReverted UpkeepFailureReason = 3 + UpkeepFailureReasonUpkeepNotNeeded UpkeepFailureReason = 4 + UpkeepFailureReasonPerformDataExceedsLimit UpkeepFailureReason = 5 + UpkeepFailureReasonInsufficientBalance UpkeepFailureReason = 6 + UpkeepFailureReasonMercuryCallbackReverted UpkeepFailureReason = 7 + UpkeepFailureReasonRevertDataExceedsLimit UpkeepFailureReason = 8 + UpkeepFailureReasonRegistryPaused UpkeepFailureReason = 9 // leaving a gap here for more onchain failure reasons in the future // upkeep failure offchain reasons - UpkeepFailureReasonMercuryAccessNotAllowed uint8 = 32 - UpkeepFailureReasonTxHashNoLongerExists uint8 = 33 - UpkeepFailureReasonInvalidRevertDataInput uint8 = 34 - UpkeepFailureReasonSimulationFailed uint8 = 35 - UpkeepFailureReasonTxHashReorged uint8 = 36 + UpkeepFailureReasonMercuryAccessNotAllowed UpkeepFailureReason = 32 + UpkeepFailureReasonTxHashNoLongerExists UpkeepFailureReason = 33 + UpkeepFailureReasonInvalidRevertDataInput UpkeepFailureReason = 34 + UpkeepFailureReasonSimulationFailed UpkeepFailureReason = 35 + UpkeepFailureReasonTxHashReorged UpkeepFailureReason = 36 - // NOTE: This enum should be kept in sync with evmregistry/v21/mercury/upkeep_states.go - // TODO (AUTO-7928) Remove this duplication // pipeline execution error - NoPipelineError uint8 = 0 - CheckBlockTooOld uint8 = 1 - CheckBlockInvalid uint8 = 2 - RpcFlakyFailure uint8 = 3 - MercuryFlakyFailure uint8 = 4 - PackUnpackDecodeFailed uint8 = 5 - MercuryUnmarshalError uint8 = 6 - InvalidMercuryRequest uint8 = 7 - InvalidMercuryResponse uint8 = 8 // this will only happen if Mercury server sends bad responses - UpkeepNotAuthorized uint8 = 9 + NoPipelineError PipelineExecutionState = 0 + CheckBlockTooOld PipelineExecutionState = 1 + CheckBlockInvalid PipelineExecutionState = 2 + RpcFlakyFailure PipelineExecutionState = 3 + MercuryFlakyFailure PipelineExecutionState = 4 + PackUnpackDecodeFailed PipelineExecutionState = 5 + MercuryUnmarshalError PipelineExecutionState = 6 + InvalidMercuryRequest PipelineExecutionState = 7 + InvalidMercuryResponse PipelineExecutionState = 8 // this will only happen if Mercury server sends bad responses + UpkeepNotAuthorized PipelineExecutionState = 9 ) type UpkeepInfo = iregistry21.KeeperRegistryBase21UpkeepInfo type Packer interface { UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw string) (ocr2keepers.CheckResult, error) - UnpackPerformResult(raw string) (uint8, bool, error) + UnpackPerformResult(raw string) (PipelineExecutionState, bool, error) UnpackLogTriggerConfig(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) PackReport(report automation_utils_2_1.KeeperRegistryBase21Report) ([]byte, error) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistryBase21Report, error) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go index 4a92b4a17ec..7db8b220a4d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go @@ -66,7 +66,7 @@ func (p *abiPacker) UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw str return result, nil } -func (p *abiPacker) UnpackPerformResult(raw string) (uint8, bool, error) { +func (p *abiPacker) UnpackPerformResult(raw string) (PipelineExecutionState, bool, error) { b, err := hexutil.Decode(raw) if err != nil { return PackUnpackDecodeFailed, false, err @@ -134,10 +134,10 @@ func (p *abiPacker) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistr } // GetIneligibleCheckResultWithoutPerformData returns an ineligible check result with ineligibility reason and pipeline execution state but without perform data -func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason uint8, state uint8, retryable bool) ocr2keepers.CheckResult { +func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason UpkeepFailureReason, state PipelineExecutionState, retryable bool) ocr2keepers.CheckResult { return ocr2keepers.CheckResult{ - IneligibilityReason: reason, - PipelineExecutionState: state, + IneligibilityReason: uint8(reason), + PipelineExecutionState: uint8(state), Retryable: retryable, UpkeepID: p.UpkeepID, Trigger: p.Trigger, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go index 71c2755f150..3f08fcf0a2d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go @@ -209,7 +209,7 @@ func TestPacker_UnpackPerformResult(t *testing.T) { tests := []struct { Name string RawData string - State uint8 + State PipelineExecutionState }{ { Name: "unpack success", diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go index 10e77bf50ce..d3a5167ef72 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go @@ -69,7 +69,7 @@ type MercuryData struct { Error error Retryable bool Bytes [][]byte - State MercuryUpkeepState + State encoding.PipelineExecutionState } type MercuryConfigProvider interface { @@ -85,7 +85,7 @@ type HttpClient interface { } type MercuryClient interface { - DoRequest(ctx context.Context, streamsLookup *StreamsLookup, pluginRetryKey string) (MercuryUpkeepState, MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) + DoRequest(ctx context.Context, streamsLookup *StreamsLookup, pluginRetryKey string) (encoding.PipelineExecutionState, encoding.UpkeepFailureReason, [][]byte, bool, time.Duration, error) } type StreamsLookupError struct { @@ -116,7 +116,7 @@ func (l *StreamsLookup) IsMercuryV03UsingBlockNumber() bool { } type Packer interface { - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) + UnpackCheckCallbackResult(callbackResp []byte) (encoding.PipelineExecutionState, bool, []byte, encoding.UpkeepFailureReason, *big.Int, error) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) @@ -149,7 +149,7 @@ func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError }, nil } -func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { +func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (encoding.PipelineExecutionState, bool, []byte, encoding.UpkeepFailureReason, *big.Int, error) { out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) if err != nil { return encoding.PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) @@ -157,7 +157,7 @@ func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) - failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) + failureReason := encoding.UpkeepFailureReason(*abi.ConvertType(out[2], new(uint8)).(*uint8)) gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) return encoding.NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go index 6095e7b9466..c7e22d45878 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go @@ -191,17 +191,17 @@ func TestPacker_UnpackCheckCallbackResult(t *testing.T) { CallbackResp []byte UpkeepNeeded bool PerformData []byte - FailureReason uint8 + FailureReason encoding.UpkeepFailureReason GasUsed *big.Int ErrorString string - State uint8 + State encoding.PipelineExecutionState }{ { Name: "unpack upkeep needed", CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, UpkeepNeeded: true, PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(encoding.UpkeepFailureReasonNone), + FailureReason: encoding.UpkeepFailureReasonNone, GasUsed: big.NewInt(11796), }, { @@ -209,7 +209,7 @@ func TestPacker_UnpackCheckCallbackResult(t *testing.T) { CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, UpkeepNeeded: false, PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(encoding.UpkeepFailureReasonUpkeepNotNeeded), + FailureReason: encoding.UpkeepFailureReasonUpkeepNotNeeded, GasUsed: big.NewInt(13008), }, { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index e1bb69f33d8..27aafeca553 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -21,6 +21,7 @@ import ( iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02" v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03" @@ -114,7 +115,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe // buildResult checks if the upkeep is allowed by Mercury and builds a streams lookup request from the check result func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keepers.CheckResult, checkResults []ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { lookupLggr := s.lggr.With("where", "StreamsLookup") - if checkResult.IneligibilityReason != uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted) { + if checkResult.IneligibilityReason != uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { // Streams Lookup only works when upkeep target check reverts return } @@ -139,7 +140,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper streamsLookupResponse := &mercury.StreamsLookup{StreamsLookupError: streamsLookupErr} if len(streamsLookupResponse.Feeds) == 0 { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput) lookupLggr.Debugf("at block %s upkeep %s has empty feeds array", block, upkeepId) return } @@ -156,13 +157,13 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper return } else if !allowed { lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) + checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonMercuryAccessNotAllowed) return } } else if !streamsLookupResponse.IsMercuryV03() { // if mercury version is not v02 or v03, set failure reason lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput) return } @@ -191,7 +192,7 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) if err != nil { checkResults[i].Retryable = false - checkResults[i].PipelineExecutionState = uint8(mercury.PackUnpackDecodeFailed) + checkResults[i].PipelineExecutionState = uint8(encoding.PackUnpackDecodeFailed) return err } @@ -204,7 +205,7 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me // call checkCallback function at the block which OCR3 has agreed upon if err = s.client.CallContext(ctx, &mercuryBytes, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { checkResults[i].Retryable = true - checkResults[i].PipelineExecutionState = uint8(mercury.RpcFlakyFailure) + checkResults[i].PipelineExecutionState = uint8(encoding.RpcFlakyFailure) return err } @@ -212,24 +213,23 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me unpackCallBackState, needed, performData, failureReason, _, err := s.packer.UnpackCheckCallbackResult(mercuryBytes) if err != nil { - checkResults[i].PipelineExecutionState = unpackCallBackState + checkResults[i].PipelineExecutionState = uint8(unpackCallBackState) return err } - if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) + if failureReason == encoding.UpkeepFailureReasonMercuryCallbackReverted { + checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonMercuryCallbackReverted) s.lggr.Debugf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) return nil - } if !needed { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) + checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonUpkeepNotNeeded) s.lggr.Debugf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) return nil } - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) + checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonNone) checkResults[i].Eligible = true checkResults[i].PerformData = performData s.lggr.Infof("at block %d upkeep %s requested time %s CheckCallback successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) @@ -238,7 +238,7 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me } func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsLookup, checkResults []ocr2keepers.CheckResult, i int) ([][]byte, error) { - state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) + state, reason, values, retryable, retryInterval, err := encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) pluginRetryKey := generatePluginRetryKey(checkResults[i].WorkID, lookup.Block) if lookup.IsMercuryV02() { @@ -263,10 +263,10 @@ func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsL // AllowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if // this upkeep is allowed to use Mercury service. -func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { +func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state encoding.PipelineExecutionState, reason encoding.UpkeepFailureReason, retryable bool, allow bool, err error) { allowed, ok := s.mercuryConfig.IsUpkeepAllowed(upkeepId.String()) if ok { - return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, allowed.(bool), nil + return encoding.NoPipelineError, encoding.UpkeepFailureReasonNone, false, allowed.(bool), nil } payload, err := s.packer.PackGetUpkeepPrivilegeConfig(upkeepId) @@ -274,7 +274,7 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s // pack error, no retryable s.lggr.Warnf("failed to pack getUpkeepPrivilegeConfig data for upkeepId %s: %s", upkeepId, err) - return mercury.PackUnpackDecodeFailed, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to pack upkeepId: %w", err) + return encoding.PackUnpackDecodeFailed, encoding.UpkeepFailureReasonNone, false, false, fmt.Errorf("failed to pack upkeepId: %w", err) } var resultBytes hexutil.Bytes @@ -284,29 +284,29 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s } if err = s.client.CallContext(opts.Context, &resultBytes, "eth_call", args, hexutil.EncodeBig(opts.BlockNumber)); err != nil { - return mercury.RpcFlakyFailure, mercury.MercuryUpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) + return encoding.RpcFlakyFailure, encoding.UpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } var upkeepPrivilegeConfigBytes []byte upkeepPrivilegeConfigBytes, err = s.packer.UnpackGetUpkeepPrivilegeConfig(resultBytes) if err != nil { - return mercury.PackUnpackDecodeFailed, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) + return encoding.PackUnpackDecodeFailed, encoding.UpkeepFailureReasonNone, false, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } if len(upkeepPrivilegeConfigBytes) == 0 { s.mercuryConfig.SetUpkeepAllowed(upkeepId.String(), false, cache.DefaultExpiration) - return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed, false, false, fmt.Errorf("upkeep privilege config is empty") + return encoding.NoPipelineError, encoding.UpkeepFailureReasonMercuryAccessNotAllowed, false, false, fmt.Errorf("upkeep privilege config is empty") } var privilegeConfig UpkeepPrivilegeConfig if err = json.Unmarshal(upkeepPrivilegeConfigBytes, &privilegeConfig); err != nil { - return mercury.MercuryUnmarshalError, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to unmarshal privilege config: %v", err) + return encoding.MercuryUnmarshalError, encoding.UpkeepFailureReasonNone, false, false, fmt.Errorf("failed to unmarshal privilege config: %v", err) } s.mercuryConfig.SetUpkeepAllowed(upkeepId.String(), privilegeConfig.MercuryEnabled, cache.DefaultExpiration) - return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil + return encoding.NoPipelineError, encoding.UpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil } func (s *streams) buildCallOpts(ctx context.Context, block *big.Int) *bind.CallOpts { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 40f32751161..31b6597bb23 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -136,7 +136,7 @@ func TestStreams_CheckCallback(t *testing.T) { performData []byte wantErr assert.ErrorAssertionFunc - state mercury.MercuryUpkeepState + state encoding.PipelineExecutionState retryable bool registry streamsRegistry }{ @@ -230,7 +230,7 @@ func TestStreams_CheckCallback(t *testing.T) { callbackResp: []byte{}, callbackErr: errors.New("bad response"), wantErr: assert.Error, - state: mercury.RpcFlakyFailure, + state: encoding.RpcFlakyFailure, retryable: true, registry: &mockRegistry{ GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -281,8 +281,8 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { allowed bool ethCallErr error err error - state mercury.MercuryUpkeepState - reason mercury.MercuryUpkeepFailureReason + state encoding.PipelineExecutionState + reason encoding.UpkeepFailureReason registry streamsRegistry retryable bool config []byte @@ -340,7 +340,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { { name: "failure - cannot unmarshal privilege config", err: fmt.Errorf("failed to unmarshal privilege config: invalid character '\\x00' looking for beginning of value"), - state: mercury.MercuryUnmarshalError, + state: encoding.MercuryUnmarshalError, config: []byte{0, 1}, registry: &mockRegistry{ GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -355,7 +355,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { name: "failure - flaky RPC", retryable: true, err: fmt.Errorf("failed to get upkeep privilege config: flaky RPC"), - state: mercury.RpcFlakyFailure, + state: encoding.RpcFlakyFailure, ethCallErr: fmt.Errorf("flaky RPC"), registry: &mockRegistry{ GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -369,7 +369,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { { name: "failure - empty upkeep privilege config", err: fmt.Errorf("upkeep privilege config is empty"), - reason: mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed, + reason: encoding.UpkeepFailureReasonMercuryAccessNotAllowed, config: []byte{}, registry: &mockRegistry{ GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -484,7 +484,7 @@ func TestStreams_StreamsLookup(t *testing.T) { Trigger: ocr2keepers.Trigger{ BlockNumber: blockNum, }, - IneligibilityReason: uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted), + IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), }, }, blobs: map[string]string{ @@ -538,7 +538,7 @@ func TestStreams_StreamsLookup(t *testing.T) { Trigger: ocr2keepers.Trigger{ BlockNumber: blockNum, }, - IneligibilityReason: uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted), + IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), }, }, blobs: map[string]string{ @@ -711,7 +711,7 @@ func TestStreams_StreamsLookup(t *testing.T) { Trigger: ocr2keepers.Trigger{ BlockNumber: blockNum, }, - IneligibilityReason: uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput), + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput), }, }, hasError: true, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go deleted file mode 100644 index 66f8bca402f..00000000000 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go +++ /dev/null @@ -1,17 +0,0 @@ -package mercury - -type MercuryUpkeepFailureReason uint8 - -// NOTE: This enum should be kept in sync with evmregistry/v21/encoding/interface.go -// TODO (AUTO-7928) Remove this duplication -const ( - // upkeep failure onchain reasons - MercuryUpkeepFailureReasonNone MercuryUpkeepFailureReason = 0 - MercuryUpkeepFailureReasonTargetCheckReverted MercuryUpkeepFailureReason = 3 - MercuryUpkeepFailureReasonUpkeepNotNeeded MercuryUpkeepFailureReason = 4 - MercuryUpkeepFailureReasonMercuryCallbackReverted MercuryUpkeepFailureReason = 7 - // leaving a gap here for more onchain failure reasons in the future - // upkeep failure offchain reasons - MercuryUpkeepFailureReasonMercuryAccessNotAllowed MercuryUpkeepFailureReason = 32 - MercuryUpkeepFailureReasonInvalidRevertDataInput MercuryUpkeepFailureReason = 34 -) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go deleted file mode 100644 index 0d8276ff2de..00000000000 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go +++ /dev/null @@ -1,16 +0,0 @@ -package mercury - -type MercuryUpkeepState uint8 - -// NOTE: This enum should be kept in sync with evmregistry/v21/encoding/interface.go -// TODO (AUTO-7928) Remove this duplication -const ( - NoPipelineError MercuryUpkeepState = 0 - RpcFlakyFailure MercuryUpkeepState = 3 - MercuryFlakyFailure MercuryUpkeepState = 4 - PackUnpackDecodeFailed MercuryUpkeepState = 5 - MercuryUnmarshalError MercuryUpkeepState = 6 - InvalidMercuryRequest MercuryUpkeepState = 7 - InvalidMercuryResponse MercuryUpkeepState = 8 // this will only happen if Mercury server sends bad responses - UpkeepNotAuthorized MercuryUpkeepState = 9 -) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go index 8135617b6fd..f69fbb35e35 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -52,11 +53,11 @@ func NewClient(mercuryConfig mercury.MercuryConfigProvider, httpClient mercury.H } } -func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (mercury.MercuryUpkeepState, mercury.MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) { +func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (encoding.PipelineExecutionState, encoding.UpkeepFailureReason, [][]byte, bool, time.Duration, error) { resultLen := len(streamsLookup.Feeds) ch := make(chan mercury.MercuryData, resultLen) if len(streamsLookup.Feeds) == 0 { - return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) + return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) } for i := range streamsLookup.Feeds { // TODO (AUTO-7209): limit the number of concurrent requests @@ -72,15 +73,15 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo retryable := true allSuccess := true // in v0.2, use the last execution error as the state, if no execution errors, state will be no error - state := mercury.NoPipelineError + state := encoding.NoPipelineError for i := 0; i < resultLen; i++ { m := <-ch if m.Error != nil { reqErr = errors.Join(reqErr, m.Error) retryable = retryable && m.Retryable allSuccess = false - if m.State != mercury.NoPipelineError { - state = mercury.MercuryUpkeepState(m.State) + if m.State != encoding.NoPipelineError { + state = encoding.PipelineExecutionState(m.State) } continue } @@ -90,7 +91,7 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo retryInterval = mercury.CalculateRetryConfigFn(pluginRetryKey, c.mercuryConfig) } // only retry when not all successful AND none are not retryable - return state, mercury.MercuryUpkeepFailureReasonNone, results, retryable && !allSuccess, retryInterval, reqErr + return state, encoding.UpkeepFailureReasonNone, results, retryable && !allSuccess, retryInterval, reqErr } func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.MercuryData, index int, sl *mercury.StreamsLookup) { @@ -107,7 +108,7 @@ func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.Mercur httpRequest, err = http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) if err != nil { - ch <- mercury.MercuryData{Index: index, Error: err, Retryable: false, State: mercury.InvalidMercuryRequest} + ch <- mercury.MercuryData{Index: index, Error: err, Retryable: false, State: encoding.InvalidMercuryRequest} return } @@ -119,7 +120,7 @@ func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.Mercur httpRequest.Header.Set(signatureHeader, signature) // in the case of multiple retries here, use the last attempt's data - state := mercury.NoPipelineError + state := encoding.NoPipelineError retryable := false sent := false retryErr := retry.Do( @@ -132,13 +133,13 @@ func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.Mercur if httpResponse, err = c.httpClient.Do(httpRequest); err != nil { c.lggr.Warnf("at block %s upkeep %s GET request fails for feed %s: %v", sl.Time.String(), sl.UpkeepId.String(), sl.Feeds[index], err) retryable = true - state = mercury.MercuryFlakyFailure + state = encoding.MercuryFlakyFailure return err } defer httpResponse.Body.Close() if responseBody, err = io.ReadAll(httpResponse.Body); err != nil { - state = mercury.InvalidMercuryResponse + state = encoding.InvalidMercuryResponse return err } @@ -146,12 +147,12 @@ func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.Mercur case http.StatusNotFound, http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: c.lggr.Warnf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.UpkeepId.String(), httpResponse.StatusCode, sl.Feeds[index]) retryable = true - state = mercury.MercuryFlakyFailure + state = encoding.MercuryFlakyFailure return errors.New(strconv.FormatInt(int64(httpResponse.StatusCode), 10)) case http.StatusOK: // continue default: - state = mercury.InvalidMercuryRequest + state = encoding.InvalidMercuryRequest return fmt.Errorf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.UpkeepId.String(), httpResponse.StatusCode, sl.Feeds[index]) } @@ -160,19 +161,19 @@ func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.Mercur var m MercuryV02Response if err = json.Unmarshal(responseBody, &m); err != nil { c.lggr.Warnf("at block %s upkeep %s failed to unmarshal body to MercuryV02Response for feed %s: %v", sl.Time.String(), sl.UpkeepId.String(), sl.Feeds[index], err) - state = mercury.MercuryUnmarshalError + state = encoding.MercuryUnmarshalError return err } if blobBytes, err = hexutil.Decode(m.ChainlinkBlob); err != nil { c.lggr.Warnf("at block %s upkeep %s failed to decode chainlinkBlob %s for feed %s: %v", sl.Time.String(), sl.UpkeepId.String(), m.ChainlinkBlob, sl.Feeds[index], err) - state = mercury.InvalidMercuryResponse + state = encoding.InvalidMercuryResponse return err } ch <- mercury.MercuryData{ Index: index, Bytes: [][]byte{blobBytes}, Retryable: false, - State: mercury.NoPipelineError, + State: encoding.NoPipelineError, } sent = true return nil diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go index ac945859d89..686b2199ebc 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -289,8 +290,8 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { expectedRetryable bool expectedRetryInterval time.Duration expectedError error - state mercury.MercuryUpkeepState - reason mercury.MercuryUpkeepFailureReason + state encoding.PipelineExecutionState + reason encoding.UpkeepFailureReason }{ { name: "success", @@ -329,7 +330,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { pluginRetries: 0, expectedRetryInterval: 1 * time.Second, expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), - state: mercury.MercuryFlakyFailure, + state: encoding.MercuryFlakyFailure, }, { name: "failure - retryable and interval is 5s", @@ -350,7 +351,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { expectedRetryable: true, expectedRetryInterval: 5 * time.Second, expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), - state: mercury.MercuryFlakyFailure, + state: encoding.MercuryFlakyFailure, }, { name: "failure - not retryable because there are many plugin retries already", @@ -370,7 +371,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { expectedValues: [][]byte{nil}, expectedRetryable: true, expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), - state: mercury.MercuryFlakyFailure, + state: encoding.MercuryFlakyFailure, }, { name: "failure - not retryable", @@ -389,7 +390,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { expectedValues: [][]byte{nil}, expectedRetryable: false, expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 25880526 upkeep 88786950015966611018675766524283132478093844178961698330929478019253453382042 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"), - state: mercury.InvalidMercuryRequest, + state: encoding.InvalidMercuryRequest, }, { name: "failure - no feeds", @@ -404,7 +405,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { UpkeepId: upkeepId, }, expectedValues: [][]byte{}, - reason: mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, + reason: encoding.UpkeepFailureReasonInvalidRevertDataInput, }, { name: "failure - invalid revert data", @@ -419,7 +420,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { UpkeepId: upkeepId, }, expectedValues: [][]byte{}, - reason: mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, + reason: encoding.UpkeepFailureReasonInvalidRevertDataInput, }, } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go index ea465ff8582..584069adde3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -60,9 +61,9 @@ func NewClient(mercuryConfig mercury.MercuryConfigProvider, httpClient mercury.H } } -func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (mercury.MercuryUpkeepState, mercury.MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) { +func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (encoding.PipelineExecutionState, encoding.UpkeepFailureReason, [][]byte, bool, time.Duration, error) { if len(streamsLookup.Feeds) == 0 { - return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) + return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) } resultLen := 1 // Only 1 multi-feed request is made for all feeds ch := make(chan mercury.MercuryData, resultLen) @@ -74,7 +75,7 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo var retryInterval time.Duration results := make([][]byte, len(streamsLookup.Feeds)) retryable := false - state := mercury.NoPipelineError + state := encoding.NoPipelineError m := <-ch if m.Error != nil { @@ -88,7 +89,7 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo results = m.Bytes } - return state, mercury.MercuryUpkeepFailureReasonNone, results, retryable, retryInterval, reqErr + return state, encoding.UpkeepFailureReasonNone, results, retryable, retryInterval, reqErr } func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.MercuryData, sl *mercury.StreamsLookup) { @@ -109,7 +110,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) if err != nil { - ch <- mercury.MercuryData{Index: 0, Error: err, Retryable: false, State: mercury.InvalidMercuryRequest} + ch <- mercury.MercuryData{Index: 0, Error: err, Retryable: false, State: encoding.InvalidMercuryRequest} return } @@ -126,7 +127,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur req.Header.Set(upkeepIDHeader, sl.UpkeepId.String()) // in the case of multiple retries here, use the last attempt's data - state := mercury.NoPipelineError + state := encoding.NoPipelineError retryable := false sent := false retryErr := retry.Do( @@ -136,7 +137,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur if err != nil { c.lggr.Warnf("at timestamp %s upkeep %s GET request fails from mercury v0.3: %v", sl.Time.String(), sl.UpkeepId.String(), err) retryable = true - state = mercury.MercuryFlakyFailure + state = encoding.MercuryFlakyFailure return err } defer resp.Body.Close() @@ -144,7 +145,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur body, err := io.ReadAll(resp.Body) if err != nil { retryable = false - state = mercury.InvalidMercuryResponse + state = encoding.InvalidMercuryResponse return err } @@ -152,27 +153,27 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur switch resp.StatusCode { case http.StatusUnauthorized: retryable = false - state = mercury.UpkeepNotAuthorized + state = encoding.UpkeepNotAuthorized return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by unauthorized upkeep", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) case http.StatusBadRequest: retryable = false - state = mercury.InvalidMercuryRequest + state = encoding.InvalidMercuryRequest return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by invalid format of timestamp", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) case http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: retryable = true - state = mercury.MercuryFlakyFailure + state = encoding.MercuryFlakyFailure return fmt.Errorf("%d", resp.StatusCode) case http.StatusPartialContent: // TODO (AUTO-5044): handle response code 206 entirely with errors field parsing c.lggr.Warnf("at timestamp %s upkeep %s requested [%s] feeds but mercury v0.3 server returned 206 status, treating it as 404 and retrying", sl.Time.String(), sl.UpkeepId.String(), sl.Feeds) retryable = true - state = mercury.MercuryFlakyFailure + state = encoding.MercuryFlakyFailure return fmt.Errorf("%d", http.StatusPartialContent) case http.StatusOK: // continue default: retryable = false - state = mercury.InvalidMercuryRequest + state = encoding.InvalidMercuryRequest return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) } c.lggr.Debugf("at block %s upkeep %s received status code %d from mercury v0.3 with BODY=%s", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode, hexutil.Encode(body)) @@ -181,7 +182,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur if err := json.Unmarshal(body, &response); err != nil { c.lggr.Warnf("at timestamp %s upkeep %s failed to unmarshal body to MercuryV03Response from mercury v0.3: %v", sl.Time.String(), sl.UpkeepId.String(), err) retryable = false - state = mercury.MercuryUnmarshalError + state = encoding.MercuryUnmarshalError return err } @@ -194,7 +195,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur } c.lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server returned 206 status with [%s] reports while we requested [%s] feeds, retrying", sl.Time.String(), sl.UpkeepId.String(), receivedFeeds, sl.Feeds) retryable = true - state = mercury.MercuryFlakyFailure + state = encoding.MercuryFlakyFailure return fmt.Errorf("%d", http.StatusNotFound) } var reportBytes [][]byte @@ -203,7 +204,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur if err != nil { c.lggr.Warnf("at timestamp %s upkeep %s failed to decode reportBlob %s: %v", sl.Time.String(), sl.UpkeepId.String(), rsp.FullReport, err) retryable = false - state = mercury.InvalidMercuryResponse + state = encoding.InvalidMercuryResponse return err } reportBytes = append(reportBytes, b) @@ -212,7 +213,7 @@ func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.Mercur Index: 0, Bytes: reportBytes, Retryable: false, - State: mercury.NoPipelineError, + State: encoding.NoPipelineError, } sent = true return nil diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go index 16ccdac5d1f..4a22ae477e6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -108,8 +109,8 @@ func TestV03_DoMercuryRequestV03(t *testing.T) { expectedRetryable bool expectedRetryInterval time.Duration expectedError error - state mercury.MercuryUpkeepState - reason mercury.MercuryUpkeepFailureReason + state encoding.PipelineExecutionState + reason encoding.UpkeepFailureReason }{ { name: "success v0.3", diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go index f5a931aae45..33d4a7157be 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go @@ -105,7 +105,7 @@ func (r *EvmRegistry) getBlockHash(blockNumber *big.Int) (common.Hash, error) { } // verifyCheckBlock checks that the check block and hash are valid, returns the pipeline execution state and retryable -func (r *EvmRegistry) verifyCheckBlock(_ context.Context, checkBlock, upkeepId *big.Int, checkHash common.Hash) (state uint8, retryable bool) { +func (r *EvmRegistry) verifyCheckBlock(_ context.Context, checkBlock, upkeepId *big.Int, checkHash common.Hash) (state encoding.PipelineExecutionState, retryable bool) { // verify check block number and hash are valid h, ok := r.bs.queryBlocksMap(checkBlock.Int64()) // if this block number/hash combo exists in block subscriber, this check block and hash still exist on chain and are valid @@ -128,7 +128,7 @@ func (r *EvmRegistry) verifyCheckBlock(_ context.Context, checkBlock, upkeepId * } // verifyLogExists checks that the log still exists on chain, returns failure reason, pipeline error, and retryable -func (r *EvmRegistry) verifyLogExists(upkeepId *big.Int, p ocr2keepers.UpkeepPayload) (uint8, uint8, bool) { +func (r *EvmRegistry) verifyLogExists(upkeepId *big.Int, p ocr2keepers.UpkeepPayload) (encoding.UpkeepFailureReason, encoding.PipelineExecutionState, bool) { logBlockNumber := int64(p.Trigger.LogTriggerExtension.BlockNumber) logBlockHash := common.BytesToHash(p.Trigger.LogTriggerExtension.BlockHash[:]) checkBlockHash := common.BytesToHash(p.Trigger.BlockHash[:]) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go index a14aaec0c5e..9867a58201b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go @@ -90,7 +90,7 @@ func TestRegistry_VerifyCheckBlock(t *testing.T) { payload ocr2keepers.UpkeepPayload blocks map[int64]string poller logpoller.LogPoller - state uint8 + state encoding.PipelineExecutionState retryable bool makeEthCall bool }{ @@ -248,8 +248,8 @@ func TestRegistry_VerifyLogExists(t *testing.T) { payload ocr2keepers.UpkeepPayload blocks map[int64]string makeEthCall bool - reason uint8 - state uint8 + reason encoding.UpkeepFailureReason + state encoding.PipelineExecutionState retryable bool ethCallErr error receipt *types.Receipt From 96652ec541aa6d5e4fbd73cbbdcd16c2bcbb21a8 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:55:58 -0500 Subject: [PATCH 077/234] Create PR label workflow for CRIB (#11544) --- .github/workflows/pr-labels.yml | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/pr-labels.yml diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 00000000000..66ef95434cf --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,54 @@ +name: PR Labels + +on: + pull_request: + types: [labeled] + +jobs: + crib: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Comment on PR + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const labelsToCheck = ["crib"]; + const { owner, repo, number: prNumber } = context.issue; + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number: prNumber }); + const labelMatches = labels.some(label => labelsToCheck.includes(label.name)); + + if (!labelMatches) { + core.info("No 'crib' PR label found. Proceeding."); + return; + } + + const comment = `## CRIB Environment Details :information_source: + + CRIB activated via the 'crib' label. To destroy the environment, remove the 'crib' PR label or close the PR. + + Please review the following details: + + ### Subdomains + + _Use these subdomains to access the CRIB environment. They are prefixes to the internal base domain._ + + - crib-chain-${prNumber}-node1. + - crib-chain-${prNumber}-node2. + - crib-chain-${prNumber}-node3. + - crib-chain-${prNumber}-node4. + - crib-chain-${prNumber}-node5. + - crib-chain-${prNumber}-node6. + - crib-chain-${prNumber}-geth-http. + - crib-chain-${prNumber}-geth-ws. + - crib-chain-${prNumber}-mockserver. + `; + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body: comment + }); From 9029fa6ce91e7fda25ce93cdcb5b5aa49425d80e Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Mon, 18 Dec 2023 15:04:47 -0500 Subject: [PATCH 078/234] Include BSC in reporting (#11606) --- .github/workflows/live-testnet-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index d060b5c510d..f202f03e51b 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -205,7 +205,7 @@ jobs: strategy: fail-fast: false matrix: - network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Scroll Sepolia, Linea Goerli] + network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Scroll Sepolia, Linea Goerli, BSC Testnet] steps: - name: Get Results id: test-results From 17420af9b3df21aff7b90784cd92f901977a7d88 Mon Sep 17 00:00:00 2001 From: Tate Date: Mon, 18 Dec 2023 17:06:39 -0700 Subject: [PATCH 079/234] Bump solana build contracts version to fix bad upload artifact version in the reused action (#11611) --- .github/workflows/integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 0c754807f31..b1caa52acec 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -852,7 +852,7 @@ jobs: ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Build contracts if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' - uses: smartcontractkit/chainlink-solana/.github/actions/build_contract_artifacts@23816fcf7d380a30c87b6d87e4fb0ca94419b259 # stable action on April 17 2023 + uses: smartcontractkit/chainlink-solana/.github/actions/build_contract_artifacts@21675b3a7dcdff8e790391708d4763020cace21e # stable action on December 18 2023 with: ref: ${{ needs.get_solana_sha.outputs.sha }} From 233445a7799ef5d154310791d2c0930a37449077 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Tue, 19 Dec 2023 03:05:43 +0100 Subject: [PATCH 080/234] BCI-2508: TXM duplicate nonces caused by trasmitchecker (#11546) * Broadcaster: run checker only for unstarted txs * attempt to fix sigscanner * fix txstore invariant check * add invariant to ensure we are processing unstarted tx * add explanation why we run check only on unstarted --- common/txmgr/broadcaster.go | 61 ++++++++++++++++----------- core/chains/evm/txmgr/evm_tx_store.go | 4 +- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 7323fba7a81..9f2204f37e2 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -485,22 +485,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc return false, nil } n++ - var a txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] - var retryable bool - a, _, _, retryable, err = eb.NewTxAttempt(ctx, *etx, eb.lggr) - if err != nil { - return retryable, fmt.Errorf("processUnstartedTxs failed on NewAttempt: %w", err) - } - - if err := eb.txStore.UpdateTxUnstartedToInProgress(ctx, etx, &a); errors.Is(err, ErrTxRemoved) { - eb.lggr.Debugw("tx removed", "txID", etx.ID, "subject", etx.Subject) - continue - } else if err != nil { - return true, fmt.Errorf("processUnstartedTxs failed on UpdateTxUnstartedToInProgress: %w", err) - } - if err, retryable := eb.handleInProgressTx(ctx, *etx, a, time.Now()); err != nil { - return retryable, fmt.Errorf("processUnstartedTxs failed on handleInProgressTx: %w", err) + if err, retryable := eb.handleUnstartedTx(ctx, etx); err != nil { + return retryable, fmt.Errorf("processUnstartedTxs failed on handleUnstartedTx: %w", err) } } } @@ -520,11 +507,14 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand return nil, false } -// There can be at most one in_progress transaction per address. -// Here we complete the job that we didn't finish last time. -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) handleInProgressTx(ctx context.Context, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time) (error, bool) { - if etx.State != TxInProgress { - return fmt.Errorf("invariant violation: expected transaction %v to be in_progress, it was %s", etx.ID, etx.State), false +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) handleUnstartedTx(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) (error, bool) { + if etx.State != TxUnstarted { + return fmt.Errorf("invariant violation: expected transaction %v to be unstarted, it was %s", etx.ID, etx.State), false + } + + attempt, _, _, retryable, err := eb.NewTxAttempt(ctx, *etx, eb.lggr) + if err != nil { + return fmt.Errorf("processUnstartedTxs failed on NewAttempt: %w", err), retryable } checkerSpec, err := etx.GetChecker() @@ -541,19 +531,40 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // If the transmit check does not complete within the timeout, the transaction will be sent // anyway. + // It's intentional that we only run `Check` for unstarted transactions. + // Running it on other states might lead to nonce duplication, as we might mark applied transactions as fatally errored. + checkCtx, cancel := context.WithTimeout(ctx, TransmitCheckTimeout) defer cancel() - err = checker.Check(checkCtx, lgr, etx, attempt) + err = checker.Check(checkCtx, lgr, *etx, attempt) if errors.Is(err, context.Canceled) { lgr.Warn("Transmission checker timed out, sending anyway") } else if err != nil { etx.Error = null.StringFrom(err.Error()) lgr.Warnw("Transmission checker failed, fatally erroring transaction.", "err", err) - return eb.saveFatallyErroredTransaction(lgr, &etx), true + return eb.saveFatallyErroredTransaction(lgr, etx), true } cancel() - lgr.Infow("Sending transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash, "err", err, "meta", etx.Meta, "feeLimit", etx.FeeLimit, "attempt", attempt, "etx", etx) + if err = eb.txStore.UpdateTxUnstartedToInProgress(ctx, etx, &attempt); errors.Is(err, ErrTxRemoved) { + eb.lggr.Debugw("tx removed", "txID", etx.ID, "subject", etx.Subject) + return nil, false + } else if err != nil { + return fmt.Errorf("processUnstartedTxs failed on UpdateTxUnstartedToInProgress: %w", err), true + } + + return eb.handleInProgressTx(ctx, *etx, attempt, time.Now()) +} + +// There can be at most one in_progress transaction per address. +// Here we complete the job that we didn't finish last time. +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) handleInProgressTx(ctx context.Context, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time) (error, bool) { + if etx.State != TxInProgress { + return fmt.Errorf("invariant violation: expected transaction %v to be in_progress, it was %s", etx.ID, etx.State), false + } + + lgr := etx.GetLogger(logger.With(eb.lggr, "fee", attempt.TxFee)) + lgr.Infow("Sending transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash, "meta", etx.Meta, "feeLimit", etx.FeeLimit, "attempt", attempt, "etx", etx) errType, err := eb.client.SendTransactionReturnCode(ctx, etx, attempt, lgr) if errType != client.Fatal { @@ -760,8 +771,8 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) save func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) saveFatallyErroredTransaction(lgr logger.Logger, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ctx, cancel := eb.chStop.NewCtx() defer cancel() - if etx.State != TxInProgress { - return fmt.Errorf("can only transition to fatal_error from in_progress, transaction is currently %s", etx.State) + if etx.State != TxInProgress && etx.State != TxUnstarted { + return fmt.Errorf("can only transition to fatal_error from in_progress or unstarted, transaction is currently %s", etx.State) } if !etx.Error.Valid { return errors.New("expected error field to be set") diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 9b46ab7a80c..da206c46c7d 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1561,8 +1561,8 @@ func (o *evmTxStore) UpdateTxFatalError(ctx context.Context, etx *Tx) error { ctx, cancel = o.mergeContexts(ctx) defer cancel() qq := o.q.WithOpts(pg.WithParentCtx(ctx)) - if etx.State != txmgr.TxInProgress { - return pkgerrors.Errorf("can only transition to fatal_error from in_progress, transaction is currently %s", etx.State) + if etx.State != txmgr.TxInProgress && etx.State != txmgr.TxUnstarted { + return pkgerrors.Errorf("can only transition to fatal_error from in_progress or unstarted, transaction is currently %s", etx.State) } if !etx.Error.Valid { return errors.New("expected error field to be set") From d4ab8779dc60f7aa4aee1c4d95b8ab1a56f48323 Mon Sep 17 00:00:00 2001 From: Iva Brajer Date: Tue, 19 Dec 2023 14:32:58 +0100 Subject: [PATCH 081/234] Added smoke test for canceling subscription on VRFv2 (#11587) * Added smoke test for canceling subscription on VRFv2 * Added smoke test for canceling subscription on VRFv2 * Extended v2 coordinator contract model with WaitForSubscriptionCanceledEvent * Added smoke test for owner canceling subscription on VRFv2 --------- Co-authored-by: David Kneisly Co-authored-by: Ilja Pavlovs --- .../contracts/contract_vrf_models.go | 2 + .../contracts/ethereum_vrfv2_contracts.go | 39 ++++ integration-tests/smoke/vrfv2_test.go | 187 ++++++++++++++++++ 3 files changed, 228 insertions(+) diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 548cac252b1..4577664774e 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -54,10 +54,12 @@ type VRFCoordinatorV2 interface { Address() string GetSubscription(ctx context.Context, subID uint64) (vrf_coordinator_v2.GetSubscription, error) PendingRequestsExist(ctx context.Context, subID uint64) (bool, error) + OwnerCancelSubscription(subID uint64) (*types.Transaction, error) CancelSubscription(subID uint64, to common.Address) (*types.Transaction, error) FindSubscriptionID(subID uint64) (uint64, error) WaitForRandomWordsFulfilledEvent(requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) + WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) } type VRFCoordinatorV2_5 interface { diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index d1637c93c87..ba606a884b3 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -224,6 +224,25 @@ func (v *EthereumVRFCoordinatorV2) PendingRequestsExist(ctx context.Context, sub return pendingRequestExists, nil } +// OwnerCancelSubscription cancels subscription, +// return funds to the subscription owner, +// down not check if pending requests for a sub exist, +// outstanding requests may fail onchain +func (v *EthereumVRFCoordinatorV2) OwnerCancelSubscription(subID uint64) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.coordinator.OwnerCancelSubscription( + opts, + subID, + ) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + // CancelSubscription cancels subscription by Sub owner, // return funds to specified address, // checks if pending requests for a sub exist @@ -300,6 +319,26 @@ func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsRequestedEvent(keyHash [][3 } } +func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) { + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled) + subscription, err := v.coordinator.WatchSubscriptionCanceled(nil, eventsChannel, subID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for SubscriptionCanceled event") + case sub := <-eventsChannel: + return sub, nil + } + } +} + // GetAllRandomWords get all VRFv2 randomness output words func (v *EthereumVRFConsumerV2) GetAllRandomWords(ctx context.Context, num int) ([]*big.Int, error) { words := make([]*big.Int, 0) diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index a8edb2c51d9..61a6b15ca1a 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -4,6 +4,7 @@ import ( "context" "math/big" "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -11,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" @@ -113,6 +115,191 @@ func TestVRFv2Basic(t *testing.T) { require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) + + t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { + testConfig := vrfv2Config + subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( + env, + testConfig, + linkToken, + vrfv2Contracts.Coordinator, + vrfv2Contracts.LoadTestConsumers, + 1, + ) + require.NoError(t, err) + subIDForCancelling := subIDsForCancelling[0] + + testWalletAddress, err := actions.GenerateWallet() + require.NoError(t, err) + + testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) + require.NoError(t, err) + + subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err, "error getting subscription information") + + subBalanceLink := subscriptionForCancelling.Balance + + l.Info(). + Str("Subscription Amount Link", subBalanceLink.String()). + Uint64("Returning funds from SubID", subIDForCancelling). + Str("Returning funds to", testWalletAddress.String()). + Msg("Canceling subscription and returning funds to subscription owner") + + tx, err := vrfv2Contracts.Coordinator.CancelSubscription(subIDForCancelling, testWalletAddress) + require.NoError(t, err, "Error canceling subscription") + + subscriptionCanceledEvent, err := vrfv2Contracts.Coordinator.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) + require.NoError(t, err, "error waiting for subscription canceled event") + + cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + require.NoError(t, err, "error getting tx cancellation Tx Receipt") + + txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) + cancellationTxFeeWei := new(big.Int).Mul(txGasUsed, cancellationTxReceipt.EffectiveGasPrice) + + l.Info(). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Effective Gas Price", cancellationTxReceipt.EffectiveGasPrice.String()). + Uint64("Gas Used", cancellationTxReceipt.GasUsed). + Msg("Cancellation TX Receipt") + + l.Info(). + Str("Returned Subscription Amount Link", subscriptionCanceledEvent.Amount.String()). + Uint64("SubID", subscriptionCanceledEvent.SubId). + Str("Returned to", subscriptionCanceledEvent.To.String()). + Msg("Subscription Canceled Event") + + require.Equal(t, subBalanceLink, subscriptionCanceledEvent.Amount, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") + + testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) + require.NoError(t, err) + + //Verify that sub was deleted from Coordinator + _, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") + + subFundsReturnedLinkActual := new(big.Int).Sub(testWalletBalanceLinkAfterSubCancelling, testWalletBalanceLinkBeforeSubCancelling) + + l.Info(). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Sub Funds Returned Actual - Link", subFundsReturnedLinkActual.String()). + Str("Sub Balance - Link", subBalanceLink.String()). + Msg("Sub funds returned") + + require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") + }) + + t.Run("Owner Canceling Sub And Returning Funds While Having Pending Requests", func(t *testing.T) { + testConfig := vrfv2Config + + // Underfund subscription to force fulfillments to fail + testConfig.SubscriptionFundingAmountLink = float64(0.000000000000000001) // 1 Juel + + subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( + env, + testConfig, + linkToken, + vrfv2Contracts.Coordinator, + vrfv2Contracts.LoadTestConsumers, + 1, + ) + require.NoError(t, err) + + subIDForCancelling := subIDsForCancelling[0] + + subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err, "Error getting subscription information") + + vrfv2_actions.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.Coordinator) + + // No GetActiveSubscriptionIds function available - skipping check + + pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err) + require.False(t, pendingRequestsExist, "Pending requests should not exist") + + // Request randomness - should fail due to underfunded subscription + randomWordsFulfilledEventTimeout := 5 * time.Second + _, err = vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + vrfv2Contracts.LoadTestConsumers[0], + vrfv2Contracts.Coordinator, + vrfv2Data, + subIDForCancelling, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + randomWordsFulfilledEventTimeout, + l, + ) + require.Error(t, err, "Error should occur while waiting for fulfilment due to low sub balance") + + pendingRequestsExist, err = vrfv2Contracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err) + require.True(t, pendingRequestsExist, "Pending requests should exist after unfilfulled requests due to low sub balance") + + walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + require.NoError(t, err) + + subscriptionForCancelling, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err, "Error getting subscription information") + subBalanceLink := subscriptionForCancelling.Balance + + l.Info(). + Str("Subscription Amount Link", subBalanceLink.String()). + Uint64("Returning funds from SubID", subIDForCancelling). + Str("Returning funds to", defaultWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + + // Call OwnerCancelSubscription + tx, err := vrfv2Contracts.Coordinator.OwnerCancelSubscription(subIDForCancelling) + require.NoError(t, err, "Error canceling subscription") + + subscriptionCanceledEvent, err := vrfv2Contracts.Coordinator.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) + require.NoError(t, err, "error waiting for subscription canceled event") + + cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + require.NoError(t, err, "error getting tx cancellation Tx Receipt") + + txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) + cancellationTxFeeWei := new(big.Int).Mul(txGasUsed, cancellationTxReceipt.EffectiveGasPrice) + + l.Info(). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Effective Gas Price", cancellationTxReceipt.EffectiveGasPrice.String()). + Uint64("Gas Used", cancellationTxReceipt.GasUsed). + Msg("Cancellation TX Receipt") + + l.Info(). + Str("Returned Subscription Amount Link", subscriptionCanceledEvent.Amount.String()). + Uint64("SubID", subscriptionCanceledEvent.SubId). + Str("Returned to", subscriptionCanceledEvent.To.String()). + Msg("Subscription Canceled Event") + + require.Equal(t, subBalanceLink, subscriptionCanceledEvent.Amount, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") + + walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + require.NoError(t, err) + + // Verify that subscription was deleted from Coordinator contract + _, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + l.Info(). + Str("Expected error message", err.Error()) + require.Error(t, err, "Error did not occur when fetching deleted subscription from the Coordinator after owner cancelation") + + subFundsReturnedLinkActual := new(big.Int).Sub(walletBalanceLinkAfterSubCancelling, walletBalanceLinkBeforeSubCancelling) + l.Info(). + Str("Wallet Balance Before Owner Cancelation", walletBalanceLinkBeforeSubCancelling.String()). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Sub Funds Returned Actual - Link", subFundsReturnedLinkActual.String()). + Str("Sub Balance - Link", subBalanceLink.String()). + Str("Wallet Balance After Owner Cancelation", walletBalanceLinkAfterSubCancelling.String()). + Msg("Sub funds returned") + + require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") + + // Again, there is no GetActiveSubscriptionIds method on the v2 Coordinator contract, so we can't double check that the cancelled + // subID is no longer in the list of active subs + }) } func TestVRFv2MultipleSendingKeys(t *testing.T) { From 69f83f39059dfff5cd6f15e8a00ea839ae81b5c3 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 19 Dec 2023 09:30:21 -0500 Subject: [PATCH 082/234] Disable Flakey Scroll Test until Fix (#11619) --- .github/workflows/live-testnet-tests.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index f202f03e51b..7a12ab6d6a0 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -10,7 +10,7 @@ name: Live Testnet Tests on: schedule: - - cron: "0 0 * * *" # Run every Sunday at midnight + - cron: "0 5 * * *" # Run every Sunday at midnight EST push: tags: - "*" @@ -144,7 +144,7 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: [sepolia-smoke-tests, bsc-testnet-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, scroll-sepolia-smoke-tests, linea-goerli-smoke-tests] + needs: [sepolia-smoke-tests, bsc-testnet-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, linea-goerli-smoke-tests] steps: - name: Debug Result run: echo ${{ join(needs.*.result, ',') }} @@ -205,7 +205,7 @@ jobs: strategy: fail-fast: false matrix: - network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Scroll Sepolia, Linea Goerli, BSC Testnet] + network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Linea Goerli, BSC Testnet] steps: - name: Get Results id: test-results @@ -775,8 +775,10 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - + scroll-sepolia-smoke-tests: + # TODO: Disabled until bug TT-767 is fixed + if: false environment: integration permissions: checks: write From 85e19b9aa14aeae03dd819296a38938416e67f9f Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:39:43 +0000 Subject: [PATCH 083/234] Automation LOOPP (#11439) * Bump dependencies * Use the AutomationProvider * Bump common and relays 1 * Bump relays 2 * Keep 2.0 as is to reduce testing impact * Bump relays * Bump cosmos * Bump starknet relayer * Bump to chainlink-solana that uses WithTestInstance * Bump latest solana and starknet --- core/scripts/go.mod | 9 ++-- core/scripts/go.sum | 20 +++++---- core/services/ocr2/delegate.go | 45 +++++++++++++++---- core/services/ocr2/plugins/ocr2keeper/util.go | 19 +++----- core/services/relay/evm/evm.go | 7 +++ core/services/relay/relay.go | 4 +- core/services/relay/relay_test.go | 8 ++++ go.mod | 9 ++-- go.sum | 20 ++++----- integration-tests/go.mod | 9 ++-- integration-tests/go.sum | 20 ++++----- 11 files changed, 105 insertions(+), 65 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 87d4fd8106a..f206ddd6d5b 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -59,7 +59,6 @@ require ( github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect @@ -236,12 +235,12 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index b4a60bd3da6..d38fddcd922 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -169,6 +169,7 @@ github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHf github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= @@ -179,8 +180,9 @@ github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUB github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 h1:SDlJ7bAm4ewvrmZtR0DaiYbQGdKPeaaIm7bM+qRhFeU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -1146,18 +1148,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 h1:7OP1znQwQP3ha1KL5sDjHeKobOfe//YTYdUQH+klkhk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 h1:7P+djpKBMQ2Cpv1ieUQdkZvDLt6owPvniHfMHSPFYjQ= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 h1:ziqC+WW/2/UI6w3DShy7HGzJMWWLIYHT5ev2Qaa3h6I= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 1b7be2b7f0e..3136de44b8f 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -441,7 +441,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { return d.newServicesOCR2VRF(lggr, jb, bootstrapPeers, kb, ocrDB, lc) case types.OCR2Keeper: - return d.newServicesOCR2Keepers(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + return d.newServicesOCR2Keepers(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) case types.Functions: const ( @@ -1026,6 +1026,7 @@ func (d *Delegate) newServicesOCR2VRF( } func (d *Delegate) newServicesOCR2Keepers( + ctx context.Context, lggr logger.SugaredLogger, jb job.Job, bootstrapPeers []commontypes.BootstrapperLocator, @@ -1046,7 +1047,7 @@ func (d *Delegate) newServicesOCR2Keepers( switch cfg.ContractVersion { case "v2.1": - return d.newServicesOCR2Keepers21(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) + return d.newServicesOCR2Keepers21(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) case "v2.0": return d.newServicesOCR2Keepers20(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) default: @@ -1055,6 +1056,7 @@ func (d *Delegate) newServicesOCR2Keepers( } func (d *Delegate) newServicesOCR2Keepers21( + ctx context.Context, lggr logger.SugaredLogger, jb job.Job, bootstrapPeers []commontypes.BootstrapperLocator, @@ -1079,14 +1081,41 @@ func (d *Delegate) newServicesOCR2Keepers21( return nil, fmt.Errorf("keeper2 services: expected EVM relayer got %s", rid.Network) } - chain, err2 := d.legacyChains.Get(rid.ChainID) - if err2 != nil { - return nil, fmt.Errorf("keeper2 services: failed to get chain %s: %w", rid.ChainID, err2) + transmitterID := spec.TransmitterID.String + relayer, err := d.RelayGetter.Get(rid) + if err != nil { + return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: "ocr2keepers"} } - keeperProvider, services, err2 := ocr2keeper.EVMDependencies21(jb, d.db, lggr, chain, mc, kb, d.cfg.Database(), d.ethKs) - if err2 != nil { - return nil, errors.Wrap(err2, "could not build dependencies for ocr2 keepers") + provider, err := relayer.NewPluginProvider(ctx, + types.RelayArgs{ + ExternalJobID: jb.ExternalJobID, + JobID: jb.ID, + ContractID: spec.ContractID, + New: d.isNewlyCreatedJob, + RelayConfig: spec.RelayConfig.Bytes(), + ProviderType: string(spec.PluginType), + }, types.PluginArgs{ + TransmitterID: transmitterID, + PluginConfig: spec.PluginConfig.Bytes(), + }) + if err != nil { + return nil, err + } + + keeperProvider, ok := provider.(types.AutomationProvider) + if !ok { + return nil, errors.New("could not coerce PluginProvider to AutomationProvider") + } + + chain, err := d.legacyChains.Get(rid.ChainID) + if err != nil { + return nil, fmt.Errorf("keeper2 services: failed to get chain %s: %w", rid.ChainID, err) + } + + services, err := ocr2keeper.EVMDependencies21(jb, d.db, lggr, chain, mc, kb, d.cfg.Database()) + if err != nil { + return nil, errors.Wrap(err, "could not build dependencies for ocr2 keepers") } // set some defaults conf := ocr2keepers21config.ReportingFactoryConfig{ diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index c3c60ad58b1..199bbac0536 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -3,6 +3,10 @@ package ocr2keeper import ( "fmt" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/jmoiron/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -12,19 +16,16 @@ import ( ocr2keepers20polling "github.com/smartcontractkit/chainlink-automation/pkg/v2/observer/polling" ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" evmregistry20 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20" evmregistry21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" evmregistry21transmit "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) type Encoder20 interface { @@ -115,24 +116,18 @@ func EVMDependencies21( mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, dbCfg pg.QConfig, - ethKeystore keystore.Eth, -) (evmrelay.OCR2KeeperProvider, evmregistry21.AutomationServices, error) { +) (evmregistry21.AutomationServices, error) { var err error - var keeperProvider evmrelay.OCR2KeeperProvider oSpec := spec.OCR2OracleSpec - // the provider will be returned as a dependency - if keeperProvider, err = EVMProvider(db, chain, lggr, spec, ethKeystore); err != nil { - return nil, nil, err - } rAddr := ethkey.MustEIP55Address(oSpec.ContractID).Address() services, err := evmregistry21.New(rAddr, chain, mc, keyring, lggr, db, dbCfg) if err != nil { - return nil, nil, err + return nil, err } - return keeperProvider, services, err + return services, err } func FilterNamesFromSpec21(spec *job.OCR2OracleSpec) (names []string, err error) { diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 303cdd3ba0e..08e25dba545 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -503,6 +503,13 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp return &medianProvider, nil } +func (r *Relayer) NewAutomationProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.AutomationProvider, error) { + lggr := r.lggr.Named("AutomationProvider").Named(rargs.ExternalJobID.String()) + ocr2keeperRelayer := NewOCR2KeeperRelayer(r.db, r.chain, lggr.Named("OCR2KeeperRelayer"), r.ks.Eth()) + + return ocr2keeperRelayer.NewOCR2KeeperProvider(rargs, pargs) +} + var _ commontypes.MedianProvider = (*medianProvider)(nil) type medianProvider struct { diff --git a/core/services/relay/relay.go b/core/services/relay/relay.go index eb6d3faf4fd..118d5935851 100644 --- a/core/services/relay/relay.go +++ b/core/services/relay/relay.go @@ -88,7 +88,9 @@ func (r *ServerAdapter) NewPluginProvider(ctx context.Context, rargs types.Relay return r.NewFunctionsProvider(ctx, rargs, pargs) case types.Mercury: return r.NewMercuryProvider(ctx, rargs, pargs) - case types.DKG, types.OCR2VRF, types.OCR2Keeper, types.GenericPlugin: + case types.OCR2Keeper: + return r.NewAutomationProvider(ctx, rargs, pargs) + case types.DKG, types.OCR2VRF, types.GenericPlugin: return r.RelayerAdapter.NewPluginProvider(ctx, rargs, pargs) case types.CCIPCommit, types.CCIPExecution: return nil, fmt.Errorf("provider type not supported: %s", rargs.ProviderType) diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index d23895699df..40a11518edd 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -97,6 +97,10 @@ type staticMercuryProvider struct { types.MercuryProvider } +type staticAutomationProvider struct { + types.AutomationProvider +} + type mockRelayer struct { types.Relayer } @@ -113,6 +117,10 @@ func (m *mockRelayer) NewMercuryProvider(rargs types.RelayArgs, pargs types.Plug return staticMercuryProvider{}, nil } +func (m *mockRelayer) NewAutomationProvider(rargs types.RelayArgs, pargs types.PluginArgs) (types.AutomationProvider, error) { + return staticAutomationProvider{}, nil +} + type mockRelayerExt struct { loop.RelayerExt } diff --git a/go.mod b/go.mod index e013a192c08..00faf4f5ee4 100644 --- a/go.mod +++ b/go.mod @@ -65,12 +65,12 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 @@ -128,7 +128,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blendle/zapdriver v1.3.1 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect diff --git a/go.sum b/go.sum index 35b136e2c9b..7bfd01bfd9e 100644 --- a/go.sum +++ b/go.sum @@ -176,8 +176,8 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 h1:SDlJ7bAm4ewvrmZtR0DaiYbQGdKPeaaIm7bM+qRhFeU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -1134,18 +1134,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 h1:7OP1znQwQP3ha1KL5sDjHeKobOfe//YTYdUQH+klkhk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 h1:7P+djpKBMQ2Cpv1ieUQdkZvDLt6owPvniHfMHSPFYjQ= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 h1:ziqC+WW/2/UI6w3DShy7HGzJMWWLIYHT5ev2Qaa3h6I= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f981f303402..5086a1c121b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -96,7 +96,6 @@ require ( github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect @@ -356,11 +355,11 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index cc08fb43dcc..6f539821d8e 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -222,8 +222,8 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 h1:SDlJ7bAm4ewvrmZtR0DaiYbQGdKPeaaIm7bM+qRhFeU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -1465,18 +1465,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68 h1:7OP1znQwQP3ha1KL5sDjHeKobOfe//YTYdUQH+klkhk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214192257-f53e314deb68/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 h1:7P+djpKBMQ2Cpv1ieUQdkZvDLt6owPvniHfMHSPFYjQ= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 h1:ziqC+WW/2/UI6w3DShy7HGzJMWWLIYHT5ev2Qaa3h6I= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= github.com/smartcontractkit/chainlink-testing-framework v1.22.0 h1:Lur628wkrceWgcLmxGZe7Mauwxht4YO71hX9Jj5YslE= github.com/smartcontractkit/chainlink-testing-framework v1.22.0/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= From 9ae0b9ad73e6d556ca469770a1b265e0e5cecda0 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Tue, 19 Dec 2023 19:49:18 +0400 Subject: [PATCH 084/234] [AUTO-8227] Update Log Trigger Load Test (#11601) * add spam log * change slack channel for testing * update test config lines * Revert "change slack channel for testing" This reverts commit 1b81c0f2505e512d1d70b41c3d540f7186f268f8. * add prometheus enable config * increase DB specs * increase geth specs * fix endTime in dashboard link * Add checkBurnAmount performBurnAmount Fix proper spam event * add load types * contracts prettier * tests tidy * add numberOfSpamNonMatchingEvents * add configOverride input to action * propagate CONFIG_OVERRIDE to test runner * avoid using array in emitlog * update specs slack message in thread * better test config printing * run lint * implement shared trigger * nodeFunding as config --- .github/workflows/automation-load-tests.yml | 5 + .../testhelpers/SimpleLogUpkeepCounter.sol | 46 ++- contracts/src/v0.8/tests/LogEmitter.sol | 7 + .../generated/log_emitter/log_emitter.go | 166 ++++++++++- .../simple_log_upkeep_counter_wrapper.go | 62 +++- ...rapper-dependency-versions-do-not-edit.txt | 4 +- .../contracts/contract_models.go | 1 + integration-tests/contracts/test_contracts.go | 12 + .../automationv2_1/automationv2_1_test.go | 274 +++++++++++------- integration-tests/load/automationv2_1/gun.go | 47 ++- .../load/automationv2_1/helpers.go | 6 +- 11 files changed, 499 insertions(+), 131 deletions(-) diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index b19ac8fd24c..96dffa01db9 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -22,6 +22,10 @@ on: description: TestInputs required: false type: string + ConfigOverride: + description: ConfigOverride + required: false + type: string slackMemberID: description: Notifies test results (Not your @) required: true @@ -43,6 +47,7 @@ jobs: SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: C03KJ5S7KEK TEST_INPUTS: ${{ inputs.TestInputs }} + CONFIG_OVERRIDE: ${{ inputs.ConfigOverride }} CHAINLINK_ENV_USER: ${{ github.actor }} REF_NAME: ${{ github.head_ref || github.ref_name }} steps: diff --git a/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol index 563c1354b66..6ef6f8a36ff 100644 --- a/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol +++ b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol @@ -4,6 +4,12 @@ pragma solidity 0.8.6; import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; +struct CheckData { + uint256 checkBurnAmount; + uint256 performBurnAmount; + bytes32 eventSig; +} + contract SimpleLogUpkeepCounter is ILogAutomation { event PerformingUpkeep( address indexed from, @@ -14,6 +20,7 @@ contract SimpleLogUpkeepCounter is ILogAutomation { uint256 timeToPerform ); + mapping(bytes32 => bool) public dummyMap; // used to force storage lookup uint256 public lastBlock; uint256 public previousPerformBlock; uint256 public initialBlock; @@ -27,8 +34,27 @@ contract SimpleLogUpkeepCounter is ILogAutomation { counter = 0; } - function checkLog(Log calldata log, bytes memory) external view override returns (bool, bytes memory) { - return (true, abi.encode(log)); + function _checkDataConfig(CheckData memory) external {} + + function checkLog(Log calldata log, bytes calldata checkData) external view override returns (bool, bytes memory) { + (uint256 checkBurnAmount, uint256 performBurnAmount, bytes32 eventSig) = abi.decode( + checkData, + (uint256, uint256, bytes32) + ); + uint256 startGas = gasleft(); + bytes32 dummyIndex = blockhash(block.number - 1); + bool dummy; + // burn gas + if (checkBurnAmount > 0) { + while (startGas - gasleft() < checkBurnAmount) { + dummy = dummy && dummyMap[dummyIndex]; // arbitrary storage reads + dummyIndex = keccak256(abi.encode(dummyIndex, address(this))); + } + } + if (log.topics[2] == eventSig) { + return (true, abi.encode(log, checkData)); + } + return (false, abi.encode(log, checkData)); } function performUpkeep(bytes calldata performData) external override { @@ -38,8 +64,22 @@ contract SimpleLogUpkeepCounter is ILogAutomation { lastBlock = block.number; counter = counter + 1; previousPerformBlock = lastBlock; - Log memory log = abi.decode(performData, (Log)); + (Log memory log, bytes memory extraData) = abi.decode(performData, (Log, bytes)); timeToPerform = block.timestamp - log.timestamp; + (uint256 checkBurnAmount, uint256 performBurnAmount, bytes32 eventSig) = abi.decode( + extraData, + (uint256, uint256, bytes32) + ); + uint256 startGas = gasleft(); + bytes32 dummyIndex = blockhash(block.number - 1); + bool dummy; + // burn gas + if (performBurnAmount > 0) { + while (startGas - gasleft() < performBurnAmount) { + dummy = dummy && dummyMap[dummyIndex]; // arbitrary storage reads + dummyIndex = keccak256(abi.encode(dummyIndex, address(this))); + } + } emit PerformingUpkeep(tx.origin, initialBlock, lastBlock, previousPerformBlock, counter, timeToPerform); } } diff --git a/contracts/src/v0.8/tests/LogEmitter.sol b/contracts/src/v0.8/tests/LogEmitter.sol index d3f950c5ccb..37306cc2bc5 100644 --- a/contracts/src/v0.8/tests/LogEmitter.sol +++ b/contracts/src/v0.8/tests/LogEmitter.sol @@ -5,6 +5,7 @@ contract LogEmitter { event Log1(uint256); event Log2(uint256 indexed); event Log3(string); + event Log4(uint256 indexed, uint256 indexed); function EmitLog1(uint256[] memory v) public { for (uint256 i = 0; i < v.length; i++) { @@ -23,4 +24,10 @@ contract LogEmitter { emit Log3(v[i]); } } + + function EmitLog4(uint256 v, uint256 w, uint256 c) public { + for (uint256 i = 0; i < c; i++) { + emit Log4(v, w); + } + } } diff --git a/core/gethwrappers/generated/log_emitter/log_emitter.go b/core/gethwrappers/generated/log_emitter/log_emitter.go index 3cb11da5125..24fef257af3 100644 --- a/core/gethwrappers/generated/log_emitter/log_emitter.go +++ b/core/gethwrappers/generated/log_emitter/log_emitter.go @@ -31,8 +31,8 @@ var ( ) var LogEmitterMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log1\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log2\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"Log3\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"v\",\"type\":\"uint256[]\"}],\"name\":\"EmitLog1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"v\",\"type\":\"uint256[]\"}],\"name\":\"EmitLog2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string[]\",\"name\":\"v\",\"type\":\"string[]\"}],\"name\":\"EmitLog3\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5061052b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063696933c914610046578063bc253bc01461005b578063d9c21f461461006e575b600080fd5b610059610054366004610269565b610081565b005b610059610069366004610269565b6100f5565b61005961007c3660046102ff565b610159565b60005b81518110156100f1577f46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a88282815181106100c0576100c0610424565b60200260200101516040516100d791815260200190565b60405180910390a1806100e981610453565b915050610084565b5050565b60005b81518110156100f15781818151811061011357610113610424565b60200260200101517f624fb00c2ce79f34cb543884c3af64816dce0f4cec3d32661959e49d488a7a9360405160405180910390a28061015181610453565b9150506100f8565b60005b81518110156100f1577fb94ec34dfe32a8a7170992a093976368d1e63decf8f0bc0b38a8eb89cc9f95cf82828151811061019857610198610424565b60200260200101516040516101ad91906104b2565b60405180910390a1806101bf81610453565b91505061015c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561023d5761023d6101c7565b604052919050565b600067ffffffffffffffff82111561025f5761025f6101c7565b5060051b60200190565b6000602080838503121561027c57600080fd5b823567ffffffffffffffff81111561029357600080fd5b8301601f810185136102a457600080fd5b80356102b76102b282610245565b6101f6565b81815260059190911b820183019083810190878311156102d657600080fd5b928401925b828410156102f4578335825292840192908401906102db565b979650505050505050565b6000602080838503121561031257600080fd5b823567ffffffffffffffff8082111561032a57600080fd5b8185019150601f868184011261033f57600080fd5b823561034d6102b282610245565b81815260059190911b8401850190858101908983111561036c57600080fd5b8686015b83811015610416578035868111156103885760008081fd5b8701603f81018c1361039a5760008081fd5b888101356040888211156103b0576103b06101c7565b6103df8b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a850116016101f6565b8281528e828486010111156103f45760008081fd5b828285018d83013760009281018c019290925250845250918701918701610370565b509998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036104ab577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b600060208083528351808285015260005b818110156104df578581018301518582016040015282016104c3565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509291505056fea164736f6c6343000813000a", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log1\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log2\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"Log3\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"Log4\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"v\",\"type\":\"uint256[]\"}],\"name\":\"EmitLog1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"v\",\"type\":\"uint256[]\"}],\"name\":\"EmitLog2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string[]\",\"name\":\"v\",\"type\":\"string[]\"}],\"name\":\"EmitLog3\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"v\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"w\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"}],\"name\":\"EmitLog4\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b506105c5806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063696933c914610051578063b4b12d9814610066578063bc253bc014610079578063d9c21f461461008c575b600080fd5b61006461005f3660046102d7565b61009f565b005b61006461007436600461036d565b610113565b6100646100873660046102d7565b610163565b61006461009a366004610399565b6101c7565b60005b815181101561010f577f46692c0e59ca9cd1ad8f984a9d11715ec83424398b7eed4e05c8ce84662415a88282815181106100de576100de6104be565b60200260200101516040516100f591815260200190565b60405180910390a180610107816104ed565b9150506100a2565b5050565b60005b8181101561015d57604051839085907fba21d5b63d64546cb4ab29e370a8972bf26f78cb0c395391b4f451699fdfdc5d90600090a380610155816104ed565b915050610116565b50505050565b60005b815181101561010f57818181518110610181576101816104be565b60200260200101517f624fb00c2ce79f34cb543884c3af64816dce0f4cec3d32661959e49d488a7a9360405160405180910390a2806101bf816104ed565b915050610166565b60005b815181101561010f577fb94ec34dfe32a8a7170992a093976368d1e63decf8f0bc0b38a8eb89cc9f95cf828281518110610206576102066104be565b602002602001015160405161021b919061054c565b60405180910390a18061022d816104ed565b9150506101ca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156102ab576102ab610235565b604052919050565b600067ffffffffffffffff8211156102cd576102cd610235565b5060051b60200190565b600060208083850312156102ea57600080fd5b823567ffffffffffffffff81111561030157600080fd5b8301601f8101851361031257600080fd5b8035610325610320826102b3565b610264565b81815260059190911b8201830190838101908783111561034457600080fd5b928401925b8284101561036257833582529284019290840190610349565b979650505050505050565b60008060006060848603121561038257600080fd5b505081359360208301359350604090920135919050565b600060208083850312156103ac57600080fd5b823567ffffffffffffffff808211156103c457600080fd5b8185019150601f86818401126103d957600080fd5b82356103e7610320826102b3565b81815260059190911b8401850190858101908983111561040657600080fd5b8686015b838110156104b0578035868111156104225760008081fd5b8701603f81018c136104345760008081fd5b8881013560408882111561044a5761044a610235565b6104798b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a85011601610264565b8281528e8284860101111561048e5760008081fd5b828285018d83013760009281018c01929092525084525091870191870161040a565b509998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610545577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b600060208083528351808285015260005b818110156105795785810183015185820160400152820161055d565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509291505056fea164736f6c6343000813000a", } var LogEmitterABI = LogEmitterMetaData.ABI @@ -207,6 +207,18 @@ func (_LogEmitter *LogEmitterTransactorSession) EmitLog3(v []string) (*types.Tra return _LogEmitter.Contract.EmitLog3(&_LogEmitter.TransactOpts, v) } +func (_LogEmitter *LogEmitterTransactor) EmitLog4(opts *bind.TransactOpts, v *big.Int, w *big.Int, c *big.Int) (*types.Transaction, error) { + return _LogEmitter.contract.Transact(opts, "EmitLog4", v, w, c) +} + +func (_LogEmitter *LogEmitterSession) EmitLog4(v *big.Int, w *big.Int, c *big.Int) (*types.Transaction, error) { + return _LogEmitter.Contract.EmitLog4(&_LogEmitter.TransactOpts, v, w, c) +} + +func (_LogEmitter *LogEmitterTransactorSession) EmitLog4(v *big.Int, w *big.Int, c *big.Int) (*types.Transaction, error) { + return _LogEmitter.Contract.EmitLog4(&_LogEmitter.TransactOpts, v, w, c) +} + type LogEmitterLog1Iterator struct { Event *LogEmitterLog1 @@ -568,6 +580,142 @@ func (_LogEmitter *LogEmitterFilterer) ParseLog3(log types.Log) (*LogEmitterLog3 return event, nil } +type LogEmitterLog4Iterator struct { + Event *LogEmitterLog4 + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LogEmitterLog4Iterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LogEmitterLog4) + 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(LogEmitterLog4) + 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 *LogEmitterLog4Iterator) Error() error { + return it.fail +} + +func (it *LogEmitterLog4Iterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LogEmitterLog4 struct { + Arg0 *big.Int + Arg1 *big.Int + Raw types.Log +} + +func (_LogEmitter *LogEmitterFilterer) FilterLog4(opts *bind.FilterOpts, arg0 []*big.Int, arg1 []*big.Int) (*LogEmitterLog4Iterator, error) { + + var arg0Rule []interface{} + for _, arg0Item := range arg0 { + arg0Rule = append(arg0Rule, arg0Item) + } + var arg1Rule []interface{} + for _, arg1Item := range arg1 { + arg1Rule = append(arg1Rule, arg1Item) + } + + logs, sub, err := _LogEmitter.contract.FilterLogs(opts, "Log4", arg0Rule, arg1Rule) + if err != nil { + return nil, err + } + return &LogEmitterLog4Iterator{contract: _LogEmitter.contract, event: "Log4", logs: logs, sub: sub}, nil +} + +func (_LogEmitter *LogEmitterFilterer) WatchLog4(opts *bind.WatchOpts, sink chan<- *LogEmitterLog4, arg0 []*big.Int, arg1 []*big.Int) (event.Subscription, error) { + + var arg0Rule []interface{} + for _, arg0Item := range arg0 { + arg0Rule = append(arg0Rule, arg0Item) + } + var arg1Rule []interface{} + for _, arg1Item := range arg1 { + arg1Rule = append(arg1Rule, arg1Item) + } + + logs, sub, err := _LogEmitter.contract.WatchLogs(opts, "Log4", arg0Rule, arg1Rule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LogEmitterLog4) + if err := _LogEmitter.contract.UnpackLog(event, "Log4", 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 (_LogEmitter *LogEmitterFilterer) ParseLog4(log types.Log) (*LogEmitterLog4, error) { + event := new(LogEmitterLog4) + if err := _LogEmitter.contract.UnpackLog(event, "Log4", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + func (_LogEmitter *LogEmitter) ParseLog(log types.Log) (generated.AbigenLog, error) { switch log.Topics[0] { case _LogEmitter.abi.Events["Log1"].ID: @@ -576,6 +724,8 @@ func (_LogEmitter *LogEmitter) ParseLog(log types.Log) (generated.AbigenLog, err return _LogEmitter.ParseLog2(log) case _LogEmitter.abi.Events["Log3"].ID: return _LogEmitter.ParseLog3(log) + case _LogEmitter.abi.Events["Log4"].ID: + return _LogEmitter.ParseLog4(log) default: return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) @@ -594,6 +744,10 @@ func (LogEmitterLog3) Topic() common.Hash { return common.HexToHash("0xb94ec34dfe32a8a7170992a093976368d1e63decf8f0bc0b38a8eb89cc9f95cf") } +func (LogEmitterLog4) Topic() common.Hash { + return common.HexToHash("0xba21d5b63d64546cb4ab29e370a8972bf26f78cb0c395391b4f451699fdfdc5d") +} + func (_LogEmitter *LogEmitter) Address() common.Address { return _LogEmitter.address } @@ -605,6 +759,8 @@ type LogEmitterInterface interface { EmitLog3(opts *bind.TransactOpts, v []string) (*types.Transaction, error) + EmitLog4(opts *bind.TransactOpts, v *big.Int, w *big.Int, c *big.Int) (*types.Transaction, error) + FilterLog1(opts *bind.FilterOpts) (*LogEmitterLog1Iterator, error) WatchLog1(opts *bind.WatchOpts, sink chan<- *LogEmitterLog1) (event.Subscription, error) @@ -623,6 +779,12 @@ type LogEmitterInterface interface { ParseLog3(log types.Log) (*LogEmitterLog3, error) + FilterLog4(opts *bind.FilterOpts, arg0 []*big.Int, arg1 []*big.Int) (*LogEmitterLog4Iterator, error) + + WatchLog4(opts *bind.WatchOpts, sink chan<- *LogEmitterLog4, arg0 []*big.Int, arg1 []*big.Int) (event.Subscription, error) + + ParseLog4(log types.Log) (*LogEmitterLog4, error) + ParseLog(log types.Log) (generated.AbigenLog, error) Address() common.Address diff --git a/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go index bb28c8bd6c4..f834fa69118 100644 --- a/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go +++ b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go @@ -30,6 +30,12 @@ var ( _ = abi.ConvertType ) +type CheckData struct { + CheckBurnAmount *big.Int + PerformBurnAmount *big.Int + EventSig [32]byte +} + type Log struct { Index *big.Int Timestamp *big.Int @@ -42,8 +48,8 @@ type Log struct { } var SimpleLogUpkeepCounterMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"counter\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeToPerform\",\"type\":\"uint256\"}],\"name\":\"PerformingUpkeep\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"log\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"checkLog\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previousPerformBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timeToPerform\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5060006001819055438155600281905560035561088a806100326000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806361bc221a1161005b57806361bc221a146100d4578063806b984f146100dd578063917d895f146100e6578063c6066f0d146100ef57600080fd5b80632cb158641461008257806340691db41461009e5780634585e33b146100bf575b600080fd5b61008b60025481565b6040519081526020015b60405180910390f35b6100b16100ac366004610384565b6100f8565b60405161009592919061055e565b6100d26100cd366004610312565b61012a565b005b61008b60035481565b61008b60005481565b61008b60015481565b61008b60045481565b6000606060018460405160200161010f91906105db565b604051602081830303815290604052915091505b9250929050565b60025461013657436002555b436000556003546101489060016107f0565b6003556000805460015561015e828401846103f1565b90508060200151426101709190610808565b6004819055600254600054600154600354604080519485526020850193909352918301526060820152608081019190915232907f4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd659060a00160405180910390a2505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101f957600080fd5b919050565b600082601f83011261020f57600080fd5b8135602067ffffffffffffffff82111561022b5761022b61084e565b8160051b61023a8282016106d6565b83815282810190868401838801850189101561025557600080fd5b600093505b8584101561027857803583526001939093019291840191840161025a565b50979650505050505050565b600082601f83011261029557600080fd5b813567ffffffffffffffff8111156102af576102af61084e565b6102e060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016106d6565b8181528460208386010111156102f557600080fd5b816020850160208301376000918101602001919091529392505050565b6000806020838503121561032557600080fd5b823567ffffffffffffffff8082111561033d57600080fd5b818501915085601f83011261035157600080fd5b81358181111561036057600080fd5b86602082850101111561037257600080fd5b60209290920196919550909350505050565b6000806040838503121561039757600080fd5b823567ffffffffffffffff808211156103af57600080fd5b9084019061010082870312156103c457600080fd5b909250602084013590808211156103da57600080fd5b506103e785828601610284565b9150509250929050565b60006020828403121561040357600080fd5b813567ffffffffffffffff8082111561041b57600080fd5b90830190610100828603121561043057600080fd5b6104386106ac565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261047060a084016101d5565b60a082015260c08301358281111561048757600080fd5b610493878286016101fe565b60c08301525060e0830135828111156104ab57600080fd5b6104b787828601610284565b60e08301525095945050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156104f857600080fd5b8260051b8083602087013760009401602001938452509192915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b821515815260006020604081840152835180604085015260005b8181101561059457858101830151858201606001528201610578565b818111156105a6576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b6020815281356020820152602082013560408201526040820135606082015260608201356080820152608082013560a082015273ffffffffffffffffffffffffffffffffffffffff61062f60a084016101d5565b1660c0820152600061064460c0840184610725565b6101008060e086015261065c610120860183856104c6565b925061066b60e087018761078c565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086850301828701526106a1848483610515565b979650505050505050565b604051610100810167ffffffffffffffff811182821017156106d0576106d061084e565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561071d5761071d61084e565b604052919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261075a57600080fd5b830160208101925035905067ffffffffffffffff81111561077a57600080fd5b8060051b360383131561012357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126107c157600080fd5b830160208101925035905067ffffffffffffffff8111156107e157600080fd5b80360383131561012357600080fd5b600082198211156108035761080361081f565b500190565b60008282101561081a5761081a61081f565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"counter\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeToPerform\",\"type\":\"uint256\"}],\"name\":\"PerformingUpkeep\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"checkBurnAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"performBurnAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"eventSig\",\"type\":\"bytes32\"}],\"internalType\":\"structCheckData\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_checkDataConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"log\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"}],\"name\":\"checkLog\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"dummyMap\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previousPerformBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timeToPerform\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5060006002819055436001556003819055600455610c63806100336000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c80636977947311610076578063806b984f1161005b578063806b984f14610147578063917d895f14610150578063c6066f0d1461015957600080fd5b806369779473146101035780637145f11b1461011457600080fd5b80632cb15864146100a857806340691db4146100c45780634585e33b146100e557806361bc221a146100fa575b600080fd5b6100b160035481565b6040519081526020015b60405180910390f35b6100d76100d236600461062d565b610162565b6040516100bb92919061088b565b6100f86100f336600461058f565b610296565b005b6100b160045481565b6100f86101113660046105d1565b50565b610137610122366004610576565b60006020819052908152604090205460ff1681565b60405190151581526020016100bb565b6100b160015481565b6100b160025481565b6100b160055481565b6000606081808061017586880188610799565b92509250925060005a9050600061018d600143610bb2565b409050600085156101fc575b855a6101a59085610bb2565b10156101fc578080156101c6575060008281526020819052604090205460ff165b60408051602081018590523091810191909152909150606001604051602081830303815290604052805190602001209150610199565b8361020a60c08d018d6109ee565b600281811061021b5761021b610bf8565b90506020020135141561025d5760018b8b8b60405160200161023f93929190610908565b6040516020818303038152906040529750975050505050505061028e565b60008b8b8b60405160200161027493929190610908565b604051602081830303815290604052975097505050505050505b935093915050565b6003546102a257436003555b4360019081556004546102b491610b9a565b6004556001546002556000806102cc8385018561069f565b915091508160200151426102e09190610bb2565b6005819055506000806000838060200190518101906102ff91906107c5565b92509250925060005a90506000610317600143610bb2565b40905060008415610386575b845a61032f9085610bb2565b101561038657808015610350575060008281526020819052604090205460ff165b60408051602081018590523091810191909152909150606001604051602081830303815290604052805190602001209150610323565b600354600154600254600454600554604080519586526020860194909452928401919091526060830152608082015232907f4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd659060a00160405180910390a250505050505050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461041457600080fd5b919050565b600082601f83011261042a57600080fd5b8135602067ffffffffffffffff82111561044657610446610c27565b8160051b610455828201610a80565b83815282810190868401838801850189101561047057600080fd5b600093505b85841015610493578035835260019390930192918401918401610475565b50979650505050505050565b60008083601f8401126104b157600080fd5b50813567ffffffffffffffff8111156104c957600080fd5b6020830191508360208285010111156104e157600080fd5b9250929050565b600082601f8301126104f957600080fd5b813567ffffffffffffffff81111561051357610513610c27565b61054460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610a80565b81815284602083860101111561055957600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561058857600080fd5b5035919050565b600080602083850312156105a257600080fd5b823567ffffffffffffffff8111156105b957600080fd5b6105c58582860161049f565b90969095509350505050565b6000606082840312156105e357600080fd5b6040516060810181811067ffffffffffffffff8211171561060657610606610c27565b80604052508235815260208301356020820152604083013560408201528091505092915050565b60008060006040848603121561064257600080fd5b833567ffffffffffffffff8082111561065a57600080fd5b90850190610100828803121561066f57600080fd5b9093506020850135908082111561068557600080fd5b506106928682870161049f565b9497909650939450505050565b600080604083850312156106b257600080fd5b823567ffffffffffffffff808211156106ca57600080fd5b9084019061010082870312156106df57600080fd5b6106e7610a56565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261071f60a084016103f0565b60a082015260c08301358281111561073657600080fd5b61074288828601610419565b60c08301525060e08301358281111561075a57600080fd5b610766888286016104e8565b60e0830152509350602085013591508082111561078257600080fd5b5061078f858286016104e8565b9150509250929050565b6000806000606084860312156107ae57600080fd5b505081359360208301359350604090920135919050565b6000806000606084860312156107da57600080fd5b8351925060208401519150604084015190509250925092565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561082557600080fd5b8260051b8083602087013760009401602001938452509192915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b821515815260006020604081840152835180604085015260005b818110156108c1578581018301518582016060015282016108a5565b818111156108d3576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b60408152833560408201526020840135606082015260408401356080820152606084013560a0820152608084013560c082015273ffffffffffffffffffffffffffffffffffffffff61095c60a086016103f0565b1660e0820152600061097160c0860186610acf565b61010084810152610987610140850182846107f3565b91505061099760e0870187610b36565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0858403016101208601526109cd838284610842565b9250505082810360208401526109e4818587610842565b9695505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610a2357600080fd5b83018035915067ffffffffffffffff821115610a3e57600080fd5b6020019150600581901b36038213156104e157600080fd5b604051610100810167ffffffffffffffff81118282101715610a7a57610a7a610c27565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610ac757610ac7610c27565b604052919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610b0457600080fd5b830160208101925035905067ffffffffffffffff811115610b2457600080fd5b8060051b36038313156104e157600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610b6b57600080fd5b830160208101925035905067ffffffffffffffff811115610b8b57600080fd5b8036038313156104e157600080fd5b60008219821115610bad57610bad610bc9565b500190565b600082821015610bc457610bc4610bc9565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", } var SimpleLogUpkeepCounterABI = SimpleLogUpkeepCounterMetaData.ABI @@ -182,9 +188,9 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorRaw) Transact(opt return _SimpleLogUpkeepCounter.Contract.contract.Transact(opts, method, params...) } -func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) CheckLog(opts *bind.CallOpts, log Log, arg1 []byte) (bool, []byte, error) { +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) CheckLog(opts *bind.CallOpts, log Log, checkData []byte) (bool, []byte, error) { var out []interface{} - err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "checkLog", log, arg1) + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "checkLog", log, checkData) if err != nil { return *new(bool), *new([]byte), err @@ -197,12 +203,12 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) CheckLog(opts *bind } -func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) CheckLog(log Log, arg1 []byte) (bool, []byte, error) { - return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, arg1) +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) CheckLog(log Log, checkData []byte) (bool, []byte, error) { + return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, checkData) } -func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) CheckLog(log Log, arg1 []byte) (bool, []byte, error) { - return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, arg1) +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) CheckLog(log Log, checkData []byte) (bool, []byte, error) { + return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, checkData) } func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) Counter(opts *bind.CallOpts) (*big.Int, error) { @@ -227,6 +233,28 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) Counter() (* return _SimpleLogUpkeepCounter.Contract.Counter(&_SimpleLogUpkeepCounter.CallOpts) } +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) DummyMap(opts *bind.CallOpts, arg0 [32]byte) (bool, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "dummyMap", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) DummyMap(arg0 [32]byte) (bool, error) { + return _SimpleLogUpkeepCounter.Contract.DummyMap(&_SimpleLogUpkeepCounter.CallOpts, arg0) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) DummyMap(arg0 [32]byte) (bool, error) { + return _SimpleLogUpkeepCounter.Contract.DummyMap(&_SimpleLogUpkeepCounter.CallOpts, arg0) +} + func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) InitialBlock(opts *bind.CallOpts) (*big.Int, error) { var out []interface{} err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "initialBlock") @@ -315,6 +343,18 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) TimeToPerfor return _SimpleLogUpkeepCounter.Contract.TimeToPerform(&_SimpleLogUpkeepCounter.CallOpts) } +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactor) CheckDataConfig(opts *bind.TransactOpts, arg0 CheckData) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.contract.Transact(opts, "_checkDataConfig", arg0) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) CheckDataConfig(arg0 CheckData) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.CheckDataConfig(&_SimpleLogUpkeepCounter.TransactOpts, arg0) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorSession) CheckDataConfig(arg0 CheckData) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.CheckDataConfig(&_SimpleLogUpkeepCounter.TransactOpts, arg0) +} + func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactor) PerformUpkeep(opts *bind.TransactOpts, performData []byte) (*types.Transaction, error) { return _SimpleLogUpkeepCounter.contract.Transact(opts, "performUpkeep", performData) } @@ -478,10 +518,12 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) Address() common.Address } type SimpleLogUpkeepCounterInterface interface { - CheckLog(opts *bind.CallOpts, log Log, arg1 []byte) (bool, []byte, error) + CheckLog(opts *bind.CallOpts, log Log, checkData []byte) (bool, []byte, error) Counter(opts *bind.CallOpts) (*big.Int, error) + DummyMap(opts *bind.CallOpts, arg0 [32]byte) (bool, error) + InitialBlock(opts *bind.CallOpts) (*big.Int, error) LastBlock(opts *bind.CallOpts) (*big.Int, error) @@ -490,6 +532,8 @@ type SimpleLogUpkeepCounterInterface interface { TimeToPerform(opts *bind.CallOpts) (*big.Int, error) + CheckDataConfig(opts *bind.TransactOpts, arg0 CheckData) (*types.Transaction, error) + PerformUpkeep(opts *bind.TransactOpts, performData []byte) (*types.Transaction, error) FilterPerformingUpkeep(opts *bind.FilterOpts, from []common.Address) (*SimpleLogUpkeepCounterPerformingUpkeepIterator, error) diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 5e9dcfc3326..819ef8d23e1 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -38,7 +38,7 @@ keeper_registry_wrapper1_3: ../../contracts/solc/v0.8.6/KeeperRegistry1_3/Keeper keeper_registry_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.bin c32dea7d5ef66b7c58ddc84ddf69aa44df1b3ae8601fbc271c95be4ff5853056 keeper_registry_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.bin 604e4a0cd980c713929b523b999462a3aa0ed06f96ff563a4c8566cf59c8445b keepers_vrf_consumer: ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin fa75572e689c9e84705c63e8dbe1b7b8aa1a8fe82d66356c4873d024bb9166e8 -log_emitter: ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin 244ba13730c036de0b02beef4e3d9c9a96946ce353c27f366baecc7f5be5a6fd +log_emitter: ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin 4b129ab93432c95ff9143f0631323e189887668889e0b36ccccf18a571e41ccf log_triggered_streams_lookup_wrapper: ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin f8da43a927c1a66238a9f4fd5d5dd7e280e361daa0444da1f7f79498ace901e1 log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin 42426bbb83f96dfbe55fc576d6c65020eaeed690e2289cf99b0c4aa810a5f4ec mock_aggregator_proxy: ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.bin b16c108f3dd384c342ddff5e94da7c0a8d39d1be5e3d8f2cf61ecc7f0e50ff42 @@ -50,7 +50,7 @@ operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.a operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin c5e1db81070d940a82ef100b0bce38e055593cbeebbc73abf9d45c30d6020cd2 oracle_wrapper: ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 -simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin 0a7a0cc4da7dc2a3d0a0c36c746b1adc044af5cad1838367356a0604f3255a01 +simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin b7bbd30531eefcaf2c30546cbc5eab10c68257dbc03f0e09c0ed85febfce786b solidity_vrf_consumer_interface: ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.bin ecc99378aa798014de9db42b2eb81320778b0663dbe208008dad75ccdc1d4366 solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f solidity_vrf_coordinator_interface: ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.bin a23d3c395156804788c7f6fbda2994e8f7184304c0f0c9f2c4ddeaf073d346d2 diff --git a/integration-tests/contracts/contract_models.go b/integration-tests/contracts/contract_models.go index 4c8d610fa1b..3d738033d68 100644 --- a/integration-tests/contracts/contract_models.go +++ b/integration-tests/contracts/contract_models.go @@ -405,6 +405,7 @@ type LogEmitter interface { Address() common.Address EmitLogInts(ints []int) (*types.Transaction, error) EmitLogIntsIndexed(ints []int) (*types.Transaction, error) + EmitLogIntMultiIndexed(ints int, ints2 int, count int) (*types.Transaction, error) EmitLogStrings(strings []string) (*types.Transaction, error) EmitLogInt(payload int) (*types.Transaction, error) EmitLogIntIndexed(payload int) (*types.Transaction, error) diff --git a/integration-tests/contracts/test_contracts.go b/integration-tests/contracts/test_contracts.go index 3080668da69..8a6d0b5be02 100644 --- a/integration-tests/contracts/test_contracts.go +++ b/integration-tests/contracts/test_contracts.go @@ -55,6 +55,18 @@ func (e *LogEmitterContract) EmitLogIntsIndexed(ints []int) (*types.Transaction, return tx, e.client.ProcessTransaction(tx) } +func (e *LogEmitterContract) EmitLogIntMultiIndexed(ints int, ints2 int, count int) (*types.Transaction, error) { + opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := e.instance.EmitLog4(opts, big.NewInt(int64(ints)), big.NewInt(int64(ints2)), big.NewInt(int64(count))) + if err != nil { + return nil, err + } + return tx, e.client.ProcessTransaction(tx) +} + func (e *LogEmitterContract) EmitLogStrings(strings []string) (*types.Transaction, error) { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index 5fde9befba5..8c27578c613 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -2,6 +2,7 @@ package automationv2_1 import ( "context" + "encoding/base64" "fmt" "math" "math/big" @@ -14,6 +15,7 @@ import ( geth "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/pelletier/go-toml/v2" "github.com/slack-go/slack" "github.com/stretchr/testify/require" @@ -31,16 +33,15 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" - "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" ) const ( @@ -77,16 +78,16 @@ ListenAddresses = ["0.0.0.0:6690"]` minimumDbSpec = map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ - "cpu": "1000m", - "memory": "1Gi", + "cpu": "4000m", + "memory": "4Gi", }, "limits": map[string]interface{}{ - "cpu": "1000m", - "memory": "1Gi", + "cpu": "4000m", + "memory": "4Gi", }, }, "stateful": true, - "capacity": "5Gi", + "capacity": "10Gi", } recNodeSpec = map[string]interface{}{ @@ -102,58 +103,110 @@ ListenAddresses = ["0.0.0.0:6690"]` }, } - recDbSpec = map[string]interface{}{ - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": "2000m", - "memory": "2Gi", - }, - "limits": map[string]interface{}{ - "cpu": "2000m", - "memory": "2Gi", - }, + recDbSpec = minimumDbSpec + + gethNodeSpec = map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "8000m", + "memory": "8Gi", + }, + "limits": map[string]interface{}{ + "cpu": "16000m", + "memory": "16Gi", }, - "stateful": true, - "capacity": "10Gi", } ) var ( - numberofNodes, _ = strconv.Atoi(getEnv("NUMBEROFNODES", "6")) // Number of nodes in the DON - numberOfUpkeeps, _ = strconv.Atoi(getEnv("NUMBEROFUPKEEPS", "100")) // Number of log triggered upkeeps - duration, _ = strconv.Atoi(getEnv("DURATION", "900")) // Test duration in seconds - blockTime, _ = strconv.Atoi(getEnv("BLOCKTIME", "1")) // Block time in seconds for geth simulated dev network - numberOfEvents, _ = strconv.Atoi(getEnv("NUMBEROFEVENTS", "1")) // Number of events to emit per trigger - specType = getEnv("SPECTYPE", "minimum") // minimum, recommended, local specs for the test - logLevel = getEnv("LOGLEVEL", "info") // log level for the chainlink nodes - pyroscope, _ = strconv.ParseBool(getEnv("PYROSCOPE", "false")) // enable pyroscope for the chainlink nodes + numberofNodes, _ = strconv.Atoi(getEnv("NUMBEROFNODES", "6")) // Number of nodes in the DON + duration, _ = strconv.Atoi(getEnv("DURATION", "900")) // Test duration in seconds + blockTime, _ = strconv.Atoi(getEnv("BLOCKTIME", "1")) // Block time in seconds for geth simulated dev network + nodeFunding, _ = strconv.ParseFloat(getEnv("NODEFUNDING", "100"), 64) // Amount of native to fund each node with + + specType = getEnv("SPECTYPE", "minimum") // minimum, recommended, local specs for the test + logLevel = getEnv("LOGLEVEL", "info") // log level for the chainlink nodes + pyroscope, _ = strconv.ParseBool(getEnv("PYROSCOPE", "false")) // enable pyroscope for the chainlink nodes + prometheus, _ = strconv.ParseBool(getEnv("PROMETHEUS", "false")) // enable prometheus for the chainlink nodes + configOverride = os.Getenv("CONFIG_OVERRIDE") // config overrides for the load config ) +type Load struct { + NumberOfEvents int `toml:",omitempty"` + NumberOfSpamMatchingEvents int `toml:",omitempty"` + NumberOfSpamNonMatchingEvents int `toml:",omitempty"` + CheckBurnAmount *big.Int `toml:",omitempty"` + PerformBurnAmount *big.Int `toml:",omitempty"` + UpkeepGasLimit uint32 `toml:",omitempty"` + NumberOfUpkeeps int `toml:",omitempty"` + SharedTrigger bool `toml:",omitempty"` +} + +type LoadConfig struct { + Load []Load `toml:",omitempty"` +} + +var defaultLoadConfig = LoadConfig{ + Load: []Load{ + { + NumberOfEvents: 1, + NumberOfSpamMatchingEvents: 1, + NumberOfSpamNonMatchingEvents: 0, + CheckBurnAmount: big.NewInt(0), + PerformBurnAmount: big.NewInt(0), + UpkeepGasLimit: 1_000_000, + NumberOfUpkeeps: 5, + SharedTrigger: false, + }, + { + NumberOfEvents: 1, + NumberOfSpamMatchingEvents: 0, + NumberOfSpamNonMatchingEvents: 1, + CheckBurnAmount: big.NewInt(0), + PerformBurnAmount: big.NewInt(0), + UpkeepGasLimit: 1_000_000, + NumberOfUpkeeps: 5, + SharedTrigger: true, + }}, +} + func TestLogTrigger(t *testing.T) { ctx := tests.Context(t) l := logging.GetTestLogger(t) + loadConfig := &LoadConfig{} + if configOverride != "" { + d, err := base64.StdEncoding.DecodeString(configOverride) + require.NoError(t, err, "Error decoding config override") + l.Info().Str("CONFIG_OVERRIDE", configOverride).Bytes("Decoded value", d).Msg("Decoding config override") + err = toml.Unmarshal(d, &loadConfig) + require.NoError(t, err, "Error unmarshalling config override") + } else { + loadConfig = &defaultLoadConfig + } + + loadConfigBytes, err := toml.Marshal(loadConfig) + require.NoError(t, err, "Error marshalling load config") + l.Info().Msg("Starting automation v2.1 log trigger load test") l.Info().Str("TEST_INPUTS", os.Getenv("TEST_INPUTS")).Int("Number of Nodes", numberofNodes). - Int("Number of Upkeeps", numberOfUpkeeps). Int("Duration", duration). Int("Block Time", blockTime). - Int("Number of Events", numberOfEvents). Str("Spec Type", specType). Str("Log Level", logLevel). Str("Image", os.Getenv(config.EnvVarCLImage)). Str("Tag", os.Getenv(config.EnvVarCLTag)). + Bytes("Load Config", loadConfigBytes). Msg("Test Config") - testConfig := fmt.Sprintf("Number of Nodes: %d\nNumber of Upkeeps: %d\nDuration: %d\nBlock Time: %d\n"+ - "Number of Events: %d\nSpec Type: %s\nLog Level: %s\nImage: %s\nTag: %s\n", numberofNodes, numberOfUpkeeps, duration, - blockTime, numberOfEvents, specType, logLevel, os.Getenv(config.EnvVarCLImage), os.Getenv(config.EnvVarCLTag)) + testConfig := fmt.Sprintf("Number of Nodes: %d\nDuration: %d\nBlock Time: %d\n"+ + "Spec Type: %s\nLog Level: %s\nImage: %s\nTag: %s\n\nLoad Config: \n%s", numberofNodes, duration, + blockTime, specType, logLevel, os.Getenv(config.EnvVarCLImage), os.Getenv(config.EnvVarCLTag), string(loadConfigBytes)) + l.Info().Str("testConfig", testConfig).Msg("Test Config") testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] testType := "load" loadDuration := time.Duration(duration) * time.Second automationDefaultLinkFunds := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(10000))) //10000 LINK - automationDefaultUpkeepGasLimit := uint32(1_000_000) registrySettings := &contracts.KeeperRegistrySettings{ PaymentPremiumPPB: uint32(0), @@ -198,6 +251,10 @@ func TestLogTrigger(t *testing.T) { key = "GRAFANA_DASHBOARD_URL" err = os.Setenv(fmt.Sprintf("TEST_%s", key), getEnv(key, "")) require.NoError(t, err, "failed to set the environment variable GRAFANA_DASHBOARD_URL for remote runner") + + key = "CONFIG_OVERRIDE" + err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable CONFIG_OVERRIDE for remote runner") } testEnvironment. @@ -206,24 +263,15 @@ func TestLogTrigger(t *testing.T) { Simulated: testNetwork.Simulated, WsURLs: testNetwork.URLs, Values: map[string]interface{}{ - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": "4000m", - "memory": "4Gi", - }, - "limits": map[string]interface{}{ - "cpu": "8000m", - "memory": "8Gi", - }, - }, + "resources": gethNodeSpec, "geth": map[string]interface{}{ "blocktime": blockTime, - "capacity": "10Gi", + "capacity": "20Gi", }, }, })) - err := testEnvironment.Run() + err = testEnvironment.Run() require.NoError(t, err, "Error launching test environment") if testEnvironment.WillUseRemoteRunner() { @@ -264,9 +312,10 @@ func TestLogTrigger(t *testing.T) { } nodeTOML = networks.AddNetworksConfig(nodeTOML, testNetwork) testEnvironment.AddHelm(chainlink.New(i, map[string]any{ - "toml": nodeTOML, - "chainlink": nodeSpec, - "db": dbSpec, + "toml": nodeTOML, + "chainlink": nodeSpec, + "db": dbSpec, + "prometheus": prometheus, })) } @@ -318,11 +367,12 @@ func TestLogTrigger(t *testing.T) { a.SetupAutomationDeployment(t) - err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(100), 0) + err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(nodeFunding), 0) require.NoError(t, err, "Error funding chainlink nodes") consumerContracts := make([]contracts.KeeperConsumer, 0) triggerContracts := make([]contracts.LogEmitter, 0) + triggerAddresses := make([]common.Address, 0) utilsABI, err := automation_utils_2_1.AutomationUtilsMetaData.GetAbi() require.NoError(t, err, "Error getting automation utils abi") @@ -336,61 +386,91 @@ func TestLogTrigger(t *testing.T) { } upkeepConfigs := make([]automationv2.UpkeepConfig, 0) + loadConfigs := make([]Load, 0) + cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) + require.NoError(t, err, "Error building concurrent chain client") + + cContractDeployer, err := contracts.NewContractDeployer(cEVMClient, l) + require.NoError(t, err, "Error building concurrent contract deployer") + for _, u := range loadConfig.Load { + for i := 0; i < u.NumberOfUpkeeps; i++ { + consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() + require.NoError(t, err, "Error deploying automation consumer contract") + consumerContracts = append(consumerContracts, consumerContract) + l.Debug(). + Str("Contract Address", consumerContract.Address()). + Int("Number", i+1). + Int("Out Of", u.NumberOfUpkeeps). + Msg("Deployed Automation Log Trigger Consumer Contract") + loadCfg := Load{ + NumberOfEvents: u.NumberOfEvents, + NumberOfSpamMatchingEvents: u.NumberOfSpamMatchingEvents, + NumberOfSpamNonMatchingEvents: u.NumberOfSpamNonMatchingEvents, + CheckBurnAmount: u.CheckBurnAmount, + PerformBurnAmount: u.PerformBurnAmount, + UpkeepGasLimit: u.UpkeepGasLimit, + SharedTrigger: u.SharedTrigger, + } + loadConfigs = append(loadConfigs, loadCfg) - for i := 0; i < numberOfUpkeeps; i++ { - consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() - require.NoError(t, err, "Error deploying automation consumer contract") - consumerContracts = append(consumerContracts, consumerContract) - l.Debug(). - Str("Contract Address", consumerContract.Address()). - Int("Number", i+1). - Int("Out Of", numberOfUpkeeps). - Msg("Deployed Automation Log Trigger Consumer Contract") - - cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) - require.NoError(t, err, "Error building concurrent chain client") - - cContractDeployer, err := contracts.NewContractDeployer(cEVMClient, l) - require.NoError(t, err, "Error building concurrent contract deployer") - - triggerContract, err := cContractDeployer.DeployLogEmitterContract() - require.NoError(t, err, "Error deploying log emitter contract") - triggerContracts = append(triggerContracts, triggerContract) - l.Debug(). - Str("Contract Address", triggerContract.Address().Hex()). - Int("Number", i+1). - Int("Out Of", numberOfUpkeeps). - Msg("Deployed Automation Log Trigger Emitter Contract") + if u.SharedTrigger && i > 0 { + triggerAddresses = append(triggerAddresses, triggerAddresses[len(triggerAddresses)-1]) + continue + } + triggerContract, err := cContractDeployer.DeployLogEmitterContract() + require.NoError(t, err, "Error deploying log emitter contract") + triggerContracts = append(triggerContracts, triggerContract) + triggerAddresses = append(triggerAddresses, triggerContract.Address()) + l.Debug(). + Str("Contract Address", triggerContract.Address().Hex()). + Int("Number", i+1). + Int("Out Of", u.NumberOfUpkeeps). + Msg("Deployed Automation Log Trigger Emitter Contract") + } + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for contracts to deploy") } - err = chainClient.WaitForEvents() - require.NoError(t, err, "Failed waiting for contracts to deploy") - for i, consumerContract := range consumerContracts { logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ - ContractAddress: triggerContracts[i].Address(), - FilterSelector: 0, - Topic0: emitterABI.Events["Log1"].ID, - Topic1: bytes0, - Topic2: bytes0, - Topic3: bytes0, + ContractAddress: triggerAddresses[i], + FilterSelector: 1, + Topic0: emitterABI.Events["Log4"].ID, + Topic1: [32]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + }, + Topic2: bytes0, + Topic3: bytes0, } encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) require.NoError(t, err, "Error encoding log trigger config") l.Debug().Bytes("Encoded Log Trigger Config", encodedLogTriggerConfig).Msg("Encoded Log Trigger Config") + checkDataStruct := simple_log_upkeep_counter_wrapper.CheckData{ + CheckBurnAmount: loadConfigs[i].CheckBurnAmount, + PerformBurnAmount: loadConfigs[i].PerformBurnAmount, + EventSig: [32]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + }, + } + + encodedCheckDataStruct, err := consumerABI.Methods["_checkDataConfig"].Inputs.Pack(&checkDataStruct) + require.NoError(t, err, "Error encoding check data struct") + l.Debug().Bytes("Encoded Check Data Struct", encodedCheckDataStruct).Msg("Encoded Check Data Struct") + upkeepConfig := automationv2.UpkeepConfig{ UpkeepName: fmt.Sprintf("LogTriggerUpkeep-%d", i), EncryptedEmail: []byte("test@mail.com"), UpkeepContract: common.HexToAddress(consumerContract.Address()), - GasLimit: automationDefaultUpkeepGasLimit, + GasLimit: loadConfigs[i].UpkeepGasLimit, AdminAddress: common.HexToAddress(chainClient.GetDefaultWallet().Address()), TriggerType: uint8(1), - CheckData: []byte("0"), + CheckData: encodedCheckDataStruct, TriggerConfig: encodedLogTriggerConfig, OffchainConfig: []byte("0"), FundingAmount: automationDefaultLinkFunds, } + l.Debug().Interface("Upkeep Config", upkeepConfig).Msg("Upkeep Config") upkeepConfigs = append(upkeepConfigs, upkeepConfig) } @@ -425,9 +505,10 @@ func TestLogTrigger(t *testing.T) { ), Gun: NewLogTriggerUser( triggerContract, - consumerContracts[i], l, - numberOfEvents, + loadConfigs[i].NumberOfEvents, + loadConfigs[i].NumberOfSpamMatchingEvents, + loadConfigs[i].NumberOfSpamNonMatchingEvents, ), CallResultBufLen: 1000000, }) @@ -436,9 +517,9 @@ func TestLogTrigger(t *testing.T) { l.Info().Msg("Starting load generators") startTime := time.Now() - err = sendSlackNotification("Started", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + ts, err := sendSlackNotification("Started", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), strconv.FormatInt(startTime.UnixMilli(), 10), "now", - []slack.Block{extraBlockWithText("\bTest Config\b\n```" + testConfig + "```")}) + []slack.Block{extraBlockWithText("\bTest Config\b\n```" + testConfig + "```")}, slack.MsgOptionBlocks()) if err != nil { l.Error().Err(err).Msg("Error sending slack notification") } @@ -460,10 +541,9 @@ func TestLogTrigger(t *testing.T) { var numberOfEventsEmitted int var batchSize uint64 = 500 - for _, gen := range p.Generators { - numberOfEventsEmitted += len(gen.GetData().OKData.Data) + for i, gen := range p.Generators { + numberOfEventsEmitted = numberOfEventsEmitted + (len(gen.GetData().OKData.Data) * loadConfigs[i].NumberOfEvents) } - numberOfEventsEmitted = numberOfEventsEmitted * numberOfEvents l.Info().Int("Number of Events Emitted", numberOfEventsEmitted).Msg("Number of Events Emitted") if endBlock-startBlock < batchSize { @@ -500,7 +580,7 @@ func TestLogTrigger(t *testing.T) { timeout = time.Duration(math.Min(float64(timeout)*2, float64(2*time.Minute))) continue } - l.Info(). + l.Debug(). Interface("FilterQuery", filterQuery). Str("Contract Address", consumerContract.Address()). Str("Timeout", timeout.String()). @@ -557,9 +637,9 @@ func TestLogTrigger(t *testing.T) { avg, median, ninetyPct, ninetyNinePct, maximum, len(allUpkeepDelays), numberOfEventsEmitted, eventsMissed, percentMissed, testDuration.String()) - err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), - strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(endTime.UnixMilli(), 10), - []slack.Block{extraBlockWithText("\bTest Report\b\n```" + testReport + "```")}) + _, err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(time.Now().UnixMilli(), 10), + []slack.Block{extraBlockWithText("\bTest Report\b\n```" + testReport + "```")}, slack.MsgOptionTS(ts)) if err != nil { l.Error().Err(err).Msg("Error sending slack notification") } diff --git a/integration-tests/load/automationv2_1/gun.go b/integration-tests/load/automationv2_1/gun.go index 2bbb654e00b..938ce8708e6 100644 --- a/integration-tests/load/automationv2_1/gun.go +++ b/integration-tests/load/automationv2_1/gun.go @@ -8,35 +8,52 @@ import ( ) type LogTriggerGun struct { - triggerContract contracts.LogEmitter - upkeepContract contracts.KeeperConsumer - logger zerolog.Logger - numberOfEvents int + triggerContract contracts.LogEmitter + logger zerolog.Logger + numberOfEvents int + numberOfSpamMatchingEvents int + numberOfSpamNonMatchingEvents int } func NewLogTriggerUser( triggerContract contracts.LogEmitter, - upkeepContract contracts.KeeperConsumer, logger zerolog.Logger, numberOfEvents int, + numberOfSpamMatchingEvents int, + numberOfSpamNonMatchingEvents int, ) *LogTriggerGun { + return &LogTriggerGun{ - triggerContract: triggerContract, - upkeepContract: upkeepContract, - logger: logger, - numberOfEvents: numberOfEvents, + triggerContract: triggerContract, + logger: logger, + numberOfEvents: numberOfEvents, + numberOfSpamMatchingEvents: numberOfSpamMatchingEvents, + numberOfSpamNonMatchingEvents: numberOfSpamNonMatchingEvents, } } func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.Response { m.logger.Debug().Str("Trigger address", m.triggerContract.Address().String()).Msg("Triggering upkeep") - payload := make([]int, 0) - for i := 0; i < m.numberOfEvents; i++ { - payload = append(payload, 1) + + if m.numberOfEvents > 0 { + _, err := m.triggerContract.EmitLogIntMultiIndexed(1, 1, m.numberOfEvents) + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } } - _, err := m.triggerContract.EmitLogInts(payload) - if err != nil { - return &wasp.Response{Error: err.Error(), Failed: true} + + if m.numberOfSpamMatchingEvents > 0 { + _, err := m.triggerContract.EmitLogIntMultiIndexed(1, 2, m.numberOfSpamMatchingEvents) + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } + } + + if m.numberOfSpamNonMatchingEvents > 0 { + _, err := m.triggerContract.EmitLogIntMultiIndexed(2, 2, m.numberOfSpamNonMatchingEvents) + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } } return &wasp.Response{} diff --git a/integration-tests/load/automationv2_1/helpers.go b/integration-tests/load/automationv2_1/helpers.go index 3c08199a9cf..68ac909af90 100644 --- a/integration-tests/load/automationv2_1/helpers.go +++ b/integration-tests/load/automationv2_1/helpers.go @@ -31,7 +31,7 @@ func extraBlockWithText(text string) slack.Block { } func sendSlackNotification(header string, l zerolog.Logger, namespace string, numberOfNodes, - startingTime string, endingTime string, extraBlocks []slack.Block) error { + startingTime string, endingTime string, extraBlocks []slack.Block, msgOption slack.MsgOption) (string, error) { slackClient := slack.New(reportModel.SlackAPIKey) headerText := ":chainlink-keepers: Automation Load Test " + header + " :white_check_mark:" @@ -65,7 +65,7 @@ func sendSlackNotification(header string, l zerolog.Logger, namespace string, nu notificationBlocks = append(notificationBlocks, extraBlocks...) } - ts, err := reportModel.SendSlackMessage(slackClient, slack.MsgOptionBlocks(notificationBlocks...)) + ts, err := reportModel.SendSlackMessage(slackClient, slack.MsgOptionBlocks(notificationBlocks...), msgOption) l.Info().Str("ts", ts).Msg("Sent Slack Message") - return err + return ts, err } From b9b406801737e610c4e6df5152039645346a346e Mon Sep 17 00:00:00 2001 From: dkneisly Date: Tue, 19 Dec 2023 08:21:18 -0800 Subject: [PATCH 085/234] Added VRF v2 oracle withdraw smoke test (#11617) --- .../contracts/contract_vrf_models.go | 1 + .../contracts/ethereum_vrfv2_contracts.go | 12 ++++ integration-tests/smoke/vrfv2_test.go | 60 +++++++++++++++++-- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 4577664774e..8a217e26766 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -60,6 +60,7 @@ type VRFCoordinatorV2 interface { WaitForRandomWordsFulfilledEvent(requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) + OracleWithdraw(recipient common.Address, amount *big.Int) error } type VRFCoordinatorV2_5 interface { diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index ba606a884b3..5d22167158a 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -224,6 +224,18 @@ func (v *EthereumVRFCoordinatorV2) PendingRequestsExist(ctx context.Context, sub return pendingRequestExists, nil } +func (v *EthereumVRFCoordinatorV2) OracleWithdraw(recipient common.Address, amount *big.Int) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.coordinator.OracleWithdraw(opts, recipient, amount) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + // OwnerCancelSubscription cancels subscription, // return funds to the subscription owner, // down not check if pending requests for a sub exist, diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 61a6b15ca1a..36f6fbf724e 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -6,19 +6,19 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" - "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -116,6 +116,58 @@ func TestVRFv2Basic(t *testing.T) { } }) + t.Run("Oracle Withdraw", func(t *testing.T) { + testConfig := vrfv2Config + subIDsForOracleWithDraw, err := vrfv2_actions.CreateFundSubsAndAddConsumers( + env, + testConfig, + linkToken, + vrfv2Contracts.Coordinator, + vrfv2Contracts.LoadTestConsumers, + 1, + ) + require.NoError(t, err) + + subIDForOracleWithdraw := subIDsForOracleWithDraw[0] + + fulfilledEventLink, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + vrfv2Contracts.LoadTestConsumers[0], + vrfv2Contracts.Coordinator, + vrfv2Data, + subIDForOracleWithdraw, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err) + + amountToWithdrawLink := fulfilledEventLink.Payment + + defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + require.NoError(t, err) + + l.Info(). + Str("Returning to", defaultWalletAddress). + Str("Amount", amountToWithdrawLink.String()). + Msg("Invoking Oracle Withdraw for LINK") + + err = vrfv2Contracts.Coordinator.OracleWithdraw(common.HexToAddress(defaultWalletAddress), amountToWithdrawLink) + require.NoError(t, err, "Error withdrawing LINK from coordinator to default wallet") + + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + + defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + require.NoError(t, err) + + require.Equal( + t, + 1, + defaultWalletBalanceLinkAfterOracleWithdraw.Cmp(defaultWalletBalanceLinkBeforeOracleWithdraw), + "LINK funds were not returned after oracle withdraw", + ) + }) t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { testConfig := vrfv2Config subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( From bf31131bda5c41e55ba6e6f5dcea498882fd3966 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Tue, 19 Dec 2023 18:51:24 +0200 Subject: [PATCH 086/234] VRF-817: run all VRF V2 tests in CI (#11620) --- .github/workflows/integration-tests.yml | 2 -- integration-tests/smoke/vrfv2_test.go | 7 +++++-- integration-tests/smoke/vrfv2plus_test.go | 15 +++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b1caa52acec..e8243c8cdfa 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -407,8 +407,6 @@ jobs: pyroscope_env: ci-smoke-vrf-evm-simulated - name: vrfv2 nodes: 1 - run: -run TestVRFv2MultipleSendingKeys - file: vrfv2 os: ubuntu-latest pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 36f6fbf724e..4167342c41f 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -358,13 +358,16 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + var vrfv2Config vrfv2_config.VRFV2Config - err := envconfig.Process("VRFV2", &vrfv2Config) + err = envconfig.Process("VRFV2", &vrfv2Config) require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). - WithGeth(). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index a850cb4fe15..f3f14ac7cee 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -611,13 +611,16 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err := envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) + err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). - WithGeth(). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). @@ -702,13 +705,17 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { func TestVRFv2PlusMigration(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err := envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) + err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) require.NoError(t, err) env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). - WithGeth(). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). From 0e5b219812f0f73b7b067079f8b6f08551850abe Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 19 Dec 2023 10:57:16 -0600 Subject: [PATCH 087/234] update common for mailbox.Monitor name fix (#11593) --- core/chains/evm/headtracker/head_broadcaster_test.go | 5 ++--- core/chains/evm/headtracker/head_tracker_test.go | 7 ++++--- core/chains/evm/log/helpers_test.go | 4 ++-- core/chains/evm/log/integration_test.go | 4 ++-- core/cmd/shell.go | 2 +- core/internal/cltest/cltest.go | 2 +- core/internal/testutils/evmtest/evmtest.go | 6 ++++-- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/directrequest/delegate_test.go | 6 +++--- core/services/functions/listener_test.go | 4 ++-- core/services/job/runner_integration_test.go | 12 ++++++------ core/services/job/spawner_test.go | 10 +++++----- .../keeper/registry_synchronizer_helper_test.go | 4 ++-- core/services/ocr/contract_tracker_test.go | 4 ++-- core/services/vrf/delegate_test.go | 6 +++--- core/web/testdata/body/health.html | 9 ++++++--- core/web/testdata/body/health.json | 8 ++++---- core/web/testdata/body/health.txt | 2 +- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 23 files changed, 59 insertions(+), 54 deletions(-) diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 21c864eda69..d2dc9863268 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -12,8 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" - + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" commonhtrk "github.com/smartcontractkit/chainlink/v2/common/headtracker" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" @@ -73,7 +72,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { orm := headtracker.NewORM(db, logger, cfg.Database(), *ethClient.ConfiguredChainID()) hs := headtracker.NewHeadSaver(logger, orm, evmCfg.EVM(), evmCfg.EVM().HeadTracker()) - mailMon := mailbox.NewMonitor(t.Name()) + mailMon := mailboxtest.NewMonitor(t) servicetest.Run(t, mailMon) hb := headtracker.NewHeadBroadcaster(logger) servicetest.Run(t, hb) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 67e76aee52b..fd7db65ae32 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -23,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -994,7 +995,7 @@ func createHeadTracker(t *testing.T, ethClient evmclient.Client, config headtrac lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) - mailMon := mailbox.NewMonitor(t.Name()) + mailMon := mailboxtest.NewMonitor(t) return &headTrackerUniverse{ mu: new(sync.Mutex), headTracker: headtracker.NewHeadTracker(lggr, ethClient, config, htConfig, hb, hs, mailMon), @@ -1009,7 +1010,7 @@ func createHeadTrackerWithNeverSleeper(t *testing.T, ethClient evmclient.Client, lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, evmcfg.EVM(), evmcfg.EVM().HeadTracker()) - mailMon := mailbox.NewMonitor(t.Name()) + mailMon := mailboxtest.NewMonitor(t) ht := headtracker.NewHeadTracker(lggr, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), hb, hs, mailMon) _, err := hs.Load(testutils.Context(t)) require.NoError(t, err) @@ -1027,7 +1028,7 @@ func createHeadTrackerWithChecker(t *testing.T, ethClient evmclient.Client, conf hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) hb.Subscribe(checker) - mailMon := mailbox.NewMonitor(t.Name()) + mailMon := mailboxtest.NewMonitor(t) ht := headtracker.NewHeadTracker(lggr, ethClient, config, htConfig, hb, hs, mailMon) return &headTrackerUniverse{ mu: new(sync.Mutex), diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index de8ff024b84..35db8f7f7bf 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -22,7 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -90,7 +90,7 @@ func newBroadcasterHelperWithEthClient(t *testing.T, ethClient evmclient.Client, }) config := evmtest.NewChainScopedConfig(t, globalConfig) lggr := logger.Test(t) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) db := pgtest.NewSqlxDB(t) orm := log.NewORM(db, lggr, config.Database(), cltest.FixtureChainID) diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index b26e87e668c..e74d06457dd 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -18,7 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -1326,7 +1326,7 @@ func TestBroadcaster_AppendLogChannel(t *testing.T) { ch3 := make(chan types.Log) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - mailMon := servicetest.RunHealthy(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.RunHealthy(t, mailboxtest.NewMonitor(t)) lb := log.NewBroadcaster(nil, ethClient, nil, logger.Test(t), nil, mailMon) chCombined := lb.ExportedAppendLogChannel(ch1, ch2) chCombined = lb.ExportedAppendLogChannel(chCombined, ch3) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 3810559cf34..e4711646cb4 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -154,7 +154,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G } keyStore := keystore.New(db, utils.GetScryptParams(cfg), appLggr, cfg.Database()) - mailMon := mailbox.NewMonitor(cfg.AppID().String()) + mailMon := mailbox.NewMonitor(cfg.AppID().String(), appLggr.Named("Mailbox")) dbListener := cfg.Database().Listener() eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), dbListener.MinReconnectInterval(), dbListener.MaxReconnectDuration(), appLggr, cfg.AppID()) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 8ebf4b84b46..51439ba80ec 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -341,7 +341,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn keyStore := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg.Database()) - mailMon := mailbox.NewMonitor(cfg.AppID().String()) + mailMon := mailbox.NewMonitor(cfg.AppID().String(), lggr.Named("Mailbox")) loopRegistry := plugins.NewLoopRegistry(lggr, nil) mercuryPool := wsrpc.NewPool(lggr, cache.Config{ diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index eb1a03530ae..9397db53acb 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains" @@ -81,8 +82,9 @@ func NewChainRelayExtenders(t testing.TB, testopts TestChainOpts) *evmrelay.Chai func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) legacyevm.ChainRelayExtenderConfig { require.NotNil(t, testopts.KeyStore) + lggr := logger.TestLogger(t) opts := legacyevm.ChainRelayExtenderConfig{ - Logger: logger.TestLogger(t), + Logger: lggr, KeyStore: testopts.KeyStore, ChainOpts: legacyevm.ChainOpts{ AppConfig: testopts.GeneralConfig, @@ -119,7 +121,7 @@ func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) legacyevm.ChainR } } if opts.MailMon == nil { - opts.MailMon = servicetest.Run(t, mailbox.NewMonitor(t.Name())) + opts.MailMon = servicetest.Run(t, mailboxtest.NewMonitor(t)) } if testopts.GasEstimator != nil { opts.GenGasEstimator = func(*big.Int) gas.EvmFeeEstimator { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index f206ddd6d5b..ba4542e74b4 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -235,7 +235,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index d38fddcd922..755def9861f 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 h1:7P+djpKBMQ2Cpv1ieUQdkZvDLt6owPvniHfMHSPFYjQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 h1:mrxa3HrQfbMi4ji6gGcQHuLptvoNaRAv4TLnGJngbLc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index be61cde4d60..3b80ba2f915 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -45,7 +45,7 @@ func TestDelegate_ServicesForSpec(t *testing.T) { c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) }) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) relayerExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: cfg, Client: ethClient, MailMon: mailMon, KeyStore: keyStore.Eth()}) lggr := logger.TestLogger(t) @@ -82,7 +82,7 @@ func NewDirectRequestUniverseWithConfig(t *testing.T, cfg chainlink.GeneralConfi runner := pipeline_mocks.NewRunner(t) broadcaster.On("AddDependents", 1) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 07bd82ed288..5d26f9a4f57 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -20,7 +20,7 @@ import ( decryptionPlugin "github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -82,7 +82,7 @@ func NewFunctionsListenerUniverse(t *testing.T, timeoutSec int, pruneFrequencySe ethClient := evmtest.NewEthClientMockWithDefaultChain(t) broadcaster := log_mocks.NewBroadcaster(t) broadcaster.On("AddDependents", 1) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) db := pgtest.NewSqlxDB(t) kst := cltest.NewKeyStore(t, db, cfg.Database()) diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index 14a5c41b396..27c0e0e8515 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -24,7 +24,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -462,7 +462,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, mailbox.NewMonitor(t.Name())), + servicetest.Run(t, mailboxtest.NewMonitor(t)), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -496,7 +496,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, mailbox.NewMonitor(t.Name())), + servicetest.Run(t, mailboxtest.NewMonitor(t)), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -524,7 +524,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, mailbox.NewMonitor(t.Name())), + servicetest.Run(t, mailboxtest.NewMonitor(t)), ) _, err = sd.ServicesForSpec(jb) require.NoError(t, err) @@ -579,7 +579,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, mailbox.NewMonitor(t.Name())), + servicetest.Run(t, mailboxtest.NewMonitor(t)), ) jb.OCROracleSpec.CaptureEATelemetry = tc.jbCaptureEATelemetry @@ -623,7 +623,7 @@ answer1 [type=median index=0]; legacyChains, lggr, config.Database(), - servicetest.Run(t, mailbox.NewMonitor(t.Name())), + servicetest.Run(t, mailboxtest.NewMonitor(t)), ) services, err := sd.ServicesForSpec(*jb) require.NoError(t, err) diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index b82aa73c0b5..335156a8c65 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" "github.com/smartcontractkit/chainlink/v2/core/bridges" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -129,7 +129,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { serviceA2 := mocks.NewServiceCtx(t) serviceA1.On("Start", mock.Anything).Return(nil).Once() serviceA2.On("Start", mock.Anything).Return(nil).Once().Run(func(mock.Arguments) { eventuallyA.ItHappened() }) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) dA := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, make(chan struct{}), dA} @@ -188,7 +188,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { lggr := logger.TestLogger(t) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ @@ -222,7 +222,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { lggr := logger.TestLogger(t) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ @@ -300,7 +300,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { jobOCR2VRF := makeOCR2VRFJobSpec(t, keyStore, config, address, chain.ID(), 2) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) processConfig := plugins.NewRegistrarConfig(loop.GRPCOpts{}, func(name string) (*plugins.RegisteredLoop, error) { return nil, nil }) ocr2DelegateConfig := ocr2.NewDelegateConfig(config.OCR2(), config.Mercury(), config.Threshold(), config.Insecure(), config.JobPipeline(), config.Database(), processConfig) diff --git a/core/services/keeper/registry_synchronizer_helper_test.go b/core/services/keeper/registry_synchronizer_helper_test.go index dff97202f6c..19ba2eedbbb 100644 --- a/core/services/keeper/registry_synchronizer_helper_test.go +++ b/core/services/keeper/registry_synchronizer_helper_test.go @@ -11,7 +11,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -73,7 +73,7 @@ func setupRegistrySync(t *testing.T, version keeper.RegistryVersion) ( })).Maybe().Return(func() {}) lbMock.On("IsConnected").Return(true).Maybe() - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) orm := keeper.NewORM(db, logger.TestLogger(t), ch.Config().Database()) synchronizer := keeper.NewRegistrySynchronizer(keeper.RegistrySynchronizerOptions{ diff --git a/core/services/ocr/contract_tracker_test.go b/core/services/ocr/contract_tracker_test.go index f7ebbe08481..185a9cd3197 100644 --- a/core/services/ocr/contract_tracker_test.go +++ b/core/services/ocr/contract_tracker_test.go @@ -16,7 +16,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -84,7 +84,7 @@ func newContractTrackerUni(t *testing.T, opts ...interface{}) (uni contractTrack uni.hb = commonmocks.NewHeadBroadcaster[*evmtypes.Head, common.Hash](t) uni.ec = evmtest.NewEthClientMock(t) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) db := pgtest.NewSqlxDB(t) uni.tracker = ocr.NewOCRContractTracker( contract, diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 1d9f97d136d..731437791b4 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -150,7 +150,7 @@ func setup(t *testing.T) (vrfUniverse, *v1.Listener, job.Job) { cfg := configtest.NewTestGeneralConfig(t) vuni := buildVrfUni(t, db, cfg) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) vd := vrf.NewDelegate( db, @@ -676,7 +676,7 @@ func Test_VRFV2PlusServiceFailsWhenVRFOwnerProvided(t *testing.T) { cfg := configtest.NewTestGeneralConfig(t) vuni := buildVrfUni(t, db, cfg) - mailMon := servicetest.Run(t, mailbox.NewMonitor(t.Name())) + mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) vd := vrf.NewDelegate( db, diff --git a/core/web/testdata/body/health.html b/core/web/testdata/body/health.html index 5999891a0f6..f6c1d6c80c8 100644 --- a/core/web/testdata/body/health.html +++ b/core/web/testdata/body/health.html @@ -73,13 +73,16 @@ JobSpawner
- Mercury + Mailbox
- WSRPCPool + Monitor
- Monitor + Mercury +
+ WSRPCPool +
PipelineORM diff --git a/core/web/testdata/body/health.json b/core/web/testdata/body/health.json index d8418560543..004988fceba 100644 --- a/core/web/testdata/body/health.json +++ b/core/web/testdata/body/health.json @@ -110,18 +110,18 @@ }, { "type": "checks", - "id": "Mercury.WSRPCPool", + "id": "Mailbox.Monitor", "attributes": { - "name": "Mercury.WSRPCPool", + "name": "Mailbox.Monitor", "status": "passing", "output": "" } }, { "type": "checks", - "id": "Monitor", + "id": "Mercury.WSRPCPool", "attributes": { - "name": "Monitor", + "name": "Mercury.WSRPCPool", "status": "passing", "output": "" } diff --git a/core/web/testdata/body/health.txt b/core/web/testdata/body/health.txt index 5b636829587..0dfa86abad0 100644 --- a/core/web/testdata/body/health.txt +++ b/core/web/testdata/body/health.txt @@ -11,8 +11,8 @@ -EVM.0.Txm.Confirmer -EVM.0.Txm.WrappedEvmEstimator -JobSpawner +-Mailbox.Monitor -Mercury.WSRPCPool --Monitor -PipelineORM -PipelineRunner -PromReporter diff --git a/go.mod b/go.mod index 00faf4f5ee4..661addeb845 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 7bfd01bfd9e..024872df5e6 100644 --- a/go.sum +++ b/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 h1:7P+djpKBMQ2Cpv1ieUQdkZvDLt6owPvniHfMHSPFYjQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 h1:mrxa3HrQfbMi4ji6gGcQHuLptvoNaRAv4TLnGJngbLc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 5086a1c121b..1126875b0d1 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 6f539821d8e..b4e1c10229b 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327 h1:7P+djpKBMQ2Cpv1ieUQdkZvDLt6owPvniHfMHSPFYjQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218150613-43bf581ae327/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 h1:mrxa3HrQfbMi4ji6gGcQHuLptvoNaRAv4TLnGJngbLc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From ff8e4a8d4ca9b7f85650a17c470910c262ecc03b Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 19 Dec 2023 16:30:10 -0500 Subject: [PATCH 088/234] Improves Fund Return Contingency Plans (#11628) --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 1126875b0d1..8b27b07212b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 - github.com/smartcontractkit/chainlink-testing-framework v1.22.0 + github.com/smartcontractkit/chainlink-testing-framework v1.22.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index b4e1c10229b..13757dfba8d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1477,8 +1477,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f31 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= -github.com/smartcontractkit/chainlink-testing-framework v1.22.0 h1:Lur628wkrceWgcLmxGZe7Mauwxht4YO71hX9Jj5YslE= -github.com/smartcontractkit/chainlink-testing-framework v1.22.0/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= +github.com/smartcontractkit/chainlink-testing-framework v1.22.1 h1:2XDxU1CTWJruUZv15/VPdaBT1W9ym4OI3I5baRbDhFg= +github.com/smartcontractkit/chainlink-testing-framework v1.22.1/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= From e66e060a51760444801dd7ccb5528247e8503a9d Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:48:12 +0400 Subject: [PATCH 089/234] [AUTO-8227] Update Log Trigger Load Test (#11632) * separate stats for fast and revovery execution * add more stats to testReport --- .../testhelpers/SimpleLogUpkeepCounter.sol | 24 +++-- .../simple_log_upkeep_counter_wrapper.go | 31 ++++++- ...rapper-dependency-versions-do-not-edit.txt | 2 +- .../automationv2_1/automationv2_1_test.go | 89 +++++++++++++++---- 4 files changed, 119 insertions(+), 27 deletions(-) diff --git a/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol index 6ef6f8a36ff..979cc6138ac 100644 --- a/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol +++ b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol @@ -17,7 +17,8 @@ contract SimpleLogUpkeepCounter is ILogAutomation { uint256 lastBlock, uint256 previousBlock, uint256 counter, - uint256 timeToPerform + uint256 timeToPerform, + bool isRecovered ); mapping(bytes32 => bool) public dummyMap; // used to force storage lookup @@ -26,6 +27,7 @@ contract SimpleLogUpkeepCounter is ILogAutomation { uint256 public initialBlock; uint256 public counter; uint256 public timeToPerform; + bool public isRecovered; constructor() { previousPerformBlock = 0; @@ -52,9 +54,9 @@ contract SimpleLogUpkeepCounter is ILogAutomation { } } if (log.topics[2] == eventSig) { - return (true, abi.encode(log, checkData)); + return (true, abi.encode(log, block.number, checkData)); } - return (false, abi.encode(log, checkData)); + return (false, abi.encode(log, block.number, checkData)); } function performUpkeep(bytes calldata performData) external override { @@ -64,8 +66,12 @@ contract SimpleLogUpkeepCounter is ILogAutomation { lastBlock = block.number; counter = counter + 1; previousPerformBlock = lastBlock; - (Log memory log, bytes memory extraData) = abi.decode(performData, (Log, bytes)); + (Log memory log, uint256 checkBlock, bytes memory extraData) = abi.decode(performData, (Log, uint256, bytes)); timeToPerform = block.timestamp - log.timestamp; + isRecovered = false; + if (checkBlock != log.blockNumber) { + isRecovered = true; + } (uint256 checkBurnAmount, uint256 performBurnAmount, bytes32 eventSig) = abi.decode( extraData, (uint256, uint256, bytes32) @@ -80,6 +86,14 @@ contract SimpleLogUpkeepCounter is ILogAutomation { dummyIndex = keccak256(abi.encode(dummyIndex, address(this))); } } - emit PerformingUpkeep(tx.origin, initialBlock, lastBlock, previousPerformBlock, counter, timeToPerform); + emit PerformingUpkeep( + tx.origin, + initialBlock, + lastBlock, + previousPerformBlock, + counter, + timeToPerform, + isRecovered + ); } } diff --git a/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go index f834fa69118..1409bcb1548 100644 --- a/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go +++ b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go @@ -48,8 +48,8 @@ type Log struct { } var SimpleLogUpkeepCounterMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"counter\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeToPerform\",\"type\":\"uint256\"}],\"name\":\"PerformingUpkeep\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"checkBurnAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"performBurnAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"eventSig\",\"type\":\"bytes32\"}],\"internalType\":\"structCheckData\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_checkDataConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"log\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"}],\"name\":\"checkLog\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"dummyMap\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previousPerformBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timeToPerform\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5060006002819055436001556003819055600455610c63806100336000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c80636977947311610076578063806b984f1161005b578063806b984f14610147578063917d895f14610150578063c6066f0d1461015957600080fd5b806369779473146101035780637145f11b1461011457600080fd5b80632cb15864146100a857806340691db4146100c45780634585e33b146100e557806361bc221a146100fa575b600080fd5b6100b160035481565b6040519081526020015b60405180910390f35b6100d76100d236600461062d565b610162565b6040516100bb92919061088b565b6100f86100f336600461058f565b610296565b005b6100b160045481565b6100f86101113660046105d1565b50565b610137610122366004610576565b60006020819052908152604090205460ff1681565b60405190151581526020016100bb565b6100b160015481565b6100b160025481565b6100b160055481565b6000606081808061017586880188610799565b92509250925060005a9050600061018d600143610bb2565b409050600085156101fc575b855a6101a59085610bb2565b10156101fc578080156101c6575060008281526020819052604090205460ff165b60408051602081018590523091810191909152909150606001604051602081830303815290604052805190602001209150610199565b8361020a60c08d018d6109ee565b600281811061021b5761021b610bf8565b90506020020135141561025d5760018b8b8b60405160200161023f93929190610908565b6040516020818303038152906040529750975050505050505061028e565b60008b8b8b60405160200161027493929190610908565b604051602081830303815290604052975097505050505050505b935093915050565b6003546102a257436003555b4360019081556004546102b491610b9a565b6004556001546002556000806102cc8385018561069f565b915091508160200151426102e09190610bb2565b6005819055506000806000838060200190518101906102ff91906107c5565b92509250925060005a90506000610317600143610bb2565b40905060008415610386575b845a61032f9085610bb2565b101561038657808015610350575060008281526020819052604090205460ff165b60408051602081018590523091810191909152909150606001604051602081830303815290604052805190602001209150610323565b600354600154600254600454600554604080519586526020860194909452928401919091526060830152608082015232907f4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd659060a00160405180910390a250505050505050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461041457600080fd5b919050565b600082601f83011261042a57600080fd5b8135602067ffffffffffffffff82111561044657610446610c27565b8160051b610455828201610a80565b83815282810190868401838801850189101561047057600080fd5b600093505b85841015610493578035835260019390930192918401918401610475565b50979650505050505050565b60008083601f8401126104b157600080fd5b50813567ffffffffffffffff8111156104c957600080fd5b6020830191508360208285010111156104e157600080fd5b9250929050565b600082601f8301126104f957600080fd5b813567ffffffffffffffff81111561051357610513610c27565b61054460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610a80565b81815284602083860101111561055957600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561058857600080fd5b5035919050565b600080602083850312156105a257600080fd5b823567ffffffffffffffff8111156105b957600080fd5b6105c58582860161049f565b90969095509350505050565b6000606082840312156105e357600080fd5b6040516060810181811067ffffffffffffffff8211171561060657610606610c27565b80604052508235815260208301356020820152604083013560408201528091505092915050565b60008060006040848603121561064257600080fd5b833567ffffffffffffffff8082111561065a57600080fd5b90850190610100828803121561066f57600080fd5b9093506020850135908082111561068557600080fd5b506106928682870161049f565b9497909650939450505050565b600080604083850312156106b257600080fd5b823567ffffffffffffffff808211156106ca57600080fd5b9084019061010082870312156106df57600080fd5b6106e7610a56565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261071f60a084016103f0565b60a082015260c08301358281111561073657600080fd5b61074288828601610419565b60c08301525060e08301358281111561075a57600080fd5b610766888286016104e8565b60e0830152509350602085013591508082111561078257600080fd5b5061078f858286016104e8565b9150509250929050565b6000806000606084860312156107ae57600080fd5b505081359360208301359350604090920135919050565b6000806000606084860312156107da57600080fd5b8351925060208401519150604084015190509250925092565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561082557600080fd5b8260051b8083602087013760009401602001938452509192915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b821515815260006020604081840152835180604085015260005b818110156108c1578581018301518582016060015282016108a5565b818111156108d3576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b60408152833560408201526020840135606082015260408401356080820152606084013560a0820152608084013560c082015273ffffffffffffffffffffffffffffffffffffffff61095c60a086016103f0565b1660e0820152600061097160c0860186610acf565b61010084810152610987610140850182846107f3565b91505061099760e0870187610b36565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0858403016101208601526109cd838284610842565b9250505082810360208401526109e4818587610842565b9695505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610a2357600080fd5b83018035915067ffffffffffffffff821115610a3e57600080fd5b6020019150600581901b36038213156104e157600080fd5b604051610100810167ffffffffffffffff81118282101715610a7a57610a7a610c27565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610ac757610ac7610c27565b604052919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610b0457600080fd5b830160208101925035905067ffffffffffffffff811115610b2457600080fd5b8060051b36038313156104e157600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610b6b57600080fd5b830160208101925035905067ffffffffffffffff811115610b8b57600080fd5b8036038313156104e157600080fd5b60008219821115610bad57610bad610bc9565b500190565b600082821015610bc457610bc4610bc9565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"counter\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeToPerform\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isRecovered\",\"type\":\"bool\"}],\"name\":\"PerformingUpkeep\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"checkBurnAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"performBurnAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"eventSig\",\"type\":\"bytes32\"}],\"internalType\":\"structCheckData\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_checkDataConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"log\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"}],\"name\":\"checkLog\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"dummyMap\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isRecovered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previousPerformBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timeToPerform\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5060006002819055436001556003819055600455610d12806100336000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80637145f11b11610076578063917d895f1161005b578063917d895f1461016b578063c6066f0d14610174578063eb950ce71461017d57600080fd5b80637145f11b1461012f578063806b984f1461016257600080fd5b80634585e33b116100a75780634585e33b1461010057806361bc221a14610115578063697794731461011e57600080fd5b80632cb15864146100c357806340691db4146100df575b600080fd5b6100cc60035481565b6040519081526020015b60405180910390f35b6100f26100ed3660046106c6565b61018a565b6040516100d692919061092d565b61011361010e366004610628565b6102c2565b005b6100cc60045481565b61011361012c36600461066a565b50565b61015261013d36600461060f565b60006020819052908152604090205460ff1681565b60405190151581526020016100d6565b6100cc60015481565b6100cc60025481565b6100cc60055481565b6006546101529060ff1681565b6000606081808061019d8688018861083b565b92509250925060005a905060006101b5600143610c61565b40905060008515610224575b855a6101cd9085610c61565b1015610224578080156101ee575060008281526020819052604090205460ff165b604080516020810185905230918101919091529091506060016040516020818303038152906040528051906020012091506101c1565b8361023260c08d018d610a9d565b600281811061024357610243610ca7565b9050602002013514156102875760018b438c8c60405160200161026994939291906109aa565b604051602081830303815290604052975097505050505050506102ba565b60008b438c8c6040516020016102a094939291906109aa565b604051602081830303815290604052975097505050505050505b935093915050565b6003546102ce57436003555b4360019081556004546102e091610c49565b600455600154600255600080806102f984860186610738565b92509250925082602001514261030f9190610c61565b600555600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556060830151821461037157600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b60008060008380602001905181019061038a9190610867565b92509250925060005a905060006103a2600143610c61565b40905060008415610411575b845a6103ba9085610c61565b1015610411578080156103db575060008281526020819052604090205460ff165b604080516020810185905230918101919091529091506060016040516020818303038152906040528051906020012091506103ae565b600354600154600254600454600554600654604080519687526020870195909552938501929092526060840152608083015260ff16151560a082015232907f29eff4cb37911c3ea85db4630638cc5474fdd0631ec42215aef1d7ec96c8e63d9060c00160405180910390a25050505050505050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104ad57600080fd5b919050565b600082601f8301126104c357600080fd5b8135602067ffffffffffffffff8211156104df576104df610cd6565b8160051b6104ee828201610b2f565b83815282810190868401838801850189101561050957600080fd5b600093505b8584101561052c57803583526001939093019291840191840161050e565b50979650505050505050565b60008083601f84011261054a57600080fd5b50813567ffffffffffffffff81111561056257600080fd5b60208301915083602082850101111561057a57600080fd5b9250929050565b600082601f83011261059257600080fd5b813567ffffffffffffffff8111156105ac576105ac610cd6565b6105dd60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610b2f565b8181528460208386010111156105f257600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561062157600080fd5b5035919050565b6000806020838503121561063b57600080fd5b823567ffffffffffffffff81111561065257600080fd5b61065e85828601610538565b90969095509350505050565b60006060828403121561067c57600080fd5b6040516060810181811067ffffffffffffffff8211171561069f5761069f610cd6565b80604052508235815260208301356020820152604083013560408201528091505092915050565b6000806000604084860312156106db57600080fd5b833567ffffffffffffffff808211156106f357600080fd5b90850190610100828803121561070857600080fd5b9093506020850135908082111561071e57600080fd5b5061072b86828701610538565b9497909650939450505050565b60008060006060848603121561074d57600080fd5b833567ffffffffffffffff8082111561076557600080fd5b90850190610100828803121561077a57600080fd5b610782610b05565b82358152602083013560208201526040830135604082015260608301356060820152608083013560808201526107ba60a08401610489565b60a082015260c0830135828111156107d157600080fd5b6107dd898286016104b2565b60c08301525060e0830135828111156107f557600080fd5b61080189828601610581565b60e083015250945060208601359350604086013591508082111561082457600080fd5b5061083186828701610581565b9150509250925092565b60008060006060848603121561085057600080fd5b505081359360208301359350604090920135919050565b60008060006060848603121561087c57600080fd5b8351925060208401519150604084015190509250925092565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156108c757600080fd5b8260051b8083602087013760009401602001938452509192915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b821515815260006020604081840152835180604085015260005b8181101561096357858101830151858201606001528201610947565b81811115610975576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b606081528435606082015260208501356080820152604085013560a0820152606085013560c0820152608085013560e082015260006109eb60a08701610489565b61010073ffffffffffffffffffffffffffffffffffffffff821681850152610a1660c0890189610b7e565b925081610120860152610a2e61016086018483610895565b92505050610a3f60e0880188610be5565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa085840301610140860152610a758382846108e4565b925050508560208401528281036040840152610a928185876108e4565b979650505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610ad257600080fd5b83018035915067ffffffffffffffff821115610aed57600080fd5b6020019150600581901b360382131561057a57600080fd5b604051610100810167ffffffffffffffff81118282101715610b2957610b29610cd6565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610b7657610b76610cd6565b604052919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610bb357600080fd5b830160208101925035905067ffffffffffffffff811115610bd357600080fd5b8060051b360383131561057a57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610c1a57600080fd5b830160208101925035905067ffffffffffffffff811115610c3a57600080fd5b80360383131561057a57600080fd5b60008219821115610c5c57610c5c610c78565b500190565b600082821015610c7357610c73610c78565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", } var SimpleLogUpkeepCounterABI = SimpleLogUpkeepCounterMetaData.ABI @@ -277,6 +277,28 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) InitialBlock return _SimpleLogUpkeepCounter.Contract.InitialBlock(&_SimpleLogUpkeepCounter.CallOpts) } +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) IsRecovered(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "isRecovered") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) IsRecovered() (bool, error) { + return _SimpleLogUpkeepCounter.Contract.IsRecovered(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) IsRecovered() (bool, error) { + return _SimpleLogUpkeepCounter.Contract.IsRecovered(&_SimpleLogUpkeepCounter.CallOpts) +} + func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) LastBlock(opts *bind.CallOpts) (*big.Int, error) { var out []interface{} err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "lastBlock") @@ -434,6 +456,7 @@ type SimpleLogUpkeepCounterPerformingUpkeep struct { PreviousBlock *big.Int Counter *big.Int TimeToPerform *big.Int + IsRecovered bool Raw types.Log } @@ -510,7 +533,7 @@ func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) ParseLog(log types.Log) ( } func (SimpleLogUpkeepCounterPerformingUpkeep) Topic() common.Hash { - return common.HexToHash("0x4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd65") + return common.HexToHash("0x29eff4cb37911c3ea85db4630638cc5474fdd0631ec42215aef1d7ec96c8e63d") } func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) Address() common.Address { @@ -526,6 +549,8 @@ type SimpleLogUpkeepCounterInterface interface { InitialBlock(opts *bind.CallOpts) (*big.Int, error) + IsRecovered(opts *bind.CallOpts) (bool, error) + LastBlock(opts *bind.CallOpts) (*big.Int, error) PreviousPerformBlock(opts *bind.CallOpts) (*big.Int, error) diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 819ef8d23e1..8a61f086148 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -50,7 +50,7 @@ operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.a operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin c5e1db81070d940a82ef100b0bce38e055593cbeebbc73abf9d45c30d6020cd2 oracle_wrapper: ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 -simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin b7bbd30531eefcaf2c30546cbc5eab10c68257dbc03f0e09c0ed85febfce786b +simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin a2532ca73e227f846be39b52fa63cfa9d088116c3cfc311d972fe8db886fa915 solidity_vrf_consumer_interface: ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.bin ecc99378aa798014de9db42b2eb81320778b0663dbe208008dad75ccdc1d4366 solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f solidity_vrf_coordinator_interface: ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.bin a23d3c395156804788c7f6fbda2994e8f7184304c0f0c9f2c4ddeaf073d346d2 diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index 8c27578c613..ab18fee19fe 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -198,8 +198,18 @@ func TestLogTrigger(t *testing.T) { Bytes("Load Config", loadConfigBytes). Msg("Test Config") - testConfig := fmt.Sprintf("Number of Nodes: %d\nDuration: %d\nBlock Time: %d\n"+ - "Spec Type: %s\nLog Level: %s\nImage: %s\nTag: %s\n\nLoad Config: \n%s", numberofNodes, duration, + testConfigFormat := `Number of Nodes: %d +Duration: %d +Block Time: %d +Spec Type: %s +Log Level: %s +Image: %s +Tag: %s + +Load Config: +%s` + + testConfig := fmt.Sprintf(testConfigFormat, numberofNodes, duration, blockTime, specType, logLevel, os.Getenv(config.EnvVarCLImage), os.Getenv(config.EnvVarCLTag), string(loadConfigBytes)) l.Info().Str("testConfig", testConfig).Msg("Test Config") @@ -537,7 +547,8 @@ func TestLogTrigger(t *testing.T) { require.NoError(t, err, "Error getting latest block number") l.Info().Uint64("Starting Block", startBlock).Uint64("Ending Block", endBlock).Msg("Test Block Range") - upkeepDelays := make([][]int64, 0) + upkeepDelaysFast := make([][]int64, 0) + upkeepDelaysRecovery := make([][]int64, 0) var numberOfEventsEmitted int var batchSize uint64 = 500 @@ -590,7 +601,8 @@ func TestLogTrigger(t *testing.T) { } if len(logs) > 0 { - delay := make([]int64, 0) + delayFast := make([]int64, 0) + delayRecovery := make([]int64, 0) for _, log := range logs { eventDetails, err := consumerABI.EventByID(log.Topics[0]) require.NoError(t, err, "Error getting event details") @@ -601,41 +613,82 @@ func TestLogTrigger(t *testing.T) { if eventDetails.Name == "PerformingUpkeep" { parsedLog, err := consumer.ParsePerformingUpkeep(log) require.NoError(t, err, "Error parsing log") - delay = append(delay, parsedLog.TimeToPerform.Int64()) + if parsedLog.IsRecovered { + delayRecovery = append(delayRecovery, parsedLog.TimeToPerform.Int64()) + } else { + delayFast = append(delayFast, parsedLog.TimeToPerform.Int64()) + } } } - upkeepDelays = append(upkeepDelays, delay) + upkeepDelaysFast = append(upkeepDelaysFast, delayFast) + upkeepDelaysRecovery = append(upkeepDelaysRecovery, delayRecovery) } } - l.Info().Interface("Upkeep Delays", upkeepDelays).Msg("Upkeep Delays") + l.Info(). + Interface("Upkeep Delays Fast", upkeepDelaysFast). + Interface("Upkeep Delays Recovered", upkeepDelaysRecovery). + Msg("Upkeep Delays") var allUpkeepDelays []int64 + var allUpkeepDelaysFast []int64 + var allUpkeepDelaysRecovery []int64 + + for _, upkeepDelay := range upkeepDelaysFast { + allUpkeepDelays = append(allUpkeepDelays, upkeepDelay...) + allUpkeepDelaysFast = append(allUpkeepDelaysFast, upkeepDelay...) + } - for _, upkeepDelay := range upkeepDelays { + for _, upkeepDelay := range upkeepDelaysRecovery { allUpkeepDelays = append(allUpkeepDelays, upkeepDelay...) + allUpkeepDelaysRecovery = append(allUpkeepDelaysRecovery, upkeepDelay...) } - avg, median, ninetyPct, ninetyNinePct, maximum := testreporters.IntListStats(allUpkeepDelays) + avgF, medianF, ninetyPctF, ninetyNinePctF, maximumF := testreporters.IntListStats(allUpkeepDelaysFast) + avgR, medianR, ninetyPctR, ninetyNinePctR, maximumR := testreporters.IntListStats(allUpkeepDelaysRecovery) eventsMissed := numberOfEventsEmitted - len(allUpkeepDelays) percentMissed := float64(eventsMissed) / float64(numberOfEventsEmitted) * 100 l.Info(). - Float64("Average", avg).Int64("Median", median). - Int64("90th Percentile", ninetyPct).Int64("99th Percentile", ninetyNinePct). - Int64("Max", maximum).Msg("Upkeep Delays in seconds") - + Float64("Average", avgF).Int64("Median", medianF). + Int64("90th Percentile", ninetyPctF).Int64("99th Percentile", ninetyNinePctF). + Int64("Max", maximumF).Msg("Upkeep Delays Fast Execution in seconds") + l.Info(). + Float64("Average", avgR).Int64("Median", medianR). + Int64("90th Percentile", ninetyPctR).Int64("99th Percentile", ninetyNinePctR). + Int64("Max", maximumR).Msg("Upkeep Delays Recovery Execution in seconds") l.Info(). Int("Total Perform Count", len(allUpkeepDelays)). + Int("Perform Count Fast Execution", len(allUpkeepDelaysFast)). + Int("Perform Count Recovery Execution", len(allUpkeepDelaysRecovery)). Int("Total Events Emitted", numberOfEventsEmitted). Int("Total Events Missed", eventsMissed). Float64("Percent Missed", percentMissed). Msg("Test completed") - testReport := fmt.Sprintf("Upkeep Delays in seconds\nAverage: %f\nMedian: %d\n90th Percentile: %d\n"+ - "99th Percentile: %d\nMax: %d\nTotal Perform Count: %d\n\nTotal Events Emitted: %d\nTotal Events Missed: %d\n"+ - "Percent Missed: %f\nTest Duration: %s\n", - avg, median, ninetyPct, ninetyNinePct, maximum, len(allUpkeepDelays), numberOfEventsEmitted, - eventsMissed, percentMissed, testDuration.String()) + testReportFormat := `Upkeep Delays in seconds - Fast Execution +Average: %f +Median: %d +90th Percentile: %d +99th Percentile: %d +Max: %d + +Upkeep Delays in seconds - Recovery Execution +Average: %f +Median: %d +90th Percentile: %d +99th Percentile: %d + +Total Perform Count: %d +Perform Count Fast Execution: %d +Perform Count Recovery Execution: %d +Total Log Triggering Events Emitted: %d +Total Events Missed: %d +Percent Missed: %f +Test Duration: %s` + + testReport := fmt.Sprintf(testReportFormat, avgF, medianF, ninetyPctF, ninetyNinePctF, maximumF, + avgR, medianR, ninetyPctR, ninetyNinePctR, len(allUpkeepDelays), len(allUpkeepDelaysFast), + len(allUpkeepDelaysRecovery), numberOfEventsEmitted, eventsMissed, percentMissed, testDuration.String()) _, err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(time.Now().UnixMilli(), 10), From 8795647d2bf151de07beeeea73dcb2554ead8ece Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Wed, 20 Dec 2023 14:40:32 +0200 Subject: [PATCH 090/234] Move core eth utils to evm (#11584) * Move core eth utils to evm * Fixes * Fix import mismatch * Minor fixes * Fix scripts * Fix dependencies --- common/txmgr/confirmer.go | 4 +- core/cbor/cbor_test.go | 4 +- core/chains/evm/client/client.go | 2 +- core/chains/evm/client/client_test.go | 2 +- core/chains/evm/client/rpc_client.go | 2 +- .../evm/gas/block_history_estimator_test.go | 2 +- .../evm/headtracker/head_broadcaster_test.go | 2 +- .../evm/headtracker/head_tracker_test.go | 2 +- core/chains/evm/headtracker/heads_test.go | 2 +- core/chains/evm/log/broadcaster.go | 3 +- core/chains/evm/log/eth_subscriber.go | 2 +- core/chains/evm/log/integration_test.go | 2 +- core/chains/evm/log/registrations_test.go | 2 +- .../evm/logpoller/log_poller_internal_test.go | 2 +- core/chains/evm/logpoller/log_poller_test.go | 2 +- .../evm/logpoller/observability_test.go | 2 +- core/chains/evm/logpoller/orm_test.go | 2 +- core/chains/evm/logpoller/query_test.go | 2 +- core/chains/evm/txmgr/broadcaster_test.go | 2 +- core/chains/evm/txmgr/confirmer_test.go | 2 +- core/chains/evm/txmgr/evm_tx_store_test.go | 2 +- core/chains/evm/txmgr/txmgr_test.go | 2 +- core/chains/evm/types/models.go | 5 +- core/chains/evm/types/models_test.go | 23 +- core/chains/evm/types/types.go | 2 +- core/chains/evm/utils/big/big.go | 5 +- core/{ => chains/evm}/utils/ethabi.go | 6 +- core/{ => chains/evm}/utils/ethabi_test.go | 4 +- core/chains/evm/utils/utils.go | 258 ++++++++++++++++ core/chains/evm/utils/utils_test.go | 231 ++++++++++++++ core/cmd/evm_transaction_commands.go | 2 +- core/cmd/forwarders_commands_test.go | 2 +- core/gethwrappers/abigen.go | 2 +- core/internal/cltest/cltest.go | 19 +- core/internal/cltest/factories.go | 15 +- core/internal/features/features_test.go | 13 +- core/scripts/chaincli/handler/debug.go | 4 +- .../scripts/chaincli/handler/keeper_launch.go | 3 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/scripts/ocr2vrf/util.go | 2 +- core/scripts/vrfv1/main.go | 3 +- core/scripts/vrfv2/genvrfnum/main.go | 3 +- core/scripts/vrfv2/testnet/main.go | 3 +- core/scripts/vrfv2/testnet/v2scripts/util.go | 2 +- core/scripts/vrfv2plus/testnet/main.go | 3 +- .../vrfv2plus/testnet/v2plusscripts/util.go | 2 +- core/services/blockhashstore/validate.go | 2 +- core/services/chainlink/application.go | 11 +- core/services/chainlink/config_test.go | 24 +- core/services/fluxmonitorv2/flags.go | 2 +- core/services/fluxmonitorv2/flags_test.go | 2 +- core/services/fluxmonitorv2/flux_monitor.go | 5 +- .../fluxmonitorv2/integrations_test.go | 11 +- .../functions/external_adapter_client.go | 6 +- core/services/gateway/api/message.go | 3 +- core/services/gateway/connector/connector.go | 3 +- .../handlers/functions/user_subscriptions.go | 2 +- .../functions/user_subscriptions_test.go | 2 +- core/services/job/models.go | 2 +- core/services/keeper/models.go | 2 +- core/services/keeper/orm_test.go | 35 +-- .../keeper/registry_synchronizer_sync.go | 2 +- core/services/keeper/upkeep_executer_test.go | 2 +- core/services/keystore/eth_test.go | 2 +- core/services/keystore/keys/vrfkey/crypto.go | 13 +- core/services/keystore/keys/vrfkey/key_v2.go | 2 +- core/services/keystore/keys/vrfkey/proof.go | 2 +- core/services/ocr/config_overrider_test.go | 3 +- core/services/ocr/database_test.go | 2 +- core/services/ocr/flags.go | 2 +- core/services/ocr/flags_test.go | 2 +- .../v1/internal/testutils.go | 2 +- .../ocr2/plugins/mercury/helpers_test.go | 2 +- .../internal/ocr2vrf_integration_test.go | 2 +- .../arbitrum_block_translator_test.go | 2 +- core/services/ocrcommon/telemetry_test.go | 2 +- core/services/pipeline/orm_test.go | 4 +- core/services/pipeline/task.estimategas.go | 2 +- .../pipeline/task.eth_abi_decode_log_test.go | 2 +- .../pipeline/task.eth_abi_decode_test.go | 2 +- core/services/pipeline/task.eth_call.go | 2 +- core/services/pipeline/task.eth_tx.go | 6 +- core/services/pipeline/task.hexdecode.go | 6 +- core/services/pipeline/task_params.go | 7 +- core/services/relay/evm/config_poller_test.go | 9 +- .../relay/evm/contract_transmitter.go | 2 +- .../relay/evm/functions/config_poller_test.go | 9 +- .../evm/functions/contract_transmitter.go | 2 +- .../relay/evm/mercury/config_poller_test.go | 11 +- .../relay/evm/mercury/helpers_test.go | 2 +- .../relay/evm/mercury/v1/data_source_test.go | 2 +- .../v1/reportcodec/report_codec_test.go | 2 +- .../relay/evm/mercury/wsrpc/pool_test.go | 2 +- core/services/relay/evm/types/types_test.go | 2 +- .../signatures/secp256k1/public_key.go | 2 +- core/services/vrf/delegate_test.go | 53 ++-- core/services/vrf/extraargs/types.go | 2 +- core/services/vrf/proof/proof_response.go | 2 +- core/services/vrf/proof/seed.go | 2 +- core/services/vrf/proof/solidity_proof.go | 2 +- .../vrf_coordinator_interface.go | 2 +- ...rf_coordinator_solidity_crosscheck_test.go | 2 +- .../vrf_solidity_crosscheck_test.go | 2 +- core/services/vrf/v1/listener_v1_test.go | 2 +- .../vrf/v2/integration_helpers_test.go | 5 +- .../vrf/v2/integration_v2_plus_test.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 28 +- .../vrf/v2/listener_v2_log_processor.go | 9 +- core/testdata/testspecs/v2_specs.go | 2 +- core/utils/hash_helpers.go | 24 -- core/utils/hash_helpers_test.go | 27 -- core/utils/utils.go | 281 +----------------- core/utils/utils_test.go | 246 --------------- core/web/evm_forwarders_controller_test.go | 2 +- core/web/evm_transfer_controller.go | 2 +- core/web/loader/loader_test.go | 2 +- go.mod | 2 +- go.sum | 4 +- .../actions/automationv2/actions.go | 2 +- .../actions/ocr2vrf_actions/ocr2vrf_steps.go | 2 +- .../actions/vrfv2_actions/vrfv2_steps.go | 2 +- .../actions/vrfv2plus/vrfv2plus_steps.go | 2 +- .../contracts/ethereum_keeper_contracts.go | 2 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/load/functions/setup.go | 2 +- 127 files changed, 802 insertions(+), 818 deletions(-) rename core/{ => chains/evm}/utils/ethabi.go (97%) rename core/{ => chains/evm}/utils/ethabi_test.go (99%) create mode 100644 core/chains/evm/utils/utils_test.go delete mode 100644 core/utils/hash_helpers.go delete mode 100644 core/utils/hash_helpers_test.go diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 112c19edf63..f10481fef56 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/chains/label" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/client" @@ -869,7 +869,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han lggr.Criticalw("Invariant violation: fatal error while re-attempting transaction", "fee", attempt.TxFee, "feeLimit", etx.FeeLimit, - "signedRawTx", utils.EnsureHexPrefix(hex.EncodeToString(attempt.SignedRawTx)), + "signedRawTx", commonhex.EnsurePrefix(hex.EncodeToString(attempt.SignedRawTx)), "blockHeight", blockHeight, ) ec.SvcErrBuffer.Append(sendError) diff --git a/core/cbor/cbor_test.go b/core/cbor/cbor_test.go index 39a2da479b7..cf1390cc908 100644 --- a/core/cbor/cbor_test.go +++ b/core/cbor/cbor_test.go @@ -10,14 +10,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_ParseCBOR(t *testing.T) { t.Parallel() - address, err := utils.TryParseHex("0x8bd112d3f8f92e41c861939545ad387307af9703") + address, err := hex.DecodeString("0x8bd112d3f8f92e41c861939545ad387307af9703") require.NoError(t, err) tests := []struct { diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index bddfdb7e7bf..61635c59c6b 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -14,8 +14,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/config" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" diff --git a/core/chains/evm/client/client_test.go b/core/chains/evm/client/client_test.go index bc3ae958461..281b8bf4227 100644 --- a/core/chains/evm/client/client_test.go +++ b/core/chains/evm/client/client_test.go @@ -26,9 +26,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func mustNewClient(t *testing.T, wsURL string, sendonlys ...url.URL) client.Client { diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 627a2833109..ce3a67162ed 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -24,8 +24,8 @@ import ( commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // RPCCLient includes all the necessary generalized RPC methods along with any additional chain-specific methods. diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index 43ea3e0bb79..eae6fbc2ad3 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -25,11 +25,11 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func NewEvmHash() common.Hash { diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index d2dc9863268..eb4daa6fde4 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -26,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func waitHeadBroadcasterToStart(t *testing.T, hb types.HeadBroadcaster) { diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index fd7db65ae32..38cbe1fbfe4 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -38,7 +39,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func firstHead(t *testing.T, db *sqlx.DB) (h evmtypes.Head) { diff --git a/core/chains/evm/headtracker/heads_test.go b/core/chains/evm/headtracker/heads_test.go index 11c1bfd4f7a..9fa5ed4e548 100644 --- a/core/chains/evm/headtracker/heads_test.go +++ b/core/chains/evm/headtracker/heads_test.go @@ -10,9 +10,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestHeads_LatestHead(t *testing.T) { diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index 393d1c1b266..524a0f03965 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -19,6 +19,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -394,7 +395,7 @@ func (b *broadcaster) reinitialize() (backfillStart *int64, abort bool) { ctx, cancel := b.chStop.NewCtx() defer cancel() - utils.RetryWithBackoff(ctx, func() bool { + evmutils.RetryWithBackoff(ctx, func() bool { var err error backfillStart, err = b.orm.Reinitialize(pg.WithParentCtx(ctx)) if err != nil { diff --git a/core/chains/evm/log/eth_subscriber.go b/core/chains/evm/log/eth_subscriber.go index 96a7a8248a6..2f9cbb07cb3 100644 --- a/core/chains/evm/log/eth_subscriber.go +++ b/core/chains/evm/log/eth_subscriber.go @@ -14,8 +14,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/null" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ( diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index e74d06457dd..02a30c6d93f 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -24,13 +24,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestBroadcaster_AwaitsInitialSubscribersOnStartup(t *testing.T) { diff --git a/core/chains/evm/log/registrations_test.go b/core/chains/evm/log/registrations_test.go index 0682564fe72..2be01dca2bf 100644 --- a/core/chains/evm/log/registrations_test.go +++ b/core/chains/evm/log/registrations_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ Listener = testListener{} diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 09e1deb864f..863ab0fddea 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -26,11 +26,11 @@ import ( evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index ed2d617065b..f50dccf1ad2 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -38,7 +39,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func logRuntime(t testing.TB, start time.Time) { diff --git a/core/chains/evm/logpoller/observability_test.go b/core/chains/evm/logpoller/observability_test.go index 749934dc4b5..eb81273af2c 100644 --- a/core/chains/evm/logpoller/observability_test.go +++ b/core/chains/evm/logpoller/observability_test.go @@ -16,11 +16,11 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestMultipleMetricsArePublished(t *testing.T) { diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 3c54b3cf654..b8468d28e98 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -18,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type block struct { diff --git a/core/chains/evm/logpoller/query_test.go b/core/chains/evm/logpoller/query_test.go index bc2b20c85c2..70ace713228 100644 --- a/core/chains/evm/logpoller/query_test.go +++ b/core/chains/evm/logpoller/query_test.go @@ -8,8 +8,8 @@ import ( "github.com/lib/pq" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_QueryArgs(t *testing.T) { diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 68a81299bf9..f12796beadd 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -37,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -47,7 +48,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // NewEthBroadcaster creates a new txmgr.EthBroadcaster for use in testing. diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index f1063ba0a79..8fa791a141e 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -33,6 +33,7 @@ import ( gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -41,7 +42,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func newTestChainScopedConfig(t *testing.T) evmconfig.ChainScopedConfig { diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 6c19026c1da..1a1ee1bcd3a 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -23,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/google/uuid" "github.com/stretchr/testify/assert" diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 13b81bcef3c..24c7e750c95 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -33,6 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -42,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func makeTestEvmTxm( diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index b7c1e5b56d5..93b300aa532 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -16,13 +16,14 @@ import ( "github.com/pkg/errors" "github.com/ugorji/go/codec" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" 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/types/internal/blocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/null" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Head represents a BlockNumber, BlockHash. @@ -500,7 +501,7 @@ func (f *FunctionSelector) SetBytes(b []byte) { copy(f[:], b[:FunctionSelectorLe var hexRegexp = regexp.MustCompile("^[0-9a-fA-F]*$") func unmarshalFromString(s string, f *FunctionSelector) error { - if utils.HasHexPrefix(s) { + if hex.HasPrefix(s) { if !hexRegexp.Match([]byte(s)[2:]) { return fmt.Errorf("function selector %s must be 0x-hex encoded", s) } diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index 6d367b44a75..a32deba697d 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -16,15 +16,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/null" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestHead_NewHead(t *testing.T) { @@ -719,10 +720,10 @@ func TestTransaction_UnmarshalJSON(t *testing.T) { ), }, want: &evmtypes.Transaction{ - GasPrice: assets.NewWei(utils.HexToBig("978a846d2")), + GasPrice: assets.NewWei(mustHexToBig(t, "978a846d2")), GasLimit: mustHextoUint32(t, "0xdbba0"), - MaxFeePerGas: assets.NewWei(utils.HexToBig("d0892241d")), - MaxPriorityFeePerGas: assets.NewWei(utils.HexToBig("3b9aca01")), + MaxFeePerGas: assets.NewWei(mustHexToBig(t, "d0892241d")), + MaxPriorityFeePerGas: assets.NewWei(mustHexToBig(t, "3b9aca01")), Type: 0x2, Hash: common.HexToHash("0x754f49f0a2ca7680806d261dd36ee95ac88a81da59fef0b5d8d691478f075d46"), }, @@ -754,7 +755,7 @@ func TestTransaction_UnmarshalJSON(t *testing.T) { }`, )}, want: &evmtypes.Transaction{ - GasPrice: assets.NewWei(utils.HexToBig("4f7915f5")), + GasPrice: assets.NewWei(mustHexToBig(t, "4f7915f5")), GasLimit: mustHextoUint32(t, "0x2dc6c0"), MaxFeePerGas: nil, MaxPriorityFeePerGas: nil, @@ -777,10 +778,10 @@ func TestTransaction_UnmarshalJSON(t *testing.T) { func TestTransaction_JSONRoundtrip(t *testing.T) { t.Parallel() want := &evmtypes.Transaction{ - GasPrice: assets.NewWei(utils.HexToBig("978a846d2")), + GasPrice: assets.NewWei(mustHexToBig(t, "978a846d2")), GasLimit: mustHextoUint32(t, "0xdbba0"), - MaxFeePerGas: assets.NewWei(utils.HexToBig("d0892241d")), - MaxPriorityFeePerGas: assets.NewWei(utils.HexToBig("3b9aca01")), + MaxFeePerGas: assets.NewWei(mustHexToBig(t, "d0892241d")), + MaxPriorityFeePerGas: assets.NewWei(mustHexToBig(t, "3b9aca01")), Type: evmtypes.TxType(2), Hash: common.HexToHash("0x754f49f0a2ca7680806d261dd36ee95ac88a81da59fef0b5d8d691478f075d46"), } @@ -854,3 +855,9 @@ func mustHextoUint32(t *testing.T, hx string) uint32 { require.NoError(t, err) return uint32(*temp) } + +func mustHexToBig(t *testing.T, hx string) *big.Int { + n, err := hex.ParseBig(hx) + require.NoError(t, err) + return n +} diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index ec1134de2b4..987fd987d3f 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -13,8 +13,8 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Configs interface { diff --git a/core/chains/evm/utils/big/big.go b/core/chains/evm/utils/big/big.go index f0ff475a7e7..4bb51e27323 100644 --- a/core/chains/evm/utils/big/big.go +++ b/core/chains/evm/utils/big/big.go @@ -9,8 +9,9 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" + "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const base10 = 10 @@ -84,7 +85,7 @@ func (b Big) MarshalJSON() ([]byte, error) { func (b *Big) UnmarshalText(input []byte) error { input = bytes.TrimQuotes(input) str := string(input) - if utils.HasHexPrefix(str) { + if hex.HasPrefix(str) { decoded, err := hexutil.DecodeBig(str) if err != nil { return err diff --git a/core/utils/ethabi.go b/core/chains/evm/utils/ethabi.go similarity index 97% rename from core/utils/ethabi.go rename to core/chains/evm/utils/ethabi.go index 76c2f186e83..61d365933d2 100644 --- a/core/utils/ethabi.go +++ b/core/chains/evm/utils/ethabi.go @@ -12,6 +12,8 @@ import ( "github.com/pkg/errors" "github.com/shopspring/decimal" "github.com/tidwall/gjson" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" ) const ( @@ -115,8 +117,8 @@ func parseDecimalString(input string) (*big.Int, error) { } func parseNumericString(input string) (*big.Int, error) { - if HasHexPrefix(input) { - output, ok := big.NewInt(0).SetString(RemoveHexPrefix(input), 16) + if hex.HasPrefix(input) { + output, ok := big.NewInt(0).SetString(hex.TrimPrefix(input), 16) if !ok { return nil, fmt.Errorf("error parsing hex %s", input) } diff --git a/core/utils/ethabi_test.go b/core/chains/evm/utils/ethabi_test.go similarity index 99% rename from core/utils/ethabi_test.go rename to core/chains/evm/utils/ethabi_test.go index 4cecab48e4c..b99d906eae7 100644 --- a/core/utils/ethabi_test.go +++ b/core/chains/evm/utils/ethabi_test.go @@ -12,6 +12,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" ) func pow2(arg int64) *big.Int { @@ -590,7 +592,7 @@ func EVMTranscodeJSONWithFormat(value gjson.Result, format string) ([]byte, erro case FormatBytes: return EVMTranscodeBytes(value) case FormatPreformatted: - return hex.DecodeString(RemoveHexPrefix(value.Str)) + return hex.DecodeString(commonhex.TrimPrefix(value.Str)) case FormatUint256: data, err := EVMTranscodeUint256(value) if err != nil { diff --git a/core/chains/evm/utils/utils.go b/core/chains/evm/utils/utils.go index abcea77124a..3806628a805 100644 --- a/core/chains/evm/utils/utils.go +++ b/core/chains/evm/utils/utils.go @@ -1,13 +1,97 @@ package utils import ( + "context" + "crypto/rand" + "fmt" + "math/big" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/jpillora/backoff" "golang.org/x/crypto/sha3" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" ) +// EVMWordByteLen the length of an EVM Word Byte +const EVMWordByteLen = 32 + +// ZeroAddress is an address of all zeroes, otherwise in Ethereum as +// 0x0000000000000000000000000000000000000000 +var ZeroAddress = common.Address{} + +// EmptyHash is a hash of all zeroes, otherwise in Ethereum as +// 0x0000000000000000000000000000000000000000000000000000000000000000 +var EmptyHash = common.Hash{} + +func RandomAddress() common.Address { + b := make([]byte, 20) + _, _ = rand.Read(b) // Assignment for errcheck. Only used in tests so we can ignore. + return common.BytesToAddress(b) +} + +// IsEmptyAddress checks that the address is empty, synonymous with the zero +// account/address. No logs can come from this address, as there is no contract +// present there. +// +// See https://stackoverflow.com/questions/48219716/what-is-address0-in-solidity +// for the more info on the zero address. +func IsEmptyAddress(addr common.Address) bool { + return addr == ZeroAddress +} + +func RandomBytes32() (r [32]byte) { + b := make([]byte, 32) + _, _ = rand.Read(b[:]) // Assignment for errcheck. Only used in tests so we can ignore. + copy(r[:], b) + return +} + +func Bytes32ToSlice(a [32]byte) (r []byte) { + r = append(r, a[:]...) + return +} + +// Uint256ToBytes is x represented as the bytes of a uint256 +func Uint256ToBytes(x *big.Int) (uint256 []byte, err error) { + if x.Cmp(MaxUint256) > 0 { + return nil, fmt.Errorf("too large to convert to uint256") + } + uint256 = common.LeftPadBytes(x.Bytes(), EVMWordByteLen) + if x.Cmp(big.NewInt(0).SetBytes(uint256)) != 0 { + panic("failed to round-trip uint256 back to source big.Int") + } + return uint256, err +} + +// NewHash return random Keccak256 +func NewHash() common.Hash { + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + return common.BytesToHash(b) +} + +// PadByteToHash returns a hash with zeros padded on the left of the given byte. +func PadByteToHash(b byte) common.Hash { + var h [32]byte + h[31] = b + return h +} + +// Uint256ToBytes32 returns the bytes32 encoding of the big int provided +func Uint256ToBytes32(n *big.Int) []byte { + if n.BitLen() > 256 { + panic("vrf.uint256ToBytes32: too big to marshal to uint256") + } + return common.LeftPadBytes(n.Bytes(), 32) +} + // MustHash returns the keccak256 hash, or panics on failure. func MustHash(in string) common.Hash { out, err := Keccak256([]byte(in)) @@ -17,6 +101,30 @@ func MustHash(in string) common.Hash { return common.BytesToHash(out) } +// HexToUint256 returns the uint256 represented by s, or an error if it doesn't +// represent one. +func HexToUint256(s string) (*big.Int, error) { + rawNum, err := hexutil.Decode(s) + if err != nil { + return nil, fmt.Errorf("error while parsing %s as hex: %w", s, err) + } + rv := big.NewInt(0).SetBytes(rawNum) // can't be negative number + if err := CheckUint256(rv); err != nil { + return nil, err + } + return rv, nil +} + +var zero = big.NewInt(0) + +// CheckUint256 returns an error if n is out of bounds for a uint256 +func CheckUint256(n *big.Int) error { + if n.Cmp(zero) < 0 || n.Cmp(MaxUint256) >= 0 { + return fmt.Errorf("number out of range for uint256") + } + return nil +} + // Keccak256 is a simplified interface for the legacy SHA3 implementation that // Ethereum uses. func Keccak256(in []byte) ([]byte, error) { @@ -25,6 +133,42 @@ func Keccak256(in []byte) ([]byte, error) { return hash.Sum(nil), err } +func Keccak256Fixed(in []byte) [32]byte { + hash := sha3.NewLegacyKeccak256() + // Note this Keccak256 cannot error https://github.com/golang/crypto/blob/master/sha3/sha3.go#L126 + // if we start supporting hashing algos which do, we can change this API to include an error. + hash.Write(in) + var h [32]byte + copy(h[:], hash.Sum(nil)) + return h +} + +// EIP55CapitalizedAddress returns true iff possibleAddressString has the correct +// capitalization for an Ethereum address, per EIP 55 +func EIP55CapitalizedAddress(possibleAddressString string) bool { + possibleAddressString = hex.EnsurePrefix(possibleAddressString) + EIP55Capitalized := common.HexToAddress(possibleAddressString).Hex() + return possibleAddressString == EIP55Capitalized +} + +// ParseEthereumAddress returns addressString as a go-ethereum Address, or an +// error if it's invalid, e.g. if EIP 55 capitalization check fails +func ParseEthereumAddress(addressString string) (common.Address, error) { + if !common.IsHexAddress(addressString) { + return common.Address{}, fmt.Errorf( + "not a valid Ethereum address: %s", addressString) + } + address := common.HexToAddress(addressString) + if !EIP55CapitalizedAddress(addressString) { + return common.Address{}, fmt.Errorf( + "%s treated as Ethereum address, but it has an invalid capitalization! "+ + "The correctly-capitalized address would be %s, but "+ + "check carefully before copying and pasting! ", + addressString, address.Hex()) + } + return address, nil +} + // NewRedialBackoff is a standard backoff to use for redialling or reconnecting to // unreachable network endpoints func NewRedialBackoff() backoff.Backoff { @@ -35,3 +179,117 @@ func NewRedialBackoff() backoff.Backoff { } } + +// Sleeper interface is used for tasks that need to be done on some +// interval, excluding Cron, like reconnecting. +type Sleeper interface { + Reset() + Sleep() + After() time.Duration + Duration() time.Duration +} + +// BackoffSleeper is a sleeper that backs off on subsequent attempts. +type BackoffSleeper struct { + backoff.Backoff + beenRun atomic.Bool +} + +// NewBackoffSleeper returns a BackoffSleeper that is configured to +// sleep for 0 seconds initially, then backs off from 1 second minimum +// to 10 seconds maximum. +func NewBackoffSleeper() *BackoffSleeper { + return &BackoffSleeper{ + Backoff: backoff.Backoff{ + Min: 1 * time.Second, + Max: 10 * time.Second, + }, + } +} + +// Sleep waits for the given duration, incrementing the back off. +func (bs *BackoffSleeper) Sleep() { + if bs.beenRun.CompareAndSwap(false, true) { + return + } + time.Sleep(bs.Backoff.Duration()) +} + +// After returns the duration for the next stop, and increments the backoff. +func (bs *BackoffSleeper) After() time.Duration { + if bs.beenRun.CompareAndSwap(false, true) { + return 0 + } + return bs.Backoff.Duration() +} + +// Duration returns the current duration value. +func (bs *BackoffSleeper) Duration() time.Duration { + if !bs.beenRun.Load() { + return 0 + } + return bs.ForAttempt(bs.Attempt()) +} + +// Reset resets the backoff intervals. +func (bs *BackoffSleeper) Reset() { + bs.beenRun.Store(false) + bs.Backoff.Reset() +} + +// RetryWithBackoff retries the sleeper and backs off if not Done +func RetryWithBackoff(ctx context.Context, fn func() (retry bool)) { + sleeper := NewBackoffSleeper() + sleeper.Reset() + for { + retry := fn() + if !retry { + return + } + + select { + case <-ctx.Done(): + return + case <-time.After(sleeper.After()): + continue + } + } +} + +// AllEqual returns true iff all the provided elements are equal to each other. +func AllEqual[T comparable](elems ...T) bool { + for i := 1; i < len(elems); i++ { + if elems[i] != elems[0] { + return false + } + } + return true +} + +// JustError takes a tuple and returns the last entry, the error. +func JustError(_ interface{}, err error) error { + return err +} + +// WrapIfError decorates an error with the given message. It is intended to +// be used with `defer` statements, like so: +// +// func SomeFunction() (err error) { +// defer WrapIfError(&err, "error in SomeFunction:") +// +// ... +// } +func WrapIfError(err *error, msg string) { + if *err != nil { + *err = fmt.Errorf(msg, *err) + } +} + +// RandUint256 generates a random bigNum up to 2 ** 256 - 1 +func RandUint256() *big.Int { + n, err := rand.Int(rand.Reader, MaxUint256) + if err != nil { + panic(err) + } + return n +} diff --git a/core/chains/evm/utils/utils_test.go b/core/chains/evm/utils/utils_test.go new file mode 100644 index 00000000000..9e8b44864ad --- /dev/null +++ b/core/chains/evm/utils/utils_test.go @@ -0,0 +1,231 @@ +package utils_test + +import ( + "context" + "math/big" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" + "go.uber.org/multierr" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" +) + +func TestKeccak256(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input string + want string + }{ + {"basic", "0xf00b", "0x2433bb36d5f9b14e4fea87c2d32d79abfe34e56808b891e471f4400fca2a336c"}, + {"long input", "0xf00b2433bb36d5f9b14e4fea87c2d32d79abfe34e56808b891e471f4400fca2a336c", "0x6b917c56ad7bea7d09132b9e1e29bb5d9aa7d32d067c638dfa886bbbf6874cdf"}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + input, err := hexutil.Decode(test.input) + assert.NoError(t, err) + result, err := utils.Keccak256(input) + assert.NoError(t, err) + + assert.Equal(t, test.want, hexutil.Encode(result)) + }) + } +} + +func TestUtils_IsEmptyAddress(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + addr common.Address + want bool + }{ + {"zero address", common.Address{}, true}, + {"non-zero address", testutils.NewAddress(), false}, + } + + for _, test := range tests { + test := test + + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + actual := utils.IsEmptyAddress(test.addr) + assert.Equal(t, test.want, actual) + }) + } +} + +// From https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md#test-cases +var testAddresses = []string{ + "0x52908400098527886E0F7030069857D2E4169EE7", + "0x8617E340B3D01FA5F11F306F4090FD50E238070D", + "0xde709f2102306220921060314715629080e2fb77", + "0x27b1fdb04752bbc536007a920d24acb045561c26", + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", +} + +func TestClient_EIP55CapitalizedAddress(t *testing.T) { + t.Parallel() + + valid := utils.EIP55CapitalizedAddress + for _, address := range testAddresses { + assert.True(t, valid(address)) + assert.False(t, valid(strings.ToLower(address)) && + valid(strings.ToUpper(address))) + } +} + +func TestClient_ParseEthereumAddress(t *testing.T) { + t.Parallel() + + parse := utils.ParseEthereumAddress + for _, address := range testAddresses { + a1, err := parse(address) + assert.NoError(t, err) + no0xPrefix := address[2:] + a2, err := parse(no0xPrefix) + assert.NoError(t, err) + assert.True(t, a1 == a2) + _, lowerErr := parse(strings.ToLower(address)) + _, upperErr := parse(strings.ToUpper(address)) + shouldBeError := multierr.Combine(lowerErr, upperErr) + assert.Error(t, shouldBeError) + assert.True(t, strings.Contains(shouldBeError.Error(), no0xPrefix)) + } + _, notHexErr := parse("0xCeci n'est pas une chaîne hexadécimale") + assert.Error(t, notHexErr) + _, tooLongErr := parse("0x0123456789abcdef0123456789abcdef0123456789abcdef") + assert.Error(t, tooLongErr) +} + +func TestUint256ToBytes(t *testing.T) { + t.Parallel() + + v := big.NewInt(0).Sub(utils.MaxUint256, big.NewInt(1)) + uint256, err := utils.Uint256ToBytes(v) + assert.NoError(t, err) + + b32 := utils.Uint256ToBytes32(v) + assert.Equal(t, uint256, b32) + + large := big.NewInt(0).Add(utils.MaxUint256, big.NewInt(1)) + _, err = utils.Uint256ToBytes(large) + assert.Error(t, err, "too large to convert to uint256") + + negative := big.NewInt(-1) + assert.Panics(t, func() { + _, _ = utils.Uint256ToBytes(negative) + }, "failed to round-trip uint256 back to source big.Int") +} + +func TestCheckUint256(t *testing.T) { + t.Parallel() + + large := big.NewInt(0).Add(utils.MaxUint256, big.NewInt(1)) + err := utils.CheckUint256(large) + assert.Error(t, err, "number out of range for uint256") + + negative := big.NewInt(-123) + err = utils.CheckUint256(negative) + assert.Error(t, err, "number out of range for uint256") + + err = utils.CheckUint256(big.NewInt(123)) + assert.NoError(t, err) +} + +func TestRandUint256(t *testing.T) { + t.Parallel() + + for i := 0; i < 1000; i++ { + uint256 := utils.RandUint256() + assert.NoError(t, utils.CheckUint256(uint256)) + } +} + +func TestHexToUint256(t *testing.T) { + t.Parallel() + + b, err := utils.HexToUint256("0x00") + assert.NoError(t, err) + assert.Zero(t, b.Cmp(big.NewInt(0))) + + b, err = utils.HexToUint256("0xFFFFFFFF") + assert.NoError(t, err) + assert.Zero(t, b.Cmp(big.NewInt(4294967295))) +} + +func TestNewHash(t *testing.T) { + t.Parallel() + + h1 := utils.NewHash() + h2 := utils.NewHash() + assert.NotEqual(t, h1, h2) + assert.NotEqual(t, h1, common.HexToHash("0x0")) + assert.NotEqual(t, h2, common.HexToHash("0x0")) +} + +func TestPadByteToHash(t *testing.T) { + t.Parallel() + + h := utils.PadByteToHash(1) + assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000001", h.String()) +} + +func TestUtils_BackoffSleeper(t *testing.T) { + t.Parallel() + + bs := utils.NewBackoffSleeper() + assert.Equal(t, time.Duration(0), bs.Duration(), "should initially return immediately") + bs.Sleep() + + d := 1 * time.Nanosecond + bs.Min = d + bs.Factor = 2 + assert.Equal(t, d, bs.Duration()) + bs.Sleep() + + d2 := 2 * time.Nanosecond + assert.Equal(t, d2, bs.Duration()) + + bs.Reset() + assert.Equal(t, time.Duration(0), bs.Duration(), "should initially return immediately") +} + +func TestRetryWithBackoff(t *testing.T) { + t.Parallel() + + var counter atomic.Int32 + ctx, cancel := context.WithCancel(testutils.Context(t)) + + utils.RetryWithBackoff(ctx, func() bool { + return false + }) + + retry := func() bool { + return counter.Add(1) < 3 + } + + go utils.RetryWithBackoff(ctx, retry) + + assert.Eventually(t, func() bool { + return counter.Load() == 3 + }, testutils.WaitTimeout(t), testutils.TestInterval) + + cancel() + + utils.RetryWithBackoff(ctx, retry) + assert.Equal(t, int32(4), counter.Load()) +} diff --git a/core/cmd/evm_transaction_commands.go b/core/cmd/evm_transaction_commands.go index a1a11ab9b37..76628944a6c 100644 --- a/core/cmd/evm_transaction_commands.go +++ b/core/cmd/evm_transaction_commands.go @@ -11,9 +11,9 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/forwarders_commands_test.go b/core/cmd/forwarders_commands_test.go index 5946d31dc3d..b5a96a73aa8 100644 --- a/core/cmd/forwarders_commands_test.go +++ b/core/cmd/forwarders_commands_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/gethwrappers/abigen.go b/core/gethwrappers/abigen.go index d96d9f8c306..ed2558f4173 100644 --- a/core/gethwrappers/abigen.go +++ b/core/gethwrappers/abigen.go @@ -17,7 +17,7 @@ import ( gethParams "github.com/ethereum/go-ethereum/params" "golang.org/x/tools/go/ast/astutil" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) const headerComment = `// Code generated - DO NOT EDIT. diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 51439ba80ec..fceb58ccdab 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -51,6 +51,7 @@ import ( httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" @@ -504,14 +505,14 @@ func NewEthMocksWithTransactionsOnBlocksAssertions(t testing.TB) *evmclimocks.Cl if len(elems) > 0 { elems[0].Result = &evmtypes.Block{ Number: 42, - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), Transactions: LegacyTransactionsFromGasPrices(9001, 9002), } } if len(elems) > 1 { elems[1].Result = &evmtypes.Block{ Number: 41, - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), Transactions: LegacyTransactionsFromGasPrices(9003, 9004), } } @@ -992,13 +993,13 @@ func Head(val interface{}) *evmtypes.Head { time := uint64(0) switch t := val.(type) { case int: - h = evmtypes.NewHead(big.NewInt(int64(t)), utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(int64(t)), evmutils.NewHash(), evmutils.NewHash(), time, ubig.New(&FixtureChainID)) case uint64: - h = evmtypes.NewHead(big.NewInt(int64(t)), utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(int64(t)), evmutils.NewHash(), evmutils.NewHash(), time, ubig.New(&FixtureChainID)) case int64: - h = evmtypes.NewHead(big.NewInt(t), utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(t), evmutils.NewHash(), evmutils.NewHash(), time, ubig.New(&FixtureChainID)) case *big.Int: - h = evmtypes.NewHead(t, utils.NewHash(), utils.NewHash(), time, ubig.New(&FixtureChainID)) + h = evmtypes.NewHead(t, evmutils.NewHash(), evmutils.NewHash(), time, ubig.New(&FixtureChainID)) default: panic(fmt.Sprintf("Could not convert %v of type %T to Head", val, val)) } @@ -1008,7 +1009,7 @@ func Head(val interface{}) *evmtypes.Head { func HeadWithHash(n int64, hash common.Hash) *evmtypes.Head { var h evmtypes.Head time := uint64(0) - h = evmtypes.NewHead(big.NewInt(n), hash, utils.NewHash(), time, ubig.New(&FixtureChainID)) + h = evmtypes.NewHead(big.NewInt(n), hash, evmutils.NewHash(), time, ubig.New(&FixtureChainID)) return &h } @@ -1385,7 +1386,7 @@ func (b *Blocks) NewHead(number uint64) *evmtypes.Head { } head := &evmtypes.Head{ Number: parent.Number + 1, - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), ParentHash: parent.Hash, Parent: parent, Timestamp: time.Unix(parent.Number+1, 0), @@ -1425,7 +1426,7 @@ func NewBlocks(t *testing.T, numHashes int) *Blocks { hashes := make([]common.Hash, 0) heads := make(map[int64]*evmtypes.Head) for i := int64(0); i < int64(numHashes); i++ { - hash := utils.NewHash() + hash := evmutils.NewHash() hashes = append(hashes, hash) heads[i] = &evmtypes.Head{Hash: hash, Number: i, Timestamp: time.Unix(i, 0), EVMChainID: ubig.New(&FixtureChainID)} diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index b96e97103e5..804dbe2d088 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -32,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -225,7 +226,7 @@ func NewLegacyEthTxAttempt(t *testing.T, etxID int64) txmgr.TxAttempt { // Just a random signed raw tx that decodes correctly // Ignore all actual values SignedRawTx: hexutil.MustDecode("0xf889808504a817c8008307a12094000000000000000000000000000000000000000080a400000000000000000000000000000000000000000000000000000000000000000000000025a0838fe165906e2547b9a052c099df08ec891813fea4fcdb3c555362285eb399c5a070db99322490eb8a0f2270be6eca6e3aedbc49ff57ef939cf2774f12d08aa85e"), - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), State: txmgrtypes.TxAttemptInProgress, } } @@ -243,7 +244,7 @@ func NewDynamicFeeEthTxAttempt(t *testing.T, etxID int64) txmgr.TxAttempt { // Just a random signed raw tx that decodes correctly // Ignore all actual values SignedRawTx: hexutil.MustDecode("0xf889808504a817c8008307a12094000000000000000000000000000000000000000080a400000000000000000000000000000000000000000000000000000000000000000000000025a0838fe165906e2547b9a052c099df08ec891813fea4fcdb3c555362285eb399c5a070db99322490eb8a0f2270be6eca6e3aedbc49ff57ef939cf2774f12d08aa85e"), - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), State: txmgrtypes.TxAttemptInProgress, ChainSpecificFeeLimit: 42, } @@ -312,7 +313,7 @@ func MustGenerateRandomKeyState(_ testing.TB) ethkey.State { } func MustInsertHead(t *testing.T, db *sqlx.DB, cfg pg.QConfig, number int64) evmtypes.Head { - h := evmtypes.NewHead(big.NewInt(number), utils.NewHash(), utils.NewHash(), 0, ubig.New(&FixtureChainID)) + h := evmtypes.NewHead(big.NewInt(number), evmutils.NewHash(), evmutils.NewHash(), 0, ubig.New(&FixtureChainID)) horm := headtracker.NewORM(db, logger.TestLogger(t), cfg, FixtureChainID) err := horm.IdempotentInsertHead(testutils.Context(t), &h) @@ -492,23 +493,23 @@ func RandomLog(t *testing.T) types.Log { topics := make([]common.Hash, 4) for i := range topics { - topics[i] = utils.NewHash() + topics[i] = evmutils.NewHash() } return types.Log{ Address: testutils.NewAddress(), - BlockHash: utils.NewHash(), + BlockHash: evmutils.NewHash(), BlockNumber: uint64(mathrand.Intn(9999999)), Index: uint(mathrand.Intn(9999999)), Data: MustRandomBytes(t, 512), - Topics: []common.Hash{utils.NewHash(), utils.NewHash(), utils.NewHash(), utils.NewHash()}, + Topics: []common.Hash{evmutils.NewHash(), evmutils.NewHash(), evmutils.NewHash(), evmutils.NewHash()}, } } func RawNewRoundLog(t *testing.T, contractAddr common.Address, blockHash common.Hash, blockNumber uint64, logIndex uint, removed bool) types.Log { t.Helper() topic := (flux_aggregator_wrapper.FluxAggregatorNewRound{}).Topic() - topics := []common.Hash{topic, utils.NewHash(), utils.NewHash()} + topics := []common.Hash{topic, evmutils.NewHash(), evmutils.NewHash()} return RawNewRoundLogWithTopics(t, contractAddr, blockHash, blockNumber, logIndex, removed, topics) } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index e8fcd0a6d37..a0588f6abdc 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -45,6 +45,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/consumer_wrapper" @@ -879,7 +880,7 @@ isBootstrapPeer = true // Raising flags to initiate hibernation _, err = flagsContract.RaiseFlag(owner, ocrContractAddress) require.NoError(t, err, "failed to raise flag for ocrContractAddress") - _, err = flagsContract.RaiseFlag(owner, utils.ZeroAddress) + _, err = flagsContract.RaiseFlag(owner, evmutils.ZeroAddress) require.NoError(t, err, "failed to raise flag for ZeroAddress") b.Commit() @@ -1105,7 +1106,7 @@ isBootstrapPeer = true // Raising flags to initiate hibernation _, err = flagsContract.RaiseFlag(owner, ocrContractAddress) require.NoError(t, err, "failed to raise flag for ocrContractAddress") - _, err = flagsContract.RaiseFlag(owner, utils.ZeroAddress) + _, err = flagsContract.RaiseFlag(owner, evmutils.ZeroAddress) require.NoError(t, err, "failed to raise flag for ZeroAddress") b.Commit() @@ -1263,22 +1264,22 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { b41 := evmtypes.Block{ Number: 41, - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), Transactions: cltest.LegacyTransactionsFromGasPrices(41_000_000_000, 41_500_000_000), } b42 := evmtypes.Block{ Number: 42, - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), Transactions: cltest.LegacyTransactionsFromGasPrices(44_000_000_000, 45_000_000_000), } b43 := evmtypes.Block{ Number: 43, - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), Transactions: cltest.LegacyTransactionsFromGasPrices(48_000_000_000, 49_000_000_000, 31_000_000_000), } evmChainID := ubig.New(evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs())) - h40 := evmtypes.Head{Hash: utils.NewHash(), Number: 40, EVMChainID: evmChainID} + h40 := evmtypes.Head{Hash: evmutils.NewHash(), Number: 40, EVMChainID: evmChainID} h41 := evmtypes.Head{Hash: b41.Hash, ParentHash: h40.Hash, Number: 41, EVMChainID: evmChainID} h42 := evmtypes.Head{Hash: b42.Hash, ParentHash: h41.Hash, Number: 42, EVMChainID: evmChainID} diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 5ad3d3561f9..46b0e42e2a0 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -24,6 +24,7 @@ import ( evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" + commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" @@ -34,7 +35,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" - "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) @@ -86,7 +86,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } // get upkeepID from command args upkeepID := big.NewInt(0) - upkeepIDNoPrefix := utils.RemoveHexPrefix(args[0]) + upkeepIDNoPrefix := commonhex.TrimPrefix(args[0]) _, wasBase10 := upkeepID.SetString(upkeepIDNoPrefix, 10) if !wasBase10 { _, wasBase16 := upkeepID.SetString(upkeepIDNoPrefix, 16) diff --git a/core/scripts/chaincli/handler/keeper_launch.go b/core/scripts/chaincli/handler/keeper_launch.go index ca0d5c403f1..e8be82a4bb7 100644 --- a/core/scripts/chaincli/handler/keeper_launch.go +++ b/core/scripts/chaincli/handler/keeper_launch.go @@ -18,6 +18,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/cmd" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" registry12 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2" @@ -409,7 +410,7 @@ func (k *Keeper) createOCR2KeeperJob(client cmd.HTTPClient, contractAddr, nodeAd // addKeyToKeeper imports the provided ETH sending key to the keeper func (k *Keeper) addKeyToKeeper(client cmd.HTTPClient, privKeyHex string) (string, error) { - privkey, err := crypto.HexToECDSA(utils.RemoveHexPrefix(privKeyHex)) + privkey, err := crypto.HexToECDSA(hex.TrimPrefix(privKeyHex)) if err != nil { log.Fatalf("Failed to decode priv key %s: %v", privKeyHex, err) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index ba4542e74b4..ee2570dcf93 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,6 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 @@ -235,7 +236,6 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 755def9861f..0bfd019b047 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 h1:mrxa3HrQfbMi4ji6gGcQHuLptvoNaRAv4TLnGJngbLc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab h1:6ckB261FRUy4K/OfSfWCQLAGkgfVLYT5PKDImmp3tZM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/scripts/ocr2vrf/util.go b/core/scripts/ocr2vrf/util.go index ef71327b52b..e57f349f1fd 100644 --- a/core/scripts/ocr2vrf/util.go +++ b/core/scripts/ocr2vrf/util.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" @@ -35,7 +36,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/vrf_beacon" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/vrf_beacon_consumer" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/vrf_coordinator" - "github.com/smartcontractkit/chainlink/v2/core/utils" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" ) diff --git a/core/scripts/vrfv1/main.go b/core/scripts/vrfv1/main.go index 11af7a0efb2..a281a922a32 100644 --- a/core/scripts/vrfv1/main.go +++ b/core/scripts/vrfv1/main.go @@ -15,6 +15,7 @@ import ( "github.com/shopspring/decimal" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" linktoken "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" @@ -203,7 +204,7 @@ func main() { } link, err := linktoken.NewLinkToken(common.HexToAddress(*linkAddr), e.Ec) helpers.PanicErr(err) - data, err := utils.ABIEncode(`[{"type":"bytes32"}]`, common.HexToHash(*keyHash)) + data, err := evmutils.ABIEncode(`[{"type":"bytes32"}]`, common.HexToHash(*keyHash)) helpers.PanicErr(err) tx, err := link.TransferAndCall(e.Owner, common.HexToAddress(*consumerAddr), payment, data) helpers.PanicErr(err) diff --git a/core/scripts/vrfv2/genvrfnum/main.go b/core/scripts/vrfv2/genvrfnum/main.go index cdd120167f7..606ac7f3443 100644 --- a/core/scripts/vrfv2/genvrfnum/main.go +++ b/core/scripts/vrfv2/genvrfnum/main.go @@ -16,6 +16,7 @@ import ( "github.com/shopspring/decimal" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/proof" @@ -227,7 +228,7 @@ func main() { } func preseed(keyHash common.Hash, sender common.Address, subID, nonce uint64) [32]byte { - encoded, err := utils.ABIEncode( + encoded, err := evmutils.ABIEncode( `[{"type":"bytes32"}, {"type":"address"}, {"type":"uint64"}, {"type", "uint64"}]`, keyHash, sender, diff --git a/core/scripts/vrfv2/testnet/main.go b/core/scripts/vrfv2/testnet/main.go index 4607c09e6e0..151549cc587 100644 --- a/core/scripts/vrfv2/testnet/main.go +++ b/core/scripts/vrfv2/testnet/main.go @@ -26,6 +26,7 @@ import ( helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -773,7 +774,7 @@ func main() { bal, err := linkToken.BalanceOf(nil, e.Owner.From) helpers.PanicErr(err) fmt.Println("OWNER BALANCE", bal, e.Owner.From.String(), amount.String()) - b, err := utils.ABIEncode(`[{"type":"uint64"}]`, created.SubId) + b, err := evmutils.ABIEncode(`[{"type":"uint64"}]`, created.SubId) helpers.PanicErr(err) e.Owner.GasLimit = 500000 tx, err := linkToken.TransferAndCall(e.Owner, coordinator.Address(), amount, b) diff --git a/core/scripts/vrfv2/testnet/v2scripts/util.go b/core/scripts/vrfv2/testnet/v2scripts/util.go index 94e381378bb..45cda3d2aa9 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/util.go +++ b/core/scripts/vrfv2/testnet/v2scripts/util.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_external_sub_owner_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper_consumer_example" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func DeployBHS(e helpers.Environment) (blockhashStoreAddress common.Address) { diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index f9c6ea7e2dc..43a0c65d7e6 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -31,6 +31,7 @@ import ( helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2plus" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -784,7 +785,7 @@ func main() { bal, err := linkToken.BalanceOf(nil, e.Owner.From) helpers.PanicErr(err) fmt.Println("OWNER BALANCE", bal, e.Owner.From.String(), amount.String()) - b, err := utils.ABIEncode(`[{"type":"uint64"}]`, created.SubId) + b, err := evmutils.ABIEncode(`[{"type":"uint64"}]`, created.SubId) helpers.PanicErr(err) e.Owner.GasLimit = 500000 tx, err := linkToken.TransferAndCall(e.Owner, coordinator.Address(), amount, b) diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go index 7598c6f398f..96f305213ea 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2plus" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -20,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_sub_owner" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func DeployBHS(e helpers.Environment) (blockhashStoreAddress common.Address) { diff --git a/core/services/blockhashstore/validate.go b/core/services/blockhashstore/validate.go index 82b813a37f4..62f90d326eb 100644 --- a/core/services/blockhashstore/validate.go +++ b/core/services/blockhashstore/validate.go @@ -7,8 +7,8 @@ import ( "github.com/pelletier/go-toml" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var EmptyAddress = utils.ZeroAddress.Hex() diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index ccf61d17943..32edaa85110 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" @@ -740,14 +741,14 @@ func (app *ChainlinkApplication) RunJobV2( Data: bytes.Join([][]byte{ jb.VRFSpec.PublicKey.MustHash().Bytes(), // key hash common.BigToHash(big.NewInt(42)).Bytes(), // seed - utils.NewHash().Bytes(), // sender - utils.NewHash().Bytes(), // fee - utils.NewHash().Bytes()}, // requestID + evmutils.NewHash().Bytes(), // sender + evmutils.NewHash().Bytes(), // fee + evmutils.NewHash().Bytes()}, // requestID []byte{}), Topics: []common.Hash{{}, jb.ExternalIDEncodeBytesToTopic()}, // jobID BYTES - TxHash: utils.NewHash(), + TxHash: evmutils.NewHash(), BlockNumber: 10, - BlockHash: utils.NewHash(), + BlockHash: evmutils.NewHash(), } vars = map[string]interface{}{ "jobSpec": map[string]interface{}{ diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 85a4c99b862..3b15395769a 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -3,6 +3,7 @@ package chainlink import ( _ "embed" "math" + "math/big" "net" "strings" "testing" @@ -18,6 +19,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" @@ -26,7 +28,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" legacy "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -96,7 +98,7 @@ var ( }, EVM: []*evmcfg.EVMConfig{ { - ChainID: big.NewI(1), + ChainID: ubig.NewI(1), Chain: evmcfg.Chain{ FinalityDepth: ptr[uint32](26), FinalityTagEnabled: ptr[bool](false), @@ -113,7 +115,7 @@ var ( }, }}, { - ChainID: big.NewI(42), + ChainID: ubig.NewI(42), Chain: evmcfg.Chain{ GasEstimator: evmcfg.GasEstimator{ PriceDefault: assets.NewWeiI(math.MaxInt64), @@ -126,7 +128,7 @@ var ( }, }}, { - ChainID: big.NewI(137), + ChainID: ubig.NewI(137), Chain: evmcfg.Chain{ GasEstimator: evmcfg.GasEstimator{ Mode: ptr("FixedPrice"), @@ -464,7 +466,7 @@ func TestConfig_Marshal(t *testing.T) { } full.EVM = []*evmcfg.EVMConfig{ { - ChainID: big.NewI(1), + ChainID: ubig.NewI(1), Enabled: ptr(false), Chain: evmcfg.Chain{ AutoCreateKey: ptr(false), @@ -493,7 +495,7 @@ func TestConfig_Marshal(t *testing.T) { TipCapDefault: assets.NewWeiI(2), TipCapMin: assets.NewWeiI(1), PriceDefault: assets.NewWeiI(math.MaxInt64), - PriceMax: assets.NewWei(utils.HexToBig("FFFFFFFFFFFF")), + PriceMax: assets.NewWei(mustHexToBig(t, "FFFFFFFFFFFF")), PriceMin: assets.NewWeiI(13), LimitJobType: evmcfg.GasLimitJobType{ @@ -519,7 +521,7 @@ func TestConfig_Marshal(t *testing.T) { { Key: mustAddress("0x2a3e23c6f242F5345320814aC8a1b4E58707D292"), GasEstimator: evmcfg.KeySpecificGasEstimator{ - PriceMax: assets.NewWei(utils.HexToBig("FFFFFFFFFFFFFFFFFFFFFFFF")), + PriceMax: assets.NewWei(mustHexToBig(t, "FFFFFFFFFFFFFFFFFFFFFFFF")), }, }, }, @@ -1467,7 +1469,7 @@ func assertValidationError(t *testing.T, invalid interface{ Validate() error }, func TestConfig_setDefaults(t *testing.T) { var c Config - c.EVM = evmcfg.EVMConfigs{{ChainID: big.NewI(99999133712345)}} + c.EVM = evmcfg.EVMConfigs{{ChainID: ubig.NewI(99999133712345)}} c.Cosmos = coscfg.TOMLConfigs{{ChainID: ptr("unknown cosmos chain")}} c.Solana = solana.TOMLConfigs{{ChainID: ptr("unknown solana chain")}} c.Starknet = stkcfg.TOMLConfigs{{ChainID: ptr("unknown starknet chain")}} @@ -1562,3 +1564,9 @@ func TestConfig_warnings(t *testing.T) { } func ptr[T any](t T) *T { return &t } + +func mustHexToBig(t *testing.T, hx string) *big.Int { + n, err := hex.ParseBig(hx) + require.NoError(t, err) + return n +} diff --git a/core/services/fluxmonitorv2/flags.go b/core/services/fluxmonitorv2/flags.go index bad5a1f2752..c50a767ae1c 100644 --- a/core/services/fluxmonitorv2/flags.go +++ b/core/services/fluxmonitorv2/flags.go @@ -7,9 +7,9 @@ import ( "github.com/ethereum/go-ethereum/core/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name Flags --output ./mocks/ --case=underscore --structname Flags --filename flags.go diff --git a/core/services/fluxmonitorv2/flags_test.go b/core/services/fluxmonitorv2/flags_test.go index 99d182a9671..c00c439d859 100644 --- a/core/services/fluxmonitorv2/flags_test.go +++ b/core/services/fluxmonitorv2/flags_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestFlags_IsLowered(t *testing.T) { diff --git a/core/services/fluxmonitorv2/flux_monitor.go b/core/services/fluxmonitorv2/flux_monitor.go index 79dd44c8014..8fe0fc7c70e 100644 --- a/core/services/fluxmonitorv2/flux_monitor.go +++ b/core/services/fluxmonitorv2/flux_monitor.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -338,12 +339,12 @@ func (fm *FluxMonitor) HandleLog(broadcast log.Broadcast) { fm.backlog.Add(PriorityAnswerUpdatedLog, broadcast) case *flags_wrapper.FlagsFlagRaised: - if log.Subject == utils.ZeroAddress || log.Subject == fm.contractAddress { + if log.Subject == evmutils.ZeroAddress || log.Subject == fm.contractAddress { fm.backlog.Add(PriorityFlagChangedLog, broadcast) } case *flags_wrapper.FlagsFlagLowered: - if log.Subject == utils.ZeroAddress || log.Subject == fm.contractAddress { + if log.Subject == evmutils.ZeroAddress || log.Subject == fm.contractAddress { fm.backlog.Add(PriorityFlagChangedLog, broadcast) } diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 729bcd76eba..48c9ca24ac3 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -29,6 +29,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" faw "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" @@ -667,7 +668,7 @@ ds1 -> ds1_parse s = fmt.Sprintf(s, fa.aggregatorContractAddress, testutils.SimulatedChainID.String(), pollTimerPeriod, mockServer.URL) // raise flags to disable polling - _, err = fa.flagsContract.RaiseFlag(fa.sergey, utils.ZeroAddress) // global kill switch + _, err = fa.flagsContract.RaiseFlag(fa.sergey, evmutils.ZeroAddress) // global kill switch require.NoError(t, err) _, err = fa.flagsContract.RaiseFlag(fa.sergey, fa.aggregatorContractAddress) require.NoError(t, err) @@ -776,7 +777,7 @@ ds1 -> ds1_parse s = fmt.Sprintf(s, fa.aggregatorContractAddress, testutils.SimulatedChainID.String(), "1000ms", mockServer.URL) // raise flags - _, err = fa.flagsContract.RaiseFlag(fa.sergey, utils.ZeroAddress) // global kill switch + _, err = fa.flagsContract.RaiseFlag(fa.sergey, evmutils.ZeroAddress) // global kill switch require.NoError(t, err) _, err = fa.flagsContract.RaiseFlag(fa.sergey, fa.aggregatorContractAddress) @@ -795,7 +796,7 @@ ds1 -> ds1_parse cltest.AssertPipelineRunsStays(t, j.PipelineSpec.ID, app.GetSqlxDB(), 0) // lower global kill switch flag - should trigger job run - _, err = fa.flagsContract.LowerFlags(fa.sergey, []common.Address{utils.ZeroAddress}) + _, err = fa.flagsContract.LowerFlags(fa.sergey, []common.Address{evmutils.ZeroAddress}) require.NoError(t, err) fa.backend.Commit() awaitSubmission(t, fa.backend, submissionReceived) @@ -816,7 +817,7 @@ ds1 -> ds1_parse // raise both flags _, err = fa.flagsContract.RaiseFlag(fa.sergey, fa.aggregatorContractAddress) require.NoError(t, err) - _, err = fa.flagsContract.RaiseFlag(fa.sergey, utils.ZeroAddress) + _, err = fa.flagsContract.RaiseFlag(fa.sergey, evmutils.ZeroAddress) require.NoError(t, err) fa.backend.Commit() @@ -887,7 +888,7 @@ ds1 -> ds1_parse s := fmt.Sprintf(toml, fa.aggregatorContractAddress, testutils.SimulatedChainID.String(), "100ms", mockServer.URL) // raise flags - _, err = fa.flagsContract.RaiseFlag(fa.sergey, utils.ZeroAddress) // global kill switch + _, err = fa.flagsContract.RaiseFlag(fa.sergey, evmutils.ZeroAddress) // global kill switch require.NoError(t, err) _, err = fa.flagsContract.RaiseFlag(fa.sergey, fa.aggregatorContractAddress) require.NoError(t, err) diff --git a/core/services/functions/external_adapter_client.go b/core/services/functions/external_adapter_client.go index db4fed30e5f..fb64924a922 100644 --- a/core/services/functions/external_adapter_client.go +++ b/core/services/functions/external_adapter_client.go @@ -14,8 +14,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ExternalAdapterClient supports two endpoints: @@ -228,13 +228,13 @@ func (ea *externalAdapterClient) request( switch eaResp.Result { case "error": - userError, err = utils.TryParseHex(eaResp.Data.Error) + userError, err = hex.DecodeString(eaResp.Data.Error) if err != nil { return nil, nil, nil, errors.Wrap(err, "error decoding userError hex string") } return nil, userError, eaResp.Data.Domains, nil case "success": - userResult, err = utils.TryParseHex(eaResp.Data.Result) + userResult, err = hex.DecodeString(eaResp.Data.Result) if err != nil { return nil, nil, nil, errors.Wrap(err, "error decoding result hex string") } diff --git a/core/services/gateway/api/message.go b/core/services/gateway/api/message.go index d0a116675ae..c01d3bb9f2e 100644 --- a/core/services/gateway/api/message.go +++ b/core/services/gateway/api/message.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" gw_common "github.com/smartcontractkit/chainlink/v2/core/services/gateway/common" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -97,7 +98,7 @@ func (m *Message) ExtractSigner() (signerAddress []byte, err error) { return nil, errors.New("nil message") } rawData := getRawMessageBody(&m.Body) - signatureBytes, err := utils.TryParseHex(m.Signature) + signatureBytes, err := hex.DecodeString(m.Signature) if err != nil { return nil, err } diff --git a/core/services/gateway/connector/connector.go b/core/services/gateway/connector/connector.go index 27db8fd44b6..6b399733a58 100644 --- a/core/services/gateway/connector/connector.go +++ b/core/services/gateway/connector/connector.go @@ -11,6 +11,7 @@ import ( "github.com/gorilla/websocket" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" @@ -85,7 +86,7 @@ func NewGatewayConnector(config *ConnectorConfig, signer Signer, handler Gateway if len(config.DonId) == 0 || len(config.DonId) > int(network.HandshakeDonIdLen) { return nil, errors.New("invalid DON ID") } - addressBytes, err := utils.TryParseHex(config.NodeAddress) + addressBytes, err := hex.DecodeString(config.NodeAddress) if err != nil { return nil, err } diff --git a/core/services/gateway/handlers/functions/user_subscriptions.go b/core/services/gateway/handlers/functions/user_subscriptions.go index c47ac8a4c90..ff3dd753995 100644 --- a/core/services/gateway/handlers/functions/user_subscriptions.go +++ b/core/services/gateway/handlers/functions/user_subscriptions.go @@ -6,8 +6,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Methods are NOT thread-safe. diff --git a/core/services/gateway/handlers/functions/user_subscriptions_test.go b/core/services/gateway/handlers/functions/user_subscriptions_test.go index 9e6a660adad..e86399eb609 100644 --- a/core/services/gateway/handlers/functions/user_subscriptions_test.go +++ b/core/services/gateway/handlers/functions/user_subscriptions_test.go @@ -4,9 +4,9 @@ import ( "math/big" "testing" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" ) diff --git a/core/services/job/models.go b/core/services/job/models.go index f60e0a12981..50e986d8db1 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -27,7 +28,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" "github.com/smartcontractkit/chainlink/v2/core/utils/tomlutils" ) diff --git a/core/services/keeper/models.go b/core/services/keeper/models.go index 8f72f0b22c9..fe034bcc505 100644 --- a/core/services/keeper/models.go +++ b/core/services/keeper/models.go @@ -8,10 +8,10 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type KeeperIndexMap map[ethkey.EIP55Address]int32 diff --git a/core/services/keeper/orm_test.go b/core/services/keeper/orm_test.go index e5b56e9511e..2ce459886ae 100644 --- a/core/services/keeper/orm_test.go +++ b/core/services/keeper/orm_test.go @@ -15,6 +15,7 @@ import ( "github.com/jmoiron/sqlx" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -177,7 +178,7 @@ func TestKeeperDB_EligibleUpkeeps_Shuffle(t *testing.T) { } cltest.AssertCount(t, db, "upkeep_registrations", 100) - eligibleUpkeeps, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, blockheight, gracePeriod, fmt.Sprintf("%b", utils.NewHash().Big())) + eligibleUpkeeps, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, blockheight, gracePeriod, fmt.Sprintf("%b", evmutils.NewHash().Big())) assert.NoError(t, err) require.Len(t, eligibleUpkeeps, 100) @@ -205,12 +206,12 @@ func TestKeeperDB_NewEligibleUpkeeps_GracePeriod(t *testing.T) { // if current keeper index = 0 and all upkeeps last perform was done by index = 0 and still within grace period upkeep := keeper.UpkeepRegistration{} require.NoError(t, db.Get(&upkeep, `UPDATE upkeep_registrations SET last_keeper_index = 0, last_run_block_height = 10 RETURNING *`)) - list0, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 21, 100, fmt.Sprintf("%b", utils.NewHash().Big())) // none eligible + list0, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 21, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) // none eligible require.NoError(t, err) require.Equal(t, 0, len(list0), "should be 0 as all last perform was done by current node") // once passed grace period - list1, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 121, 100, fmt.Sprintf("%b", utils.NewHash().Big())) // none eligible + list1, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 121, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) // none eligible require.NoError(t, err) require.NotEqual(t, 0, len(list1), "should get some eligible upkeeps now that they are outside grace period") } @@ -230,13 +231,13 @@ func TestKeeperDB_EligibleUpkeeps_TurnsRandom(t *testing.T) { cltest.AssertCount(t, db, "upkeep_registrations", 1000) // 3 keepers 10 block turns should be different every turn - list1, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 20, 100, fmt.Sprintf("%b", utils.NewHash().Big())) + list1, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 20, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) require.NoError(t, err) - list2, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 31, 100, fmt.Sprintf("%b", utils.NewHash().Big())) + list2, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 31, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) require.NoError(t, err) - list3, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 42, 100, fmt.Sprintf("%b", utils.NewHash().Big())) + list3, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 42, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) require.NoError(t, err) - list4, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 53, 100, fmt.Sprintf("%b", utils.NewHash().Big())) + list4, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 53, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) require.NoError(t, err) // sort before compare @@ -275,7 +276,7 @@ func TestKeeperDB_NewEligibleUpkeeps_SkipIfLastPerformedByCurrentKeeper(t *testi // if current keeper index = 0 and all upkeeps last perform was done by index = 0 then skip as it would not pass required turn taking upkeep := keeper.UpkeepRegistration{} require.NoError(t, db.Get(&upkeep, `UPDATE upkeep_registrations SET last_keeper_index = 0 RETURNING *`)) - list0, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 21, 100, fmt.Sprintf("%b", utils.NewHash().Big())) // none eligible + list0, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 21, 100, fmt.Sprintf("%b", evmutils.NewHash().Big())) // none eligible require.NoError(t, err) require.Equal(t, 0, len(list0), "should be 0 as all last perform was done by current node") } @@ -295,7 +296,7 @@ func TestKeeperDB_NewEligibleUpkeeps_CoverBuddy(t *testing.T) { cltest.AssertCount(t, db, "upkeep_registrations", 100) upkeep := keeper.UpkeepRegistration{} - binaryHash := fmt.Sprintf("%b", utils.NewHash().Big()) + binaryHash := fmt.Sprintf("%b", evmutils.NewHash().Big()) listBefore, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 21, 100, binaryHash) // normal require.NoError(t, err) require.NoError(t, db.Get(&upkeep, `UPDATE upkeep_registrations SET last_keeper_index = 0 RETURNING *`)) @@ -318,7 +319,7 @@ func TestKeeperDB_NewEligibleUpkeeps_FirstTurn(t *testing.T) { cltest.AssertCount(t, db, "keeper_registries", 1) cltest.AssertCount(t, db, "upkeep_registrations", 100) - binaryHash := fmt.Sprintf("%b", utils.NewHash().Big()) + binaryHash := fmt.Sprintf("%b", evmutils.NewHash().Big()) // last keeper index is null to simulate a normal first run listKpr0, err := orm.EligibleUpkeepsForRegistry(registry.ContractAddress, 21, 100, binaryHash) // someone eligible only kpr0 turn require.NoError(t, err) @@ -339,7 +340,7 @@ func TestKeeperDB_NewEligibleUpkeeps_FiltersByRegistry(t *testing.T) { cltest.AssertCount(t, db, "keeper_registries", 2) cltest.AssertCount(t, db, "upkeep_registrations", 2) - binaryHash := fmt.Sprintf("%b", utils.NewHash().Big()) + binaryHash := fmt.Sprintf("%b", evmutils.NewHash().Big()) list1, err := orm.EligibleUpkeepsForRegistry(registry1.ContractAddress, 20, 100, binaryHash) require.NoError(t, err) list2, err := orm.EligibleUpkeepsForRegistry(registry2.ContractAddress, 20, 100, binaryHash) @@ -400,8 +401,8 @@ func TestKeeperDB_NewSetLastRunInfoForUpkeepOnJob(t *testing.T) { upkeep := cltest.MustInsertUpkeepForRegistry(t, db, config.Database(), registry) registry.NumKeepers = 2 registry.KeeperIndexMap = map[ethkey.EIP55Address]int32{ - registry.FromAddress: 0, - ethkey.EIP55AddressFromAddress(utils.ZeroAddress): 1, + registry.FromAddress: 0, + ethkey.EIP55AddressFromAddress(evmutils.ZeroAddress): 1, } err := orm.UpsertRegistry(®istry) require.NoError(t, err, "UPDATE keeper_registries") @@ -417,7 +418,7 @@ func TestKeeperDB_NewSetLastRunInfoForUpkeepOnJob(t *testing.T) { require.Equal(t, rowsAffected, int64(0)) assertLastRunHeight(t, db, upkeep, 100, 0) // update to same block height allowed - rowsAffected, err = orm.SetLastRunInfoForUpkeepOnJob(j.ID, upkeep.UpkeepID, 100, ethkey.EIP55AddressFromAddress(utils.ZeroAddress)) + rowsAffected, err = orm.SetLastRunInfoForUpkeepOnJob(j.ID, upkeep.UpkeepID, 100, ethkey.EIP55AddressFromAddress(evmutils.ZeroAddress)) require.NoError(t, err) require.Equal(t, rowsAffected, int64(1)) assertLastRunHeight(t, db, upkeep, 100, 1) @@ -489,11 +490,11 @@ func TestKeeperDB_Uint256ToBit(t *testing.T) { }, { name: "max", - input: utils.MaxUint256, + input: evmutils.MaxUint256, }, { name: "rand", - input: utils.RandUint256(), + input: evmutils.RandUint256(), }, { name: "needs pading", @@ -501,7 +502,7 @@ func TestKeeperDB_Uint256ToBit(t *testing.T) { }, { name: "overflow", - input: bigmath.Add(utils.MaxUint256, big.NewInt(1)), + input: bigmath.Add(evmutils.MaxUint256, big.NewInt(1)), errorExpected: true, }, } { diff --git a/core/services/keeper/registry_synchronizer_sync.go b/core/services/keeper/registry_synchronizer_sync.go index f90e0bc85d7..7614ed15edb 100644 --- a/core/services/keeper/registry_synchronizer_sync.go +++ b/core/services/keeper/registry_synchronizer_sync.go @@ -7,9 +7,9 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func (rs *RegistrySynchronizer) fullSync() { diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 42e79425cff..590c9720cb2 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -23,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "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/cltest" @@ -36,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keeper" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func newHead() evmtypes.Head { diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index 54b19145212..61e0e92d82b 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -20,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_EthKeyStore(t *testing.T) { diff --git a/core/services/keystore/keys/vrfkey/crypto.go b/core/services/keystore/keys/vrfkey/crypto.go index a3792b98075..fe105d13f11 100644 --- a/core/services/keystore/keys/vrfkey/crypto.go +++ b/core/services/keystore/keys/vrfkey/crypto.go @@ -7,8 +7,9 @@ import ( "github.com/ethereum/go-ethereum/common" "go.dedis.ch/kyber/v3" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" bm "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) @@ -19,7 +20,7 @@ import ( var ( // FieldSize is number of elements in secp256k1's base field, i.e. GF(FieldSize) - FieldSize = utils.HexToBig( + FieldSize = mustParseBig( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", ) Secp256k1Curve = &secp256k1.Secp256k1{} @@ -176,3 +177,11 @@ func ScalarFromCurvePoints( msg = append(msg, uWitness[:]...) return bm.I().SetBytes(utils.MustHash(string(msg)).Bytes()) } + +func mustParseBig(hx string) *big.Int { + n, err := hex.ParseBig(hx) + if err != nil { + panic(fmt.Errorf(`failed to convert "%s" as hex to big.Int`, hx)) + } + return n +} diff --git a/core/services/keystore/keys/vrfkey/key_v2.go b/core/services/keystore/keys/vrfkey/key_v2.go index 49c5a4999d8..786bd39b3f0 100644 --- a/core/services/keystore/keys/vrfkey/key_v2.go +++ b/core/services/keystore/keys/vrfkey/key_v2.go @@ -9,8 +9,8 @@ import ( "github.com/pkg/errors" "go.dedis.ch/kyber/v3" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" bm "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/services/keystore/keys/vrfkey/proof.go b/core/services/keystore/keys/vrfkey/proof.go index d4ee9b160b7..b7f9032255b 100644 --- a/core/services/keystore/keys/vrfkey/proof.go +++ b/core/services/keystore/keys/vrfkey/proof.go @@ -6,8 +6,8 @@ import ( "go.dedis.ch/kyber/v3" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" bm "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/services/ocr/config_overrider_test.go b/core/services/ocr/config_overrider_test.go index acd5245e19b..e01102a62f8 100644 --- a/core/services/ocr/config_overrider_test.go +++ b/core/services/ocr/config_overrider_test.go @@ -15,6 +15,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -187,7 +188,7 @@ func Test_OCRConfigOverrider(t *testing.T) { func checkFlagsAddress(t *testing.T, contractAddress ethkey.EIP55Address) func(args mock.Arguments) { return func(args mock.Arguments) { require.Equal(t, []common.Address{ - utils.ZeroAddress, + evmutils.ZeroAddress, contractAddress.Address(), }, args.Get(1).([]common.Address)) } diff --git a/core/services/ocr/database_test.go b/core/services/ocr/database_test.go index 6a72c27aa65..5ccf257b2bb 100644 --- a/core/services/ocr/database_test.go +++ b/core/services/ocr/database_test.go @@ -13,12 +13,12 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_DB_ReadWriteState(t *testing.T) { diff --git a/core/services/ocr/flags.go b/core/services/ocr/flags.go index 8e4eb802e5e..6a18d73cd7e 100644 --- a/core/services/ocr/flags.go +++ b/core/services/ocr/flags.go @@ -5,8 +5,8 @@ import ( "github.com/pkg/errors" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ContractFlags wraps the a contract diff --git a/core/services/ocr/flags_test.go b/core/services/ocr/flags_test.go index 9e57b8489ed..bcce1ed2f96 100644 --- a/core/services/ocr/flags_test.go +++ b/core/services/ocr/flags_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestFlags_IsLowered(t *testing.T) { diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index 3e6e728f57a..9388b93329b 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_allow_list" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_client_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" @@ -48,7 +49,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var nilOpts *bind.CallOpts diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index ed59213840c..68fb27fa82e 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -40,7 +41,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ pb.MercuryServer = &mercuryServer{} diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index e9326d07d29..5c83e71946f 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -32,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" @@ -54,7 +55,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ocr2vrfUniverse struct { diff --git a/core/services/ocrcommon/arbitrum_block_translator_test.go b/core/services/ocrcommon/arbitrum_block_translator_test.go index b7d1b4e60c2..1ad3a6c5950 100644 --- a/core/services/ocrcommon/arbitrum_block_translator_test.go +++ b/core/services/ocrcommon/arbitrum_block_translator_test.go @@ -6,12 +6,12 @@ import ( "testing" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index 7627a627dea..e607c859d78 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -19,6 +19,7 @@ import ( mercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" mercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -28,7 +29,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const bridgeResponse = `{ diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index 92a6c25da39..638977863d6 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -25,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ormconfig struct { @@ -348,7 +348,7 @@ func Test_PipelineORM_StoreRun_UpdateTaskRunResult(t *testing.T) { ds1_id := uuid.New() now := time.Now() - address, err := utils.TryParseHex("0x8bd112d3f8f92e41c861939545ad387307af9703") + address, err := hex.DecodeString("0x8bd112d3f8f92e41c861939545ad387307af9703") require.NoError(t, err) cborOutput := map[string]interface{}{ "blockNum": "0x13babbd", diff --git a/core/services/pipeline/task.estimategas.go b/core/services/pipeline/task.estimategas.go index 1c0159819b4..43c148b287f 100644 --- a/core/services/pipeline/task.estimategas.go +++ b/core/services/pipeline/task.estimategas.go @@ -12,9 +12,9 @@ import ( "github.com/shopspring/decimal" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Return types: diff --git a/core/services/pipeline/task.eth_abi_decode_log_test.go b/core/services/pipeline/task.eth_abi_decode_log_test.go index f8958f3517e..62cd005a047 100644 --- a/core/services/pipeline/task.eth_abi_decode_log_test.go +++ b/core/services/pipeline/task.eth_abi_decode_log_test.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestETHABIDecodeLogTask(t *testing.T) { diff --git a/core/services/pipeline/task.eth_abi_decode_test.go b/core/services/pipeline/task.eth_abi_decode_test.go index b0f03402278..3c7f5b4776b 100644 --- a/core/services/pipeline/task.eth_abi_decode_test.go +++ b/core/services/pipeline/task.eth_abi_decode_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var testsABIDecode = []struct { diff --git a/core/services/pipeline/task.eth_call.go b/core/services/pipeline/task.eth_call.go index 3862ea10301..56b2df08c4e 100644 --- a/core/services/pipeline/task.eth_call.go +++ b/core/services/pipeline/task.eth_call.go @@ -13,9 +13,9 @@ import ( "go.uber.org/multierr" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Return types: diff --git a/core/services/pipeline/task.eth_tx.go b/core/services/pipeline/task.eth_tx.go index 58e9f6f2c15..1687c974140 100644 --- a/core/services/pipeline/task.eth_tx.go +++ b/core/services/pipeline/task.eth_tx.go @@ -13,12 +13,12 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Return types: @@ -189,7 +189,7 @@ func decodeMeta(metaMap MapParam) (*txmgr.TxMeta, error) { i, err2 := strconv.ParseInt(data.(string), 10, 32) return int32(i), err2 case reflect.TypeOf(common.Hash{}): - hb, err := utils.TryParseHex(data.(string)) + hb, err := hex.DecodeString(data.(string)) if err != nil { return nil, err } @@ -220,7 +220,7 @@ func decodeTransmitChecker(checkerMap MapParam) (txmgr.TransmitCheckerSpec, erro case stringType: switch to { case reflect.TypeOf(common.Address{}): - ab, err := utils.TryParseHex(data.(string)) + ab, err := hex.DecodeString(data.(string)) if err != nil { return nil, err } diff --git a/core/services/pipeline/task.hexdecode.go b/core/services/pipeline/task.hexdecode.go index 7feca555111..893130bcf2c 100644 --- a/core/services/pipeline/task.hexdecode.go +++ b/core/services/pipeline/task.hexdecode.go @@ -7,8 +7,8 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Return types: @@ -40,8 +40,8 @@ func (t *HexDecodeTask) Run(_ context.Context, _ logger.Logger, vars Vars, input return Result{Error: err}, runInfo } - if utils.HasHexPrefix(input.String()) { - noHexPrefix := utils.RemoveHexPrefix(input.String()) + if commonhex.HasPrefix(input.String()) { + noHexPrefix := commonhex.TrimPrefix(input.String()) bs, err := hex.DecodeString(noHexPrefix) if err == nil { return Result{Value: bs}, runInfo diff --git a/core/services/pipeline/task_params.go b/core/services/pipeline/task_params.go index dc020c30715..261728fbe9e 100644 --- a/core/services/pipeline/task_params.go +++ b/core/services/pipeline/task_params.go @@ -13,6 +13,7 @@ import ( "github.com/pkg/errors" "github.com/shopspring/decimal" + commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -122,8 +123,8 @@ func (b *BytesParam) UnmarshalPipelineParam(val interface{}) error { switch v := val.(type) { case string: // first check if this is a valid hex-encoded string - if utils.HasHexPrefix(v) { - noHexPrefix := utils.RemoveHexPrefix(v) + if commonhex.HasPrefix(v) { + noHexPrefix := commonhex.TrimPrefix(v) bs, err := hex.DecodeString(noHexPrefix) if err == nil { *b = bs @@ -452,7 +453,7 @@ func (a *AddressParam) UnmarshalPipelineParam(val interface{}) error { case []byte: switch len(v) { case 42: - bs, err := utils.TryParseHex(string(v)) + bs, err := commonhex.DecodeString(string(v)) if err == nil { *a = AddressParam(common.BytesToAddress(bs)) return nil diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index 3409c2f1591..79533a06f01 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -33,6 +33,7 @@ import ( evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -326,12 +327,12 @@ func setConfig(t *testing.T, pluginConfig median.OffchainConfig, ocrContract *oc for i := 0; i < 4; i++ { oracles = append(oracles, confighelper2.OracleIdentityExtra{ OracleIdentity: confighelper2.OracleIdentity{ - OnchainPublicKey: utils.RandomAddress().Bytes(), - TransmitAccount: ocrtypes2.Account(utils.RandomAddress().Hex()), - OffchainPublicKey: utils.RandomBytes32(), + OnchainPublicKey: evmutils.RandomAddress().Bytes(), + TransmitAccount: ocrtypes2.Account(evmutils.RandomAddress().Hex()), + OffchainPublicKey: evmutils.RandomBytes32(), PeerID: utils.MustNewPeerID(), }, - ConfigEncryptionPublicKey: utils.RandomBytes32(), + ConfigEncryptionPublicKey: evmutils.RandomBytes32(), }) } // Gnerate OnchainConfig diff --git a/core/services/relay/evm/contract_transmitter.go b/core/services/relay/evm/contract_transmitter.go index 470b5bae076..76360e34e1a 100644 --- a/core/services/relay/evm/contract_transmitter.go +++ b/core/services/relay/evm/contract_transmitter.go @@ -16,10 +16,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ContractTransmitter interface { diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index a5e5c1f8058..2cf373d2e86 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -24,6 +24,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -158,12 +159,12 @@ func setFunctionsConfig(t *testing.T, pluginConfig *functionsConfig.ReportingPlu for i := 0; i < 4; i++ { oracles = append(oracles, confighelper2.OracleIdentityExtra{ OracleIdentity: confighelper2.OracleIdentity{ - OnchainPublicKey: utils.RandomAddress().Bytes(), - TransmitAccount: ocrtypes2.Account(utils.RandomAddress().String()), - OffchainPublicKey: utils.RandomBytes32(), + OnchainPublicKey: evmutils.RandomAddress().Bytes(), + TransmitAccount: ocrtypes2.Account(evmutils.RandomAddress().String()), + OffchainPublicKey: evmutils.RandomBytes32(), PeerID: utils.MustNewPeerID(), }, - ConfigEncryptionPublicKey: utils.RandomBytes32(), + ConfigEncryptionPublicKey: evmutils.RandomBytes32(), }) } diff --git a/core/services/relay/evm/functions/contract_transmitter.go b/core/services/relay/evm/functions/contract_transmitter.go index 17baab4525c..2a62db31a8c 100644 --- a/core/services/relay/evm/functions/contract_transmitter.go +++ b/core/services/relay/evm/functions/contract_transmitter.go @@ -18,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/pg" evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type FunctionsContractTransmitter interface { diff --git a/core/services/relay/evm/mercury/config_poller_test.go b/core/services/relay/evm/mercury/config_poller_test.go index 1b3ba72128d..71e88d41a22 100644 --- a/core/services/relay/evm/mercury/config_poller_test.go +++ b/core/services/relay/evm/mercury/config_poller_test.go @@ -16,13 +16,14 @@ import ( "github.com/stretchr/testify/require" "github.com/umbracle/ethgo/abi" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestMercuryConfigPoller(t *testing.T) { - feedID := utils.NewHash() + feedID := evmutils.NewHash() feedIDBytes := [32]byte(feedID) th := SetupTH(t, feedID) @@ -42,12 +43,12 @@ func TestMercuryConfigPoller(t *testing.T) { for i := 0; i < n; i++ { oracles = append(oracles, confighelper2.OracleIdentityExtra{ OracleIdentity: confighelper2.OracleIdentity{ - OnchainPublicKey: utils.RandomAddress().Bytes(), - TransmitAccount: ocrtypes2.Account(utils.RandomAddress().String()), - OffchainPublicKey: utils.RandomBytes32(), + OnchainPublicKey: evmutils.RandomAddress().Bytes(), + TransmitAccount: ocrtypes2.Account(evmutils.RandomAddress().String()), + OffchainPublicKey: evmutils.RandomBytes32(), PeerID: utils.MustNewPeerID(), }, - ConfigEncryptionPublicKey: utils.RandomBytes32(), + ConfigEncryptionPublicKey: evmutils.RandomBytes32(), }) } f := uint8(1) diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index 59e0e587813..0703f878eed 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "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/llo-feeds/generated/verifier" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -29,7 +30,6 @@ import ( reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" reportcodecv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/reportcodec" reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index ce0d71acc6f..e02efd8d9a4 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -21,6 +21,7 @@ import ( commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -30,7 +31,6 @@ import ( mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ mercurytypes.ServerFetcher = &mockFetcher{} diff --git a/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go index 2e50faf47d4..3e898d6c1da 100644 --- a/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) var hash = hexutil.MustDecode("0x552c2cea3ab43bae137d89ee6142a01db3ae2b5678bc3c9bd5f509f537bea57b") diff --git a/core/services/relay/evm/mercury/wsrpc/pool_test.go b/core/services/relay/evm/mercury/wsrpc/pool_test.go index 3d418d39d87..bb5ceec0bb6 100644 --- a/core/services/relay/evm/mercury/wsrpc/pool_test.go +++ b/core/services/relay/evm/mercury/wsrpc/pool_test.go @@ -9,12 +9,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ Client = &mockClient{} diff --git a/core/services/relay/evm/types/types_test.go b/core/services/relay/evm/types/types_test.go index 6952c35a706..fb394ddf38b 100644 --- a/core/services/relay/evm/types/types_test.go +++ b/core/services/relay/evm/types/types_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ChainID *big.Big `json:"chainID"` diff --git a/core/services/signatures/secp256k1/public_key.go b/core/services/signatures/secp256k1/public_key.go index 1f7fd9c0ebd..c39565984ae 100644 --- a/core/services/signatures/secp256k1/public_key.go +++ b/core/services/signatures/secp256k1/public_key.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "go.dedis.ch/kyber/v3" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) // PublicKey is a secp256k1 point in compressed format diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 731437791b4..591ca3e9508 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -26,6 +26,7 @@ import ( log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -124,14 +125,14 @@ func generateCallbackReturnValues(t *testing.T, fulfilled bool) []byte { // Empty callback b, err2 := args.Pack(solidity_vrf_coordinator_interface.Callbacks{ RandomnessFee: big.NewInt(10), - SeedAndBlockNum: utils.EmptyHash, + SeedAndBlockNum: evmutils.EmptyHash, }) require.NoError(t, err2) return b } b, err := args.Pack(solidity_vrf_coordinator_interface.Callbacks{ RandomnessFee: big.NewInt(10), - SeedAndBlockNum: utils.NewHash(), + SeedAndBlockNum: evmutils.NewHash(), }) require.NoError(t, err) return b @@ -185,7 +186,7 @@ func TestDelegate_ReorgAttackProtection(t *testing.T) { vuni, listener, jb := setup(t) // Same request has already been fulfilled twice - reqID := utils.NewHash() + reqID := evmutils.NewHash() var reqIDBytes [32]byte copy(reqIDBytes[:], reqID.Bytes()) listener.SetRespCount(reqIDBytes, 2) @@ -198,17 +199,17 @@ func TestDelegate_ReorgAttackProtection(t *testing.T) { added <- struct{}{} }) preSeed := common.BigToHash(big.NewInt(42)).Bytes() - txHash := utils.NewHash() + txHash := evmutils.NewHash() vuni.lb.On("WasAlreadyConsumed", mock.Anything, mock.Anything).Return(false, nil).Maybe() vuni.lb.On("MarkConsumed", mock.Anything, mock.Anything).Return(nil).Maybe() vuni.ec.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(generateCallbackReturnValues(t, false), nil).Maybe() listener.HandleLog(log.NewLogBroadcast(types.Log{ // Data has all the NON-indexed parameters Data: bytes.Join([][]byte{pk.MustHash().Bytes(), // key hash - preSeed, // preSeed - utils.NewHash().Bytes(), // sender - utils.NewHash().Bytes(), // fee - reqID.Bytes()}, []byte{}, // requestID + preSeed, // preSeed + evmutils.NewHash().Bytes(), // sender + evmutils.NewHash().Bytes(), // fee + reqID.Bytes()}, []byte{}, // requestID ), // JobID is indexed, thats why it lives in the Topics. Topics: []common.Hash{ @@ -230,9 +231,9 @@ func TestDelegate_ReorgAttackProtection(t *testing.T) { func TestDelegate_ValidLog(t *testing.T) { vuni, listener, jb := setup(t) - txHash := utils.NewHash() - reqID1 := utils.NewHash() - reqID2 := utils.NewHash() + txHash := evmutils.NewHash() + reqID1 := evmutils.NewHash() + reqID2 := evmutils.NewHash() keyID := vuni.vrfkey.PublicKey.String() pk, err := secp256k1.NewPublicKeyFromHex(keyID) require.NoError(t, err) @@ -241,7 +242,7 @@ func TestDelegate_ValidLog(t *testing.T) { added <- struct{}{} }) preSeed := common.BigToHash(big.NewInt(42)).Bytes() - bh := utils.NewHash() + bh := evmutils.NewHash() var tt = []struct { reqID [32]byte log types.Log @@ -253,8 +254,8 @@ func TestDelegate_ValidLog(t *testing.T) { Data: bytes.Join([][]byte{ pk.MustHash().Bytes(), // key hash common.BigToHash(big.NewInt(42)).Bytes(), // seed - utils.NewHash().Bytes(), // sender - utils.NewHash().Bytes(), // fee + evmutils.NewHash().Bytes(), // sender + evmutils.NewHash().Bytes(), // fee reqID1.Bytes()}, // requestID []byte{}), // JobID is indexed, thats why it lives in the Topics. @@ -275,8 +276,8 @@ func TestDelegate_ValidLog(t *testing.T) { Data: bytes.Join([][]byte{ pk.MustHash().Bytes(), // key hash common.BigToHash(big.NewInt(42)).Bytes(), // seed - utils.NewHash().Bytes(), // sender - utils.NewHash().Bytes(), // fee + evmutils.NewHash().Bytes(), // sender + evmutils.NewHash().Bytes(), // fee reqID2.Bytes()}, // requestID []byte{}), Topics: []common.Hash{ @@ -324,7 +325,7 @@ func TestDelegate_ValidLog(t *testing.T) { // Should have 4 tasks all completed assert.Len(t, runs[0].PipelineTaskRuns, 4) - p, err := vuni.ks.VRF().GenerateProof(keyID, utils.MustHash(string(bytes.Join([][]byte{preSeed, bh.Bytes()}, []byte{}))).Big()) + p, err := vuni.ks.VRF().GenerateProof(keyID, evmutils.MustHash(string(bytes.Join([][]byte{preSeed, bh.Bytes()}, []byte{}))).Big()) require.NoError(t, err) vuni.lb.On("WasAlreadyConsumed", mock.Anything, mock.Anything).Return(false, nil) vuni.lb.On("MarkConsumed", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { @@ -367,11 +368,11 @@ func TestDelegate_InvalidLog(t *testing.T) { listener.HandleLog(log.NewLogBroadcast(types.Log{ // Data has all the NON-indexed parameters Data: append(append(append(append( - utils.NewHash().Bytes(), // key hash + evmutils.NewHash().Bytes(), // key hash common.BigToHash(big.NewInt(42)).Bytes()...), // seed - utils.NewHash().Bytes()...), // sender - utils.NewHash().Bytes()...), // fee - utils.NewHash().Bytes()...), // requestID + evmutils.NewHash().Bytes()...), // sender + evmutils.NewHash().Bytes()...), // fee + evmutils.NewHash().Bytes()...), // requestID // JobID is indexed, that's why it lives in the Topics. Topics: []common.Hash{ solidity_cross_tests.VRFRandomnessRequestLogTopic(), @@ -435,18 +436,18 @@ func TestFulfilledCheck(t *testing.T) { Data: bytes.Join([][]byte{ vuni.vrfkey.PublicKey.MustHash().Bytes(), // key hash common.BigToHash(big.NewInt(42)).Bytes(), // seed - utils.NewHash().Bytes(), // sender - utils.NewHash().Bytes(), // fee - utils.NewHash().Bytes()}, // requestID + evmutils.NewHash().Bytes(), // sender + evmutils.NewHash().Bytes(), // fee + evmutils.NewHash().Bytes()}, // requestID []byte{}), // JobID is indexed, that's why it lives in the Topics. Topics: []common.Hash{ solidity_cross_tests.VRFRandomnessRequestLogTopic(), jb.ExternalIDEncodeBytesToTopic(), // jobID STRING }, - //TxHash: utils.NewHash().Bytes(), + //TxHash: evmutils.NewHash().Bytes(), BlockNumber: 10, - //BlockHash: utils.NewHash().Bytes(), + //BlockHash: evmutils.NewHash().Bytes(), }, vuni.cid, nil)) // Should queue the request, even though its already fulfilled diff --git a/core/services/vrf/extraargs/types.go b/core/services/vrf/extraargs/types.go index 4dcc87e5d04..eecd0bfa334 100644 --- a/core/services/vrf/extraargs/types.go +++ b/core/services/vrf/extraargs/types.go @@ -5,7 +5,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) const functionSignatureLength = 4 diff --git a/core/services/vrf/proof/proof_response.go b/core/services/vrf/proof/proof_response.go index 4cb58d921a4..f4e332616ca 100644 --- a/core/services/vrf/proof/proof_response.go +++ b/core/services/vrf/proof/proof_response.go @@ -13,9 +13,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ProofResponse is the data which is sent back to the VRFCoordinator, so that diff --git a/core/services/vrf/proof/seed.go b/core/services/vrf/proof/seed.go index 176a5e013ec..75dc441d881 100644 --- a/core/services/vrf/proof/seed.go +++ b/core/services/vrf/proof/seed.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) // Seed represents a VRF seed as a serialized uint256 diff --git a/core/services/vrf/proof/solidity_proof.go b/core/services/vrf/proof/solidity_proof.go index c5823289cf1..48f2f9fd984 100644 --- a/core/services/vrf/proof/solidity_proof.go +++ b/core/services/vrf/proof/solidity_proof.go @@ -11,9 +11,9 @@ import ( "github.com/pkg/errors" "go.dedis.ch/kyber/v3" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" bm "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go index 35556c6b45f..3603230fea0 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // RawRandomnessRequestLog is used to parse a RandomnessRequest log into types diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go index 8dfbdae0148..e758689593a 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go @@ -13,9 +13,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/solidity_cross_tests" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" ) diff --git a/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go index 06875edd74e..90d6ab2c354 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go @@ -20,10 +20,10 @@ import ( "go.dedis.ch/kyber/v3" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Cross-checks of golang implementation details vs corresponding solidity diff --git a/core/services/vrf/v1/listener_v1_test.go b/core/services/vrf/v1/listener_v1_test.go index 4ab5d7ab368..c05358686df 100644 --- a/core/services/vrf/v1/listener_v1_test.go +++ b/core/services/vrf/v1/listener_v1_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/theodesp/go-heaps/pairing" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestConfirmedLogExtraction(t *testing.T) { diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index d8a7da70a86..47b839c9b8a 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_consumer_v2_upgradeable_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_external_sub_owner_example" @@ -870,7 +871,7 @@ func setupAndFundSubscriptionAndConsumer( uni.backend.Commit() if vrfVersion == vrfcommon.V2Plus { - b, err2 := utils.ABIEncode(`[{"type":"uint256"}]`, subID) + b, err2 := evmutils.ABIEncode(`[{"type":"uint256"}]`, subID) require.NoError(t, err2) _, err2 = uni.linkContract.TransferAndCall( uni.sergey, coordinatorAddress, fundingAmount, b) @@ -878,7 +879,7 @@ func setupAndFundSubscriptionAndConsumer( uni.backend.Commit() return } - b, err := utils.ABIEncode(`[{"type":"uint64"}]`, subID.Uint64()) + b, err := evmutils.ABIEncode(`[{"type":"uint64"}]`, subID.Uint64()) require.NoError(t, err) _, err = uni.linkContract.TransferAndCall( uni.sergey, coordinatorAddress, fundingAmount, b) diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 1564f0f6343..927f0ff2939 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2plus" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -51,7 +52,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type coordinatorV2PlusUniverse struct { diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 8d6354c4fd8..dffce9544d2 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -31,6 +31,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -40,6 +41,7 @@ import ( evmlogger "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" @@ -428,7 +430,7 @@ func deployOldCoordinator( ) { ctx := testutils.Context(t) bytecode := hexutil.MustDecode("0x60e06040523480156200001157600080fd5b506040516200608c3803806200608c8339810160408190526200003491620001b1565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000e8565b5050506001600160601b0319606093841b811660805290831b811660a052911b1660c052620001fb565b6001600160a01b038116331415620001435760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001ac57600080fd5b919050565b600080600060608486031215620001c757600080fd5b620001d28462000194565b9250620001e26020850162000194565b9150620001f26040850162000194565b90509250925092565b60805160601c60a05160601c60c05160601c615e2762000265600039600081816105260152613bd901526000818161061d015261402401526000818161036d01528181611599015281816125960152818161302c0152818161318201526138360152615e276000f3fe608060405234801561001057600080fd5b506004361061025b5760003560e01c80636f64f03f11610145578063ad178361116100bd578063d2f9f9a71161008c578063e72f6e3011610071578063e72f6e30146106fa578063e82ad7d41461070d578063f2fde38b1461073057600080fd5b8063d2f9f9a7146106d4578063d7ae1d30146106e757600080fd5b8063ad17836114610618578063af198b971461063f578063c3f909d41461066f578063caf70c4a146106c157600080fd5b80638da5cb5b11610114578063a21a23e4116100f9578063a21a23e4146105da578063a47c7696146105e2578063a4c0ed361461060557600080fd5b80638da5cb5b146105a95780639f87fad7146105c757600080fd5b80636f64f03f146105685780637341c10c1461057b57806379ba50971461058e578063823597401461059657600080fd5b8063356dac71116101d85780635fbbc0d2116101a757806366316d8d1161018c57806366316d8d1461050e578063689c45171461052157806369bcdb7d1461054857600080fd5b80635fbbc0d21461040057806364d51a2a1461050657600080fd5b8063356dac71146103b457806340d6bb82146103bc5780634cb48a54146103da5780635d3b1d30146103ed57600080fd5b806308821d581161022f57806315c48b841161021457806315c48b841461030e578063181f5a77146103295780631b6b6d231461036857600080fd5b806308821d58146102cf57806312b58349146102e257600080fd5b80620122911461026057806302bcc5b61461028057806304c357cb1461029557806306bfa637146102a8575b600080fd5b610268610743565b60405161027793929190615964565b60405180910390f35b61029361028e366004615792565b6107bf565b005b6102936102a33660046157ad565b61086b565b60055467ffffffffffffffff165b60405167ffffffffffffffff9091168152602001610277565b6102936102dd3660046154a3565b610a60565b6005546801000000000000000090046bffffffffffffffffffffffff165b604051908152602001610277565b61031660c881565b60405161ffff9091168152602001610277565b604080518082018252601681527f565246436f6f7264696e61746f72563220312e302e30000000000000000000006020820152905161027791906158f1565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610277565b600a54610300565b6103c56101f481565b60405163ffffffff9091168152602001610277565b6102936103e836600461563c565b610c3f565b6103006103fb366004615516565b611036565b600c546040805163ffffffff80841682526401000000008404811660208301526801000000000000000084048116928201929092526c010000000000000000000000008304821660608201527001000000000000000000000000000000008304909116608082015262ffffff740100000000000000000000000000000000000000008304811660a0830152770100000000000000000000000000000000000000000000008304811660c08301527a0100000000000000000000000000000000000000000000000000008304811660e08301527d01000000000000000000000000000000000000000000000000000000000090920490911661010082015261012001610277565b610316606481565b61029361051c36600461545b565b611444565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b610300610556366004615779565b60009081526009602052604090205490565b6102936105763660046153a0565b6116ad565b6102936105893660046157ad565b6117f7565b610293611a85565b6102936105a4366004615792565b611b82565b60005473ffffffffffffffffffffffffffffffffffffffff1661038f565b6102936105d53660046157ad565b611d7c565b6102b66121fd565b6105f56105f0366004615792565b6123ed565b6040516102779493929190615b02565b6102936106133660046153d4565b612537565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b61065261064d366004615574565b6127a8565b6040516bffffffffffffffffffffffff9091168152602001610277565b600b546040805161ffff8316815263ffffffff6201000084048116602083015267010000000000000084048116928201929092526b010000000000000000000000909204166060820152608001610277565b6103006106cf3660046154bf565b612c6d565b6103c56106e2366004615792565b612c9d565b6102936106f53660046157ad565b612e92565b610293610708366004615385565b612ff3565b61072061071b366004615792565b613257565b6040519015158152602001610277565b61029361073e366004615385565b6134ae565b600b546007805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff169391928391908301828280156107ad57602002820191906000526020600020905b815481526020019060010190808311610799575b50505050509050925092509250909192565b6107c76134bf565b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff1661082d576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205461086890829073ffffffffffffffffffffffffffffffffffffffff16613542565b50565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff16806108d4576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614610940576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b600b546601000000000000900460ff1615610987576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff848116911614610a5a5767ffffffffffffffff841660008181526003602090815260409182902060010180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b610a686134bf565b604080518082018252600091610a97919084906002908390839080828437600092019190915250612c6d915050565b60008181526006602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1680610af9576040517f77f5b84c00000000000000000000000000000000000000000000000000000000815260048101839052602401610937565b600082815260066020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b600754811015610be9578260078281548110610b4c57610b4c615dbc565b90600052602060002001541415610bd7576007805460009190610b7190600190615c76565b81548110610b8157610b81615dbc565b906000526020600020015490508060078381548110610ba257610ba2615dbc565b6000918252602090912001556007805480610bbf57610bbf615d8d565b60019003818190600052602060002001600090559055505b80610be181615cba565b915050610b2e565b508073ffffffffffffffffffffffffffffffffffffffff167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610c3291815260200190565b60405180910390a2505050565b610c476134bf565b60c861ffff87161115610c9a576040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff871660048201819052602482015260c86044820152606401610937565b60008213610cd7576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610937565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600b80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001690971762010000909502949094177fffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff166701000000000000009092027fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff16919091176b010000000000000000000000909302929092179093558651600c80549489015189890151938a0151978a0151968a015160c08b015160e08c01516101008d01519588167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009099169890981764010000000093881693909302929092177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1668010000000000000000958716959095027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff16949094176c0100000000000000000000000098861698909802979097177fffffffffffffffffff00000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000096909416959095027fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff16929092177401000000000000000000000000000000000000000062ffffff92831602177fffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffff1677010000000000000000000000000000000000000000000000958216959095027fffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16949094177a01000000000000000000000000000000000000000000000000000092851692909202919091177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167d0100000000000000000000000000000000000000000000000000000000009390911692909202919091178155600a84905590517fc21e3bd2e0b339d2848f0dd956947a88966c242c0c0c582a33137a5c1ceb5cb2916110269189918991899189918991906159c3565b60405180910390a1505050505050565b600b546000906601000000000000900460ff1615611080576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff166110e6576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260026020908152604080832067ffffffffffffffff808a1685529252909120541680611156576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff87166004820152336024820152604401610937565b600b5461ffff9081169086161080611172575060c861ffff8616115b156111c257600b546040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff8088166004830152909116602482015260c86044820152606401610937565b600b5463ffffffff620100009091048116908516111561122957600b546040517ff5d7e01e00000000000000000000000000000000000000000000000000000000815263ffffffff8087166004830152620100009092049091166024820152604401610937565b6101f463ffffffff8416111561127b576040517f47386bec00000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526101f46024820152604401610937565b6000611288826001615bd2565b6040805160208082018c9052338284015267ffffffffffffffff808c16606084015284166080808401919091528351808403909101815260a08301845280519082012060c083018d905260e080840182905284518085039091018152610100909301909352815191012091925060009182916040805160208101849052439181019190915267ffffffffffffffff8c16606082015263ffffffff808b166080830152891660a08201523360c0820152919350915060e001604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152828252805160209182012060008681526009835283902055848352820183905261ffff8a169082015263ffffffff808916606083015287166080820152339067ffffffffffffffff8b16908c907f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a97729060a00160405180910390a45033600090815260026020908152604080832067ffffffffffffffff808d16855292529091208054919093167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790915591505095945050505050565b600b546601000000000000900460ff161561148b576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600860205260409020546bffffffffffffffffffffffff808316911610156114e5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260086020526040812080548392906115129084906bffffffffffffffffffffffff16615c8d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080600560088282829054906101000a90046bffffffffffffffffffffffff166115699190615c8d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b815260040161162192919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b602060405180830381600087803b15801561163b57600080fd5b505af115801561164f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167391906154db565b6116a9576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6116b56134bf565b6040805180820182526000916116e4919084906002908390839080828437600092019190915250612c6d915050565b60008181526006602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1615611746576040517f4a0b8fa700000000000000000000000000000000000000000000000000000000815260048101829052602401610937565b600081815260066020908152604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091556007805460018101825594527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610c32565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680611860576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216146118c7576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610937565b600b546601000000000000900460ff161561190e576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206002015460641415611965576040517f05a48e0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff808916855292529091205416156119ac57610a5a565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260026020818152604080842067ffffffffffffffff8a1680865290835281852080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001908117909155600384528286209094018054948501815585529382902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001685179055905192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09101610a51565b60015473ffffffffffffffffffffffffffffffffffffffff163314611b06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610937565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600b546601000000000000900460ff1615611bc9576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff16611c2f576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff163314611cd15767ffffffffffffffff8116600090815260036020526040908190206001015490517fd084e97500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610937565b67ffffffffffffffff81166000818152600360209081526040918290208054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560019093018054909316909255835173ffffffffffffffffffffffffffffffffffffffff909116808252928101919091529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f0910160405180910390a25050565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680611de5576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614611e4c576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610937565b600b546601000000000000900460ff1615611e93576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff808916855292529091205416611f2e576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff84166024820152604401610937565b67ffffffffffffffff8416600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611fa957602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611f7e575b50505050509050600060018251611fc09190615c76565b905060005b825181101561215f578573ffffffffffffffffffffffffffffffffffffffff16838281518110611ff757611ff7615dbc565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16141561214d57600083838151811061202f5761202f615dbc565b6020026020010151905080600360008a67ffffffffffffffff1667ffffffffffffffff168152602001908152602001600020600201838154811061207557612075615dbc565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff8a1681526003909152604090206002018054806120ef576120ef615d8d565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190555061215f565b8061215781615cba565b915050611fc5565b5073ffffffffffffffffffffffffffffffffffffffff8516600081815260026020908152604080832067ffffffffffffffff8b168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b91015b60405180910390a2505050505050565b600b546000906601000000000000900460ff1615612247576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005805467ffffffffffffffff1690600061226183615cf3565b82546101009290920a67ffffffffffffffff8181021990931691831602179091556005541690506000806040519080825280602002602001820160405280156122b4578160200160208202803683370190505b506040805180820182526000808252602080830182815267ffffffffffffffff888116808552600484528685209551865493516bffffffffffffffffffffffff9091167fffffffffffffffffffffffff0000000000000000000000000000000000000000948516176c010000000000000000000000009190931602919091179094558451606081018652338152808301848152818701888152958552600384529590932083518154831673ffffffffffffffffffffffffffffffffffffffff918216178255955160018201805490931696169590951790559151805194955090936123a592600285019201906150c5565b505060405133815267ffffffffffffffff841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b67ffffffffffffffff81166000908152600360205260408120548190819060609073ffffffffffffffffffffffffffffffffffffffff1661245a576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff80861660009081526004602090815260408083205460038352928190208054600290910180548351818602810186019094528084526bffffffffffffffffffffffff8616966c010000000000000000000000009096049095169473ffffffffffffffffffffffffffffffffffffffff90921693909291839183018282801561252157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116124f6575b5050505050905093509350935093509193509193565b600b546601000000000000900460ff161561257e576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146125ed576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208114612627576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061263582840184615792565b67ffffffffffffffff811660009081526003602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1661269e576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260046020526040812080546bffffffffffffffffffffffff16918691906126d58385615bfe565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555084600560088282829054906101000a90046bffffffffffffffffffffffff1661272c9190615bfe565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508167ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88287846127939190615bba565b604080519283526020830191909152016121ed565b600b546000906601000000000000900460ff16156127f2576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a9050600080600061280687876139b5565b9250925092506000866060015163ffffffff1667ffffffffffffffff81111561283157612831615deb565b60405190808252806020026020018201604052801561285a578160200160208202803683370190505b50905060005b876060015163ffffffff168110156128ce5760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c8282815181106128b1576128b1615dbc565b6020908102919091010152806128c681615cba565b915050612860565b506000838152600960205260408082208290555181907f1fe543e300000000000000000000000000000000000000000000000000000000906129169087908690602401615ab4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff166601000000000000179055908a015160808b01519192506000916129e49163ffffffff169084613d04565b600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff1690556020808c01805167ffffffffffffffff9081166000908152600490935260408084205492518216845290922080549394506c01000000000000000000000000918290048316936001939192600c92612a68928692900416615bd2565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000612abf8a600b600001600b9054906101000a900463ffffffff1663ffffffff16612ab985612c9d565b3a613d52565b6020808e015167ffffffffffffffff166000908152600490915260409020549091506bffffffffffffffffffffffff80831691161015612b2b576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808d015167ffffffffffffffff1660009081526004909152604081208054839290612b679084906bffffffffffffffffffffffff16615c8d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b81526006602090815260408083205473ffffffffffffffffffffffffffffffffffffffff1683526008909152812080548594509092612bd091859116615bfe565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550877f7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4888386604051612c53939291909283526bffffffffffffffffffffffff9190911660208301521515604082015260600190565b60405180910390a299505050505050505050505b92915050565b600081604051602001612c8091906158e3565b604051602081830303815290604052805190602001209050919050565b6040805161012081018252600c5463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c010000000000000000000000008104831660608301527001000000000000000000000000000000008104909216608082015262ffffff740100000000000000000000000000000000000000008304811660a08301819052770100000000000000000000000000000000000000000000008404821660c08401527a0100000000000000000000000000000000000000000000000000008404821660e08401527d0100000000000000000000000000000000000000000000000000000000009093041661010082015260009167ffffffffffffffff841611612dbb575192915050565b8267ffffffffffffffff168160a0015162ffffff16108015612df057508060c0015162ffffff168367ffffffffffffffff1611155b15612dff576020015192915050565b8267ffffffffffffffff168160c0015162ffffff16108015612e3457508060e0015162ffffff168367ffffffffffffffff1611155b15612e43576040015192915050565b8267ffffffffffffffff168160e0015162ffffff16108015612e79575080610100015162ffffff168367ffffffffffffffff1611155b15612e88576060015192915050565b6080015192915050565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680612efb576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614612f62576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610937565b600b546601000000000000900460ff1615612fa9576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612fb284613257565b15612fe9576040517fb42f66e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5a8484613542565b612ffb6134bf565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561308357600080fd5b505afa158015613097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bb91906154fd565b6005549091506801000000000000000090046bffffffffffffffffffffffff168181111561311f576040517fa99da3020000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401610937565b818110156132525760006131338284615c76565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401602060405180830381600087803b1580156131c857600080fd5b505af11580156131dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320091906154db565b506040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a1505b505050565b67ffffffffffffffff811660009081526003602090815260408083208151606081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561330657602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116132db575b505050505081525050905060005b8160400151518110156134a45760005b60075481101561349157600061345a6007838154811061334657613346615dbc565b90600052602060002001548560400151858151811061336757613367615dbc565b602002602001015188600260008960400151898151811061338a5761338a615dbc565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff808f168352935220541660408051602080820187905273ffffffffffffffffffffffffffffffffffffffff959095168183015267ffffffffffffffff9384166060820152919092166080808301919091528251808303909101815260a08201835280519084012060c082019490945260e080820185905282518083039091018152610100909101909152805191012091565b506000818152600960205260409020549091501561347e5750600195945050505050565b508061348981615cba565b915050613324565b508061349c81615cba565b915050613314565b5060009392505050565b6134b66134bf565b61086881613e5a565b60005473ffffffffffffffffffffffffffffffffffffffff163314613540576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610937565b565b600b546601000000000000900460ff1615613589576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff821660009081526003602090815260408083208151606081018352815473ffffffffffffffffffffffffffffffffffffffff90811682526001830154168185015260028201805484518187028101870186528181529295939486019383018282801561363457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311613609575b5050509190925250505067ffffffffffffffff80851660009081526004602090815260408083208151808301909252546bffffffffffffffffffffffff81168083526c01000000000000000000000000909104909416918101919091529293505b83604001515181101561373b5760026000856040015183815181106136bc576136bc615dbc565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff8a168252909252902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690558061373381615cba565b915050613695565b5067ffffffffffffffff8516600090815260036020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054909116905590613796600283018261514f565b505067ffffffffffffffff8516600090815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600580548291906008906138069084906801000000000000000090046bffffffffffffffffffffffff16615c8d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85836bffffffffffffffffffffffff166040518363ffffffff1660e01b81526004016138be92919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b602060405180830381600087803b1580156138d857600080fd5b505af11580156138ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061391091906154db565b613946576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff861681526bffffffffffffffffffffffff8316602082015267ffffffffffffffff8716917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050565b60008060006139c78560000151612c6d565b60008181526006602052604090205490935073ffffffffffffffffffffffffffffffffffffffff1680613a29576040517f77f5b84c00000000000000000000000000000000000000000000000000000000815260048101859052602401610937565b6080860151604051613a48918691602001918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600990935291205490935080613ac5576040517f3688124a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516020808801516040808a015160608b015160808c01519251613b3e968b96909594910195865267ffffffffffffffff948516602087015292909316604085015263ffffffff908116606085015291909116608083015273ffffffffffffffffffffffffffffffffffffffff1660a082015260c00190565b604051602081830303815290604052805190602001208114613b8c576040517fd529142c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855167ffffffffffffffff164080613cb05786516040517fe9413d3800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e9413d389060240160206040518083038186803b158015613c3057600080fd5b505afa158015613c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c6891906154fd565b905080613cb05786516040517f175dadad00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610937565b6000886080015182604051602001613cd2929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c9050613cf78982613f50565b9450505050509250925092565b60005a611388811015613d1657600080fd5b611388810390508460408204820311613d2e57600080fd5b50823b613d3a57600080fd5b60008083516020850160008789f190505b9392505050565b600080613d5d613fd9565b905060008113613d9c576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101829052602401610937565b6000815a613daa8989615bba565b613db49190615c76565b613dc686670de0b6b3a7640000615c39565b613dd09190615c39565b613dda9190615c25565b90506000613df363ffffffff871664e8d4a51000615c39565b9050613e0b816b033b2e3c9fd0803ce8000000615c76565b821115613e44576040517fe80fa38100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e4e8183615bba565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116331415613eda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610937565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000613f848360000151846020015185604001518660600151868860a001518960c001518a60e001518b61010001516140ed565b60038360200151604051602001613f9c929190615aa0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b600b54604080517ffeaf968c0000000000000000000000000000000000000000000000000000000081529051600092670100000000000000900463ffffffff169182151591849182917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163feaf968c9160048083019260a0929190829003018186803b15801561407f57600080fd5b505afa158015614093573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140b791906157d7565b5094509092508491505080156140db57506140d28242615c76565b8463ffffffff16105b156140e55750600a545b949350505050565b6140f6896143c4565b61415c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e2063757276650000000000006044820152606401610937565b614165886143c4565b6141cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f67616d6d61206973206e6f74206f6e20637572766500000000000000000000006044820152606401610937565b6141d4836143c4565b61423a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610937565b614243826143c4565b6142a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610937565b6142b5878a888761451f565b61431b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e657373000000000000006044820152606401610937565b60006143278a876146c2565b9050600061433a898b878b868989614726565b9050600061434b838d8d8a866148ae565b9050808a146143b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c69642070726f6f66000000000000000000000000000000000000006044820152606401610937565b505050505050505050505050565b80516000907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f11614451576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420782d6f7264696e61746500000000000000000000000000006044820152606401610937565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f116144de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420792d6f7264696e61746500000000000000000000000000006044820152606401610937565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9080096145188360005b602002015161490c565b1492915050565b600073ffffffffffffffffffffffffffffffffffffffff821661459e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f626164207769746e6573730000000000000000000000000000000000000000006044820152606401610937565b6020840151600090600116156145b557601c6145b8565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418587600060200201510986517ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa15801561466f573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff9081169088161495505050505050949350505050565b6146ca61516d565b6146f7600184846040516020016146e3939291906158c2565b604051602081830303815290604052614964565b90505b614703816143c4565b612c6757805160408051602081019290925261471f91016146e3565b90506146fa565b61472e61516d565b825186517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f90819006910614156147c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610937565b6147cc8789886149cd565b614832576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4669727374206d756c20636865636b206661696c6564000000000000000000006044820152606401610937565b61483d8486856149cd565b6148a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c65640000000000000000006044820152606401610937565b613e4e868484614b5a565b6000600286868685876040516020016148cc96959493929190615850565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209695505050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80848509840990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f600782089392505050565b61496c61516d565b61497582614c89565b815261498a61498582600061450e565b614cde565b6020820181905260029006600114156149c8576020810180517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0390525b919050565b600082614a36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f7a65726f207363616c61720000000000000000000000000000000000000000006044820152606401610937565b83516020850151600090614a4c90600290615d1b565b15614a5857601c614a5b565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614adb573d6000803e3d6000fd5b505050602060405103519050600086604051602001614afa919061583e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012073ffffffffffffffffffffffffffffffffffffffff92831692169190911498975050505050505050565b614b6261516d565b835160208086015185519186015160009384938493614b8393909190614d18565b919450925090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f858209600114614c17576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a000000000000006044820152606401610937565b60405180604001604052807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80614c5057614c50615d5e565b87860981526020017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8785099052979650505050505050565b805160208201205b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f81106149c857604080516020808201939093528151808203840181529082019091528051910120614c91565b6000612c67826002614d117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f6001615bba565b901c614eae565b60008080600180827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f897ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038808905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038a0890506000614dc083838585614fa2565b9098509050614dd188828e88614ffa565b9098509050614de288828c87614ffa565b90985090506000614df58d878b85614ffa565b9098509050614e0688828686614fa2565b9098509050614e1788828e89614ffa565b9098509050818114614e9a577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f818a0998507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f82890997507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183099650614e9e565b8196505b5050505050509450945094915050565b600080614eb961518b565b6020808252818101819052604082015260608101859052608081018490527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f60a0820152614f056151a9565b60208160c08460057ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa925082614f98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6269674d6f64457870206661696c7572652100000000000000000000000000006044820152606401610937565b5195945050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487097ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487099097909650945050505050565b600080807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f878509905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f030990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183087ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f86890990999098509650505050505050565b82805482825590600052602060002090810192821561513f579160200282015b8281111561513f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161782556020909201916001909101906150e5565b5061514b9291506151c7565b5090565b508054600082559060005260206000209081019061086891906151c7565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b8082111561514b57600081556001016151c8565b803573ffffffffffffffffffffffffffffffffffffffff811681146149c857600080fd5b8060408101831015612c6757600080fd5b600082601f83011261522257600080fd5b6040516040810181811067ffffffffffffffff8211171561524557615245615deb565b806040525080838560408601111561525c57600080fd5b60005b600281101561527e57813583526020928301929091019060010161525f565b509195945050505050565b600060a0828403121561529b57600080fd5b60405160a0810181811067ffffffffffffffff821117156152be576152be615deb565b6040529050806152cd83615353565b81526152db60208401615353565b60208201526152ec6040840161533f565b60408201526152fd6060840161533f565b606082015261530e608084016151dc565b60808201525092915050565b803561ffff811681146149c857600080fd5b803562ffffff811681146149c857600080fd5b803563ffffffff811681146149c857600080fd5b803567ffffffffffffffff811681146149c857600080fd5b805169ffffffffffffffffffff811681146149c857600080fd5b60006020828403121561539757600080fd5b613d4b826151dc565b600080606083850312156153b357600080fd5b6153bc836151dc565b91506153cb8460208501615200565b90509250929050565b600080600080606085870312156153ea57600080fd5b6153f3856151dc565b935060208501359250604085013567ffffffffffffffff8082111561541757600080fd5b818701915087601f83011261542b57600080fd5b81358181111561543a57600080fd5b88602082850101111561544c57600080fd5b95989497505060200194505050565b6000806040838503121561546e57600080fd5b615477836151dc565b915060208301356bffffffffffffffffffffffff8116811461549857600080fd5b809150509250929050565b6000604082840312156154b557600080fd5b613d4b8383615200565b6000604082840312156154d157600080fd5b613d4b8383615211565b6000602082840312156154ed57600080fd5b81518015158114613d4b57600080fd5b60006020828403121561550f57600080fd5b5051919050565b600080600080600060a0868803121561552e57600080fd5b8535945061553e60208701615353565b935061554c6040870161531a565b925061555a6060870161533f565b91506155686080870161533f565b90509295509295909350565b60008082840361024081121561558957600080fd5b6101a08082121561559957600080fd5b6155a1615b90565b91506155ad8686615211565b82526155bc8660408701615211565b60208301526080850135604083015260a0850135606083015260c085013560808301526155eb60e086016151dc565b60a08301526101006155ff87828801615211565b60c0840152615612876101408801615211565b60e0840152610180860135818401525081935061563186828701615289565b925050509250929050565b6000806000806000808688036101c081121561565757600080fd5b6156608861531a565b965061566e6020890161533f565b955061567c6040890161533f565b945061568a6060890161533f565b935060808801359250610120807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60830112156156c557600080fd5b6156cd615b90565b91506156db60a08a0161533f565b82526156e960c08a0161533f565b60208301526156fa60e08a0161533f565b604083015261010061570d818b0161533f565b606084015261571d828b0161533f565b608084015261572f6101408b0161532c565b60a08401526157416101608b0161532c565b60c08401526157536101808b0161532c565b60e08401526157656101a08b0161532c565b818401525050809150509295509295509295565b60006020828403121561578b57600080fd5b5035919050565b6000602082840312156157a457600080fd5b613d4b82615353565b600080604083850312156157c057600080fd5b6157c983615353565b91506153cb602084016151dc565b600080600080600060a086880312156157ef57600080fd5b6157f88661536b565b94506020860151935060408601519250606086015191506155686080870161536b565b8060005b6002811015610a5a57815184526020938401939091019060010161581f565b615848818361581b565b604001919050565b868152615860602082018761581b565b61586d606082018661581b565b61587a60a082018561581b565b61588760e082018461581b565b60609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166101208201526101340195945050505050565b8381526158d2602082018461581b565b606081019190915260800192915050565b60408101612c67828461581b565b600060208083528351808285015260005b8181101561591e57858101830151858201604001528201615902565b81811115615930576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b818110156159b557845183529383019391830191600101615999565b509098975050505050505050565b60006101c08201905061ffff8816825263ffffffff808816602084015280871660408401528086166060840152846080840152835481811660a0850152615a1760c08501838360201c1663ffffffff169052565b615a2e60e08501838360401c1663ffffffff169052565b615a466101008501838360601c1663ffffffff169052565b615a5e6101208501838360801c1663ffffffff169052565b62ffffff60a082901c811661014086015260b882901c811661016086015260d082901c1661018085015260e81c6101a090930192909252979650505050505050565b82815260608101613d4b602083018461581b565b6000604082018483526020604081850152818551808452606086019150828701935060005b81811015615af557845183529383019391830191600101615ad9565b5090979650505050505050565b6000608082016bffffffffffffffffffffffff87168352602067ffffffffffffffff87168185015273ffffffffffffffffffffffffffffffffffffffff80871660408601526080606086015282865180855260a087019150838801945060005b81811015615b80578551841683529484019491840191600101615b62565b50909a9950505050505050505050565b604051610120810167ffffffffffffffff81118282101715615bb457615bb4615deb565b60405290565b60008219821115615bcd57615bcd615d2f565b500190565b600067ffffffffffffffff808316818516808303821115615bf557615bf5615d2f565b01949350505050565b60006bffffffffffffffffffffffff808316818516808303821115615bf557615bf5615d2f565b600082615c3457615c34615d5e565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615c7157615c71615d2f565b500290565b600082821015615c8857615c88615d2f565b500390565b60006bffffffffffffffffffffffff83811690831681811015615cb257615cb2615d2f565b039392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615cec57615cec615d2f565b5060010190565b600067ffffffffffffffff80831681811415615d1157615d11615d2f565b6001019392505050565b600082615d2a57615d2a615d5e565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a") - ctorArgs, err := utils.ABIEncode(`[{"type":"address"}, {"type":"address"}, {"type":"address"}]`, linkAddress, bhsAddress, linkEthFeed) + ctorArgs, err := evmutils.ABIEncode(`[{"type":"address"}, {"type":"address"}, {"type":"address"}]`, linkAddress, bhsAddress, linkEthFeed) require.NoError(t, err) bytecode = append(bytecode, ctorArgs...) nonce, err := backend.PendingNonceAt(ctx, neil.From) @@ -1175,7 +1177,7 @@ func TestVRFV2Integration_SingleConsumer_Wrapper(t *testing.T) { require.NoError(t, err) // Fund Subscription. - b, err := utils.ABIEncode(`[{"type":"uint64"}]`, wrapperSubID) + b, err := evmutils.ABIEncode(`[{"type":"uint64"}]`, wrapperSubID) require.NoError(t, err) _, err = uni.linkContract.TransferAndCall(uni.sergey, uni.rootContractAddress, assets.Ether(100).ToInt(), b) require.NoError(t, err) @@ -1257,7 +1259,7 @@ func TestVRFV2Integration_Wrapper_High_Gas(t *testing.T) { require.NoError(t, err) // Fund Subscription. - b, err := utils.ABIEncode(`[{"type":"uint64"}]`, wrapperSubID) + b, err := evmutils.ABIEncode(`[{"type":"uint64"}]`, wrapperSubID) require.NoError(t, err) _, err = uni.linkContract.TransferAndCall(uni.sergey, uni.rootContractAddress, assets.Ether(100).ToInt(), b) require.NoError(t, err) @@ -1530,7 +1532,7 @@ func TestExternalOwnerConsumerExample(t *testing.T) { _, err = coordinator.CreateSubscription(owner) require.NoError(t, err) backend.Commit() - b, err := utils.ABIEncode(`[{"type":"uint64"}]`, uint64(1)) + b, err := evmutils.ABIEncode(`[{"type":"uint64"}]`, uint64(1)) require.NoError(t, err) _, err = linkContract.TransferAndCall(owner, coordinatorAddress, big.NewInt(0), b) require.NoError(t, err) @@ -2044,14 +2046,14 @@ func TestStartingCountsV1(t *testing.T) { require.NoError(t, err) b := time.Now() n1, n2, n3, n4 := evmtypes.Nonce(0), evmtypes.Nonce(1), evmtypes.Nonce(2), evmtypes.Nonce(3) - reqID := utils.PadByteToHash(0x10) + reqID := evmutils.PadByteToHash(0x10) m1 := txmgr.TxMeta{ RequestID: &reqID, } md1, err := json.Marshal(&m1) require.NoError(t, err) md1SQL := sqlutil.JSON(md1) - reqID2 := utils.PadByteToHash(0x11) + reqID2 := evmutils.PadByteToHash(0x11) m2 := txmgr.TxMeta{ RequestID: &reqID2, } @@ -2112,7 +2114,7 @@ func TestStartingCountsV1(t *testing.T) { // add unconfirmed txes unconfirmedTxes := []txmgr.Tx{} for i := int64(4); i < 6; i++ { - reqID3 := utils.PadByteToHash(0x12) + reqID3 := evmutils.PadByteToHash(0x12) md, err2 := json.Marshal(&txmgr.TxMeta{ RequestID: &reqID3, }) @@ -2146,7 +2148,7 @@ func TestStartingCountsV1(t *testing.T) { TxID: int64(i + 1), TxFee: gas.EvmFee{Legacy: assets.NewWeiI(100)}, SignedRawTx: []byte(`blah`), - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), BroadcastBeforeBlockNum: &broadcastBlock, State: txmgrtypes.TxAttemptBroadcast, CreatedAt: time.Now(), @@ -2159,7 +2161,7 @@ func TestStartingCountsV1(t *testing.T) { TxID: int64(i + 1 + len(confirmedTxes)), TxFee: gas.EvmFee{Legacy: assets.NewWeiI(100)}, SignedRawTx: []byte(`blah`), - Hash: utils.NewHash(), + Hash: evmutils.NewHash(), State: txmgrtypes.TxAttemptInProgress, CreatedAt: time.Now(), ChainSpecificFeeLimit: uint32(100), @@ -2177,7 +2179,7 @@ func TestStartingCountsV1(t *testing.T) { receipts := []evmtypes.Receipt{} for i := 0; i < 4; i++ { receipts = append(receipts, evmtypes.Receipt{ - BlockHash: utils.NewHash(), + BlockHash: evmutils.NewHash(), TxHash: txAttempts[i].Hash, BlockNumber: big.NewInt(broadcastBlock), TransactionIndex: 1, @@ -2191,9 +2193,9 @@ func TestStartingCountsV1(t *testing.T) { counts, err = listenerV1.GetStartingResponseCountsV1(testutils.Context(t)) require.NoError(t, err) assert.Equal(t, 3, len(counts)) - assert.Equal(t, uint64(1), counts[utils.PadByteToHash(0x10)]) - assert.Equal(t, uint64(2), counts[utils.PadByteToHash(0x11)]) - assert.Equal(t, uint64(2), counts[utils.PadByteToHash(0x12)]) + assert.Equal(t, uint64(1), counts[evmutils.PadByteToHash(0x10)]) + assert.Equal(t, uint64(2), counts[evmutils.PadByteToHash(0x11)]) + assert.Equal(t, uint64(2), counts[evmutils.PadByteToHash(0x12)]) countsV2, err := listenerV2.GetStartingResponseCountsV2(testutils.Context(t)) require.NoError(t, err) diff --git a/core/services/vrf/v2/listener_v2_log_processor.go b/core/services/vrf/v2/listener_v2_log_processor.go index 004ab4c4905..eebe9038c0c 100644 --- a/core/services/vrf/v2/listener_v2_log_processor.go +++ b/core/services/vrf/v2/listener_v2_log_processor.go @@ -20,6 +20,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -1182,7 +1183,13 @@ func (lsn *listenerV2) simulateFulfillment( res.err = errors.New("expected []uint8 final result") return res } - res.maxFee = utils.HexToBig(hexutil.Encode(b)[2:]) + + res.maxFee, err = hex.ParseBig(hexutil.Encode(b)[2:]) + if err != nil { + res.err = err + return res + } + for _, trr := range trrs { if trr.Task.Type() == pipeline.TaskTypeVRFV2 { m := trr.Result.Value.(map[string]interface{}) diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index e9dd25fa0f7..f2a40ff332a 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -9,9 +9,9 @@ import ( "github.com/google/uuid" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/core/utils/hash_helpers.go b/core/utils/hash_helpers.go deleted file mode 100644 index b0a284be716..00000000000 --- a/core/utils/hash_helpers.go +++ /dev/null @@ -1,24 +0,0 @@ -package utils - -import ( - "crypto/rand" - - "github.com/ethereum/go-ethereum/common" -) - -// NewHash return random Keccak256 -func NewHash() common.Hash { - b := make([]byte, 32) - _, err := rand.Read(b) - if err != nil { - panic(err) - } - return common.BytesToHash(b) -} - -// PadByteToHash returns a hash with zeros padded on the left of the given byte. -func PadByteToHash(b byte) common.Hash { - var h [32]byte - h[31] = b - return h -} diff --git a/core/utils/hash_helpers_test.go b/core/utils/hash_helpers_test.go deleted file mode 100644 index 8fc864dc407..00000000000 --- a/core/utils/hash_helpers_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package utils_test - -import ( - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -func TestNewHash(t *testing.T) { - t.Parallel() - - h1 := utils.NewHash() - h2 := utils.NewHash() - assert.NotEqual(t, h1, h2) - assert.NotEqual(t, h1, common.HexToHash("0x0")) - assert.NotEqual(t, h2, common.HexToHash("0x0")) -} - -func TestPadByteToHash(t *testing.T) { - t.Parallel() - - h := utils.PadByteToHash(1) - assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000001", h.String()) -} diff --git a/core/utils/utils.go b/core/utils/utils.go index 4f3f9212337..3a53e664ecd 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( + "bytes" "context" "crypto/ed25519" "crypto/rand" @@ -11,7 +12,6 @@ import ( "errors" "fmt" "math" - "math/big" mrand "math/rand" "sort" "strings" @@ -19,8 +19,6 @@ import ( "sync/atomic" "time" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/google/uuid" "github.com/jpillora/backoff" pkgerrors "github.com/pkg/errors" @@ -33,34 +31,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" ) -const ( - // DefaultSecretSize is the entropy in bytes to generate a base64 string of 64 characters. - DefaultSecretSize = 48 - // EVMWordByteLen the length of an EVM Word Byte - EVMWordByteLen = 32 -) - -// ZeroAddress is an address of all zeroes, otherwise in Ethereum as -// 0x0000000000000000000000000000000000000000 -var ZeroAddress = common.Address{} - -func RandomAddress() common.Address { - b := make([]byte, 20) - _, _ = rand.Read(b) // Assignment for errcheck. Only used in tests so we can ignore. - return common.BytesToAddress(b) -} - -func RandomBytes32() (r [32]byte) { - b := make([]byte, 32) - _, _ = rand.Read(b[:]) // Assignment for errcheck. Only used in tests so we can ignore. - copy(r[:], b) - return -} - -func Bytes32ToSlice(a [32]byte) (r []byte) { - r = append(r, a[:]...) - return -} +// DefaultSecretSize is the entropy in bytes to generate a base64 string of 64 characters. +const DefaultSecretSize = 48 func MustNewPeerID() string { pubKey, _, err := ed25519.GenerateKey(rand.Reader) @@ -74,22 +46,6 @@ func MustNewPeerID() string { return peerID.String() } -// EmptyHash is a hash of all zeroes, otherwise in Ethereum as -// 0x0000000000000000000000000000000000000000000000000000000000000000 -var EmptyHash = common.Hash{} - -// Uint256ToBytes is x represented as the bytes of a uint256 -func Uint256ToBytes(x *big.Int) (uint256 []byte, err error) { - if x.Cmp(MaxUint256) > 0 { - return nil, fmt.Errorf("too large to convert to uint256") - } - uint256 = common.LeftPadBytes(x.Bytes(), EVMWordByteLen) - if x.Cmp(big.NewInt(0).SetBytes(uint256)) != 0 { - panic("failed to round-trip uint256 back to source big.Int") - } - return uint256, err -} - // ISO8601UTC formats given time to ISO8601. func ISO8601UTC(t time.Time) string { return t.UTC().Format(time.RFC3339) @@ -125,29 +81,6 @@ func NewSecret(n int) string { return base64.StdEncoding.EncodeToString(b) } -// RemoveHexPrefix removes the prefix (0x) of a given hex string. -func RemoveHexPrefix(str string) string { - if HasHexPrefix(str) { - return str[2:] - } - return str -} - -// HasHexPrefix returns true if the string starts with 0x. -func HasHexPrefix(str string) bool { - return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') -} - -// IsEmptyAddress checks that the address is empty, synonymous with the zero -// account/address. No logs can come from this address, as there is no contract -// present there. -// -// See https://stackoverflow.com/questions/48219716/what-is-address0-in-solidity -// for the more info on the zero address. -func IsEmptyAddress(addr common.Address) bool { - return addr == ZeroAddress -} - // StringToHex converts a standard string to a hex encoded string. func StringToHex(in string) string { return AddHexPrefix(hex.EncodeToString([]byte(in))) @@ -171,82 +104,6 @@ func IsEmpty(bytes []byte) bool { return true } -// Sleeper interface is used for tasks that need to be done on some -// interval, excluding Cron, like reconnecting. -type Sleeper interface { - Reset() - Sleep() - After() time.Duration - Duration() time.Duration -} - -// BackoffSleeper is a sleeper that backs off on subsequent attempts. -type BackoffSleeper struct { - backoff.Backoff - beenRun atomic.Bool -} - -// NewBackoffSleeper returns a BackoffSleeper that is configured to -// sleep for 0 seconds initially, then backs off from 1 second minimum -// to 10 seconds maximum. -func NewBackoffSleeper() *BackoffSleeper { - return &BackoffSleeper{ - Backoff: backoff.Backoff{ - Min: 1 * time.Second, - Max: 10 * time.Second, - }, - } -} - -// Sleep waits for the given duration, incrementing the back off. -func (bs *BackoffSleeper) Sleep() { - if bs.beenRun.CompareAndSwap(false, true) { - return - } - time.Sleep(bs.Backoff.Duration()) -} - -// After returns the duration for the next stop, and increments the backoff. -func (bs *BackoffSleeper) After() time.Duration { - if bs.beenRun.CompareAndSwap(false, true) { - return 0 - } - return bs.Backoff.Duration() -} - -// Duration returns the current duration value. -func (bs *BackoffSleeper) Duration() time.Duration { - if !bs.beenRun.Load() { - return 0 - } - return bs.ForAttempt(bs.Attempt()) -} - -// Reset resets the backoff intervals. -func (bs *BackoffSleeper) Reset() { - bs.beenRun.Store(false) - bs.Backoff.Reset() -} - -// RetryWithBackoff retries the sleeper and backs off if not Done -func RetryWithBackoff(ctx context.Context, fn func() (retry bool)) { - sleeper := NewBackoffSleeper() - sleeper.Reset() - for { - retry := fn() - if !retry { - return - } - - select { - case <-ctx.Done(): - return - case <-time.After(sleeper.After()): - continue - } - } -} - // UnmarshalToMap takes an input json string and returns a map[string]interface i.e. a raw object func UnmarshalToMap(input string) (map[string]interface{}, error) { var output map[string]interface{} @@ -275,24 +132,6 @@ func CheckPasswordHash(password, hash string) bool { return err == nil } -// Keccak256 is a simplified interface for the legacy SHA3 implementation that -// Ethereum uses. -func Keccak256(in []byte) ([]byte, error) { - hash := sha3.NewLegacyKeccak256() - _, err := hash.Write(in) - return hash.Sum(nil), err -} - -func Keccak256Fixed(in []byte) [32]byte { - hash := sha3.NewLegacyKeccak256() - // Note this Keccak256 cannot error https://github.com/golang/crypto/blob/master/sha3/sha3.go#L126 - // if we start supporting hashing algos which do, we can change this API to include an error. - hash.Write(in) - var h [32]byte - copy(h[:], hash.Sum(nil)) - return h -} - // Sha256 returns a hexadecimal encoded string of a hashed input func Sha256(in string) (string, error) { hasher := sha3.New256() @@ -303,89 +142,11 @@ func Sha256(in string) (string, error) { return hex.EncodeToString(hasher.Sum(nil)), nil } -// EIP55CapitalizedAddress returns true iff possibleAddressString has the correct -// capitalization for an Ethereum address, per EIP 55 -func EIP55CapitalizedAddress(possibleAddressString string) bool { - if !HasHexPrefix(possibleAddressString) { - possibleAddressString = "0x" + possibleAddressString - } - EIP55Capitalized := common.HexToAddress(possibleAddressString).Hex() - return possibleAddressString == EIP55Capitalized -} - -// ParseEthereumAddress returns addressString as a go-ethereum Address, or an -// error if it's invalid, e.g. if EIP 55 capitalization check fails -func ParseEthereumAddress(addressString string) (common.Address, error) { - if !common.IsHexAddress(addressString) { - return common.Address{}, fmt.Errorf( - "not a valid Ethereum address: %s", addressString) - } - address := common.HexToAddress(addressString) - if !EIP55CapitalizedAddress(addressString) { - return common.Address{}, fmt.Errorf( - "%s treated as Ethereum address, but it has an invalid capitalization! "+ - "The correctly-capitalized address would be %s, but "+ - "check carefully before copying and pasting! ", - addressString, address.Hex()) - } - return address, nil -} - -// MustHash returns the keccak256 hash, or panics on failure. -func MustHash(in string) common.Hash { - out, err := Keccak256([]byte(in)) - if err != nil { - panic(err) - } - return common.BytesToHash(out) -} - // JustError takes a tuple and returns the last entry, the error. func JustError(_ interface{}, err error) error { return err } -var zero = big.NewInt(0) - -// CheckUint256 returns an error if n is out of bounds for a uint256 -func CheckUint256(n *big.Int) error { - if n.Cmp(zero) < 0 || n.Cmp(MaxUint256) >= 0 { - return fmt.Errorf("number out of range for uint256") - } - return nil -} - -// HexToUint256 returns the uint256 represented by s, or an error if it doesn't -// represent one. -func HexToUint256(s string) (*big.Int, error) { - rawNum, err := hexutil.Decode(s) - if err != nil { - return nil, pkgerrors.Wrapf(err, "while parsing %s as hex: ", s) - } - rv := big.NewInt(0).SetBytes(rawNum) // can't be negative number - if err := CheckUint256(rv); err != nil { - return nil, err - } - return rv, nil -} - -// HexToBig parses the given hex string or panics if it is invalid. -func HexToBig(s string) *big.Int { - n, ok := new(big.Int).SetString(s, 16) - if !ok { - panic(fmt.Errorf(`failed to convert "%s" as hex to big.Int`, s)) - } - return n -} - -// Uint256ToBytes32 returns the bytes32 encoding of the big int provided -func Uint256ToBytes32(n *big.Int) []byte { - if n.BitLen() > 256 { - panic("vrf.uint256ToBytes32: too big to marshal to uint256") - } - return common.LeftPadBytes(n.Bytes(), 32) -} - // WaitGroupChan creates a channel that closes when the provided sync.WaitGroup is done. func WaitGroupChan(wg *sync.WaitGroup) <-chan struct{} { chAwait := make(chan struct{}) @@ -750,16 +511,6 @@ func (t *ResettableTimer) Reset(duration time.Duration) { t.timer = time.NewTimer(duration) } -// EVMBytesToUint64 converts -// a bytebuffer to uint64 -func EVMBytesToUint64(buf []byte) uint64 { - var result uint64 - for _, b := range buf { - result = result<<8 + uint64(b) - } - return result -} - var ( ErrAlreadyStopped = errors.New("already stopped") ErrCannotStopUnstarted = errors.New("cannot stop unstarted service") @@ -846,13 +597,9 @@ func AllEqual[T comparable](elems ...T) bool { return true } -// RandUint256 generates a random bigNum up to 2 ** 256 - 1 -func RandUint256() *big.Int { - n, err := rand.Int(rand.Reader, MaxUint256) - if err != nil { - panic(err) - } - return n +// ConcatBytes appends a bunch of byte arrays into a single byte array +func ConcatBytes(bufs ...[]byte) []byte { + return bytes.Join(bufs, []byte{}) } func LeftPadBitString(input string, length int) string { @@ -862,22 +609,6 @@ func LeftPadBitString(input string, length int) string { return strings.Repeat("0", length-len(input)) + input } -// TryParseHex parses the given hex string to bytes, -// it can return error if the hex string is invalid. -// Follows the semantic of ethereum's FromHex. -func TryParseHex(s string) (b []byte, err error) { - if !HasHexPrefix(s) { - err = errors.New("hex string must have 0x prefix") - } else { - s = s[2:] - if len(s)%2 == 1 { - s = "0" + s - } - b, err = hex.DecodeString(s) - } - return -} - // ErrorBuffer uses joinedErrors interface to join multiple errors into a single error. // This is useful to track the most recent N errors in a service and flush them as a single error. type ErrorBuffer struct { diff --git a/core/utils/utils_test.go b/core/utils/utils_test.go index e11c01a8e4f..e2a4f8e3939 100644 --- a/core/utils/utils_test.go +++ b/core/utils/utils_test.go @@ -4,8 +4,6 @@ import ( "context" "encoding/hex" "fmt" - "math/big" - "strings" "sync" "sync/atomic" "testing" @@ -17,11 +15,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.uber.org/multierr" ) func TestUtils_NewBytes32ID(t *testing.T) { @@ -52,30 +47,6 @@ func TestUtils_NewSecret(t *testing.T) { } } -func TestUtils_IsEmptyAddress(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - addr common.Address - want bool - }{ - {"zero address", common.Address{}, true}, - {"non-zero address", testutils.NewAddress(), false}, - } - - for _, test := range tests { - test := test - - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - actual := utils.IsEmptyAddress(test.addr) - assert.Equal(t, test.want, actual) - }) - } -} - func TestUtils_StringToHex(t *testing.T) { t.Parallel() @@ -99,26 +70,6 @@ func TestUtils_StringToHex(t *testing.T) { } } -func TestUtils_BackoffSleeper(t *testing.T) { - t.Parallel() - - bs := utils.NewBackoffSleeper() - assert.Equal(t, time.Duration(0), bs.Duration(), "should initially return immediately") - bs.Sleep() - - d := 1 * time.Nanosecond - bs.Min = d - bs.Factor = 2 - assert.Equal(t, d, bs.Duration()) - bs.Sleep() - - d2 := 2 * time.Nanosecond - assert.Equal(t, d2, bs.Duration()) - - bs.Reset() - assert.Equal(t, time.Duration(0), bs.Duration(), "should initially return immediately") -} - func TestUtils_DurationFromNow(t *testing.T) { t.Parallel() @@ -127,76 +78,6 @@ func TestUtils_DurationFromNow(t *testing.T) { assert.True(t, 0 < duration) } -func TestKeccak256(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - input string - want string - }{ - {"basic", "0xf00b", "0x2433bb36d5f9b14e4fea87c2d32d79abfe34e56808b891e471f4400fca2a336c"}, - {"long input", "0xf00b2433bb36d5f9b14e4fea87c2d32d79abfe34e56808b891e471f4400fca2a336c", "0x6b917c56ad7bea7d09132b9e1e29bb5d9aa7d32d067c638dfa886bbbf6874cdf"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - input, err := hexutil.Decode(test.input) - assert.NoError(t, err) - result, err := utils.Keccak256(input) - assert.NoError(t, err) - - assert.Equal(t, test.want, hexutil.Encode(result)) - }) - } -} - -// From https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md#test-cases -var testAddresses = []string{ - "0x52908400098527886E0F7030069857D2E4169EE7", - "0x8617E340B3D01FA5F11F306F4090FD50E238070D", - "0xde709f2102306220921060314715629080e2fb77", - "0x27b1fdb04752bbc536007a920d24acb045561c26", - "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", - "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", - "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", - "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", -} - -func TestClient_EIP55CapitalizedAddress(t *testing.T) { - t.Parallel() - - valid := utils.EIP55CapitalizedAddress - for _, address := range testAddresses { - assert.True(t, valid(address)) - assert.False(t, valid(strings.ToLower(address)) && - valid(strings.ToUpper(address))) - } -} - -func TestClient_ParseEthereumAddress(t *testing.T) { - t.Parallel() - - parse := utils.ParseEthereumAddress - for _, address := range testAddresses { - a1, err := parse(address) - assert.NoError(t, err) - no0xPrefix := address[2:] - a2, err := parse(no0xPrefix) - assert.NoError(t, err) - assert.True(t, a1 == a2) - _, lowerErr := parse(strings.ToLower(address)) - _, upperErr := parse(strings.ToUpper(address)) - shouldBeError := multierr.Combine(lowerErr, upperErr) - assert.Error(t, shouldBeError) - assert.True(t, strings.Contains(shouldBeError.Error(), no0xPrefix)) - } - _, notHexErr := parse("0xCeci n'est pas une chaîne hexadécimale") - assert.Error(t, notHexErr) - _, tooLongErr := parse("0x0123456789abcdef0123456789abcdef0123456789abcdef") - assert.Error(t, tooLongErr) -} - func TestWaitGroupChan(t *testing.T) { t.Parallel() @@ -351,17 +232,6 @@ func TestBoundedPriorityQueue(t *testing.T) { require.Zero(t, q.Take()) } -func TestEVMBytesToUint64(t *testing.T) { - t.Parallel() - - require.Equal(t, uint64(257), utils.EVMBytesToUint64([]byte{0x01, 0x01})) - require.Equal(t, uint64(257), utils.EVMBytesToUint64([]byte{0x00, 0x00, 0x01, 0x01})) - require.Equal(t, uint64(299140445700113), utils.EVMBytesToUint64([]byte{0x00, 0x01, 0x10, 0x11, 0x10, 0x01, 0x00, 0x11})) - - // overflows without erroring - require.Equal(t, uint64(17), utils.EVMBytesToUint64([]byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11})) -} - func Test_WithJitter(t *testing.T) { t.Parallel() @@ -424,26 +294,6 @@ func TestBoxOutput(t *testing.T) { assert.Equal(t, expected, output) } -func TestUint256ToBytes(t *testing.T) { - t.Parallel() - - v := big.NewInt(0).Sub(utils.MaxUint256, big.NewInt(1)) - uint256, err := utils.Uint256ToBytes(v) - assert.NoError(t, err) - - b32 := utils.Uint256ToBytes32(v) - assert.Equal(t, uint256, b32) - - large := big.NewInt(0).Add(utils.MaxUint256, big.NewInt(1)) - _, err = utils.Uint256ToBytes(large) - assert.Error(t, err, "too large to convert to uint256") - - negative := big.NewInt(-1) - assert.Panics(t, func() { - _, _ = utils.Uint256ToBytes(negative) - }, "failed to round-trip uint256 back to source big.Int") -} - func TestISO8601UTC(t *testing.T) { t.Parallel() @@ -461,32 +311,6 @@ func TestFormatJSON(t *testing.T) { assert.Equal(t, "\"{\\\"foo\\\":123}\"", string(formatted)) } -func TestRetryWithBackoff(t *testing.T) { - t.Parallel() - - var counter atomic.Int32 - ctx, cancel := context.WithCancel(testutils.Context(t)) - - utils.RetryWithBackoff(ctx, func() bool { - return false - }) - - retry := func() bool { - return counter.Add(1) < 3 - } - - go utils.RetryWithBackoff(ctx, retry) - - assert.Eventually(t, func() bool { - return counter.Load() == 3 - }, testutils.WaitTimeout(t), testutils.TestInterval) - - cancel() - - utils.RetryWithBackoff(ctx, retry) - assert.Equal(t, int32(4), counter.Load()) -} - func TestMustUnmarshalToMap(t *testing.T) { t.Parallel() @@ -516,42 +340,6 @@ func TestSha256(t *testing.T) { assert.Len(t, hash, 32) } -func TestCheckUint256(t *testing.T) { - t.Parallel() - - large := big.NewInt(0).Add(utils.MaxUint256, big.NewInt(1)) - err := utils.CheckUint256(large) - assert.Error(t, err, "number out of range for uint256") - - negative := big.NewInt(-123) - err = utils.CheckUint256(negative) - assert.Error(t, err, "number out of range for uint256") - - err = utils.CheckUint256(big.NewInt(123)) - assert.NoError(t, err) -} - -func TestRandUint256(t *testing.T) { - t.Parallel() - - for i := 0; i < 1000; i++ { - uint256 := utils.RandUint256() - assert.NoError(t, utils.CheckUint256(uint256)) - } -} - -func TestHexToUint256(t *testing.T) { - t.Parallel() - - b, err := utils.HexToUint256("0x00") - assert.NoError(t, err) - assert.Zero(t, b.Cmp(big.NewInt(0))) - - b, err = utils.HexToUint256("0xFFFFFFFF") - assert.NoError(t, err) - assert.Zero(t, b.Cmp(big.NewInt(4294967295))) -} - func TestWithCloseChan(t *testing.T) { t.Parallel() @@ -772,40 +560,6 @@ func TestCronTicker(t *testing.T) { assert.Equal(t, c, counter.Load()) } -func TestTryParseHex(t *testing.T) { - t.Parallel() - - t.Run("0x prefix missing", func(t *testing.T) { - t.Parallel() - - _, err := utils.TryParseHex("abcd") - assert.Error(t, err) - }) - - t.Run("wrong hex characters", func(t *testing.T) { - t.Parallel() - - _, err := utils.TryParseHex("0xabcdzzz") - assert.Error(t, err) - }) - - t.Run("valid hex string", func(t *testing.T) { - t.Parallel() - - b, err := utils.TryParseHex("0x1234") - assert.NoError(t, err) - assert.Equal(t, []byte{0x12, 0x34}, b) - }) - - t.Run("prepend odd length with zero", func(t *testing.T) { - t.Parallel() - - b, err := utils.TryParseHex("0x123") - assert.NoError(t, err) - assert.Equal(t, []byte{0x1, 0x23}, b) - }) -} - func TestErrorBuffer(t *testing.T) { t.Parallel() diff --git a/core/web/evm_forwarders_controller_test.go b/core/web/evm_forwarders_controller_test.go index 49671157cbd..031a1a61c03 100644 --- a/core/web/evm_forwarders_controller_test.go +++ b/core/web/evm_forwarders_controller_test.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/require" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index 6ab621661a6..4a1bf46feea 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -13,11 +13,11 @@ import ( commontxmgr "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" "github.com/gin-gonic/gin" diff --git a/core/web/loader/loader_test.go b/core/web/loader/loader_test.go index cbd73a575a9..a039834997e 100644 --- a/core/web/loader/loader_test.go +++ b/core/web/loader/loader_test.go @@ -17,6 +17,7 @@ import ( "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" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" coremocks "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -27,7 +28,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" jobORMMocks "github.com/smartcontractkit/chainlink/v2/core/services/job/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestLoader_Chains(t *testing.T) { diff --git a/go.mod b/go.mod index 661addeb845..6e67bbd9a52 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 024872df5e6..037bcc95225 100644 --- a/go.sum +++ b/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 h1:mrxa3HrQfbMi4ji6gGcQHuLptvoNaRAv4TLnGJngbLc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab h1:6ckB261FRUy4K/OfSfWCQLAGkgfVLYT5PKDImmp3tZM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index febdd892150..bccd3ef1675 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -32,12 +32,12 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registrar_wrapper2_0" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" ) diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go index c3add16ec67..dd51e302744 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go @@ -16,8 +16,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants" diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index 4ea4a4a8534..17c3948b340 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -20,7 +20,7 @@ import ( "github.com/google/uuid" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" + chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 70320530aa8..5db187cf932 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -23,9 +23,9 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" - chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 7519b5de4cf..ad191d65d16 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/testreporters" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_consumer_benchmark" registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" @@ -39,7 +40,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_transcoder" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_consumer_performance_wrapper" diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 8b27b07212b..5d0c23e96de 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab github.com/smartcontractkit/chainlink-testing-framework v1.22.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 13757dfba8d..3c9efdbcb16 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94 h1:mrxa3HrQfbMi4ji6gGcQHuLptvoNaRAv4TLnGJngbLc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231218160643-bd451bb2dd94/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab h1:6ckB261FRUy4K/OfSfWCQLAGkgfVLYT5PKDImmp3tZM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/load/functions/setup.go b/integration-tests/load/functions/setup.go index c0be47ca836..e96eac3cf82 100644 --- a/integration-tests/load/functions/setup.go +++ b/integration-tests/load/functions/setup.go @@ -18,7 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" + chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) type FunctionsTest struct { From 14d5d3b5afde361b3e21802680ca0ae5a719e7d4 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:00:03 -0500 Subject: [PATCH 091/234] Add optional ingress support for helm chart (#11534) * Create empty ingress for helm chart * Add helm chart testing config and yaml lint config * Exclude files generated during chart testing * Bump version and fix apiVersion * Support optional ingress on helm chart * Rename workflow * Create GHA workflow to lint helm charts * Add explicit URL to mockserver Helm repository * Include self in the paths filter * Render out annotations in key/value format * Spec out all 6 nodes in values for ingress hosts * Hardcode standard ingress values * Change annotations data type * Use different values for ingress annotations and hardcode others --- .ct.yml | 5 + ...elm-publish.yml => helm-chart-publish.yml} | 0 .github/workflows/helm-chart.yml | 25 +++++ charts/chainlink-cluster/.gitignore | 3 + charts/chainlink-cluster/Chart.yaml | 8 +- .../chainlink-cluster/templates/ingress.yaml | 42 ++++++++ charts/chainlink-cluster/values.yaml | 97 +++++++++++++++++++ lintconf.yaml | 46 +++++++++ 8 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 .ct.yml rename .github/workflows/{helm-publish.yml => helm-chart-publish.yml} (100%) create mode 100644 .github/workflows/helm-chart.yml create mode 100644 charts/chainlink-cluster/.gitignore create mode 100644 charts/chainlink-cluster/templates/ingress.yaml create mode 100644 lintconf.yaml diff --git a/.ct.yml b/.ct.yml new file mode 100644 index 00000000000..68a9198e920 --- /dev/null +++ b/.ct.yml @@ -0,0 +1,5 @@ +# See: https://github.com/helm/chart-testing +target-branch: develop +chart-dirs: 'charts' +check-version-increment: false +validate-maintainers: false diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-chart-publish.yml similarity index 100% rename from .github/workflows/helm-publish.yml rename to .github/workflows/helm-chart-publish.yml diff --git a/.github/workflows/helm-chart.yml b/.github/workflows/helm-chart.yml new file mode 100644 index 00000000000..c988d14f30c --- /dev/null +++ b/.github/workflows/helm-chart.yml @@ -0,0 +1,25 @@ +name: Helm Chart + +on: + pull_request: + paths: + - "charts/**" + - ".github/workflows/helm-chart.yml" + +jobs: + ci-lint-helm-charts: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + actions: read + steps: + - name: ci-lint-helm-charts + uses: smartcontractkit/.github/actions/ci-lint-charts@9fd15fe8e698a5e28bfd06b3a91471c56568dcb3 # ci-lint-charts@0.1.1 + with: + # chart testing inputs + chart-testing-extra-args: "--lint-conf=lintconf.yaml" + # grafana inputs + metrics-job-name: ci-lint-helm-charts + gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/charts/chainlink-cluster/.gitignore b/charts/chainlink-cluster/.gitignore new file mode 100644 index 00000000000..3ee791f740a --- /dev/null +++ b/charts/chainlink-cluster/.gitignore @@ -0,0 +1,3 @@ +# Helm +charts/ +requirements.lock diff --git a/charts/chainlink-cluster/Chart.yaml b/charts/chainlink-cluster/Chart.yaml index 127f5b6e326..498b0670812 100644 --- a/charts/chainlink-cluster/Chart.yaml +++ b/charts/chainlink-cluster/Chart.yaml @@ -1,10 +1,10 @@ -apiVersion: v1 +apiVersion: v2 name: chainlink-cluster description: Chainlink nodes cluster -version: 0.1.3 +version: 0.2.0 appVersion: "2.6.0" dependencies: - name: mockserver version: "5.14.0" - repository: "@mockserver" - condition: mockserver.enabled \ No newline at end of file + repository: "https://www.mock-server.com" + condition: mockserver.enabled diff --git a/charts/chainlink-cluster/templates/ingress.yaml b/charts/chainlink-cluster/templates/ingress.yaml new file mode 100644 index 00000000000..f7d9791155b --- /dev/null +++ b/charts/chainlink-cluster/templates/ingress.yaml @@ -0,0 +1,42 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $.Release.Name }} + labels: + app: {{ $.Release.Name }} + release: {{ $.Release.Name }} + {{- range $key, $value := $.Values.labels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + annotations: + alb.ingress.kubernetes.io/backend-protocol: HTTP + alb.ingress.kubernetes.io/certificate-arn: {{ $.Values.ingress.annotation_certificate_arn | quote }} + alb.ingress.kubernetes.io/group.name: {{ $.Values.ingress.annotation_group_name | quote }} + alb.ingress.kubernetes.io/scheme: internal + alb.ingress.kubernetes.io/target-type: ip + {{- if .Values.ingress.extra_annotations }} + {{- range $key, $value := .Values.ingress.extra_annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} +spec: + {{- with .Values.ingress.ingressClassName }} + ingressClassName: {{ . }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host }} + http: + paths: + {{- range .http.paths }} + - path: / + pathType: ImplementationSpecific + backend: + service: + name: {{ .backend.service.name }} + port: + number: {{ .backend.service.port.number }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/chainlink-cluster/values.yaml b/charts/chainlink-cluster/values.yaml index feba1414444..9394bda1213 100644 --- a/charts/chainlink-cluster/values.yaml +++ b/charts/chainlink-cluster/values.yaml @@ -135,6 +135,8 @@ geth: mockserver: enabled: true releasenameOverride: mockserver + service: + type: ClusterIP app: runAsUser: 999 readOnlyRootFilesystem: false @@ -178,6 +180,101 @@ runner: type: NodePort port: 8080 +ingress: + enabled: false + annotations: {} + ingressClassName: alb + hosts: + - host: chainlink-node-1.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: chainlink-node-1 + port: + number: 6688 + - host: chainlink-node-2.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: chainlink-node-2 + port: + number: 6688 + - host: chainlink-node-3.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: chainlink-node-3 + port: + number: 6688 + - host: chainlink-node-4.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: chainlink-node-4 + port: + number: 6688 + - host: chainlink-node-5.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: chainlink-node-5 + port: + number: 6688 + - host: chainlink-node-6.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: chainlink-node-6 + port: + number: 6688 + - host: chainlink-geth-http.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: geth + port: + number: 8544 + - host: chainlink-geth-ws.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: geth + port: + number: 8546 + - host: chainlink-mockserver.local + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: mockserver + port: + number: 1080 # monitoring.coreos.com/v1 PodMonitor for each node prometheusMonitor: true diff --git a/lintconf.yaml b/lintconf.yaml new file mode 100644 index 00000000000..ff37371d476 --- /dev/null +++ b/lintconf.yaml @@ -0,0 +1,46 @@ +--- +# Copied from: +# https://redhat-cop.github.io/ci/linting-testing-helm-charts.html +# with `min-spaces-from-content` changed to be compatible with prettier. +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 1 + document-end: disable + document-start: disable # No --- to start a file + empty-lines: + max: 2 + max-start: 0 + max-end: 0 + hyphens: + max-spaces-after: 1 + indentation: + spaces: consistent + indent-sequences: whatever # - list indentation will handle both indentation and without + check-multi-line-strings: false + key-duplicates: enable + line-length: disable # Lines can be any length + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: enable + truthy: + level: warning + From fbe25a2c7f16f96c4449ac0176e2bd2c51304c26 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 20 Dec 2023 08:47:37 -0600 Subject: [PATCH 092/234] core/services/relay/evm/mercury/wsrpc: forward health and readiness from CacheSet (#11592) --- core/services/relay/evm/mercury/wsrpc/cache/cache_set.go | 5 +++-- core/services/relay/evm/mercury/wsrpc/pool.go | 7 ++++--- core/web/testdata/body/health.html | 5 ++++- core/web/testdata/body/health.json | 9 +++++++++ core/web/testdata/body/health.txt | 1 + 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go index 3171c8ab643..98388442d9a 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go @@ -104,8 +104,9 @@ func (cs *cacheSet) HealthReport() map[string]error { cs.Name(): cs.Ready(), } cs.RLock() - defer cs.RUnlock() - for _, c := range cs.caches { + caches := maps.Values(cs.caches) + cs.RUnlock() + for _, c := range caches { services.CopyHealth(report, c.HealthReport()) } return report diff --git a/core/services/relay/evm/mercury/wsrpc/pool.go b/core/services/relay/evm/mercury/wsrpc/pool.go index 76f9bc808b9..dd85381469b 100644 --- a/core/services/relay/evm/mercury/wsrpc/pool.go +++ b/core/services/relay/evm/mercury/wsrpc/pool.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/smartcontractkit/wsrpc/credentials" + "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" @@ -224,7 +225,7 @@ func (p *pool) Ready() error { } func (p *pool) HealthReport() map[string]error { - return map[string]error{ - p.Name(): p.Ready(), - } + hp := map[string]error{p.Name(): p.Ready()} + maps.Copy(hp, p.cacheSet.HealthReport()) + return hp } diff --git a/core/web/testdata/body/health.html b/core/web/testdata/body/health.html index f6c1d6c80c8..d1b208f4a0d 100644 --- a/core/web/testdata/body/health.html +++ b/core/web/testdata/body/health.html @@ -81,7 +81,10 @@
Mercury
- WSRPCPool + WSRPCPool +
+ CacheSet +
diff --git a/core/web/testdata/body/health.json b/core/web/testdata/body/health.json index 004988fceba..3c0117de7ec 100644 --- a/core/web/testdata/body/health.json +++ b/core/web/testdata/body/health.json @@ -126,6 +126,15 @@ "output": "" } }, + { + "type": "checks", + "id": "Mercury.WSRPCPool.CacheSet", + "attributes": { + "name": "Mercury.WSRPCPool.CacheSet", + "status": "passing", + "output": "" + } + }, { "type": "checks", "id": "PipelineORM", diff --git a/core/web/testdata/body/health.txt b/core/web/testdata/body/health.txt index 0dfa86abad0..59f63c26413 100644 --- a/core/web/testdata/body/health.txt +++ b/core/web/testdata/body/health.txt @@ -13,6 +13,7 @@ -JobSpawner -Mailbox.Monitor -Mercury.WSRPCPool +-Mercury.WSRPCPool.CacheSet -PipelineORM -PipelineRunner -PromReporter From cc1fe341e19f00f74c686b7c473dd9bbdfdd2581 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 20 Dec 2023 10:07:53 -0500 Subject: [PATCH 093/234] Add extra logging to bridge response errors (#11627) --- core/services/ocrcommon/telemetry.go | 15 ++++++++++----- core/services/ocrcommon/telemetry_test.go | 10 +++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 5fbda456088..b641bd2b3cf 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -234,19 +234,23 @@ func (e *EnhancedTelemetryService[T]) collectAndSend(trrs *pipeline.TaskRunResul if trr.Task.Type() != pipeline.TaskTypeBridge { continue } + var bridgeName string + if b, is := trr.Task.(*pipeline.BridgeTask); is { + bridgeName = b.Name + } if trr.Result.Error != nil { - e.lggr.Warnw(fmt.Sprintf("cannot get bridge response from bridge task, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", trr.Result.Error) + e.lggr.Warnw(fmt.Sprintf("cannot get bridge response from bridge task, job=%d, id=%s, name=%q", e.job.ID, trr.Task.DotID(), bridgeName), "err", trr.Result.Error, "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) continue } bridgeRawResponse, ok := trr.Result.Value.(string) if !ok { - e.lggr.Warnf("cannot parse bridge response from bridge task, job %d, id %s: expected string, got: %v (type %T)", e.job.ID, trr.Task.DotID(), trr.Result.Value, trr.Result.Value) + e.lggr.Warnw(fmt.Sprintf("cannot parse bridge response from bridge task, job=%d, id=%s, name=%q: expected string, got: %v (type %T)", e.job.ID, trr.Task.DotID(), bridgeName, trr.Result.Value, trr.Result.Value), "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) continue } eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) if err != nil { - e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", err) + e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job=%d, id=%s, name=%q", e.job.ID, trr.Task.DotID(), bridgeName), "err", err, "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) continue } value := e.getParsedValue(trrs, trr) @@ -359,15 +363,16 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced continue } bridgeTask := trr.Task.(*pipeline.BridgeTask) + bridgeName := bridgeTask.Name bridgeRawResponse, ok := trr.Result.Value.(string) if !ok { - e.lggr.Warnf("cannot get bridge response from bridge task, job %d, id %s, expected string got %T", e.job.ID, trr.Task.DotID(), trr.Result.Value) + e.lggr.Warnw(fmt.Sprintf("cannot get bridge response from bridge task, job=%d, id=%s, name=%q, expected string got %T", e.job.ID, trr.Task.DotID(), bridgeName, trr.Result.Value), "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) continue } eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) if err != nil { - e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", err) + e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job=%d, id=%s, name=%q", e.job.ID, trr.Task.DotID(), bridgeName), "err", err, "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) } assetSymbol := e.getAssetSymbolFromRequestData(bridgeTask.RequestData) diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index e607c859d78..caa8ccfcc01 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -46,6 +46,7 @@ const bridgeResponse = `{ var trrs = pipeline.TaskRunResults{ pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ + Name: "test-bridge-1", BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), }, Result: pipeline.Result{ @@ -62,6 +63,7 @@ var trrs = pipeline.TaskRunResults{ }, pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ + Name: "test-bridge-2", BaseTask: pipeline.NewBaseTask(0, "ds2", nil, nil, 0), }, Result: pipeline.Result{ @@ -78,6 +80,7 @@ var trrs = pipeline.TaskRunResults{ }, pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ + Name: "test-bridge-3", BaseTask: pipeline.NewBaseTask(0, "ds3", nil, nil, 0), }, Result: pipeline.Result{ @@ -390,6 +393,7 @@ func TestCollectAndSend(t *testing.T) { var trrsMercuryV1 = pipeline.TaskRunResults{ pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ + Name: "link-usd-test-bridge-v1", BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), RequestData: `{"data":{"to":"LINK","from":"USD"}}`, }, @@ -426,6 +430,7 @@ var trrsMercuryV1 = pipeline.TaskRunResults{ var trrsMercuryV2 = pipeline.TaskRunResults{ pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ + Name: "link-usd-test-bridge-v2", BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), RequestData: `{"data":{"to":"LINK","from":"USD"}}`, }, @@ -477,6 +482,7 @@ func TestGetPricesFromResults(t *testing.T) { trrs2 := pipeline.TaskRunResults{ pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ + Name: "test-bridge-1", BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), }, Result: pipeline.Result{ @@ -630,6 +636,7 @@ func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { chTelem <- EnhancedTelemetryMercuryData{ TaskRunResults: pipeline.TaskRunResults{ pipeline.TaskRunResult{Task: &pipeline.BridgeTask{ + Name: "test-mercury-bridge-1", BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), }, Result: pipeline.Result{ @@ -657,7 +664,7 @@ func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { wg.Wait() require.Equal(t, 2, logs.Len()) - require.Contains(t, logs.All()[0].Message, "cannot get bridge response from bridge task") + require.Contains(t, logs.All()[0].Message, `cannot get bridge response from bridge task, job=0, id=ds1, name="test-mercury-bridge-1"`) require.Contains(t, logs.All()[1].Message, "cannot parse EA telemetry") chDone <- struct{}{} } @@ -744,6 +751,7 @@ func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { chTelem <- EnhancedTelemetryMercuryData{ TaskRunResults: pipeline.TaskRunResults{ pipeline.TaskRunResult{Task: &pipeline.BridgeTask{ + Name: "test-mercury-bridge-2", BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), }, Result: pipeline.Result{ From 9603e5ea794aba8f88935741de192eefc3109601 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 20 Dec 2023 16:04:09 -0500 Subject: [PATCH 094/234] [TT-765] Moves Client Compatability Tests to Nightly Run (#11610) * Moves Client Compatability Tests to Nightly Run * midnight 30 * adds Slack notification * Start independent action * parameterize slack notify action * Actually checkout repo * Fix action * Better Debutg * Link and docs * Fix comma * Finish Cleanup * Freedom --- .../notify-slack-jobs-result/README.md | 37 +++ .../notify-slack-jobs-result/action.yml | 110 +++++++ .../notify-slack-jobs-result/image.png | Bin 0 -> 47298 bytes .../workflows/client-compatibility-tests.yml | 269 ++++++++++++++++++ .github/workflows/integration-tests.yml | 67 +---- .github/workflows/live-testnet-tests.yml | 93 +----- 6 files changed, 438 insertions(+), 138 deletions(-) create mode 100644 .github/actions/notify-slack-jobs-result/README.md create mode 100644 .github/actions/notify-slack-jobs-result/action.yml create mode 100644 .github/actions/notify-slack-jobs-result/image.png create mode 100644 .github/workflows/client-compatibility-tests.yml diff --git a/.github/actions/notify-slack-jobs-result/README.md b/.github/actions/notify-slack-jobs-result/README.md new file mode 100644 index 00000000000..298930c0d95 --- /dev/null +++ b/.github/actions/notify-slack-jobs-result/README.md @@ -0,0 +1,37 @@ +# Notify Slack Jobs Result + +Sends a Slack message to a specified channel detailing the results of one to many GHA job results using a regex. The job results will be grouped by the `github_job_name_regex` and displayed underneath the `message_title`, with the regex matching group displayed as an individual result. This is primarily designed for when you have test groups running in a matrix, and would like condensed reporting on their status by group. It's often accompanied by posting a Slack message before to start a thread, then attaching all the results to that thread like we do in the reporting section of the [live-testnet-test.yml workflow](../../workflows/live-testnet-tests.yml). Check out the example below, where we post an initial summary message, then use this action to thread together specific results: + +```yaml +message_title: Optimism Goerli +github_job_name_regex: ^Optimism Goerli (?.*?) Tests$ # Note that the regex MUST have a capturing group named "cap" +``` + +![example](image.png) + +## Inputs + +```yaml +inputs: + github_token: + description: "The GitHub token to use for authentication (usually ${{ github.token }})" + required: true + github_repository: + description: "The GitHub owner/repository to use for authentication (usually ${{ github.repository }}))" + required: true + workflow_run_id: + description: "The workflow run ID to get the results from (usually ${{ github.run_id }})" + required: true + github_job_name_regex: + description: "The regex to use to match 1..many job name(s) to collect results from. Should include a capture group named 'cap' for the part of the job name you want to display in the Slack message (e.g. ^Client Compatability Test (?.*?)$)" + required: true + message_title: + description: "The title of the Slack message" + required: true + slack_channel_id: + description: "The Slack channel ID to post the message to" + required: true + slack_thread_ts: + description: "The Slack thread timestamp to post the message to, handy for keeping multiple related results in a single thread" + required: false +``` diff --git a/.github/actions/notify-slack-jobs-result/action.yml b/.github/actions/notify-slack-jobs-result/action.yml new file mode 100644 index 00000000000..63840cfa393 --- /dev/null +++ b/.github/actions/notify-slack-jobs-result/action.yml @@ -0,0 +1,110 @@ +name: Notify Slack Jobs Result +description: Will send a notification in Slack for the result of a GitHub action run, typically for test results +inputs: + github_token: + description: "The GitHub token to use for authentication (usually github.token)" + required: true + github_repository: + description: "The GitHub owner/repository to use for authentication (usually github.repository))" + required: true + workflow_run_id: + description: "The workflow run ID to get the results from (usually github.run_id)" + required: true + github_job_name_regex: + description: "The regex to use to match 1..many job name(s) to collect results from. Should include a capture group named 'cap' for the part of the job name you want to display in the Slack message (e.g. ^Client Compatability Test (?.*?)$)" + required: true + message_title: + description: "The title of the Slack message" + required: true + slack_channel_id: + description: "The Slack channel ID to post the message to" + required: true + slack_bot_token: + description: "The Slack bot token to use for authentication which needs permission and an installed app in the channel" + required: true + slack_thread_ts: + description: "The Slack thread timestamp to post the message to, handy for keeping multiple related results in a single thread" + required: false + +runs: + using: composite + steps: + - name: Get Results + shell: bash + id: test-results + run: | + # I feel like there's some clever, fully jq way to do this, but I ain't got the motivation to figure it out + echo "Querying test results at https://api.github.com/repos/${{inputs.github_repository}}/actions/runs/${{ inputs.workflow_run_id }}/jobs" + + PARSED_RESULTS=$(curl \ + -H "Authorization: Bearer ${{ inputs.github_token }}" \ + 'https://api.github.com/repos/${{inputs.github_repository}}/actions/runs/${{ inputs.workflow_run_id }}/jobs' \ + | jq -r --arg pattern "${{ inputs.github_job_name_regex }}" '.jobs[] + | select(.name | test($pattern)) as $job + | $job.steps[] + | select(.name == "Run Tests") + | { conclusion: (if .conclusion == "success" then ":white_check_mark:" else ":x:" end), cap: ("*" + ($job.name | capture($pattern).cap) + "*"), html_url: $job.html_url }') + + echo "Parsed Results:" + echo $PARSED_RESULTS + + ALL_SUCCESS=true + echo "Checking for failures" + echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")' + for row in $(echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")'); do + ALL_SUCCESS=false + break + done + echo "Success: $ALL_SUCCESS" + + echo all_success=$ALL_SUCCESS >> $GITHUB_OUTPUT + + FORMATTED_RESULTS=$(echo $PARSED_RESULTS | jq -s '[.[] + | { + conclusion: .conclusion, + cap: .cap, + html_url: .html_url + } + ] + | map("{\"type\": \"section\", \"text\": {\"type\": \"mrkdwn\", \"text\": \"<\(.html_url)|\(.cap)>: \(.conclusion)\"}}") + | join(",")') + + echo "Formatted Results:" + echo $FORMATTED_RESULTS + + # Cleans out backslashes and quotes from jq + CLEAN_RESULTS=$(echo "$FORMATTED_RESULTS" | sed 's/\\\"/"/g' | sed 's/^"//;s/"$//') + + echo "Clean Results" + echo $CLEAN_RESULTS + + echo results=$CLEAN_RESULTS >> $GITHUB_OUTPUT + - name: Post Results + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + env: + SLACK_BOT_TOKEN: ${{ inputs.slack_bot_token }} + with: + channel-id: ${{ inputs.slack_channel_id }} + payload: | + { + "thread_ts": "${{ inputs.slack_thread_ts }}", + "attachments": [ + { + "color": "${{ steps.test-results.outputs.all_success == 'true' && '#2E7D32' || '#C62828' }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "${{ inputs.message_title }} ${{ steps.test-results.outputs.all_success == 'true' && ':white_check_mark:' || ':x:'}}", + "emoji": true + } + }, + { + "type": "divider" + }, + ${{ steps.test-results.outputs.results }} + ] + } + ] + } diff --git a/.github/actions/notify-slack-jobs-result/image.png b/.github/actions/notify-slack-jobs-result/image.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd398101fbdc8e4a0eb4c5addd7332f130190ab GIT binary patch literal 47298 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 literal 0 HcmV?d00001 diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml new file mode 100644 index 00000000000..8688e51e1df --- /dev/null +++ b/.github/workflows/client-compatibility-tests.yml @@ -0,0 +1,269 @@ +name: Client Compatibility Tests +on: + schedule: + - cron: "30 5 * * *" # Run every night at midnight + 30min EST + push: + tags: + - "*" + workflow_dispatch: + +jobs: + build-chainlink: + environment: integration + permissions: + id-token: write + contents: read + name: Build Chainlink Image + runs-on: ubuntu-latest + steps: + - name: Collect Metrics + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: Build Chainlink Image + continue-on-error: true + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Build Chainlink Image + uses: ./.github/actions/build-chainlink-image + with: + tag_suffix: "" + dockerfile: core/chainlink.Dockerfile + git_commit_sha: ${{ github.sha }} + GRAFANA_CLOUD_BASIC_AUTH: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} + AWS_REGION: ${{ secrets.QA_AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + + client-compatibility-matrix: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink] + env: + SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 + CHAINLINK_COMMIT_SHA: ${{ github.sha }} + CHAINLINK_ENV_USER: ${{ github.actor }} + TEST_LOG_LEVEL: debug + strategy: + fail-fast: false + matrix: + product: + - name: ocr-geth + os: ubuntu-latest + run: -run TestOCRBasic + file: ocr + client: geth + pyroscope_env: ci-smoke-ocr-evm-simulated + # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE + # - name: ocr-nethermind + # run: -run TestOCRBasic + # file: ocr + # client: nethermind + # pyroscope_env: ci-smoke-ocr-evm-simulated + - name: ocr-besu + run: -run TestOCRBasic + file: ocr + client: besu + pyroscope_env: ci-smoke-ocr-evm-simulated + - name: ocr-erigon + run: -run TestOCRBasic + file: ocr + client: erigon + pyroscope_env: ci-smoke-ocr-evm-simulated + - name: ocr2-geth + run: -run TestOCRv2Basic + file: ocr2 + client: geth + pyroscope_env: ci-smoke-ocr2-evm-simulated + # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE + # - name: ocr2-nethermind + # run: -run TestOCRv2Basic + # file: ocr2 + # client: nethermind + # pyroscope_env: ci-smoke-ocr2-evm-simulated + - name: ocr2-besu + run: -run TestOCRv2Basic + file: ocr2 + client: besu + pyroscope_env: ci-smoke-ocr2-evm-simulated + - name: ocr2-erigon + run: -run TestOCRv2Basic + file: ocr2 + client: erigon + pyroscope_env: ci-smoke-ocr2-evm-simulated + runs-on: ubuntu-latest + name: Client Compatibility Test ${{ matrix.product.name }} + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Build Go Test Command + id: build-go-test-command + run: | + # if the matrix.product.run is set, use it for a different command + if [ "${{ matrix.product.run }}" != "" ]; then + echo "run_command=${{ matrix.product.run }} ./smoke/${{ matrix.product.file }}_test.go" >> "$GITHUB_OUTPUT" + else + echo "run_command=./smoke/${{ matrix.product.name }}_test.go" >> "$GITHUB_OUTPUT" + fi + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + env: + PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + ETH2_EL_CLIENT: ${{matrix.product.client}} + LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} + LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} + GRAFANA_URL: ${{ vars.GRAFANA_URL }} + GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} + RUN_ID: ${{ github.run_id }} + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + artifacts_name: ${{ matrix.product.name }}-test-logs + artifacts_location: ./integration-tests/smoke/logs/ + publish_check_name: ${{ matrix.product.name }} + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: "" + - name: Collect Metrics + if: always() + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true + - name: Print failed test summary + if: always() + run: | + directory="./integration-tests/smoke/.test_summary" + files=("$directory"/*) + if [ -d "$directory" ]; then + echo "Test summary folder found" + if [ ${#files[@]} -gt 0 ]; then + first_file="${files[0]}" + echo "Name of the first test summary file: $(basename "$first_file")" + echo "### Failed Test Execution Logs Dashboard (over VPN):" >> $GITHUB_STEP_SUMMARY + cat "$first_file" | jq -r '.loki[] | "* [\(.test_name)](\(.value))"' >> $GITHUB_STEP_SUMMARY + if [ ${#files[@]} -gt 1 ]; then + echo "Found more than one test summary file. This is incorrect, there should be only one file" + fi + else + echo "Test summary directory is empty. This should not happen" + fi + else + echo "No test summary folder found. If no test failed or log collection wasn't explicitly requested this is correct. Exiting" + fi + + start-slack-thread: + name: Start Slack Thread + if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} + environment: integration + outputs: + thread_ts: ${{ steps.slack.outputs.thread_ts }} + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + runs-on: ubuntu-latest + needs: [client-compatibility-matrix] + steps: + - name: Debug Result + run: echo ${{ join(needs.*.result, ',') }} + - name: Main Slack Notification + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + id: slack + with: + channel-id: ${{ secrets.QA_SLACK_CHANNEL }} + payload: | + { + "attachments": [ + { + "color": "${{ contains(join(needs.*.result, ','), 'failure') && '#C62828' || '#2E7D32' }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "Live Smoke Test Results ${{ contains(join(needs.*.result, ','), 'failure') && ':x:' || ':white_check_mark:'}}", + "emoji": true + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "${{ contains(join(needs.*.result, ','), 'failure') && 'Some tests failed, notifying <@U01Q4N37KFG> and <@U060CGGPY8H>' || 'All Good!' }}" + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "<${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}|${{ github.ref_name }}> | <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}> | <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Run>" + } + } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + + post-test-results-to-slack: + name: Post Test Results for ${{matrix.product}} + if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + runs-on: ubuntu-latest + needs: start-slack-thread + strategy: + fail-fast: false + matrix: + product: [ocr, ocr2] + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Post Test Results to Slack + uses: ./.github/actions/notify-slack-jobs-result + with: + github_token: ${{ github.token }} + github_repository: ${{ github.repository }} + workflow_run_id: ${{ github.run_id }} + github_job_name_regex: ^Client Compatability Test ${{ matrix.product }}-(?.*?)$ + message_title: ${{ matrix.product }} + slack_channel_id: ${{ secrets.QA_SLACK_CHANNEL }} + slack_bot_token: ${{ secrets.QA_SLACK_API_KEY }} + slack_thread_ts: ${{ needs.start-slack-thread.outputs.thread_ts }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e8243c8cdfa..0fa9370b60d 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -327,75 +327,23 @@ jobs: os: ubuntu-latest run: -run TestOCRJobReplacement file: ocr - pyroscope_env: ci-smoke-ocr-evm-simulated - - name: ocr-geth - nodes: 1 - os: ubuntu-latest - run: -run TestOCRBasic - file: ocr - client: geth - pyroscope_env: ci-smoke-ocr-evm-simulated - # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE - # - name: ocr-nethermind - # nodes: 1 - # os: ubuntu20.04-8cores-32GB - # run: -run TestOCRBasic - # file: ocr - # client: nethermind - # pyroscope_env: ci-smoke-ocr-evm-simulated - - name: ocr-besu - nodes: 1 - os: ubuntu20.04-8cores-32GB - run: -run TestOCRBasic - file: ocr - client: besu - pyroscope_env: ci-smoke-ocr-evm-simulated - - name: ocr-erigon - nodes: 1 - os: ubuntu20.04-8cores-32GB - run: -run TestOCRBasic - file: ocr - client: erigon - pyroscope_env: ci-smoke-ocr-evm-simulated + pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest run: -run TestOCRv2JobReplacement file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated - - name: ocr2-geth - nodes: 1 - os: ubuntu20.04-8cores-32GB - run: -run TestOCRv2Basic - file: ocr2 - client: geth - pyroscope_env: ci-smoke-ocr2-evm-simulated - # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE - # - name: ocr2-nethermind - # nodes: 1 - # os: ubuntu20.04-8cores-32GB - # run: -run TestOCRv2Basic - # file: ocr2 - # client: nethermind - # pyroscope_env: ci-smoke-ocr2-evm-simulated - - name: ocr2-besu - nodes: 1 - os: ubuntu20.04-8cores-32GB - run: -run TestOCRv2Basic - file: ocr2 - client: besu - pyroscope_env: ci-smoke-ocr2-evm-simulated - - name: ocr2-erigon + - name: ocr2 nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest run: -run TestOCRv2Basic file: ocr2 - client: erigon - pyroscope_env: ci-smoke-ocr2-evm-simulated + pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 nodes: 1 - os: ubuntu20.04-8cores-32GB - pyroscope_env: ci-smoke-ocr2-evm-simulated + os: ubuntu-latest + pyroscope_env: ci-smoke-ocr2-plugins-evm-simulated tag_suffix: "-plugins" - name: runlog nodes: 1 @@ -497,7 +445,6 @@ jobs: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - ETH2_EL_CLIENT: ${{matrix.product.client}} LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} LOKI_URL: ${{ secrets.LOKI_URL }} LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 7a12ab6d6a0..e68dd410c10 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -10,7 +10,7 @@ name: Live Testnet Tests on: schedule: - - cron: "0 5 * * *" # Run every Sunday at midnight EST + - cron: "0 5 * * *" # Run every night at midnight EST push: tags: - "*" @@ -207,84 +207,21 @@ jobs: matrix: network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Linea Goerli, BSC Testnet] steps: - - name: Get Results - id: test-results - run: | - # I feel like there's some clever, fully jq way to do this, but I ain't got the motivation to figure it out - echo "Querying test results" - - PARSED_RESULTS=$(curl \ - -H "Authorization: Bearer ${{ github.token }}" \ - 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ - | jq -r --arg pattern "^${{ matrix.network }} (?.*?) Tests$" '.jobs[] - | select(.name | test($pattern)) as $job - | $job.steps[] - | select(.name == "Run Tests") - | { conclusion: (if .conclusion == "success" then ":white_check_mark:" else ":x:" end), product: ("*" + ($job.name | capture($pattern).product) + "*") }') - - echo "Parsed Results:" - echo $PARSED_RESULTS - - ALL_SUCCESS=true - echo "Checking for failures" - echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")' - for row in $(echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")'); do - ALL_SUCCESS=false - break - done - echo "Success: $ALL_SUCCESS" - - echo all_success=$ALL_SUCCESS >> $GITHUB_OUTPUT - - FORMATTED_RESULTS=$(echo $PARSED_RESULTS | jq -s '[.[] - | { - conclusion: .conclusion, - product: .product - } - ] - | map("{\"type\": \"section\", \"text\": {\"type\": \"mrkdwn\", \"text\": \"\(.product): \(.conclusion)\"}}") - | join(",")') - - echo "Formatted Results:" - echo $FORMATTED_RESULTS - - # Cleans out backslashes and quotes from jq - CLEAN_RESULTS=$(echo "$FORMATTED_RESULTS" | sed 's/\\\"/"/g' | sed 's/^"//;s/"$//') - - echo "Clean Results" - echo $CLEAN_RESULTS - - echo results=$CLEAN_RESULTS >> $GITHUB_OUTPUT - - - name: Test Details - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: - channel-id: ${{ secrets.QA_SLACK_CHANNEL }} - payload: | - { - "thread_ts": "${{ needs.start-slack-thread.outputs.thread_ts }}", - "attachments": [ - { - "color": "${{ steps.test-results.outputs.all_success == 'true' && '#2E7D32' || '#C62828' }}", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "${{ matrix.network }} ${{ steps.test-results.outputs.all_success == 'true' && ':white_check_mark:' || ':x:'}}", - "emoji": true - } - }, - { - "type": "divider" - }, - ${{ steps.test-results.outputs.results }} - ] - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Post Test Results + uses: ./.github/actions/notify-slack-jobs-result + with: + github_token: ${{ github.token }} + github_repository: ${{ github.repository }} + workflow_run_id: ${{ github.run_id }} + github_job_name_regex: ^${{ matrix.network }} (?.*?) Tests$ + message_title: ${{ matrix.network }} + slack_channel_id: ${{ secrets.QA_SLACK_CHANNEL }} + slack_bot_token: ${{ secrets.QA_SLACK_API_KEY }} + slack_thread_ts: ${{ needs.start-slack-thread.outputs.thread_ts }} # End Reporting Jobs From be5351965c2986c128a574f773108986dd239ed6 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 20 Dec 2023 16:20:23 -0500 Subject: [PATCH 095/234] Temporarily Disables BSC in Live Tests (#11642) --- .github/workflows/live-testnet-tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index e68dd410c10..2b5ecb2d5da 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -144,7 +144,7 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: [sepolia-smoke-tests, bsc-testnet-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, linea-goerli-smoke-tests] + needs: [sepolia-smoke-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, linea-goerli-smoke-tests] steps: - name: Debug Result run: echo ${{ join(needs.*.result, ',') }} @@ -205,7 +205,7 @@ jobs: strategy: fail-fast: false matrix: - network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Linea Goerli, BSC Testnet] + network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Linea Goerli] steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -276,6 +276,8 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} bsc-testnet-tests: + # TODO: BSC RPCs are all in a bad state right now, so we're skipping these tests until they're fixed + if: false environment: integration permissions: checks: write From c5aa49bf0e2a19550886f0dfcc06a92b07058518 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 21 Dec 2023 09:51:26 -0500 Subject: [PATCH 096/234] Increase disablement of cache if LatestReportTTL=0 (#11636) * Increase disablement of cache if LatestReportTTL=0 * Also reset transport on consecutive LatestReport request failures * Optimize case where caching disabled * Fix comment * Fix tests --- .../evm/mercury/wsrpc/cache/cache_set.go | 6 ++- .../evm/mercury/wsrpc/cache/cache_set_test.go | 13 +++++- .../relay/evm/mercury/wsrpc/client.go | 45 ++++++++++--------- .../relay/evm/mercury/wsrpc/client_test.go | 15 +++---- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go index 98388442d9a..01d47743950 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go @@ -46,7 +46,7 @@ func newCacheSet(lggr logger.Logger, cfg Config) *cacheSet { func (cs *cacheSet) Start(context.Context) error { return cs.StartOnce("CacheSet", func() error { - cs.lggr.Debugw("CacheSet starting", "config", cs.cfg) + cs.lggr.Debugw("CacheSet starting", "config", cs.cfg, "cachingEnabled", cs.cfg.LatestReportTTL > 0) return nil }) } @@ -65,6 +65,10 @@ func (cs *cacheSet) Close() error { } func (cs *cacheSet) Get(ctx context.Context, client Client) (f Fetcher, err error) { + if cs.cfg.LatestReportTTL == 0 { + // caching disabled + return client, nil + } ok := cs.IfStarted(func() { f, err = cs.get(ctx, client) }) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go index 7d754b8326e..59be76ed265 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go @@ -13,7 +13,8 @@ import ( func Test_CacheSet(t *testing.T) { lggr := logger.TestLogger(t) - cs := newCacheSet(lggr, Config{}) + cs := newCacheSet(lggr, Config{LatestReportTTL: 1}) + disabledCs := newCacheSet(lggr, Config{LatestReportTTL: 0}) ctx := testutils.Context(t) servicetest.Run(t, cs) @@ -22,6 +23,16 @@ func Test_CacheSet(t *testing.T) { var err error var f Fetcher + t.Run("with caching disabled, returns the passed client", func(t *testing.T) { + assert.Len(t, disabledCs.caches, 0) + + f, err = disabledCs.Get(ctx, c) + require.NoError(t, err) + + assert.Same(t, c, f) + assert.Len(t, disabledCs.caches, 0) + }) + t.Run("with virgin cacheset, makes new entry and returns it", func(t *testing.T) { assert.Len(t, cs.caches, 0) diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index 5b6bfa1a9b0..c9533717757 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -24,9 +24,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -// MaxConsecutiveTransmitFailures controls how many consecutive requests are +// MaxConsecutiveRequestFailures controls how many consecutive requests are // allowed to time out before we reset the connection -const MaxConsecutiveTransmitFailures = 5 +const MaxConsecutiveRequestFailures = 10 var ( timeoutCount = promauto.NewCounterVec(prometheus.CounterOpts{ @@ -55,7 +55,7 @@ var ( ) connectionResetCount = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "mercury_connection_reset_count", - Help: fmt.Sprintf("Running count of times connection to mercury server has been reset (connection reset happens automatically after %d consecutive transmit failures)", MaxConsecutiveTransmitFailures), + Help: fmt.Sprintf("Running count of times connection to mercury server has been reset (connection reset happens automatically after %d consecutive request failures)", MaxConsecutiveRequestFailures), }, []string{"serverURL"}, ) @@ -256,13 +256,26 @@ func (w *client) Transmit(ctx context.Context, req *pb.TransmitRequest) (resp *p return nil, errors.Wrap(err, "Transmit call failed") } resp, err = w.rawClient.Transmit(ctx, req) + w.handleTimeout(err) + if err != nil { + w.logger.Warnw("Transmit call failed due to networking error", "err", err, "resp", resp) + incRequestStatusMetric(statusFailed) + } else { + w.logger.Tracew("Transmit call succeeded", "resp", resp) + incRequestStatusMetric(statusSuccess) + setRequestLatencyMetric(float64(time.Since(start).Milliseconds())) + } + return +} + +func (w *client) handleTimeout(err error) { if errors.Is(err, context.DeadlineExceeded) { w.timeoutCountMetric.Inc() cnt := w.consecutiveTimeoutCnt.Add(1) - if cnt == MaxConsecutiveTransmitFailures { + if cnt == MaxConsecutiveRequestFailures { w.logger.Errorf("Timed out on %d consecutive transmits, resetting transport", cnt) - // NOTE: If we get 5+ request timeouts in a row, close and re-open - // the websocket connection. + // NOTE: If we get at least MaxConsecutiveRequestFailures request + // timeouts in a row, close and re-open the websocket connection. // // This *shouldn't* be necessary in theory (ideally, wsrpc would // handle it for us) but it acts as a "belts and braces" approach @@ -271,11 +284,11 @@ func (w *client) Transmit(ctx context.Context, req *pb.TransmitRequest) (resp *p select { case w.chResetTransport <- struct{}{}: default: - // This can happen if we had 5 consecutive timeouts, already - // sent a reset signal, then the connection started working - // again (resetting the count) then we got 5 additional - // failures before the runloop was able to close the bad - // connection. + // This can happen if we had MaxConsecutiveRequestFailures + // consecutive timeouts, already sent a reset signal, then the + // connection started working again (resetting the count) then + // we got MaxConsecutiveRequestFailures additional failures + // before the runloop was able to close the bad connection. // // It should be safe to just ignore in this case. // @@ -286,15 +299,6 @@ func (w *client) Transmit(ctx context.Context, req *pb.TransmitRequest) (resp *p } else { w.consecutiveTimeoutCnt.Store(0) } - if err != nil { - w.logger.Warnw("Transmit call failed due to networking error", "err", err, "resp", resp) - incRequestStatusMetric(statusFailed) - } else { - w.logger.Tracew("Transmit call succeeded", "resp", resp) - incRequestStatusMetric(statusSuccess) - setRequestLatencyMetric(float64(time.Since(start).Milliseconds())) - } - return } func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) { @@ -306,6 +310,7 @@ func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) var cached bool if w.cache == nil { resp, err = w.rawClient.LatestReport(ctx, req) + w.handleTimeout(err) } else { cached = true resp, err = w.cache.LatestReport(ctx, req) diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index f265d54879c..10712461ae1 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -54,7 +54,7 @@ func Test_Client_Transmit(t *testing.T) { noopCacheSet := newNoopCacheSet() - t.Run("sends on reset channel after MaxConsecutiveTransmitFailures timed out transmits", func(t *testing.T) { + t.Run("sends on reset channel after MaxConsecutiveRequestFailures timed out transmits", func(t *testing.T) { calls := 0 transmitErr := context.DeadlineExceeded wsrpcClient := &mocks.MockWSRPCClient{ @@ -70,11 +70,11 @@ func Test_Client_Transmit(t *testing.T) { c.conn = conn c.rawClient = wsrpcClient require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) - for i := 1; i < MaxConsecutiveTransmitFailures; i++ { + for i := 1; i < MaxConsecutiveRequestFailures; i++ { _, err := c.Transmit(ctx, req) require.EqualError(t, err, "context deadline exceeded") } - assert.Equal(t, 4, calls) + assert.Equal(t, MaxConsecutiveRequestFailures-1, calls) select { case <-c.chResetTransport: t.Fatal("unexpected send on chResetTransport") @@ -82,7 +82,7 @@ func Test_Client_Transmit(t *testing.T) { } _, err := c.Transmit(ctx, req) require.EqualError(t, err, "context deadline exceeded") - assert.Equal(t, 5, calls) + assert.Equal(t, MaxConsecutiveRequestFailures, calls) select { case <-c.chResetTransport: default: @@ -94,14 +94,14 @@ func Test_Client_Transmit(t *testing.T) { // working transmit to reset counter _, err = c.Transmit(ctx, req) require.NoError(t, err) - assert.Equal(t, 6, calls) + assert.Equal(t, MaxConsecutiveRequestFailures+1, calls) assert.Equal(t, 0, int(c.consecutiveTimeoutCnt.Load())) }) t.Run("doesn't block in case channel is full", func(t *testing.T) { transmitErr = context.DeadlineExceeded c.chResetTransport = nil // simulate full channel - for i := 0; i < MaxConsecutiveTransmitFailures; i++ { + for i := 0; i < MaxConsecutiveRequestFailures; i++ { _, err := c.Transmit(ctx, req) require.EqualError(t, err, "context deadline exceeded") } @@ -162,10 +162,7 @@ func Test_Client_LatestReport(t *testing.T) { // simulate start without dialling require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) - var err error servicetest.Run(t, cacheSet) - c.cache, err = cacheSet.Get(ctx, c) - require.NoError(t, err) for i := 0; i < 5; i++ { r, err := c.LatestReport(ctx, req) From 09067a354b0c6145511b584f3330eacd390aeb7e Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Thu, 21 Dec 2023 19:04:42 +0200 Subject: [PATCH 097/234] Remove all utils dependencies from evm (#11622) * Move core eth utils to evm * Fixes * Fix import mismatch * Move error handling utils * Remove all utils dependencies from evm * Fix Sleepertask * Remove unused utils * Fix errors * Undo test removal * Upgrade chainlink-common --------- Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- core/chains/evm/config/toml/config.go | 80 +++++------ core/chains/evm/config/toml/defaults.go | 4 +- core/chains/evm/gas/arbitrum_estimator.go | 2 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 2 +- .../evm/gas/suggested_price_estimator.go | 2 +- core/chains/evm/log/broadcaster.go | 2 +- core/chains/evm/log/orm.go | 3 +- core/chains/evm/logpoller/log_poller.go | 2 +- core/chains/evm/logpoller/log_poller_test.go | 4 +- core/chains/evm/monitor/balance.go | 4 +- .../evm/monitor/balance_helpers_test.go | 4 +- core/chains/evm/txmgr/broadcaster_test.go | 5 +- core/chains/evm/txmgr/client.go | 3 +- core/chains/evm/txmgr/reaper_test.go | 3 +- core/chains/evm/txmgr/transmitchecker.go | 6 +- core/chains/evm/txmgr/txmgr_test.go | 6 +- core/chains/evm/utils/utils.go | 82 +++-------- core/cmd/admin_commands.go | 6 +- core/cmd/cosmos_keys_commands.go | 2 +- core/cmd/cosmos_keys_commands_test.go | 2 +- core/cmd/csa_keys_commands.go | 3 +- core/cmd/csa_keys_commands_test.go | 2 +- core/cmd/dkgencrypt_keys_commands.go | 2 +- core/cmd/dkgencrypt_keys_commands_test.go | 2 +- core/cmd/dkgsign_keys_commands.go | 2 +- core/cmd/dkgsign_keys_commands_test.go | 2 +- core/cmd/eth_keys_commands.go | 3 +- core/cmd/eth_keys_commands_test.go | 3 +- core/cmd/ocr2_keys_commands.go | 5 +- core/cmd/ocr2_keys_commands_test.go | 2 +- core/cmd/ocr_keys_commands.go | 5 +- core/cmd/ocr_keys_commands_test.go | 2 +- core/cmd/p2p_keys_commands.go | 5 +- core/cmd/p2p_keys_commands_test.go | 2 +- core/cmd/shell_local.go | 3 +- core/cmd/solana_keys_commands.go | 2 +- core/cmd/solana_keys_commands_test.go | 2 +- core/cmd/starknet_keys_commands.go | 2 +- core/cmd/starknet_keys_commands_test.go | 2 +- core/cmd/vrf_keys_commands_test.go | 4 +- core/config/docs/defaults.go | 2 +- core/config/docs/docs_test.go | 2 +- core/gethwrappers/go_generate_test.go | 3 +- core/internal/testutils/pgtest/pgtest.go | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/chainlink/application.go | 6 +- core/services/chainlink/config_general.go | 6 +- .../services/chainlink/config_general_test.go | 2 +- core/services/chainlink/config_test.go | 2 +- core/services/feeds/orm_test.go | 2 +- core/services/job/spawner.go | 2 +- core/services/job/spawner_test.go | 2 +- core/services/keystore/cosmos_test.go | 2 +- core/services/keystore/eth_test.go | 9 +- core/services/keystore/ocr_test.go | 2 +- core/services/keystore/p2p_test.go | 2 +- core/services/keystore/solana_test.go | 2 +- core/services/keystore/starknet_test.go | 2 +- core/services/keystore/vrf_test.go | 2 +- .../internal/ocr2vrf_integration_test.go | 13 +- core/services/pg/event_broadcaster.go | 7 +- core/services/pg/lease_lock.go | 2 +- core/services/pipeline/common.go | 3 +- core/services/pipeline/runner.go | 6 +- .../vrf_solidity_crosscheck_test.go | 2 +- .../vrf_v08_solidity_crosscheck_test.go | 2 +- core/sessions/ldapauth/sync.go | 4 +- core/sessions/localauth/reaper.go | 4 +- core/utils/config/toml.go | 22 --- core/utils/sleeper_task.go | 129 ------------------ core/utils/sleeper_task_test.go | 118 ---------------- core/utils/utils.go | 73 ---------- core/utils/utils_test.go | 71 ---------- core/web/cosmos_keys_controller_test.go | 2 +- core/web/dkgencrypt_keys_controller_test.go | 2 +- core/web/dkgsign_keys_controller_test.go | 2 +- core/web/jobs_controller_test.go | 2 +- core/web/ocr2_keys_controller_test.go | 2 +- core/web/ocr_keys_controller_test.go | 2 +- core/web/p2p_keys_controller_test.go | 2 +- core/web/solana_keys_controller_test.go | 2 +- core/web/starknet_keys_controller_test.go | 2 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/types/config/node/core.go | 3 +- 88 files changed, 198 insertions(+), 628 deletions(-) delete mode 100644 core/utils/config/toml.go delete mode 100644 core/utils/sleeper_task.go delete mode 100644 core/utils/sleeper_task_test.go diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index ff6a9872840..ee0492aafa8 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -13,6 +13,7 @@ import ( "gopkg.in/guregu/null.v4" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/common/config" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" - configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) type HasEVMConfigs interface { @@ -37,41 +37,41 @@ func (cs EVMConfigs) ValidateConfig() (err error) { func (cs EVMConfigs) validateKeys() (err error) { // Unique chain IDs - chainIDs := configutils.UniqueStrings{} + chainIDs := commonconfig.UniqueStrings{} for i, c := range cs { if chainIDs.IsDupeFmt(c.ChainID) { - err = multierr.Append(err, configutils.NewErrDuplicate(fmt.Sprintf("%d.ChainID", i), c.ChainID.String())) + err = multierr.Append(err, commonconfig.NewErrDuplicate(fmt.Sprintf("%d.ChainID", i), c.ChainID.String())) } } // Unique node names - names := configutils.UniqueStrings{} + names := commonconfig.UniqueStrings{} for i, c := range cs { for j, n := range c.Nodes { if names.IsDupe(n.Name) { - err = multierr.Append(err, configutils.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.Name", i, j), *n.Name)) + err = multierr.Append(err, commonconfig.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.Name", i, j), *n.Name)) } } } // Unique node WSURLs - wsURLs := configutils.UniqueStrings{} + wsURLs := commonconfig.UniqueStrings{} for i, c := range cs { for j, n := range c.Nodes { u := (*url.URL)(n.WSURL) if wsURLs.IsDupeFmt(u) { - err = multierr.Append(err, configutils.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.WSURL", i, j), u.String())) + err = multierr.Append(err, commonconfig.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.WSURL", i, j), u.String())) } } } // Unique node HTTPURLs - httpURLs := configutils.UniqueStrings{} + httpURLs := commonconfig.UniqueStrings{} for i, c := range cs { for j, n := range c.Nodes { u := (*url.URL)(n.HTTPURL) if httpURLs.IsDupeFmt(u) { - err = multierr.Append(err, configutils.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.HTTPURL", i, j), u.String())) + err = multierr.Append(err, commonconfig.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.HTTPURL", i, j), u.String())) } } } @@ -290,29 +290,29 @@ func (c *EVMConfig) SetFrom(f *EVMConfig) { func (c *EVMConfig) ValidateConfig() (err error) { if c.ChainID == nil { - err = multierr.Append(err, configutils.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) + err = multierr.Append(err, commonconfig.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) } else if c.ChainID.String() == "" { - err = multierr.Append(err, configutils.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) + err = multierr.Append(err, commonconfig.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) } else if must, ok := ChainTypeForID(c.ChainID); ok { // known chain id if c.ChainType == nil && must != "" { - err = multierr.Append(err, configutils.ErrMissing{Name: "ChainType", + err = multierr.Append(err, commonconfig.ErrMissing{Name: "ChainType", Msg: fmt.Sprintf("only %q can be used with this chain id", must)}) } else if c.ChainType != nil && *c.ChainType != string(must) { if *c.ChainType == "" { - err = multierr.Append(err, configutils.ErrEmpty{Name: "ChainType", + err = multierr.Append(err, commonconfig.ErrEmpty{Name: "ChainType", Msg: fmt.Sprintf("only %q can be used with this chain id", must)}) } else if must == "" { - err = multierr.Append(err, configutils.ErrInvalid{Name: "ChainType", Value: *c.ChainType, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "ChainType", Value: *c.ChainType, Msg: "must not be set with this chain id"}) } else { - err = multierr.Append(err, configutils.ErrInvalid{Name: "ChainType", Value: *c.ChainType, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "ChainType", Value: *c.ChainType, Msg: fmt.Sprintf("only %q can be used with this chain id", must)}) } } } if len(c.Nodes) == 0 { - err = multierr.Append(err, configutils.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) + err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) } else { var hasPrimary bool for _, n := range c.Nodes { @@ -323,7 +323,7 @@ func (c *EVMConfig) ValidateConfig() (err error) { break } if !hasPrimary { - err = multierr.Append(err, configutils.ErrMissing{Name: "Nodes", + err = multierr.Append(err, commonconfig.ErrMissing{Name: "Nodes", Msg: "must have at least one primary node with WSURL"}) } } @@ -377,24 +377,24 @@ func (c *Chain) ValidateConfig() (err error) { chainType = config.ChainType(*c.ChainType) } if !chainType.IsValid() { - err = multierr.Append(err, configutils.ErrInvalid{Name: "ChainType", Value: *c.ChainType, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "ChainType", Value: *c.ChainType, Msg: config.ErrInvalidChainType.Error()}) } if c.GasEstimator.BumpTxDepth != nil && uint32(*c.GasEstimator.BumpTxDepth) > *c.Transactions.MaxInFlight { - err = multierr.Append(err, configutils.ErrInvalid{Name: "GasEstimator.BumpTxDepth", Value: *c.GasEstimator.BumpTxDepth, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "GasEstimator.BumpTxDepth", Value: *c.GasEstimator.BumpTxDepth, Msg: "must be less than or equal to Transactions.MaxInFlight"}) } if *c.HeadTracker.HistoryDepth < *c.FinalityDepth { - err = multierr.Append(err, configutils.ErrInvalid{Name: "HeadTracker.HistoryDepth", Value: *c.HeadTracker.HistoryDepth, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "HeadTracker.HistoryDepth", Value: *c.HeadTracker.HistoryDepth, Msg: "must be equal to or greater than FinalityDepth"}) } if *c.FinalityDepth < 1 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "FinalityDepth", Value: *c.FinalityDepth, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "FinalityDepth", Value: *c.FinalityDepth, Msg: "must be greater than or equal to 1"}) } if *c.MinIncomingConfirmations < 1 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "MinIncomingConfirmations", Value: *c.MinIncomingConfirmations, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "MinIncomingConfirmations", Value: *c.MinIncomingConfirmations, Msg: "must be greater than or equal to 1"}) } return @@ -487,36 +487,36 @@ type GasEstimator struct { func (e *GasEstimator) ValidateConfig() (err error) { if uint64(*e.BumpPercent) < txpool.DefaultConfig.PriceBump { - err = multierr.Append(err, configutils.ErrInvalid{Name: "BumpPercent", Value: *e.BumpPercent, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "BumpPercent", Value: *e.BumpPercent, Msg: fmt.Sprintf("may not be less than Geth's default of %d", txpool.DefaultConfig.PriceBump)}) } if e.TipCapDefault.Cmp(e.TipCapMin) < 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "TipCapDefault", Value: e.TipCapDefault, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "TipCapDefault", Value: e.TipCapDefault, Msg: "must be greater than or equal to TipCapMinimum"}) } if e.FeeCapDefault.Cmp(e.TipCapDefault) < 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "FeeCapDefault", Value: e.TipCapDefault, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "FeeCapDefault", Value: e.TipCapDefault, Msg: "must be greater than or equal to TipCapDefault"}) } if *e.Mode == "FixedPrice" && *e.BumpThreshold == 0 && *e.EIP1559DynamicFees && e.FeeCapDefault.Cmp(e.PriceMax) != 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "FeeCapDefault", Value: e.FeeCapDefault, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "FeeCapDefault", Value: e.FeeCapDefault, Msg: fmt.Sprintf("must be equal to PriceMax (%s) since you are using FixedPrice estimation with gas bumping disabled in "+ "EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault", e.PriceMax)}) } else if e.FeeCapDefault.Cmp(e.PriceMax) > 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "FeeCapDefault", Value: e.FeeCapDefault, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "FeeCapDefault", Value: e.FeeCapDefault, Msg: fmt.Sprintf("must be less than or equal to PriceMax (%s)", e.PriceMax)}) } if e.PriceMin.Cmp(e.PriceDefault) > 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "PriceMin", Value: e.PriceMin, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "PriceMin", Value: e.PriceMin, Msg: "must be less than or equal to PriceDefault"}) } if e.PriceMax.Cmp(e.PriceDefault) < 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "PriceMax", Value: e.PriceMin, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "PriceMax", Value: e.PriceMin, Msg: "must be greater than or equal to PriceDefault"}) } if *e.Mode == "BlockHistory" && *e.BlockHistory.BlockHistorySize <= 0 { - err = multierr.Append(err, configutils.ErrInvalid{Name: "BlockHistory.BlockHistorySize", Value: *e.BlockHistory.BlockHistorySize, + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "BlockHistory.BlockHistorySize", Value: *e.BlockHistory.BlockHistorySize, Msg: "must be greater than or equal to 1 with BlockHistory Mode"}) } @@ -643,7 +643,7 @@ func (ks KeySpecificConfig) ValidateConfig() (err error) { for _, k := range ks { addr := k.Key.String() if _, ok := addrs[addr]; ok { - err = multierr.Append(err, configutils.NewErrDuplicate("Key", addr)) + err = multierr.Append(err, commonconfig.NewErrDuplicate("Key", addr)) } else { addrs[addr] = struct{}{} } @@ -750,9 +750,9 @@ type Node struct { func (n *Node) ValidateConfig() (err error) { if n.Name == nil { - err = multierr.Append(err, configutils.ErrMissing{Name: "Name", Msg: "required for all nodes"}) + err = multierr.Append(err, commonconfig.ErrMissing{Name: "Name", Msg: "required for all nodes"}) } else if *n.Name == "" { - err = multierr.Append(err, configutils.ErrEmpty{Name: "Name", Msg: "required for all nodes"}) + err = multierr.Append(err, commonconfig.ErrEmpty{Name: "Name", Msg: "required for all nodes"}) } var sendOnly bool @@ -761,34 +761,34 @@ func (n *Node) ValidateConfig() (err error) { } if n.WSURL == nil { if !sendOnly { - err = multierr.Append(err, configutils.ErrMissing{Name: "WSURL", Msg: "required for primary nodes"}) + err = multierr.Append(err, commonconfig.ErrMissing{Name: "WSURL", Msg: "required for primary nodes"}) } } else if n.WSURL.IsZero() { if !sendOnly { - err = multierr.Append(err, configutils.ErrEmpty{Name: "WSURL", Msg: "required for primary nodes"}) + err = multierr.Append(err, commonconfig.ErrEmpty{Name: "WSURL", Msg: "required for primary nodes"}) } } else { switch n.WSURL.Scheme { case "ws", "wss": default: - err = multierr.Append(err, configutils.ErrInvalid{Name: "WSURL", Value: n.WSURL.Scheme, Msg: "must be ws or wss"}) + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "WSURL", Value: n.WSURL.Scheme, Msg: "must be ws or wss"}) } } if n.HTTPURL == nil { - err = multierr.Append(err, configutils.ErrMissing{Name: "HTTPURL", Msg: "required for all nodes"}) + err = multierr.Append(err, commonconfig.ErrMissing{Name: "HTTPURL", Msg: "required for all nodes"}) } else if n.HTTPURL.IsZero() { - err = multierr.Append(err, configutils.ErrEmpty{Name: "HTTPURL", Msg: "required for all nodes"}) + err = multierr.Append(err, commonconfig.ErrEmpty{Name: "HTTPURL", Msg: "required for all nodes"}) } else { switch n.HTTPURL.Scheme { case "http", "https": default: - err = multierr.Append(err, configutils.ErrInvalid{Name: "HTTPURL", Value: n.HTTPURL.Scheme, Msg: "must be http or https"}) + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "HTTPURL", Value: n.HTTPURL.Scheme, Msg: "must be http or https"}) } } if n.Order != nil && (*n.Order < 1 || *n.Order > 100) { - err = multierr.Append(err, configutils.ErrInvalid{Name: "Order", Value: *n.Order, Msg: "must be between 1 and 100"}) + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Order", Value: *n.Order, Msg: "must be between 1 and 100"}) } else if n.Order == nil { z := int32(100) n.Order = &z diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index 27127993d8a..5e9a10de003 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -8,9 +8,9 @@ import ( "slices" "strings" + cconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) var ( @@ -40,7 +40,7 @@ func init() { Chain }{} - if err := configutils.DecodeTOML(bytes.NewReader(b), &config); err != nil { + if err := cconfig.DecodeTOML(bytes.NewReader(b), &config); err != nil { log.Fatalf("failed to decode %q: %v", path, err) } if fe.Name() == "fallback.toml" { diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index ee020f67002..ca819ba04b3 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -15,11 +15,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ArbConfig interface { diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index ce1a50aa320..8df817f7864 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -14,11 +14,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name ethClient --output ./mocks/ --case=underscore --structname ETHClient diff --git a/core/chains/evm/gas/suggested_price_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go index 2e0d32bfc94..3b02b7f4362 100644 --- a/core/chains/evm/gas/suggested_price_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -11,12 +11,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index 524a0f03965..55a53211fc9 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -23,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name Broadcaster --output ./mocks/ --case=underscore --structname Broadcaster --filename broadcaster.go diff --git a/core/chains/evm/log/orm.go b/core/chains/evm/log/orm.go index 3feb4bb836b..51ed9f2f132 100644 --- a/core/chains/evm/log/orm.go +++ b/core/chains/evm/log/orm.go @@ -12,9 +12,10 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // ORM is the interface for log broadcasts. diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 7bc131afef4..f82cec62b74 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -22,13 +22,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name LogPoller --output ./mocks/ --case=underscore --structname LogPoller --filename log_poller.go diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index f50dccf1ad2..81d1b74e42d 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -27,6 +27,8 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -1294,7 +1296,7 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { require.ErrorContains(t, err, "Invalid replay block number") // Force a db error while loading the filters (tx aborted, already rolled back) - require.Error(t, utils.JustError(db.Exec(`invalid query`))) + require.Error(t, commonutils.JustError(db.Exec(`invalid query`))) go func() { err = lp.Replay(ctx, 2) assert.ErrorContains(t, err, "current transaction is aborted") diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index c3b9a49c7af..b0f0fbc9c91 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -15,13 +15,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name BalanceMonitor --output ../mocks/ --case=underscore @@ -42,7 +42,7 @@ type ( ethKeyStore keystore.Eth ethBalances map[gethCommon.Address]*assets.Eth ethBalancesMtx *sync.RWMutex - sleeperTask utils.SleeperTask + sleeperTask *utils.SleeperTask } NullBalanceMonitor struct{} diff --git a/core/chains/evm/monitor/balance_helpers_test.go b/core/chains/evm/monitor/balance_helpers_test.go index 624aa69f061..ed949882295 100644 --- a/core/chains/evm/monitor/balance_helpers_test.go +++ b/core/chains/evm/monitor/balance_helpers_test.go @@ -1,7 +1,5 @@ package monitor func (bm *balanceMonitor) WorkDone() <-chan struct{} { - return bm.sleeperTask.(interface { - WorkDone() <-chan struct{} - }).WorkDone() + return bm.sleeperTask.WorkDone() } diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index f12796beadd..a0711a55a56 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" @@ -649,7 +650,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi } // Simulate a "PruneQueue" call - assert.NoError(t, utils.JustError(db.Exec(`DELETE FROM evm.txes WHERE state = 'unstarted'`))) + assert.NoError(t, commonutils.JustError(db.Exec(`DELETE FROM evm.txes WHERE state = 'unstarted'`))) close(chBlock) }() @@ -1009,7 +1010,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) ctx := testutils.Context(t) - require.NoError(t, utils.JustError(db.Exec(`SET CONSTRAINTS pipeline_runs_pipeline_spec_id_fkey DEFERRED`))) + require.NoError(t, commonutils.JustError(db.Exec(`SET CONSTRAINTS pipeline_runs_pipeline_spec_id_fkey DEFERRED`))) t.Run("if external wallet sent a transaction from the account and now the nonce is one higher than it should be and we got replacement underpriced then we assume a previous transaction of ours was the one that succeeded, and hand off to EthConfirmer", func(t *testing.T) { mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index 9ff6f8d5d74..0aa03536276 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -14,11 +14,12 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + 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/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ TxmClient = (*evmTxmClient)(nil) diff --git a/core/chains/evm/txmgr/reaper_test.go b/core/chains/evm/txmgr/reaper_test.go index 2da8e7a93c8..a539f0ac8cb 100644 --- a/core/chains/evm/txmgr/reaper_test.go +++ b/core/chains/evm/txmgr/reaper_test.go @@ -9,13 +9,14 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" txmgrmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func newReaperWithChainID(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], cfg txmgrtypes.ReaperChainConfig, txConfig txmgrtypes.ReaperTransactionsConfig, cid *big.Int) *txmgr.Reaper { diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go index 919fb509fee..5a5cc3dbcd4 100644 --- a/core/chains/evm/txmgr/transmitchecker.go +++ b/core/chains/evm/txmgr/transmitchecker.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" + "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -23,7 +24,6 @@ import ( v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" v2 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ( @@ -252,7 +252,7 @@ func (v *VRFV1Checker) Check( "meta", tx.Meta, "reqID", reqID) return nil - } else if utils.IsEmpty(callback.SeedAndBlockNum[:]) { + } else if bytes.IsEmpty(callback.SeedAndBlockNum[:]) { // Request already fulfilled l.Infow("Request already fulfilled", "err", err, @@ -344,7 +344,7 @@ func (v *VRFV2Checker) Check( "blockNumber", h.Number, ) return nil - } else if utils.IsEmpty(callback[:]) { + } else if bytes.IsEmpty(callback[:]) { // If seedAndBlockNumber is zero then the response has been fulfilled and we should skip it. l.Infow("Request already fulfilled.", "ethTxID", tx.ID, diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 24c7e750c95..6fafff1a5c1 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -22,6 +22,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" @@ -419,7 +421,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { require.Equal(t, payload, etx.EncodedPayload) }) - require.NoError(t, utils.JustError(db.Exec(`DELETE FROM evm.txes WHERE from_address = $1`, thisKey.Address))) + require.NoError(t, commonutils.JustError(db.Exec(`DELETE FROM evm.txes WHERE from_address = $1`, thisKey.Address))) t.Run("if this key has any transactions with insufficient eth errors, inserts it anyway", func(t *testing.T) { payload := cltest.MustRandomBytes(t, 100) @@ -442,7 +444,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { require.Equal(t, payload, etx.EncodedPayload) }) - require.NoError(t, utils.JustError(db.Exec(`DELETE FROM evm.txes WHERE from_address = $1`, thisKey.Address))) + require.NoError(t, commonutils.JustError(db.Exec(`DELETE FROM evm.txes WHERE from_address = $1`, thisKey.Address))) t.Run("if this key has transactions but no insufficient eth errors, transmits as normal", func(t *testing.T) { payload := cltest.MustRandomBytes(t, 100) diff --git a/core/chains/evm/utils/utils.go b/core/chains/evm/utils/utils.go index 3806628a805..97aaf3b2bd4 100644 --- a/core/chains/evm/utils/utils.go +++ b/core/chains/evm/utils/utils.go @@ -180,19 +180,23 @@ func NewRedialBackoff() backoff.Backoff { } -// Sleeper interface is used for tasks that need to be done on some -// interval, excluding Cron, like reconnecting. -type Sleeper interface { - Reset() - Sleep() - After() time.Duration - Duration() time.Duration -} +// RetryWithBackoff retries the sleeper and backs off if not Done +func RetryWithBackoff(ctx context.Context, fn func() (retry bool)) { + sleeper := NewBackoffSleeper() + sleeper.Reset() + for { + retry := fn() + if !retry { + return + } -// BackoffSleeper is a sleeper that backs off on subsequent attempts. -type BackoffSleeper struct { - backoff.Backoff - beenRun atomic.Bool + select { + case <-ctx.Done(): + return + case <-time.After(sleeper.After()): + continue + } + } } // NewBackoffSleeper returns a BackoffSleeper that is configured to @@ -207,6 +211,12 @@ func NewBackoffSleeper() *BackoffSleeper { } } +// BackoffSleeper is a sleeper that backs off on subsequent attempts. +type BackoffSleeper struct { + backoff.Backoff + beenRun atomic.Bool +} + // Sleep waits for the given duration, incrementing the back off. func (bs *BackoffSleeper) Sleep() { if bs.beenRun.CompareAndSwap(false, true) { @@ -237,54 +247,6 @@ func (bs *BackoffSleeper) Reset() { bs.Backoff.Reset() } -// RetryWithBackoff retries the sleeper and backs off if not Done -func RetryWithBackoff(ctx context.Context, fn func() (retry bool)) { - sleeper := NewBackoffSleeper() - sleeper.Reset() - for { - retry := fn() - if !retry { - return - } - - select { - case <-ctx.Done(): - return - case <-time.After(sleeper.After()): - continue - } - } -} - -// AllEqual returns true iff all the provided elements are equal to each other. -func AllEqual[T comparable](elems ...T) bool { - for i := 1; i < len(elems); i++ { - if elems[i] != elems[0] { - return false - } - } - return true -} - -// JustError takes a tuple and returns the last entry, the error. -func JustError(_ interface{}, err error) error { - return err -} - -// WrapIfError decorates an error with the given message. It is intended to -// be used with `defer` statements, like so: -// -// func SomeFunction() (err error) { -// defer WrapIfError(&err, "error in SomeFunction:") -// -// ... -// } -func WrapIfError(err *error, msg string) { - if *err != nil { - *err = fmt.Errorf(msg, *err) - } -} - // RandUint256 generates a random bigNum up to 2 ** 256 - 1 func RandUint256() *big.Int { n, err := rand.Int(rand.Reader, MaxUint256) diff --git a/core/cmd/admin_commands.go b/core/cmd/admin_commands.go index 24e78e5e2bb..a0e69867e71 100644 --- a/core/cmd/admin_commands.go +++ b/core/cmd/admin_commands.go @@ -17,6 +17,8 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -155,7 +157,7 @@ func (p *AdminUsersPresenter) RenderTable(rt RendererTable) error { renderList(adminUsersTableHeaders, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } type AdminUsersPresenters []AdminUsersPresenter @@ -173,7 +175,7 @@ func (ps AdminUsersPresenters) RenderTable(rt RendererTable) error { } renderList(adminUsersTableHeaders, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } // ListUsers renders all API users and their roles diff --git a/core/cmd/cosmos_keys_commands.go b/core/cmd/cosmos_keys_commands.go index e951ff37a18..11974198fd6 100644 --- a/core/cmd/cosmos_keys_commands.go +++ b/core/cmd/cosmos_keys_commands.go @@ -1,8 +1,8 @@ package cmd import ( + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/cosmos_keys_commands_test.go b/core/cmd/cosmos_keys_commands_test.go index 2cab11379d0..16609daadc6 100644 --- a/core/cmd/cosmos_keys_commands_test.go +++ b/core/cmd/cosmos_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/csa_keys_commands.go b/core/cmd/csa_keys_commands.go index 4874176d335..c2ab3697b18 100644 --- a/core/cmd/csa_keys_commands.go +++ b/core/cmd/csa_keys_commands.go @@ -12,6 +12,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -102,7 +103,7 @@ func (ps CSAKeyPresenters) RenderTable(rt RendererTable) error { return err } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } // ListCSAKeys retrieves a list of all CSA keys diff --git a/core/cmd/csa_keys_commands_test.go b/core/cmd/csa_keys_commands_test.go index 869608fa72b..a181922979a 100644 --- a/core/cmd/csa_keys_commands_test.go +++ b/core/cmd/csa_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/dkgencrypt_keys_commands.go b/core/cmd/dkgencrypt_keys_commands.go index 6a8334454b8..ddcc80a33be 100644 --- a/core/cmd/dkgencrypt_keys_commands.go +++ b/core/cmd/dkgencrypt_keys_commands.go @@ -1,8 +1,8 @@ package cmd import ( + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgsignkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/dkgencrypt_keys_commands_test.go b/core/cmd/dkgencrypt_keys_commands_test.go index a7505ce46dc..b4c6d6f6e91 100644 --- a/core/cmd/dkgencrypt_keys_commands_test.go +++ b/core/cmd/dkgencrypt_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgencryptkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/dkgsign_keys_commands.go b/core/cmd/dkgsign_keys_commands.go index 6c345ce42a2..b0435450e32 100644 --- a/core/cmd/dkgsign_keys_commands.go +++ b/core/cmd/dkgsign_keys_commands.go @@ -1,8 +1,8 @@ package cmd import ( + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgsignkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/dkgsign_keys_commands_test.go b/core/cmd/dkgsign_keys_commands_test.go index 1948800d677..717f988b328 100644 --- a/core/cmd/dkgsign_keys_commands_test.go +++ b/core/cmd/dkgsign_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgsignkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/eth_keys_commands.go b/core/cmd/eth_keys_commands.go index c13bea80f2f..ad76a7655ef 100644 --- a/core/cmd/eth_keys_commands.go +++ b/core/cmd/eth_keys_commands.go @@ -14,6 +14,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -143,7 +144,7 @@ func (p *EthKeyPresenter) RenderTable(rt RendererTable) error { renderList(ethKeysTableHeaders, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } type EthKeyPresenters []EthKeyPresenter diff --git a/core/cmd/eth_keys_commands_test.go b/core/cmd/eth_keys_commands_test.go index 7c85b779ecc..e9121270191 100644 --- a/core/cmd/eth_keys_commands_test.go +++ b/core/cmd/eth_keys_commands_test.go @@ -13,13 +13,14 @@ import ( "github.com/pkg/errors" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" "github.com/stretchr/testify/assert" diff --git a/core/cmd/ocr2_keys_commands.go b/core/cmd/ocr2_keys_commands.go index f1df2626fbc..ad4b30df4c2 100644 --- a/core/cmd/ocr2_keys_commands.go +++ b/core/cmd/ocr2_keys_commands.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -92,7 +93,7 @@ func (p *OCR2KeyBundlePresenter) RenderTable(rt RendererTable) error { } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } func (p *OCR2KeyBundlePresenter) ToRow() []string { @@ -121,7 +122,7 @@ func (ps OCR2KeyBundlePresenters) RenderTable(rt RendererTable) error { } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } // ListOCR2KeyBundles lists the available OCR2 Key Bundles diff --git a/core/cmd/ocr2_keys_commands_test.go b/core/cmd/ocr2_keys_commands_test.go index dd2ac2544da..5a861fafa7c 100644 --- a/core/cmd/ocr2_keys_commands_test.go +++ b/core/cmd/ocr2_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/ocr_keys_commands.go b/core/cmd/ocr_keys_commands.go index b9343a4a3f3..2628cd9b270 100644 --- a/core/cmd/ocr_keys_commands.go +++ b/core/cmd/ocr_keys_commands.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -91,7 +92,7 @@ func (p *OCRKeyBundlePresenter) RenderTable(rt RendererTable) error { } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } func (p *OCRKeyBundlePresenter) ToRow() []string { @@ -135,7 +136,7 @@ func (ps OCRKeyBundlePresenters) RenderTable(rt RendererTable) error { } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } // CreateOCR2KeyBundle creates an OCR key bundle and saves it to the keystore diff --git a/core/cmd/ocr_keys_commands_test.go b/core/cmd/ocr_keys_commands_test.go index aeda9006610..f5da3294903 100644 --- a/core/cmd/ocr_keys_commands_test.go +++ b/core/cmd/ocr_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocrkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/p2p_keys_commands.go b/core/cmd/p2p_keys_commands.go index 4ef3c349faf..4ec03da96ca 100644 --- a/core/cmd/p2p_keys_commands.go +++ b/core/cmd/p2p_keys_commands.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -90,7 +91,7 @@ func (p *P2PKeyPresenter) RenderTable(rt RendererTable) error { } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } func (p *P2PKeyPresenter) ToRow() []string { @@ -119,7 +120,7 @@ func (ps P2PKeyPresenters) RenderTable(rt RendererTable) error { } renderList(headers, rows, rt.Writer) - return utils.JustError(rt.Write([]byte("\n"))) + return cutils.JustError(rt.Write([]byte("\n"))) } // ListP2PKeys retrieves a list of all P2P keys diff --git a/core/cmd/p2p_keys_commands_test.go b/core/cmd/p2p_keys_commands_test.go index 683129fafa7..87269e02711 100644 --- a/core/cmd/p2p_keys_commands_test.go +++ b/core/cmd/p2p_keys_commands_test.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index e4c29a0e5c2..d4fb796c3e2 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -33,6 +33,7 @@ import ( "github.com/jmoiron/sqlx" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -808,7 +809,7 @@ func dropDanglingTestDBs(lggr logger.Logger, db *sqlx.DB) (err error) { defer wg.Done() for dbname := range ch { lggr.Infof("Dropping old, dangling test database: %q", dbname) - gerr := utils.JustError(db.Exec(fmt.Sprintf(`DROP DATABASE IF EXISTS %s`, dbname))) + gerr := cutils.JustError(db.Exec(fmt.Sprintf(`DROP DATABASE IF EXISTS %s`, dbname))) errCh <- gerr } }() diff --git a/core/cmd/solana_keys_commands.go b/core/cmd/solana_keys_commands.go index 4dac505af51..636f8a7014a 100644 --- a/core/cmd/solana_keys_commands.go +++ b/core/cmd/solana_keys_commands.go @@ -1,8 +1,8 @@ package cmd import ( + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/solana_keys_commands_test.go b/core/cmd/solana_keys_commands_test.go index e72343be45c..d58c3657019 100644 --- a/core/cmd/solana_keys_commands_test.go +++ b/core/cmd/solana_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/starknet_keys_commands.go b/core/cmd/starknet_keys_commands.go index 3b98a1d11e8..5722e5bd946 100644 --- a/core/cmd/starknet_keys_commands.go +++ b/core/cmd/starknet_keys_commands.go @@ -1,8 +1,8 @@ package cmd import ( + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/starknet_keys_commands_test.go b/core/cmd/starknet_keys_commands_test.go index b1fa4d88f05..0cf0065129d 100644 --- a/core/cmd/starknet_keys_commands_test.go +++ b/core/cmd/starknet_keys_commands_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/cmd/vrf_keys_commands_test.go b/core/cmd/vrf_keys_commands_test.go index a061067771d..f912e861a64 100644 --- a/core/cmd/vrf_keys_commands_test.go +++ b/core/cmd/vrf_keys_commands_test.go @@ -8,14 +8,14 @@ import ( "github.com/urfave/cli" - "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/web/presenters" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) func TestVRFKeyPresenter_RenderTable(t *testing.T) { diff --git a/core/config/docs/defaults.go b/core/config/docs/defaults.go index 8946d75cc65..53e6433a8ef 100644 --- a/core/config/docs/defaults.go +++ b/core/config/docs/defaults.go @@ -4,10 +4,10 @@ import ( "log" "strings" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/cfgtest" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" - "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) var ( diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 74fa6682c96..30688c38879 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/docs" @@ -21,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/cfgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) func TestDoc(t *testing.T) { diff --git a/core/gethwrappers/go_generate_test.go b/core/gethwrappers/go_generate_test.go index 9ded44c0785..e0779c306c4 100644 --- a/core/gethwrappers/go_generate_test.go +++ b/core/gethwrappers/go_generate_test.go @@ -14,6 +14,7 @@ import ( gethParams "github.com/ethereum/go-ethereum/params" "github.com/fatih/color" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" @@ -112,7 +113,7 @@ func init() { for db.Scan() { line := strings.Fields(db.Text()) if stripTrailingColon(line[0], "") != "GETH_VERSION" { - if os.IsNotExist(utils.JustError(os.Stat(line[1]))) { + if os.IsNotExist(cutils.JustError(os.Stat(line[1]))) { solidityArtifactsMissing = append(solidityArtifactsMissing, line[1]) } } diff --git a/core/internal/testutils/pgtest/pgtest.go b/core/internal/testutils/pgtest/pgtest.go index 01024b39c37..686483f2d41 100644 --- a/core/internal/testutils/pgtest/pgtest.go +++ b/core/internal/testutils/pgtest/pgtest.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func NewQConfig(logSQL bool) pg.QConfig { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index ee2570dcf93..1899270d325 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 0bfd019b047..47ef9711147 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab h1:6ckB261FRUy4K/OfSfWCQLAGkgfVLYT5PKDImmp3tZM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 h1:tpJnh0cJaUhgbwRzDUGqzdp2XJnn369T4nbDV2w8LZY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 32edaa85110..ee109db2119 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/static" @@ -58,7 +59,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/sessions/ldapauth" "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -130,7 +130,7 @@ type ChainlinkApplication struct { Config GeneralConfig KeyStore keystore.Master ExternalInitiatorManager webhook.ExternalInitiatorManager - SessionReaper utils.SleeperTask + SessionReaper *utils.SleeperTask shutdownOnce sync.Once srvcs []services.ServiceCtx HealthChecker services.Checker @@ -267,7 +267,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { // localDB auth or remote LDAP auth authMethod := cfg.WebServer().AuthenticationMethod() var authenticationProvider sessions.AuthenticationProvider - var sessionReaper utils.SleeperTask + var sessionReaper *utils.SleeperTask switch sessions.AuthenticationProviderName(authMethod) { case sessions.LDAPAuth: diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 5df2b177bdd..cb68ed0e72d 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana" starknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" @@ -26,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) // generalConfig is a wrapper to adapt Config to the config.GeneralConfig interface. @@ -97,7 +97,7 @@ func (o *GeneralConfigOpts) Setup(configFiles []string, secretsFiles []string) e // parseConfig sets Config from the given TOML string, overriding any existing duplicate Config fields. func (o *GeneralConfigOpts) parseConfig(config string) error { var c Config - if err2 := configutils.DecodeTOML(strings.NewReader(config), &c); err2 != nil { + if err2 := commonconfig.DecodeTOML(strings.NewReader(config), &c); err2 != nil { return fmt.Errorf("failed to decode config TOML: %w", err2) } @@ -111,7 +111,7 @@ func (o *GeneralConfigOpts) parseConfig(config string) error { // parseSecrets sets Secrets from the given TOML string. Errors on overrides func (o *GeneralConfigOpts) parseSecrets(secrets string) error { var s Secrets - if err2 := configutils.DecodeTOML(strings.NewReader(secrets), &s); err2 != nil { + if err2 := commonconfig.DecodeTOML(strings.NewReader(secrets), &s); err2 != nil { return fmt.Errorf("failed to decode secrets TOML: %w", err2) } diff --git a/core/services/chainlink/config_general_test.go b/core/services/chainlink/config_general_test.go index c122f8f968c..444f34abcbb 100644 --- a/core/services/chainlink/config_general_test.go +++ b/core/services/chainlink/config_general_test.go @@ -14,9 +14,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) func TestTOMLGeneralConfig_Defaults(t *testing.T) { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 3b15395769a..f0c88db65fe 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -18,6 +18,7 @@ import ( ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/config" commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" @@ -37,7 +38,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) var ( diff --git a/core/services/feeds/orm_test.go b/core/services/feeds/orm_test.go index 02b9e24739c..88d3b132beb 100644 --- a/core/services/feeds/orm_test.go +++ b/core/services/feeds/orm_test.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -24,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" ) diff --git a/core/services/job/spawner.go b/core/services/job/spawner.go index 5ed017b8743..1d44cedaad9 100644 --- a/core/services/job/spawner.go +++ b/core/services/job/spawner.go @@ -12,10 +12,10 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name Spawner --output ./mocks/ --case=underscore diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 335156a8c65..a46d9ca6c32 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -35,7 +36,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" evmrelayer "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/plugins" ) diff --git a/core/services/keystore/cosmos_test.go b/core/services/keystore/cosmos_test.go index 3c33f16282d..30c669f7545 100644 --- a/core/services/keystore/cosmos_test.go +++ b/core/services/keystore/cosmos_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_CosmosKeyStore_E2E(t *testing.T) { diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index 61e0e92d82b..dd42f4049c3 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -35,8 +36,8 @@ func Test_EthKeyStore(t *testing.T) { ethKeyStore := keyStore.Eth() reset := func() { keyStore.ResetXXXTestOnly() - require.NoError(t, utils.JustError(db.Exec("DELETE FROM encrypted_key_rings"))) - require.NoError(t, utils.JustError(db.Exec("DELETE FROM evm.key_states"))) + require.NoError(t, commonutils.JustError(db.Exec("DELETE FROM encrypted_key_rings"))) + require.NoError(t, commonutils.JustError(db.Exec("DELETE FROM evm.key_states"))) require.NoError(t, keyStore.Unlock(cltest.Password)) } const statesTableName = "evm.key_states" @@ -360,8 +361,8 @@ func Test_EthKeyStore_E2E(t *testing.T) { ks := keyStore.Eth() reset := func() { keyStore.ResetXXXTestOnly() - require.NoError(t, utils.JustError(db.Exec("DELETE FROM encrypted_key_rings"))) - require.NoError(t, utils.JustError(db.Exec("DELETE FROM evm.key_states"))) + require.NoError(t, commonutils.JustError(db.Exec("DELETE FROM encrypted_key_rings"))) + require.NoError(t, commonutils.JustError(db.Exec("DELETE FROM evm.key_states"))) require.NoError(t, keyStore.Unlock(cltest.Password)) } diff --git a/core/services/keystore/ocr_test.go b/core/services/keystore/ocr_test.go index 200d62415eb..c65d576c452 100644 --- a/core/services/keystore/ocr_test.go +++ b/core/services/keystore/ocr_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocrkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_OCRKeyStore_E2E(t *testing.T) { diff --git a/core/services/keystore/p2p_test.go b/core/services/keystore/p2p_test.go index 829df9812fd..4e9ca75c456 100644 --- a/core/services/keystore/p2p_test.go +++ b/core/services/keystore/p2p_test.go @@ -9,13 +9,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_P2PKeyStore_E2E(t *testing.T) { diff --git a/core/services/keystore/solana_test.go b/core/services/keystore/solana_test.go index 6e895a56117..cf2515f5f70 100644 --- a/core/services/keystore/solana_test.go +++ b/core/services/keystore/solana_test.go @@ -6,13 +6,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_SolanaKeyStore_E2E(t *testing.T) { diff --git a/core/services/keystore/starknet_test.go b/core/services/keystore/starknet_test.go index 7fc5718bac0..a007b01f120 100644 --- a/core/services/keystore/starknet_test.go +++ b/core/services/keystore/starknet_test.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/caigo" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -20,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" starktxm "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/txm" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func Test_StarkNetKeyStore_E2E(t *testing.T) { diff --git a/core/services/keystore/vrf_test.go b/core/services/keystore/vrf_test.go index 7a2e91ffec3..77fccd865ff 100644 --- a/core/services/keystore/vrf_test.go +++ b/core/services/keystore/vrf_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/require" ) diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index 5c83e71946f..55517d5719d 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -133,13 +134,13 @@ func setupOCR2VRFContracts( require.NoError(t, err) b.Commit() - require.NoError(t, utils.JustError(coordinator.SetCallbackConfig(owner, vrf_wrapper.VRFCoordinatorCallbackConfig{ + require.NoError(t, commonutils.JustError(coordinator.SetCallbackConfig(owner, vrf_wrapper.VRFCoordinatorCallbackConfig{ MaxCallbackGasLimit: 2.5e6, MaxCallbackArgumentsLength: 160, // 5 EVM words }))) b.Commit() - require.NoError(t, utils.JustError(coordinator.SetCoordinatorConfig(owner, vrf_wrapper.VRFBeaconTypesCoordinatorConfig{ + require.NoError(t, commonutils.JustError(coordinator.SetCoordinatorConfig(owner, vrf_wrapper.VRFBeaconTypesCoordinatorConfig{ RedeemableRequestGasOverhead: 50_000, CallbackRequestGasOverhead: 50_000, StalenessSeconds: 60, @@ -163,7 +164,7 @@ func setupOCR2VRFContracts( b.Commit() // Set up coordinator subscription for billing. - require.NoError(t, utils.JustError(coordinator.CreateSubscription(owner))) + require.NoError(t, commonutils.JustError(coordinator.CreateSubscription(owner))) b.Commit() fopts := &bind.FilterOpts{} @@ -174,13 +175,13 @@ func setupOCR2VRFContracts( require.True(t, subscriptionIterator.Next()) subID := subscriptionIterator.Event.SubId - require.NoError(t, utils.JustError(coordinator.AddConsumer(owner, subID, consumerAddress))) + require.NoError(t, commonutils.JustError(coordinator.AddConsumer(owner, subID, consumerAddress))) b.Commit() - require.NoError(t, utils.JustError(coordinator.AddConsumer(owner, subID, loadTestConsumerAddress))) + require.NoError(t, commonutils.JustError(coordinator.AddConsumer(owner, subID, loadTestConsumerAddress))) b.Commit() data, err := utils.ABIEncode(`[{"type":"uint256"}]`, subID) require.NoError(t, err) - require.NoError(t, utils.JustError(link.TransferAndCall(owner, coordinatorAddress, big.NewInt(5e18), data))) + require.NoError(t, commonutils.JustError(link.TransferAndCall(owner, coordinatorAddress, big.NewInt(5e18), data))) b.Commit() _, err = dkg.AddClient(owner, keyID, beaconAddress) diff --git a/core/services/pg/event_broadcaster.go b/core/services/pg/event_broadcaster.go index 70008f4c0de..a575ab33489 100644 --- a/core/services/pg/event_broadcaster.go +++ b/core/services/pg/event_broadcaster.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/static" @@ -179,8 +180,8 @@ func (b *eventBroadcaster) Subscribe(channel, payloadFilter string) (Subscriptio chDone: make(chan struct{}), lggr: logger.Sugared(b.lggr), } - sub.processQueueWorker = utils.NewSleeperTask( - utils.SleeperFuncTask(sub.processQueue, "SubscriptionQueueProcessor"), + sub.processQueueWorker = commonutils.NewSleeperTask( + commonutils.SleeperFuncTask(sub.processQueue, "SubscriptionQueueProcessor"), ) b.subscriptions[channel][sub] = struct{}{} return sub, nil @@ -247,7 +248,7 @@ type subscription struct { payloadFilter string eventBroadcaster *eventBroadcaster queue *utils.BoundedQueue[Event] - processQueueWorker utils.SleeperTask + processQueueWorker *commonutils.SleeperTask chEvents chan Event chDone chan struct{} lggr logger.SugaredLogger diff --git a/core/services/pg/lease_lock.go b/core/services/pg/lease_lock.go index e21cec44bda..58ec2781245 100644 --- a/core/services/pg/lease_lock.go +++ b/core/services/pg/lease_lock.go @@ -12,8 +12,8 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // LeaseLock handles taking an exclusive lease on database access. This is not diff --git a/core/services/pipeline/common.go b/core/services/pipeline/common.go index 8cc54d540fb..0e72bed4226 100644 --- a/core/services/pipeline/common.go +++ b/core/services/pipeline/common.go @@ -20,6 +20,7 @@ import ( pkgerrors "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/logger" cnull "github.com/smartcontractkit/chainlink/v2/core/null" @@ -409,7 +410,7 @@ var ( ) func UnmarshalTaskFromMap(taskType TaskType, taskMap interface{}, ID int, dotID string) (_ Task, err error) { - defer utils.WrapIfError(&err, "UnmarshalTaskFromMap") + defer cutils.WrapIfError(&err, "UnmarshalTaskFromMap") switch taskMap.(type) { default: diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index e4d60db881b..3e5d77db5f2 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -62,7 +62,7 @@ type runner struct { legacyEVMChains legacyevm.LegacyChainContainer ethKeyStore ETHKeyStore vrfKeyStore VRFKeyStore - runReaperWorker utils.SleeperTask + runReaperWorker *commonutils.SleeperTask lggr logger.Logger httpClient *http.Client unrestrictedHTTPClient *http.Client @@ -120,8 +120,8 @@ func NewRunner(orm ORM, btORM bridges.ORM, cfg Config, bridgeCfg BridgeConfig, l httpClient: httpClient, unrestrictedHTTPClient: unrestrictedHTTPClient, } - r.runReaperWorker = utils.NewSleeperTask( - utils.SleeperFuncTask(r.runReaper, "PipelineRunnerReaper"), + r.runReaperWorker = commonutils.NewSleeperTask( + commonutils.SleeperFuncTask(r.runReaper, "PipelineRunnerReaper"), ) return r } diff --git a/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go index 90d6ab2c354..2476ee04ce2 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go @@ -19,8 +19,8 @@ import ( "github.com/stretchr/testify/require" "go.dedis.ch/kyber/v3" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" diff --git a/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go index d1b21b58647..0552f93fea1 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go @@ -15,11 +15,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Note these tests are identical to the ones in vrf_solidity_crosscheck_test.go, diff --git a/core/sessions/ldapauth/sync.go b/core/sessions/ldapauth/sync.go index 67f101b62a4..74c606a9684 100644 --- a/core/sessions/ldapauth/sync.go +++ b/core/sessions/ldapauth/sync.go @@ -9,11 +9,11 @@ import ( "github.com/jmoiron/sqlx" "github.com/lib/pq" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/sessions" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type LDAPServerStateSyncer struct { @@ -30,7 +30,7 @@ func NewLDAPServerStateSync( pgCfg pg.QConfig, config config.LDAP, lggr logger.Logger, -) utils.SleeperTask { +) *utils.SleeperTask { namedLogger := lggr.Named("LDAPServerStateSync") serverSync := LDAPServerStateSyncer{ q: pg.NewQ(db, namedLogger, pgCfg), diff --git a/core/sessions/localauth/reaper.go b/core/sessions/localauth/reaper.go index 77d1b1abef2..48112456418 100644 --- a/core/sessions/localauth/reaper.go +++ b/core/sessions/localauth/reaper.go @@ -4,9 +4,9 @@ import ( "database/sql" "time" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type sessionReaper struct { @@ -21,7 +21,7 @@ type SessionReaperConfig interface { } // NewSessionReaper creates a reaper that cleans stale sessions from the store. -func NewSessionReaper(db *sql.DB, config SessionReaperConfig, lggr logger.Logger) utils.SleeperTask { +func NewSessionReaper(db *sql.DB, config SessionReaperConfig, lggr logger.Logger) *utils.SleeperTask { return utils.NewSleeperTask(&sessionReaper{ db, config, diff --git a/core/utils/config/toml.go b/core/utils/config/toml.go deleted file mode 100644 index f51db76365e..00000000000 --- a/core/utils/config/toml.go +++ /dev/null @@ -1,22 +0,0 @@ -package config - -import ( - "errors" - "io" - - "github.com/pelletier/go-toml/v2" -) - -// DecodeTOML decodes toml from r in to v. -// Requires strict field matches and returns full toml.StrictMissingError details. -func DecodeTOML(r io.Reader, v any) error { - d := toml.NewDecoder(r).DisallowUnknownFields() - if err := d.Decode(v); err != nil { - var strict *toml.StrictMissingError - if errors.As(err, &strict) { - return errors.New(strict.String()) - } - return err - } - return nil -} diff --git a/core/utils/sleeper_task.go b/core/utils/sleeper_task.go deleted file mode 100644 index d84457e9325..00000000000 --- a/core/utils/sleeper_task.go +++ /dev/null @@ -1,129 +0,0 @@ -package utils - -import ( - "fmt" - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/services" -) - -// SleeperTask represents a task that waits in the background to process some work. -type SleeperTask interface { - Stop() error - WakeUp() - WakeUpIfStarted() -} - -// Worker is a simple interface that represents some work to do repeatedly -type Worker interface { - Work() - Name() string -} - -type sleeperTask struct { - services.StateMachine - worker Worker - chQueue chan struct{} - chStop chan struct{} - chDone chan struct{} - chWorkDone chan struct{} -} - -// NewSleeperTask takes a worker and returns a SleeperTask. -// -// SleeperTask is guaranteed to call Work on the worker at least once for every -// WakeUp call. -// If the Worker is busy when WakeUp is called, the Worker will be called again -// immediately after it is finished. For this reason you should take care to -// make sure that Worker is idempotent. -// WakeUp does not block. -func NewSleeperTask(worker Worker) SleeperTask { - s := &sleeperTask{ - worker: worker, - chQueue: make(chan struct{}, 1), - chStop: make(chan struct{}), - chDone: make(chan struct{}), - chWorkDone: make(chan struct{}, 10), - } - - _ = s.StartOnce("SleeperTask-"+worker.Name(), func() error { - go s.workerLoop() - return nil - }) - - return s -} - -// Stop stops the SleeperTask -func (s *sleeperTask) Stop() error { - return s.StopOnce("SleeperTask-"+s.worker.Name(), func() error { - close(s.chStop) - select { - case <-s.chDone: - case <-time.After(15 * time.Second): - return fmt.Errorf("SleeperTask-%s took too long to stop", s.worker.Name()) - } - return nil - }) -} - -func (s *sleeperTask) WakeUpIfStarted() { - s.IfStarted(func() { - select { - case s.chQueue <- struct{}{}: - default: - } - }) -} - -// WakeUp wakes up the sleeper task, asking it to execute its Worker. -func (s *sleeperTask) WakeUp() { - if !s.IfStarted(func() { - select { - case s.chQueue <- struct{}{}: - default: - } - }) { - panic("cannot wake up stopped sleeper task") - } -} - -func (s *sleeperTask) workDone() { - select { - case s.chWorkDone <- struct{}{}: - default: - } -} - -// WorkDone isn't part of the SleeperTask interface, but can be -// useful in tests to assert that the work has been done. -func (s *sleeperTask) WorkDone() <-chan struct{} { - return s.chWorkDone -} - -func (s *sleeperTask) workerLoop() { - defer close(s.chDone) - - for { - select { - case <-s.chQueue: - s.worker.Work() - s.workDone() - case <-s.chStop: - return - } - } -} - -type sleeperTaskWorker struct { - name string - work func() -} - -// SleeperFuncTask returns a Worker to execute the given work function. -func SleeperFuncTask(work func(), name string) Worker { - return &sleeperTaskWorker{name: name, work: work} -} - -func (w *sleeperTaskWorker) Name() string { return w.name } -func (w *sleeperTaskWorker) Work() { w.work() } diff --git a/core/utils/sleeper_task_test.go b/core/utils/sleeper_task_test.go deleted file mode 100644 index 8c09350f523..00000000000 --- a/core/utils/sleeper_task_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package utils_test - -import ( - "sync/atomic" - "testing" - "time" - - "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" -) - -type countingWorker struct { - numJobsPerformed atomic.Int32 - delay time.Duration -} - -func (t *countingWorker) Name() string { - return "CountingWorker" -} - -func (t *countingWorker) Work() { - if t.delay != 0 { - time.Sleep(t.delay) - } - // Without an atomic, the race detector fails - t.numJobsPerformed.Add(1) -} - -func (t *countingWorker) getNumJobsPerformed() int { - return int(t.numJobsPerformed.Load()) -} - -func TestSleeperTask_WakeupAfterStopPanics(t *testing.T) { - t.Parallel() - - worker := &countingWorker{} - sleeper := utils.NewSleeperTask(worker) - - require.NoError(t, sleeper.Stop()) - - require.Panics(t, func() { - sleeper.WakeUp() - }) - gomega.NewWithT(t).Eventually(worker.getNumJobsPerformed).Should(gomega.Equal(0)) -} - -func TestSleeperTask_CallingStopTwiceFails(t *testing.T) { - t.Parallel() - - worker := &countingWorker{} - sleeper := utils.NewSleeperTask(worker) - require.NoError(t, sleeper.Stop()) - require.Error(t, sleeper.Stop()) -} - -func TestSleeperTask_WakeupPerformsWork(t *testing.T) { - t.Parallel() - - worker := &countingWorker{} - sleeper := utils.NewSleeperTask(worker) - - sleeper.WakeUp() - gomega.NewWithT(t).Eventually(worker.getNumJobsPerformed).Should(gomega.Equal(1)) - require.NoError(t, sleeper.Stop()) -} - -type controllableWorker struct { - countingWorker - awaitWorkStarted chan struct{} - allowResumeWork chan struct{} - ignoreSignals bool -} - -func (w *controllableWorker) Work() { - if !w.ignoreSignals { - w.awaitWorkStarted <- struct{}{} - <-w.allowResumeWork - } - w.countingWorker.Work() -} - -func TestSleeperTask_WakeupEnqueuesMaxTwice(t *testing.T) { - t.Parallel() - - worker := &controllableWorker{awaitWorkStarted: make(chan struct{}), allowResumeWork: make(chan struct{})} - sleeper := utils.NewSleeperTask(worker) - - sleeper.WakeUp() - <-worker.awaitWorkStarted - sleeper.WakeUp() - sleeper.WakeUp() - sleeper.WakeUp() - sleeper.WakeUp() - sleeper.WakeUp() - worker.ignoreSignals = true - worker.allowResumeWork <- struct{}{} - - gomega.NewWithT(t).Eventually(worker.getNumJobsPerformed).Should(gomega.Equal(2)) - gomega.NewWithT(t).Consistently(worker.getNumJobsPerformed).Should(gomega.BeNumerically("<", 3)) - require.NoError(t, sleeper.Stop()) -} - -func TestSleeperTask_StopWaitsUntilWorkFinishes(t *testing.T) { - t.Parallel() - - worker := &controllableWorker{awaitWorkStarted: make(chan struct{}), allowResumeWork: make(chan struct{})} - sleeper := utils.NewSleeperTask(worker) - - sleeper.WakeUp() - <-worker.awaitWorkStarted - require.Equal(t, 0, worker.getNumJobsPerformed()) - worker.allowResumeWork <- struct{}{} - - require.NoError(t, sleeper.Stop()) - require.Equal(t, worker.getNumJobsPerformed(), 1) -} diff --git a/core/utils/utils.go b/core/utils/utils.go index 3a53e664ecd..78151517c62 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -142,21 +142,6 @@ func Sha256(in string) (string, error) { return hex.EncodeToString(hasher.Sum(nil)), nil } -// JustError takes a tuple and returns the last entry, the error. -func JustError(_ interface{}, err error) error { - return err -} - -// WaitGroupChan creates a channel that closes when the provided sync.WaitGroup is done. -func WaitGroupChan(wg *sync.WaitGroup) <-chan struct{} { - chAwait := make(chan struct{}) - go func() { - defer close(chAwait) - wg.Wait() - }() - return chAwait -} - // WithCloseChan wraps a context so that it is canceled if the passed in channel is closed. // Deprecated: Call [services.StopChan.Ctx] directly func WithCloseChan(parentCtx context.Context, chStop chan struct{}) (context.Context, context.CancelFunc) { @@ -181,40 +166,6 @@ type StopChan = services.StopChan // Deprecated: use services.StopRChan type StopRChan = services.StopRChan -// DependentAwaiter contains Dependent funcs -type DependentAwaiter interface { - AwaitDependents() <-chan struct{} - AddDependents(n int) - DependentReady() -} - -type dependentAwaiter struct { - wg *sync.WaitGroup - ch <-chan struct{} -} - -// NewDependentAwaiter creates a new DependentAwaiter -func NewDependentAwaiter() DependentAwaiter { - return &dependentAwaiter{ - wg: &sync.WaitGroup{}, - } -} - -func (da *dependentAwaiter) AwaitDependents() <-chan struct{} { - if da.ch == nil { - da.ch = WaitGroupChan(da.wg) - } - return da.ch -} - -func (da *dependentAwaiter) AddDependents(n int) { - da.wg.Add(n) -} - -func (da *dependentAwaiter) DependentReady() { - da.wg.Done() -} - // BoundedQueue is a FIFO queue that discards older items when it reaches its capacity. type BoundedQueue[T any] struct { capacity int @@ -335,20 +286,6 @@ func (q *BoundedPriorityQueue[T]) Empty() bool { return true } -// WrapIfError decorates an error with the given message. It is intended to -// be used with `defer` statements, like so: -// -// func SomeFunction() (err error) { -// defer WrapIfError(&err, "error in SomeFunction:") -// -// ... -// } -func WrapIfError(err *error, msg string) { - if *err != nil { - *err = pkgerrors.Wrap(*err, msg) - } -} - // TickerBase is an interface for pausable tickers. type TickerBase interface { Resume() @@ -587,16 +524,6 @@ func BoxOutput(errorMsgTemplate string, errorMsgValues ...interface{}) string { "\n\n" } -// AllEqual returns true iff all the provided elements are equal to each other. -func AllEqual[T comparable](elems ...T) bool { - for i := 1; i < len(elems); i++ { - if elems[i] != elems[0] { - return false - } - } - return true -} - // ConcatBytes appends a bunch of byte arrays into a single byte array func ConcatBytes(bufs ...[]byte) []byte { return bytes.Join(bufs, []byte{}) diff --git a/core/utils/utils_test.go b/core/utils/utils_test.go index e2a4f8e3939..c08983ff4b8 100644 --- a/core/utils/utils_test.go +++ b/core/utils/utils_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/hex" "fmt" - "sync" "sync/atomic" "testing" "time" @@ -78,68 +77,6 @@ func TestUtils_DurationFromNow(t *testing.T) { assert.True(t, 0 < duration) } -func TestWaitGroupChan(t *testing.T) { - t.Parallel() - - wg := &sync.WaitGroup{} - wg.Add(2) - - ch := utils.WaitGroupChan(wg) - - select { - case <-ch: - t.Fatal("should not fire immediately") - default: - } - - wg.Done() - - select { - case <-ch: - t.Fatal("should not fire until finished") - default: - } - - go func() { - time.Sleep(2 * time.Second) - wg.Done() - }() - - cltest.CallbackOrTimeout(t, "WaitGroupChan fires", func() { - <-ch - }, 5*time.Second) -} - -func TestDependentAwaiter(t *testing.T) { - t.Parallel() - - da := utils.NewDependentAwaiter() - da.AddDependents(2) - - select { - case <-da.AwaitDependents(): - t.Fatal("should not fire immediately") - default: - } - - da.DependentReady() - - select { - case <-da.AwaitDependents(): - t.Fatal("should not fire until finished") - default: - } - - go func() { - time.Sleep(2 * time.Second) - da.DependentReady() - }() - - cltest.CallbackOrTimeout(t, "dependents are now ready", func() { - <-da.AwaitDependents() - }, 5*time.Second) -} - func TestBoundedQueue(t *testing.T) { t.Parallel() @@ -244,14 +181,6 @@ func Test_WithJitter(t *testing.T) { } } -func TestAllEqual(t *testing.T) { - t.Parallel() - - require.False(t, utils.AllEqual(1, 2, 3, 4, 5)) - require.True(t, utils.AllEqual(1, 1, 1, 1, 1)) - require.False(t, utils.AllEqual(1, 1, 1, 2, 1, 1, 1)) -} - func TestIsEmpty(t *testing.T) { t.Parallel() diff --git a/core/web/cosmos_keys_controller_test.go b/core/web/cosmos_keys_controller_test.go index af421416388..3b777de7b7c 100644 --- a/core/web/cosmos_keys_controller_test.go +++ b/core/web/cosmos_keys_controller_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/dkgencrypt_keys_controller_test.go b/core/web/dkgencrypt_keys_controller_test.go index 41d7eb4a752..7100cbbb1cd 100644 --- a/core/web/dkgencrypt_keys_controller_test.go +++ b/core/web/dkgencrypt_keys_controller_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/web/dkgsign_keys_controller_test.go b/core/web/dkgsign_keys_controller_test.go index 611c5b3ef0a..ed67d71a0d5 100644 --- a/core/web/dkgsign_keys_controller_test.go +++ b/core/web/dkgsign_keys_controller_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 27d4433b63c..0a40c8a9c71 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -26,6 +26,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/utils" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -37,7 +38,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/tomlutils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/ocr2_keys_controller_test.go b/core/web/ocr2_keys_controller_test.go index c7549cd2da5..815ae3ac20b 100644 --- a/core/web/ocr2_keys_controller_test.go +++ b/core/web/ocr2_keys_controller_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/ocr_keys_controller_test.go b/core/web/ocr_keys_controller_test.go index ebb671bc1e2..31422f47c0c 100644 --- a/core/web/ocr_keys_controller_test.go +++ b/core/web/ocr_keys_controller_test.go @@ -4,10 +4,10 @@ import ( "net/http" "testing" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/p2p_keys_controller_test.go b/core/web/p2p_keys_controller_test.go index af15e21a0f6..df6f556fcb8 100644 --- a/core/web/p2p_keys_controller_test.go +++ b/core/web/p2p_keys_controller_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/solana_keys_controller_test.go b/core/web/solana_keys_controller_test.go index 3dfbcfd252a..94b11207c92 100644 --- a/core/web/solana_keys_controller_test.go +++ b/core/web/solana_keys_controller_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/starknet_keys_controller_test.go b/core/web/starknet_keys_controller_test.go index a633b4f16c9..9215fb8f9c5 100644 --- a/core/web/starknet_keys_controller_test.go +++ b/core/web/starknet_keys_controller_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/go.mod b/go.mod index 6e67bbd9a52..95af6730b58 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 037bcc95225..12495af1110 100644 --- a/go.sum +++ b/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab h1:6ckB261FRUy4K/OfSfWCQLAGkgfVLYT5PKDImmp3tZM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 h1:tpJnh0cJaUhgbwRzDUGqzdp2XJnn369T4nbDV2w8LZY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 5d0c23e96de..627c4dfda5e 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 github.com/smartcontractkit/chainlink-testing-framework v1.22.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 3c9efdbcb16..8bf02a6b9b2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab h1:6ckB261FRUy4K/OfSfWCQLAGkgfVLYT5PKDImmp3tZM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231219165257-be61f25afdab/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 h1:tpJnh0cJaUhgbwRzDUGqzdp2XJnn369T4nbDV2w8LZY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 7436c05a107..d8536c1395e 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -12,6 +12,8 @@ import ( "github.com/segmentio/ksuid" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -22,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/utils/config" it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) From 01146e9aa70ff330236135b3ec416c1305402437 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 21 Dec 2023 14:48:03 -0500 Subject: [PATCH 098/234] Removes Arbitrary Funding Sleep (#11650) --- integration-tests/docker/test_env/test_env.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 20e231e5985..b928bc9e664 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -8,7 +8,6 @@ import ( "os" "runtime/debug" "testing" - "time" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/rs/zerolog" @@ -194,7 +193,6 @@ func (te *CLClusterTestEnv) FundChainlinkNodes(amount *big.Float) error { if err := cl.Fund(te.EVMClient, amount); err != nil { return fmt.Errorf("%s, err: %w", ErrFundCLNode, err) } - time.Sleep(5 * time.Second) } return te.EVMClient.WaitForEvents() } From 7236361119339369127a488a7c238745e4c44099 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 21 Dec 2023 14:32:31 -0600 Subject: [PATCH 099/234] core/services/relay/evm: start RequestRoundTracker; report full health (#11643) * core/services/relay/evm: start RequestRoundTracker; report full health * Tests round requests and implicit changes separately * Add test to CI * Fixes other OCR2 checks --------- Co-authored-by: Adam Hamrick --- .github/workflows/integration-tests.yml | 6 ++ core/services/relay/evm/evm.go | 25 +++-- core/services/relay/evm/median.go | 22 ++++- integration-tests/actions/ocr2_helpers.go | 20 ++++ .../smoke/forwarders_ocr2_test.go | 4 +- integration-tests/smoke/ocr2_test.go | 92 ++++++++++++++++++- 6 files changed, 144 insertions(+), 25 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 0fa9370b60d..08e24fc40a9 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -340,6 +340,12 @@ jobs: run: -run TestOCRv2Basic file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated + - name: ocr2 + nodes: 1 + os: ubuntu-latest + run: -run TestOCRv2Request + file: ocr2 + pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 nodes: 1 os: ubuntu-latest diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 08e25dba545..37e6d64452e 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -12,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" pkgerrors "github.com/pkg/errors" - "go.uber.org/multierr" "golang.org/x/exp/maps" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" @@ -483,6 +482,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } medianProvider := medianProvider{ + lggr: lggr.Named("MedianProvider"), configWatcher: configWatcher, reportCodec: reportCodec, contractTransmitter: contractTransmitter, @@ -513,6 +513,7 @@ func (r *Relayer) NewAutomationProvider(rargs commontypes.RelayArgs, pargs commo var _ commontypes.MedianProvider = (*medianProvider)(nil) type medianProvider struct { + lggr logger.Logger configWatcher *configWatcher contractTransmitter ContractTransmitter reportCodec median.ReportCodec @@ -521,26 +522,22 @@ type medianProvider struct { ms services.MultiStart } -func (p *medianProvider) Name() string { - return "EVM.MedianProvider" -} +func (p *medianProvider) Name() string { return p.lggr.Name() } func (p *medianProvider) Start(ctx context.Context) error { - return p.ms.Start(ctx, p.configWatcher, p.contractTransmitter) + return p.ms.Start(ctx, p.configWatcher, p.contractTransmitter, p.medianContract) } -func (p *medianProvider) Close() error { - return p.ms.Close() -} +func (p *medianProvider) Close() error { return p.ms.Close() } -func (p *medianProvider) Ready() error { - return multierr.Combine(p.configWatcher.Ready(), p.contractTransmitter.Ready()) -} +func (p *medianProvider) Ready() error { return nil } func (p *medianProvider) HealthReport() map[string]error { - report := p.configWatcher.HealthReport() - services.CopyHealth(report, p.contractTransmitter.HealthReport()) - return report + hp := map[string]error{p.Name(): p.Ready()} + services.CopyHealth(hp, p.configWatcher.HealthReport()) + services.CopyHealth(hp, p.contractTransmitter.HealthReport()) + services.CopyHealth(hp, p.medianContract.HealthReport()) + return hp } func (p *medianProvider) ContractTransmitter() ocrtypes.ContractTransmitter { diff --git a/core/services/relay/evm/median.go b/core/services/relay/evm/median.go index db521a97208..e3200d8e867 100644 --- a/core/services/relay/evm/median.go +++ b/core/services/relay/evm/median.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -22,12 +23,15 @@ import ( var _ median.MedianContract = &medianContract{} type medianContract struct { + services.StateMachine + lggr logger.Logger configTracker types.ContractConfigTracker contractCaller *ocr2aggregator.OCR2AggregatorCaller requestRoundTracker *RequestRoundTracker } func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain legacyevm.Chain, specID int32, db *sqlx.DB, lggr logger.Logger) (*medianContract, error) { + lggr = lggr.Named("MedianContract") contract, err := offchain_aggregator_wrapper.NewOffchainAggregator(contractAddress, chain.Client()) if err != nil { return nil, errors.Wrap(err, "could not instantiate NewOffchainAggregator") @@ -44,6 +48,7 @@ func newMedianContract(configTracker types.ContractConfigTracker, contractAddres } return &medianContract{ + lggr: lggr, configTracker: configTracker, contractCaller: contractCaller, requestRoundTracker: NewRequestRoundTracker( @@ -60,13 +65,22 @@ func newMedianContract(configTracker types.ContractConfigTracker, contractAddres ), }, nil } - -func (oc *medianContract) Start() error { - return oc.requestRoundTracker.Start() +func (oc *medianContract) Start(context.Context) error { + return oc.StartOnce("MedianContract", func() error { + return oc.requestRoundTracker.Start() + }) } func (oc *medianContract) Close() error { - return oc.requestRoundTracker.Close() + return oc.StopOnce("MedianContract", func() error { + return oc.requestRoundTracker.Close() + }) +} + +func (oc *medianContract) Name() string { return oc.lggr.Name() } + +func (oc *medianContract) HealthReport() map[string]error { + return map[string]error{oc.Name(): oc.Ready()} } func (oc *medianContract) LatestTransmissionDetails(ctx context.Context) (ocrtypes.ConfigDigest, uint32, uint8, *big.Int, time.Time, error) { diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index c4bc30c7c5e..37db348815b 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -391,6 +391,26 @@ func StartNewOCR2Round( return nil } +// WatchNewOCR2Round is the same as StartNewOCR2Round but does NOT explicitly request a new round +// as that can cause odd behavior in tandem with changing adapter values in OCR2 +func WatchNewOCR2Round( + roundNumber int64, + ocrInstances []contracts.OffchainAggregatorV2, + client blockchain.EVMClient, + timeout time.Duration, + logger zerolog.Logger, +) error { + for i := 0; i < len(ocrInstances); i++ { + ocrRound := contracts.NewOffchainAggregatorV2RoundConfirmer(ocrInstances[i], big.NewInt(roundNumber), timeout, logger) + client.AddHeaderEventSubscription(ocrInstances[i].Address(), ocrRound) + err := client.WaitForEvents() + if err != nil { + return fmt.Errorf("failed to wait for event subscriptions of OCR instance %d: %w", i+1, err) + } + } + return nil +} + // SetOCR2AdapterResponse sets a single adapter response that correlates with an ocr contract and a chainlink node // used for OCR2 tests func SetOCR2AdapterResponse( diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index c2e2b12e51d..71d35508175 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -90,7 +90,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, ocrInstances) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.StartNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) + err = actions.WatchNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -101,7 +101,7 @@ func TestForwarderOCR2Basic(t *testing.T) { ocrRoundVal := (5 + i) % 10 err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, ocrRoundVal) require.NoError(t, err) - err = actions.StartNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) + err = actions.WatchNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index a1a37c3b18e..376c56e9d64 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" @@ -75,7 +76,7 @@ func TestOCRv2Basic(t *testing.T) { err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") @@ -86,7 +87,7 @@ func TestOCRv2Basic(t *testing.T) { err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) - err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) @@ -97,6 +98,87 @@ func TestOCRv2Basic(t *testing.T) { ) } +// Tests that just calling requestNewRound() will properly induce more rounds +func TestOCRv2Request(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). + WithMockAdapter(). + WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), + node.WithOCR2(), + node.WithP2Pv2(), + node.WithTracing(), + )). + WithCLNodes(6). + WithFunding(big.NewFloat(.1)). + WithStandardCleanup(). + WithLogStream(). + Build() + require.NoError(t, err) + + env.ParallelTransactions(true) + + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + linkToken, err := env.ContractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + + err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + require.NoError(t, err, "Error funding Chainlink nodes") + + // Gather transmitters + var transmitters []string + for _, node := range workerNodes { + addr, err := node.PrimaryEthAddress() + if err != nil { + require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) + } + transmitters = append(transmitters, addr) + } + + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() + aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) + require.NoError(t, err, "Error building OCRv2 config") + + err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) + require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") + + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 5 but got %d", + roundData.Answer.Int64(), + ) + + // Keep the mockserver value the same and continually request new rounds + for round := 2; round <= 4; round++ { + err = actions.StartNewOCR2Round(int64(round), aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(int64(round))) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), roundData.Answer.Int64(), + "Expected round %d answer from OCR contract to be 5 but got %d", + round, + roundData.Answer.Int64(), + ) + } +} + func TestOCRv2JobReplacement(t *testing.T) { l := logging.GetTestLogger(t) @@ -150,7 +232,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") @@ -161,7 +243,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) - err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) @@ -180,7 +262,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false) require.NoError(t, err, "Error creating OCRv2 jobs") - err = actions.StartNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) + err = actions.WatchNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) require.NoError(t, err, "Error starting new OCR2 round") roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") From c3ba3a503b8a3e7a1fab0567e0a1cd4b630505d5 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 21 Dec 2023 15:37:34 -0500 Subject: [PATCH 100/234] Fixes Compatibility Tests (#11652) * Cleanup * Fix title * Fix CL image * Cleanup and convert to build binary * Words are hard --- .../workflows/client-compatibility-tests.yml | 157 +++++++++--------- .github/workflows/integration-tests.yml | 10 +- .github/workflows/live-testnet-tests.yml | 2 +- 3 files changed, 76 insertions(+), 93 deletions(-) diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index 8688e51e1df..865c72258cd 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -7,7 +7,14 @@ on: - "*" workflow_dispatch: +env: + CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink + INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com + MOD_CACHE_VERSION: 2 + jobs: + # Build Test Dependencies + build-chainlink: environment: integration permissions: @@ -38,6 +45,38 @@ jobs: GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + + build-tests: + environment: integration + permissions: + id-token: write + contents: read + name: Build Tests Binary + runs-on: ubuntu-latest + steps: + - name: Collect Metrics + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: Build Tests Binary + continue-on-error: true + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Build Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + with: + test_download_vendor_packages_command: cd ./integration-tests && go mod download + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + binary_name: tests + + # End Build Test Dependencies client-compatibility-matrix: environment: integration @@ -46,7 +85,7 @@ jobs: pull-requests: write id-token: write contents: read - needs: [build-chainlink] + needs: [build-chainlink, build-tests] env: SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 CHAINLINK_COMMIT_SHA: ${{ github.sha }} @@ -55,127 +94,79 @@ jobs: strategy: fail-fast: false matrix: - product: + include: - name: ocr-geth os: ubuntu-latest - run: -run TestOCRBasic + test: TestOCRBasic file: ocr client: geth - pyroscope_env: ci-smoke-ocr-evm-simulated + pyroscope_env: ci-smoke-ocr-geth-simulated # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE # - name: ocr-nethermind - # run: -run TestOCRBasic + # test: TestOCRBasic # file: ocr # client: nethermind - # pyroscope_env: ci-smoke-ocr-evm-simulated + # pyroscope_env: ci-smoke-ocr-nethermind-simulated - name: ocr-besu - run: -run TestOCRBasic + test: TestOCRBasic file: ocr client: besu - pyroscope_env: ci-smoke-ocr-evm-simulated + pyroscope_env: ci-smoke-ocr-besu-simulated - name: ocr-erigon - run: -run TestOCRBasic + test: TestOCRBasic file: ocr client: erigon - pyroscope_env: ci-smoke-ocr-evm-simulated + pyroscope_env: ci-smoke-ocr-erigon-simulated - name: ocr2-geth - run: -run TestOCRv2Basic + test: TestOCRv2Basic file: ocr2 client: geth - pyroscope_env: ci-smoke-ocr2-evm-simulated + pyroscope_env: ci-smoke-ocr2-geth-simulated # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE # - name: ocr2-nethermind - # run: -run TestOCRv2Basic + # test: TestOCRv2Basic # file: ocr2 # client: nethermind - # pyroscope_env: ci-smoke-ocr2-evm-simulated + # pyroscope_env: ci-smoke-nethermind-evm-simulated - name: ocr2-besu - run: -run TestOCRv2Basic + test: TestOCRv2Basic file: ocr2 client: besu - pyroscope_env: ci-smoke-ocr2-evm-simulated + pyroscope_env: ci-smoke-ocr2-besu-simulated - name: ocr2-erigon - run: -run TestOCRv2Basic + test: TestOCRv2Basic file: ocr2 client: erigon - pyroscope_env: ci-smoke-ocr2-evm-simulated + pyroscope_env: ci-smoke-ocr2-erigon-simulated runs-on: ubuntu-latest - name: Client Compatibility Test ${{ matrix.product.name }} + name: Client Compatibility Test ${{ matrix.name }} steps: - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Download Tests Binary + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - - name: Build Go Test Command - id: build-go-test-command - run: | - # if the matrix.product.run is set, use it for a different command - if [ "${{ matrix.product.run }}" != "" ]; then - echo "run_command=${{ matrix.product.run }} ./smoke/${{ matrix.product.file }}_test.go" >> "$GITHUB_OUTPUT" - else - echo "run_command=./smoke/${{ matrix.product.name }}_test.go" >> "$GITHUB_OUTPUT" - fi + name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 env: - PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 - PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-client-compatability-${{ matrix.client }}-testnet PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - ETH2_EL_CLIENT: ${{matrix.product.client}} - LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} - LOKI_URL: ${{ secrets.LOKI_URL }} - LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} - LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} - GRAFANA_URL: ${{ vars.GRAFANA_URL }} - GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} - RUN_ID: ${{ github.run_id }} + ETH2_EL_CLIENT: ${{matrix.client}} with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt - test_download_vendor_packages_command: cd ./integration-tests && go mod download + test_command_to_run: ./tests -test.timeout 30m -test.run ${{ matrix.test }} + binary_name: tests cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }} + cl_image_tag: ${{ github.sha }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - artifacts_name: ${{ matrix.product.name }}-test-logs - artifacts_location: ./integration-tests/smoke/logs/ - publish_check_name: ${{ matrix.product.name }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./logs token: ${{ secrets.GITHUB_TOKEN }} - go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: "" - - name: Collect Metrics - if: always() - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - with: - basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - continue-on-error: true - - name: Print failed test summary - if: always() - run: | - directory="./integration-tests/smoke/.test_summary" - files=("$directory"/*) - if [ -d "$directory" ]; then - echo "Test summary folder found" - if [ ${#files[@]} -gt 0 ]; then - first_file="${files[0]}" - echo "Name of the first test summary file: $(basename "$first_file")" - echo "### Failed Test Execution Logs Dashboard (over VPN):" >> $GITHUB_STEP_SUMMARY - cat "$first_file" | jq -r '.loki[] | "* [\(.test_name)](\(.value))"' >> $GITHUB_STEP_SUMMARY - if [ ${#files[@]} -gt 1 ]; then - echo "Found more than one test summary file. This is incorrect, there should be only one file" - fi - else - echo "Test summary directory is empty. This should not happen" - fi - else - echo "No test summary folder found. If no test failed or log collection wasn't explicitly requested this is correct. Exiting" - fi + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} start-slack-thread: name: Start Slack Thread @@ -208,7 +199,7 @@ jobs: "type": "header", "text": { "type": "plain_text", - "text": "Live Smoke Test Results ${{ contains(join(needs.*.result, ','), 'failure') && ':x:' || ':white_check_mark:'}}", + "text": "Client Compatability Test Results ${{ contains(join(needs.*.result, ','), 'failure') && ':x:' || ':white_check_mark:'}}", "emoji": true } }, @@ -262,7 +253,7 @@ jobs: github_token: ${{ github.token }} github_repository: ${{ github.repository }} workflow_run_id: ${{ github.run_id }} - github_job_name_regex: ^Client Compatability Test ${{ matrix.product }}-(?.*?)$ + github_job_name_regex: ^Client Compatibility Test ${{ matrix.product }}-(?.*?)$ message_title: ${{ matrix.product }} slack_channel_id: ${{ secrets.QA_SLACK_CHANNEL }} slack_bot_token: ${{ secrets.QA_SLACK_API_KEY }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 08e24fc40a9..e588ab509f2 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -325,21 +325,13 @@ jobs: - name: ocr nodes: 1 os: ubuntu-latest - run: -run TestOCRJobReplacement file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 nodes: 1 os: ubuntu-latest - run: -run TestOCRv2JobReplacement file: ocr2 - pyroscope_env: ci-smoke-ocr2-evm-simulated - - name: ocr2 - nodes: 1 - os: ubuntu-latest - run: -run TestOCRv2Basic - file: ocr2 - pyroscope_env: ci-smoke-ocr2-evm-simulated + pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 nodes: 1 os: ubuntu-latest diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 2b5ecb2d5da..3298c45813a 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -66,7 +66,7 @@ env: jobs: # Build Test Dependencies - + build-chainlink: environment: integration permissions: From 9b425d0bb2e2129b3e80e57ffc09f256b416f2fc Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 21 Dec 2023 17:36:45 -0600 Subject: [PATCH 101/234] integration-tests/smoke: add plugins variant to TestOCRv2Basic (#11633) --- integration-tests/docker/test_env/cl_node.go | 10 ++ integration-tests/smoke/ocr2_test.go | 162 ++++++++++--------- 2 files changed, 99 insertions(+), 73 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 95a0d4c7d84..d7228b1ce8f 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -3,6 +3,7 @@ package test_env import ( "context" "fmt" + "maps" "math/big" "net/url" "os" @@ -60,6 +61,15 @@ func WithSecrets(secretsTOML string) ClNodeOption { } } +func WithNodeEnvVars(ev map[string]string) ClNodeOption { + return func(n *ClNode) { + if n.ContainerEnvs == nil { + n.ContainerEnvs = map[string]string{} + } + maps.Copy(n.ContainerEnvs, ev) + } +} + // Sets custom node container name if name is not empty func WithNodeContainerName(name string) ClNodeOption { return func(c *ClNode) { diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 376c56e9d64..266fcea6382 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -16,86 +16,102 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/v2/core/config/env" ) // Tests a basic OCRv2 median feed func TestOCRv2Basic(t *testing.T) { t.Parallel() - l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) - require.NoError(t, err, "Error building ethereum network config") - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), - node.WithOCR2(), - node.WithP2Pv2(), - node.WithTracing(), - )). - WithCLNodes(6). - WithFunding(big.NewFloat(.1)). - WithStandardCleanup(). - WithLogStream(). - Build() - require.NoError(t, err) - - env.ParallelTransactions(true) - - nodeClients := env.ClCluster.NodeAPIs() - bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - // Gather transmitters - var transmitters []string - for _, node := range workerNodes { - addr, err := node.PrimaryEthAddress() - if err != nil { - require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) - } - transmitters = append(transmitters, addr) + for _, test := range []struct { + name string + env map[string]string + }{ + {"legacy", map[string]string{string(env.MedianPluginCmd): ""}}, + {"plugins", map[string]string{string(env.MedianPluginCmd): "chainlink-feeds"}}, + } { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + l := logging.GetTestLogger(t) + + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). + WithMockAdapter(). + WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), + node.WithOCR2(), + node.WithP2Pv2(), + node.WithTracing(), + )). + WithCLNodeOptions(test_env.WithNodeEnvVars(test.env)). + WithCLNodes(6). + WithFunding(big.NewFloat(.1)). + WithStandardCleanup(). + WithLogStream(). + Build() + require.NoError(t, err) + + env.ParallelTransactions(true) + + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + linkToken, err := env.ContractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + + err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + require.NoError(t, err, "Error funding Chainlink nodes") + + // Gather transmitters + var transmitters []string + for _, node := range workerNodes { + addr, err := node.PrimaryEthAddress() + if err != nil { + require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) + } + transmitters = append(transmitters, addr) + } + + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() + aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) + require.NoError(t, err, "Error building OCRv2 config") + + err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) + require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") + + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 5 but got %d", + roundData.Answer.Int64(), + ) + + err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + require.NoError(t, err) + err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err) + + roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 10 but got %d", + roundData.Answer.Int64(), + ) + }) } - - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) - require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) - require.NoError(t, err, "Error creating OCRv2 jobs") - - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") - - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error starting new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 5 but got %d", - roundData.Answer.Int64(), - ) - - err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) - require.NoError(t, err) - err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err) - - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) - require.NoError(t, err, "Error getting latest OCR answer") - require.Equal(t, int64(10), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 10 but got %d", - roundData.Answer.Int64(), - ) } // Tests that just calling requestNewRound() will properly induce more rounds From 3661f48763b5e17b6cd39f7e9663aebba54b415e Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 21 Dec 2023 19:30:37 -0600 Subject: [PATCH 102/234] service cleanup (#11655) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- .../relay/evm/functions/logpoller_wrapper.go | 12 +++--------- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 7 files changed, 12 insertions(+), 18 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 1899270d325..60b05693ac8 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 47ef9711147..defd2e4b0bc 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 h1:tpJnh0cJaUhgbwRzDUGqzdp2XJnn369T4nbDV2w8LZY= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a h1:ViX8kP1/WYW1dLG85Frqpvus1nRGif/1QiK7qeyS+Rc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index e7f3a1a96af..e76b567b42b 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -108,7 +108,7 @@ func NewLogPollerWrapper(routerContractAddress common.Address, pluginConfig conf client: client, subscribers: make(map[string]evmRelayTypes.RouteUpdateSubscriber), stopCh: make(services.StopChan), - lggr: lggr, + lggr: lggr.Named("LogPollerWrapper"), }, nil } @@ -136,16 +136,10 @@ func (l *logPollerWrapper) Close() error { } func (l *logPollerWrapper) HealthReport() map[string]error { - return make(map[string]error) + return map[string]error{l.Name(): l.Ready()} } -func (l *logPollerWrapper) Name() string { - return "LogPollerWrapper" -} - -func (l *logPollerWrapper) Ready() error { - return nil -} +func (l *logPollerWrapper) Name() string { return l.lggr.Name() } // methods of LogPollerWrapper func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmRelayTypes.OracleResponse, error) { diff --git a/go.mod b/go.mod index 95af6730b58..c812144ad6f 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 12495af1110..49c889febbd 100644 --- a/go.sum +++ b/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 h1:tpJnh0cJaUhgbwRzDUGqzdp2XJnn369T4nbDV2w8LZY= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a h1:ViX8kP1/WYW1dLG85Frqpvus1nRGif/1QiK7qeyS+Rc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 627c4dfda5e..d492f0c59da 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a github.com/smartcontractkit/chainlink-testing-framework v1.22.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 8bf02a6b9b2..bdd5bcf9804 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909 h1:tpJnh0cJaUhgbwRzDUGqzdp2XJnn369T4nbDV2w8LZY= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231221161428-25a1256df909/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a h1:ViX8kP1/WYW1dLG85Frqpvus1nRGif/1QiK7qeyS+Rc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From d483612b409e8659f68051214070363788dca950 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:01:47 -0500 Subject: [PATCH 103/234] Create network policies for helm chart (#11653) * Create network policies for helm chart * Add network policy for runner --- .../templates/chainlink-db-networkpolicy.yaml | 27 +++++++++ .../chainlink-node-networkpolicy.yaml | 57 +++++++++++++++++++ .../templates/geth-networkpolicy.yaml | 31 ++++++++++ .../templates/mockserver-networkpolicy.yaml | 27 +++++++++ .../templates/networkpolicy-default-deny.yaml | 9 +++ 5 files changed, 151 insertions(+) create mode 100644 charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/geth-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml diff --git a/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml b/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml new file mode 100644 index 00000000000..bd989e8732b --- /dev/null +++ b/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml @@ -0,0 +1,27 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-db +spec: + podSelector: + matchLabels: + app: {{ $.Release.Name }}-db + policyTypes: + - Ingress + ingress: + # Allow all node pods to access the database pods. + - from: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + ports: + - protocol: TCP + port: 5432 + # Allow all runner pods to access the database pods. + - from: + - podSelector: + matchLabels: + app: runner + ports: + - protocol: TCP + port: 5432 diff --git a/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml b/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml new file mode 100644 index 00000000000..8ae02d7a46e --- /dev/null +++ b/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml @@ -0,0 +1,57 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-node +spec: + podSelector: + matchLabels: + app: {{ $.Release.Name }} + policyTypes: + - Ingress + - Egress + ingress: + # Allow all ingress traffic between the node pods and from runner pod. + - from: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + - from: + - podSelector: + matchLabels: + app: runner + egress: + # Allow all egress traffic between the node pods and to runner pod. + - to: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + - to: + - podSelector: + matchLabels: + app: runner + # Allow all node pods to access the database pods. + - to: + - podSelector: + matchLabels: + app: {{ $.Release.Name }}-db + ports: + - protocol: TCP + port: 5432 + # Allow all node pods to access the geth pods. + - to: + - podSelector: + matchLabels: + app: geth + ports: + - protocol: TCP + port: 8544 + - protocol: TCP + port: 8546 + # Allow all node pods to access the mockserver pods. + - to: + - podSelector: + matchLabels: + app: mockserver + ports: + - protocol: TCP + port: 1080 diff --git a/charts/chainlink-cluster/templates/geth-networkpolicy.yaml b/charts/chainlink-cluster/templates/geth-networkpolicy.yaml new file mode 100644 index 00000000000..87d6ac1c535 --- /dev/null +++ b/charts/chainlink-cluster/templates/geth-networkpolicy.yaml @@ -0,0 +1,31 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-geth +spec: + podSelector: + matchLabels: + app: geth + policyTypes: + - Ingress + ingress: + # Allow http and websocket connections from the node pods. + - from: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + ports: + - protocol: TCP + port: 8544 + - protocol: TCP + port: 8546 + # Allow http and websocket connections from the runner pods. + - from: + - podSelector: + matchLabels: + app: runner + ports: + - protocol: TCP + port: 8544 + - protocol: TCP + port: 8546 diff --git a/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml b/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml new file mode 100644 index 00000000000..f5c56c79690 --- /dev/null +++ b/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml @@ -0,0 +1,27 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-mockserver +spec: + podSelector: + matchLabels: + app: mockserver + policyTypes: + - Ingress + ingress: + # Allow http traffic from the node pods. + - from: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + ports: + - protocol: TCP + port: 1080 + # Allow http traffic from the runner pods. + - from: + - podSelector: + matchLabels: + app: runner + ports: + - protocol: TCP + port: 1080 diff --git a/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml b/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml new file mode 100644 index 00000000000..69f1da2e0b5 --- /dev/null +++ b/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny-all +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress From 1fa50ce9a56708d6098d4bd5cee776be1f9df8c6 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Sat, 23 Dec 2023 01:13:15 +0400 Subject: [PATCH 104/234] [AUTO-8227] use multicall3 for loadgen (#11638) * use multicall3 for loadgen * clean up * go tidy * lint * split load gen calls * divide data in loadgen * increase CallTimeout to 60s * fix numberOfEventsEmitted * increase CallTimeout to 3m fix testReport add test logs with durations for stages * numberOfEventsEmitted calculated using actual event count * async loadgen * simplify loadgen Call * fix time reporting --- .../contracts/contract_deployer.go | 34 ++++ integration-tests/contracts/multicall.go | 95 +++++++++ integration-tests/go.mod | 2 +- .../automationv2_1/automationv2_1_test.go | 182 +++++++++++++----- integration-tests/load/automationv2_1/gun.go | 112 +++++++---- 5 files changed, 339 insertions(+), 86 deletions(-) create mode 100644 integration-tests/contracts/multicall.go diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 464ae7a340b..ef1eb34e4ea 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -1,11 +1,15 @@ package contracts import ( + "context" "errors" "fmt" "math/big" + "strings" "time" + "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" @@ -145,6 +149,7 @@ type ContractDeployer interface { DeployMercuryFeeManager(linkAddress common.Address, nativeAddress common.Address, proxyAddress common.Address, rewardManagerAddress common.Address) (MercuryFeeManager, error) DeployMercuryRewardManager(linkAddress common.Address) (MercuryRewardManager, error) DeployLogEmitterContract() (LogEmitter, error) + DeployMultiCallContract() (common.Address, error) } // NewContractDeployer returns an instance of a contract deployer based on the client type @@ -1711,3 +1716,32 @@ func (e *EthereumContractDeployer) DeployLogEmitterContract() (LogEmitter, error l: e.l, }, err } + +func (e *EthereumContractDeployer) DeployMultiCallContract() (common.Address, error) { + multiCallABI, err := abi.JSON(strings.NewReader(MultiCallABI)) + if err != nil { + return common.Address{}, err + } + address, tx, _, err := e.client.DeployContract("MultiCall Contract", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + address, tx, contract, err := bind.DeployContract(auth, multiCallABI, common.FromHex(MultiCallBIN), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, contract, err + }) + if err != nil { + return common.Address{}, err + } + r, err := bind.WaitMined(context.Background(), e.client.DeployBackend(), tx) + if err != nil { + return common.Address{}, err + } + if r.Status != types.ReceiptStatusSuccessful { + return common.Address{}, fmt.Errorf("deploy multicall failed") + } + return *address, nil + +} diff --git a/integration-tests/contracts/multicall.go b/integration-tests/contracts/multicall.go new file mode 100644 index 00000000000..b809c20021d --- /dev/null +++ b/integration-tests/contracts/multicall.go @@ -0,0 +1,95 @@ +package contracts + +import ( + "context" + "fmt" + "math/big" + "strings" + + "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/pkg/errors" + "github.com/rs/zerolog/log" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +const ( + MultiCallABI = "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowFailure\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Call3[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate3\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowFailure\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Call3Value[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate3Value\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"blockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBasefee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockCoinbase\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockDifficulty\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockGasLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryAggregate\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryBlockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]" + MultiCallBIN = "0x608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c0033" +) + +type CallWithValue struct { + Target common.Address + AllowFailure bool + Value *big.Int + CallData []byte +} + +type Call struct { + Target common.Address + AllowFailure bool + CallData []byte +} + +type Result struct { + Success bool + ReturnData []byte +} + +func WaitForSuccessfulTxMined(evmClient blockchain.EVMClient, tx *types.Transaction) error { + log.Debug().Str("tx", tx.Hash().Hex()).Msg("waiting for tx to be mined") + receipt, err := bind.WaitMined(context.Background(), evmClient.DeployBackend(), tx) + if err != nil { + return err + } + if receipt.Status != types.ReceiptStatusSuccessful { + return fmt.Errorf("tx failed %s", tx.Hash().Hex()) + } + log.Debug().Str("tx", tx.Hash().Hex()).Str("Network", evmClient.GetNetworkName()).Msg("tx mined successfully") + return nil +} + +func MultiCallLogTriggerLoadGen( + evmClient blockchain.EVMClient, + multiCallAddress string, + logTriggerAddress []string, + logTriggerData [][]byte, +) (*types.Transaction, error) { + + contractAddress := common.HexToAddress(multiCallAddress) + multiCallABI, err := abi.JSON(strings.NewReader(MultiCallABI)) + if err != nil { + return nil, err + } + boundContract := bind.NewBoundContract(contractAddress, multiCallABI, evmClient.Backend(), evmClient.Backend(), evmClient.Backend()) + + var call []Call + for i, d := range logTriggerData { + data := Call{Target: common.HexToAddress(logTriggerAddress[i]), AllowFailure: false, CallData: d} + call = append(call, data) + } + + opts, err := evmClient.TransactionOpts(evmClient.GetDefaultWallet()) + if err != nil { + return nil, err + } + + // call aggregate3 to group all msg call data and send them in a single transaction + tx, err := boundContract.Transact(opts, "aggregate3", call) + if err != nil { + return nil, err + } + err = evmClient.MarkTxAsSentOnL2(tx) + if err != nil { + return nil, err + } + err = WaitForSuccessfulTxMined(evmClient, tx) + if err != nil { + return nil, errors.Wrapf(err, "multicall failed for log trigger load gen; multicall %s", contractAddress.Hex()) + } + return tx, nil + +} diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d492f0c59da..73e27848f09 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -19,6 +19,7 @@ require ( github.com/manifoldco/promptui v0.9.0 github.com/onsi/gomega v1.30.0 github.com/pelletier/go-toml/v2 v2.1.1 + github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 @@ -330,7 +331,6 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/alertmanager v0.26.0 // indirect diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index ab18fee19fe..d2bca2e670c 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -131,9 +131,9 @@ var ( ) type Load struct { - NumberOfEvents int `toml:",omitempty"` - NumberOfSpamMatchingEvents int `toml:",omitempty"` - NumberOfSpamNonMatchingEvents int `toml:",omitempty"` + NumberOfEvents int64 `toml:",omitempty"` + NumberOfSpamMatchingEvents int64 `toml:",omitempty"` + NumberOfSpamNonMatchingEvents int64 `toml:",omitempty"` CheckBurnAmount *big.Int `toml:",omitempty"` PerformBurnAmount *big.Int `toml:",omitempty"` UpkeepGasLimit uint32 `toml:",omitempty"` @@ -343,6 +343,9 @@ Load Config: chainClient.ParallelTransactions(true) + multicallAddress, err := contractDeployer.DeployMultiCallContract() + require.NoError(t, err, "Error deploying multicall contract") + a := automationv2.NewAutomationTestK8s(chainClient, contractDeployer, chainlinkNodes) a.RegistrySettings = *registrySettings a.RegistrarSettings = contracts.KeeperRegistrarSettings{ @@ -375,6 +378,9 @@ Load Config: F: 1, } + startTimeTestSetup := time.Now() + l.Info().Str("START_TIME", startTimeTestSetup.String()).Msg("Test setup started") + a.SetupAutomationDeployment(t) err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(nodeFunding), 0) @@ -395,13 +401,15 @@ Load Config: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } + var bytes1 = [32]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + } + upkeepConfigs := make([]automationv2.UpkeepConfig, 0) loadConfigs := make([]Load, 0) cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) require.NoError(t, err, "Error building concurrent chain client") - cContractDeployer, err := contracts.NewContractDeployer(cEVMClient, l) - require.NoError(t, err, "Error building concurrent contract deployer") for _, u := range loadConfig.Load { for i := 0; i < u.NumberOfUpkeeps; i++ { consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() @@ -427,7 +435,7 @@ Load Config: triggerAddresses = append(triggerAddresses, triggerAddresses[len(triggerAddresses)-1]) continue } - triggerContract, err := cContractDeployer.DeployLogEmitterContract() + triggerContract, err := contractDeployer.DeployLogEmitterContract() require.NoError(t, err, "Error deploying log emitter contract") triggerContracts = append(triggerContracts, triggerContract) triggerAddresses = append(triggerAddresses, triggerContract.Address()) @@ -446,11 +454,9 @@ Load Config: ContractAddress: triggerAddresses[i], FilterSelector: 1, Topic0: emitterABI.Events["Log4"].ID, - Topic1: [32]byte{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - }, - Topic2: bytes0, - Topic3: bytes0, + Topic1: bytes1, + Topic2: bytes0, + Topic3: bytes0, } encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) require.NoError(t, err, "Error encoding log trigger config") @@ -459,9 +465,7 @@ Load Config: checkDataStruct := simple_log_upkeep_counter_wrapper.CheckData{ CheckBurnAmount: loadConfigs[i].CheckBurnAmount, PerformBurnAmount: loadConfigs[i].PerformBurnAmount, - EventSig: [32]byte{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - }, + EventSig: bytes1, } encodedCheckDataStruct, err := consumerABI.Methods["_checkDataConfig"].Inputs.Pack(&checkDataStruct) @@ -503,36 +507,58 @@ Load Config: p := wasp.NewProfile() + configs := make([]LogTriggerConfig, 0) + var numberOfEventsEmitted int64 + var numberOfEventsEmittedPerSec int64 + for i, triggerContract := range triggerContracts { - g, err := wasp.NewGenerator(&wasp.Config{ - T: t, - LoadType: wasp.RPS, - GenName: fmt.Sprintf("log_trigger_gen_%s", triggerContract.Address().String()), - CallTimeout: time.Second * 10, - Schedule: wasp.Plain( - 1, - loadDuration, - ), - Gun: NewLogTriggerUser( - triggerContract, - l, - loadConfigs[i].NumberOfEvents, - loadConfigs[i].NumberOfSpamMatchingEvents, - loadConfigs[i].NumberOfSpamNonMatchingEvents, - ), - CallResultBufLen: 1000000, - }) - p.Add(g, err) + c := LogTriggerConfig{ + Address: triggerContract.Address().String(), + NumberOfEvents: loadConfigs[i].NumberOfEvents, + NumberOfSpamMatchingEvents: loadConfigs[i].NumberOfSpamMatchingEvents, + NumberOfSpamNonMatchingEvents: loadConfigs[i].NumberOfSpamNonMatchingEvents, + } + numberOfEventsEmittedPerSec = numberOfEventsEmittedPerSec + loadConfigs[i].NumberOfEvents + configs = append(configs, c) } - l.Info().Msg("Starting load generators") - startTime := time.Now() + endTimeTestSetup := time.Now() + testSetupDuration := endTimeTestSetup.Sub(startTimeTestSetup) + l.Info(). + Str("END_TIME", endTimeTestSetup.String()). + Str("Duration", testSetupDuration.String()). + Msg("Test setup ended") + ts, err := sendSlackNotification("Started", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), - strconv.FormatInt(startTime.UnixMilli(), 10), "now", + strconv.FormatInt(startTimeTestSetup.UnixMilli(), 10), "now", []slack.Block{extraBlockWithText("\bTest Config\b\n```" + testConfig + "```")}, slack.MsgOptionBlocks()) if err != nil { l.Error().Err(err).Msg("Error sending slack notification") } + + g, err := wasp.NewGenerator(&wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: "log_trigger_gen", + CallTimeout: time.Minute * 3, + Schedule: wasp.Plain( + 1, + loadDuration, + ), + Gun: NewLogTriggerUser( + l, + configs, + cEVMClient, + multicallAddress.Hex(), + ), + CallResultBufLen: 1000, + }) + p.Add(g, err) + + startTimeTestEx := time.Now() + l.Info().Str("START_TIME", startTimeTestEx.String()).Msg("Test execution started") + + l.Info().Msg("Starting load generators") _, err = p.Run(true) require.NoError(t, err, "Error running load generators") @@ -540,22 +566,25 @@ Load Config: l.Info().Str("STOP_WAIT_TIME", StopWaitTime.String()).Msg("Waiting for upkeeps to be performed") time.Sleep(StopWaitTime) l.Info().Msg("Finished waiting 60s for upkeeps to be performed") - endTime := time.Now() - testDuration := endTime.Sub(startTime) - l.Info().Str("Duration", testDuration.String()).Msg("Test Duration") + endTimeTestEx := time.Now() + testExDuration := endTimeTestEx.Sub(startTimeTestEx) + l.Info(). + Str("END_TIME", endTimeTestEx.String()). + Str("Duration", testExDuration.String()). + Msg("Test execution ended") + + l.Info().Str("Duration", testExDuration.String()).Msg("Test Execution Duration") endBlock, err := chainClient.LatestBlockNumber(ctx) require.NoError(t, err, "Error getting latest block number") l.Info().Uint64("Starting Block", startBlock).Uint64("Ending Block", endBlock).Msg("Test Block Range") + startTimeTestReport := time.Now() + l.Info().Str("START_TIME", startTimeTestReport.String()).Msg("Test reporting started") + upkeepDelaysFast := make([][]int64, 0) upkeepDelaysRecovery := make([][]int64, 0) - var numberOfEventsEmitted int - var batchSize uint64 = 500 - for i, gen := range p.Generators { - numberOfEventsEmitted = numberOfEventsEmitted + (len(gen.GetData().OKData.Data) * loadConfigs[i].NumberOfEvents) - } - l.Info().Int("Number of Events Emitted", numberOfEventsEmitted).Msg("Number of Events Emitted") + var batchSize uint64 = 500 if endBlock-startBlock < batchSize { batchSize = endBlock - startBlock @@ -625,6 +654,49 @@ Load Config: } } + for _, triggerContract := range triggerContracts { + var ( + logs []types.Log + address = triggerContract.Address() + timeout = 5 * time.Second + ) + for fromBlock := startBlock; fromBlock < endBlock; fromBlock += batchSize + 1 { + filterQuery := geth.FilterQuery{ + Addresses: []common.Address{address}, + FromBlock: big.NewInt(0).SetUint64(fromBlock), + ToBlock: big.NewInt(0).SetUint64(fromBlock + batchSize), + Topics: [][]common.Hash{{emitterABI.Events["Log4"].ID}, {bytes1}, {bytes1}}, + } + err = fmt.Errorf("initial error") // to ensure our for loop runs at least once + for err != nil { + var ( + logsInBatch []types.Log + ) + ctx2, cancel := context.WithTimeout(ctx, timeout) + logsInBatch, err = chainClient.FilterLogs(ctx2, filterQuery) + cancel() + if err != nil { + l.Error().Err(err). + Interface("FilterQuery", filterQuery). + Str("Contract Address", triggerContract.Address().Hex()). + Str("Timeout", timeout.String()). + Msg("Error getting logs") + timeout = time.Duration(math.Min(float64(timeout)*2, float64(2*time.Minute))) + continue + } + l.Debug(). + Interface("FilterQuery", filterQuery). + Str("Contract Address", triggerContract.Address().Hex()). + Str("Timeout", timeout.String()). + Msg("Collected logs") + logs = append(logs, logsInBatch...) + } + } + numberOfEventsEmitted = numberOfEventsEmitted + int64(len(logs)) + } + + l.Info().Int64("Number of Events Emitted", numberOfEventsEmitted).Msg("Number of Events Emitted") + l.Info(). Interface("Upkeep Delays Fast", upkeepDelaysFast). Interface("Upkeep Delays Recovered", upkeepDelaysRecovery). @@ -646,7 +718,7 @@ Load Config: avgF, medianF, ninetyPctF, ninetyNinePctF, maximumF := testreporters.IntListStats(allUpkeepDelaysFast) avgR, medianR, ninetyPctR, ninetyNinePctR, maximumR := testreporters.IntListStats(allUpkeepDelaysRecovery) - eventsMissed := numberOfEventsEmitted - len(allUpkeepDelays) + eventsMissed := (numberOfEventsEmitted) - int64(len(allUpkeepDelays)) percentMissed := float64(eventsMissed) / float64(numberOfEventsEmitted) * 100 l.Info(). Float64("Average", avgF).Int64("Median", medianF). @@ -660,8 +732,8 @@ Load Config: Int("Total Perform Count", len(allUpkeepDelays)). Int("Perform Count Fast Execution", len(allUpkeepDelaysFast)). Int("Perform Count Recovery Execution", len(allUpkeepDelaysRecovery)). - Int("Total Events Emitted", numberOfEventsEmitted). - Int("Total Events Missed", eventsMissed). + Int64("Total Events Emitted", numberOfEventsEmitted). + Int64("Total Events Missed", eventsMissed). Float64("Percent Missed", percentMissed). Msg("Test completed") @@ -677,6 +749,7 @@ Average: %f Median: %d 90th Percentile: %d 99th Percentile: %d +Max: %d Total Perform Count: %d Perform Count Fast Execution: %d @@ -686,12 +759,19 @@ Total Events Missed: %d Percent Missed: %f Test Duration: %s` + endTimeTestReport := time.Now() + testReDuration := endTimeTestReport.Sub(startTimeTestReport) + l.Info(). + Str("END_TIME", endTimeTestReport.String()). + Str("Duration", testReDuration.String()). + Msg("Test reporting ended") + testReport := fmt.Sprintf(testReportFormat, avgF, medianF, ninetyPctF, ninetyNinePctF, maximumF, - avgR, medianR, ninetyPctR, ninetyNinePctR, len(allUpkeepDelays), len(allUpkeepDelaysFast), - len(allUpkeepDelaysRecovery), numberOfEventsEmitted, eventsMissed, percentMissed, testDuration.String()) + avgR, medianR, ninetyPctR, ninetyNinePctR, maximumR, len(allUpkeepDelays), len(allUpkeepDelaysFast), + len(allUpkeepDelaysRecovery), numberOfEventsEmitted, eventsMissed, percentMissed, testExDuration.String()) _, err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), - strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(time.Now().UnixMilli(), 10), + strconv.FormatInt(startTimeTestSetup.UnixMilli(), 10), strconv.FormatInt(time.Now().UnixMilli(), 10), []slack.Block{extraBlockWithText("\bTest Report\b\n```" + testReport + "```")}, slack.MsgOptionTS(ts)) if err != nil { l.Error().Err(err).Msg("Error sending slack notification") diff --git a/integration-tests/load/automationv2_1/gun.go b/integration-tests/load/automationv2_1/gun.go index 938ce8708e6..c80c9cf7cc1 100644 --- a/integration-tests/load/automationv2_1/gun.go +++ b/integration-tests/load/automationv2_1/gun.go @@ -1,60 +1,104 @@ package automationv2_1 import ( + "math/big" + "sync" + "github.com/rs/zerolog" "github.com/smartcontractkit/wasp" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) +type LogTriggerConfig struct { + Address string + NumberOfEvents int64 + NumberOfSpamMatchingEvents int64 + NumberOfSpamNonMatchingEvents int64 +} + type LogTriggerGun struct { - triggerContract contracts.LogEmitter - logger zerolog.Logger - numberOfEvents int - numberOfSpamMatchingEvents int - numberOfSpamNonMatchingEvents int + data [][]byte + addresses []string + multiCallAddress string + evmClient blockchain.EVMClient + logger zerolog.Logger +} + +func generateCallData(int1 int64, int2 int64, count int64) []byte { + abi, err := log_emitter.LogEmitterMetaData.GetAbi() + if err != nil { + panic(err) + } + data, err := abi.Pack("EmitLog4", big.NewInt(int1), big.NewInt(int2), big.NewInt(count)) + if err != nil { + panic(err) + } + return data } func NewLogTriggerUser( - triggerContract contracts.LogEmitter, logger zerolog.Logger, - numberOfEvents int, - numberOfSpamMatchingEvents int, - numberOfSpamNonMatchingEvents int, + TriggerConfigs []LogTriggerConfig, + evmClient blockchain.EVMClient, + multicallAddress string, ) *LogTriggerGun { + var data [][]byte + var addresses []string + + for _, c := range TriggerConfigs { + if c.NumberOfEvents > 0 { + d := generateCallData(1, 1, c.NumberOfEvents) + data = append(data, d) + addresses = append(addresses, c.Address) + } + if c.NumberOfSpamMatchingEvents > 0 { + d := generateCallData(1, 2, c.NumberOfSpamMatchingEvents) + data = append(data, d) + addresses = append(addresses, c.Address) + } + if c.NumberOfSpamNonMatchingEvents > 0 { + d := generateCallData(2, 2, c.NumberOfSpamNonMatchingEvents) + data = append(data, d) + addresses = append(addresses, c.Address) + } + } return &LogTriggerGun{ - triggerContract: triggerContract, - logger: logger, - numberOfEvents: numberOfEvents, - numberOfSpamMatchingEvents: numberOfSpamMatchingEvents, - numberOfSpamNonMatchingEvents: numberOfSpamNonMatchingEvents, + addresses: addresses, + data: data, + logger: logger, + multiCallAddress: multicallAddress, + evmClient: evmClient, } } func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.Response { - m.logger.Debug().Str("Trigger address", m.triggerContract.Address().String()).Msg("Triggering upkeep") - - if m.numberOfEvents > 0 { - _, err := m.triggerContract.EmitLogIntMultiIndexed(1, 1, m.numberOfEvents) - if err != nil { - return &wasp.Response{Error: err.Error(), Failed: true} - } - } - - if m.numberOfSpamMatchingEvents > 0 { - _, err := m.triggerContract.EmitLogIntMultiIndexed(1, 2, m.numberOfSpamMatchingEvents) - if err != nil { - return &wasp.Response{Error: err.Error(), Failed: true} + var wg sync.WaitGroup + var dividedData [][][]byte + d := m.data + chunkSize := 100 + for i := 0; i < len(d); i += chunkSize { + end := i + chunkSize + if end > len(d) { + end = len(d) } + dividedData = append(dividedData, d[i:end]) } - - if m.numberOfSpamNonMatchingEvents > 0 { - _, err := m.triggerContract.EmitLogIntMultiIndexed(2, 2, m.numberOfSpamNonMatchingEvents) - if err != nil { - return &wasp.Response{Error: err.Error(), Failed: true} - } + for _, a := range dividedData { + wg.Add(1) + go func(a [][]byte, m *LogTriggerGun) *wasp.Response { + defer wg.Done() + _, err := contracts.MultiCallLogTriggerLoadGen(m.evmClient, m.multiCallAddress, m.addresses, a) + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } + return &wasp.Response{} + }(a, m) } - + wg.Wait() return &wasp.Response{} } From 6b740c57bb9adcedac883491d47f723a1f029183 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 2 Jan 2024 11:00:43 -0500 Subject: [PATCH 105/234] Fix infinite stack overflow if caching is disabled (#11669) --- .../relay/evm/mercury/wsrpc/cache/cache_set.go | 2 +- .../evm/mercury/wsrpc/cache/cache_set_test.go | 4 ++-- .../relay/evm/mercury/wsrpc/client_test.go | 17 ++++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go index 01d47743950..7101cec39f3 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go @@ -67,7 +67,7 @@ func (cs *cacheSet) Close() error { func (cs *cacheSet) Get(ctx context.Context, client Client) (f Fetcher, err error) { if cs.cfg.LatestReportTTL == 0 { // caching disabled - return client, nil + return nil, nil } ok := cs.IfStarted(func() { f, err = cs.get(ctx, client) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go index 59be76ed265..f12dc8a9bc7 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go @@ -23,13 +23,13 @@ func Test_CacheSet(t *testing.T) { var err error var f Fetcher - t.Run("with caching disabled, returns the passed client", func(t *testing.T) { + t.Run("with caching disabled, returns nil, nil", func(t *testing.T) { assert.Len(t, disabledCs.caches, 0) f, err = disabledCs.Get(ctx, c) require.NoError(t, err) - assert.Same(t, c, f) + assert.Nil(t, f) assert.Len(t, disabledCs.caches, 0) }) diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index 10712461ae1..21accbf6d28 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -17,6 +17,14 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) +// simulate start without dialling +func simulateStart(ctx context.Context, t *testing.T, c *client) { + require.NoError(t, c.StartOnce("Mock WSRPC Client", func() (err error) { + c.cache, err = c.cacheSet.Get(ctx, c) + return err + })) +} + var _ cache.CacheSet = &mockCacheSet{} type mockCacheSet struct{} @@ -160,9 +168,8 @@ func Test_Client_LatestReport(t *testing.T) { c.conn = conn c.rawClient = wsrpcClient - // simulate start without dialling - require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) servicetest.Run(t, cacheSet) + simulateStart(ctx, t, c) for i := 0; i < 5; i++ { r, err := c.LatestReport(ctx, req) @@ -195,12 +202,8 @@ func Test_Client_LatestReport(t *testing.T) { c.conn = conn c.rawClient = wsrpcClient - // simulate start without dialling - require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) - var err error servicetest.Run(t, cacheSet) - c.cache, err = cacheSet.Get(ctx, c) - require.NoError(t, err) + simulateStart(ctx, t, c) for i := 0; i < 5; i++ { r, err := c.LatestReport(ctx, req) From c8eaac73a566c5aa9593abfeda4bda311ef83854 Mon Sep 17 00:00:00 2001 From: jinhoonbang Date: Tue, 2 Jan 2024 11:21:52 -0800 Subject: [PATCH 106/234] remove oracle withdraw and allow contract owner to withdraw (#11551) * remove oracle withdraw and allow contract owner to withdraw * offchain changes * fix integration tests * fix integration tests * Remove amount arg in withdraw and withdraw all the withdrawable amount * Off-chain changes to remove amount arg in withdraw and withdraw all the withdrawable amount * address comments * fix lint issue and small refactor in vrf integration tests * address comment --------- Co-authored-by: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> --- .../src/v0.8/vrf/dev/SubscriptionAPI.sol | 22 +-- .../src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol | 33 ++--- .../testhelpers/ExposedVRFCoordinatorV2_5.sol | 16 +-- .../VRFCoordinatorV2PlusUpgradedVersion.sol | 20 ++- .../vrf/VRFCoordinatorV2Plus_Migration.t.sol | 4 +- .../test/v0.8/foundry/vrf/VRFV2Plus.t.sol | 4 +- .../vrf/VRFV2PlusSubscriptionAPI.t.sol | 74 ++++++---- .../vrf_coordinator_v2_5.go | 136 ++++++++---------- .../vrf_v2plus_upgraded_version.go | 99 ++++++------- ...rapper-dependency-versions-do-not-edit.txt | 4 +- core/scripts/vrfv2plus/testnet/main.go | 2 - .../testnet/v2plusscripts/super_scripts.go | 2 +- .../vrfv2plus/testnet/v2plusscripts/util.go | 4 +- .../vrf/v2/coordinator_v2x_interface.go | 33 ++++- .../vrf/v2/integration_helpers_test.go | 7 +- .../vrf/v2/integration_v2_plus_test.go | 15 +- core/services/vrf/v2/integration_v2_test.go | 33 ++--- .../actions/vrfv2plus/vrfv2plus_steps.go | 15 +- .../contracts/contract_vrf_models.go | 6 +- .../contracts/ethereum_vrfv2plus_contracts.go | 16 +-- .../load/vrfv2plus/vrfv2plus_test.go | 2 - integration-tests/smoke/vrfv2plus_test.go | 42 +++--- 22 files changed, 268 insertions(+), 321 deletions(-) diff --git a/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol b/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol index d7cc5b86c5a..9d4acecdef9 100644 --- a/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol +++ b/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol @@ -83,8 +83,8 @@ abstract contract SubscriptionAPI is ConfirmedOwner, IERC677Receiver, IVRFSubscr // A discrepancy with this contract's native balance indicates someone // sent native using transfer and so we may need to use recoverNativeFunds. uint96 public s_totalNativeBalance; - mapping(address => uint96) /* oracle */ /* LINK balance */ internal s_withdrawableTokens; - mapping(address => uint96) /* oracle */ /* native balance */ internal s_withdrawableNative; + uint96 internal s_withdrawableTokens; + uint96 internal s_withdrawableNative; event SubscriptionCreated(uint256 indexed subId, address owner); event SubscriptionFunded(uint256 indexed subId, uint256 oldBalance, uint256 newBalance); @@ -204,18 +204,19 @@ abstract contract SubscriptionAPI is ConfirmedOwner, IERC677Receiver, IVRFSubscr } /* - * @notice Oracle withdraw LINK earned through fulfilling requests + * @notice withdraw LINK earned through fulfilling requests * @param recipient where to send the funds * @param amount amount to withdraw */ - function oracleWithdraw(address recipient, uint96 amount) external nonReentrant { + function withdraw(address recipient) external nonReentrant onlyOwner { if (address(LINK) == address(0)) { revert LinkNotSet(); } - if (s_withdrawableTokens[msg.sender] < amount) { + if (s_withdrawableTokens == 0) { revert InsufficientBalance(); } - s_withdrawableTokens[msg.sender] -= amount; + uint96 amount = s_withdrawableTokens; + s_withdrawableTokens -= amount; s_totalBalance -= amount; if (!LINK.transfer(recipient, amount)) { revert InsufficientBalance(); @@ -223,16 +224,17 @@ abstract contract SubscriptionAPI is ConfirmedOwner, IERC677Receiver, IVRFSubscr } /* - * @notice Oracle withdraw native earned through fulfilling requests + * @notice withdraw native earned through fulfilling requests * @param recipient where to send the funds * @param amount amount to withdraw */ - function oracleWithdrawNative(address payable recipient, uint96 amount) external nonReentrant { - if (s_withdrawableNative[msg.sender] < amount) { + function withdrawNative(address payable recipient) external nonReentrant onlyOwner { + if (s_withdrawableNative == 0) { revert InsufficientBalance(); } // Prevent re-entrancy by updating state before transfer. - s_withdrawableNative[msg.sender] -= amount; + uint96 amount = s_withdrawableNative; + s_withdrawableNative -= amount; s_totalNativeBalance -= amount; (bool sent, ) = recipient.call{value: amount}(""); if (!sent) { diff --git a/contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol b/contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol index e0e46fe67b7..a265cea4ed9 100644 --- a/contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol +++ b/contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol @@ -44,11 +44,11 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus { address sender; bytes extraArgs; } - mapping(bytes32 => address) /* keyHash */ /* oracle */ public s_provingKeys; + mapping(bytes32 => bool) /* keyHash */ /* exists */ public s_provingKeys; bytes32[] public s_provingKeyHashes; mapping(uint256 => bytes32) /* requestID */ /* commitment */ public s_requestCommitments; - event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); - event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle); + event ProvingKeyRegistered(bytes32 keyHash); + event ProvingKeyDeregistered(bytes32 keyHash); event RandomWordsRequested( bytes32 indexed keyHash, uint256 requestId, @@ -94,28 +94,26 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus { } /** - * @notice Registers a proving key to an oracle. - * @param oracle address of the oracle + * @notice Registers a proving key to. * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ - function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { + function registerProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); - if (s_provingKeys[kh] != address(0)) { + if (s_provingKeys[kh]) { revert ProvingKeyAlreadyRegistered(kh); } - s_provingKeys[kh] = oracle; + s_provingKeys[kh] = true; s_provingKeyHashes.push(kh); - emit ProvingKeyRegistered(kh, oracle); + emit ProvingKeyRegistered(kh); } /** - * @notice Deregisters a proving key to an oracle. + * @notice Deregisters a proving key. * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); - address oracle = s_provingKeys[kh]; - if (oracle == address(0)) { + if (!s_provingKeys[kh]) { revert NoSuchProvingKey(kh); } delete s_provingKeys[kh]; @@ -127,7 +125,7 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus { s_provingKeyHashes.pop(); } } - emit ProvingKeyDeregistered(kh, oracle); + emit ProvingKeyDeregistered(kh); } /** @@ -355,8 +353,7 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus { ) internal view returns (Output memory) { bytes32 keyHash = hashOfKey(proof.pk); // Only registered proving keys are permitted. - address oracle = s_provingKeys[keyHash]; - if (oracle == address(0)) { + if (!s_provingKeys[keyHash]) { revert NoSuchProvingKey(keyHash); } uint256 requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); @@ -423,7 +420,7 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus { bool nativePayment = uint8(rc.extraArgs[rc.extraArgs.length - 1]) == 1; // We want to charge users exactly for how much gas they use in their callback. // The gasAfterPaymentCalculation is meant to cover these additional operations where we - // decrement the subscription balance and increment the oracles withdrawable balance. + // decrement the subscription balance and increment the withdrawable balance. uint96 payment = _calculatePaymentAmount( startGas, s_config.gasAfterPaymentCalculation, @@ -435,13 +432,13 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus { revert InsufficientBalance(); } s_subscriptions[rc.subId].nativeBalance -= payment; - s_withdrawableNative[s_provingKeys[output.keyHash]] += payment; + s_withdrawableNative += payment; } else { if (s_subscriptions[rc.subId].balance < payment) { revert InsufficientBalance(); } s_subscriptions[rc.subId].balance -= payment; - s_withdrawableTokens[s_provingKeys[output.keyHash]] += payment; + s_withdrawableTokens += payment; } // Include payment in the event for tracking costs. diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/ExposedVRFCoordinatorV2_5.sol b/contracts/src/v0.8/vrf/dev/testhelpers/ExposedVRFCoordinatorV2_5.sol index 02cb15e38a4..ded916699b8 100644 --- a/contracts/src/v0.8/vrf/dev/testhelpers/ExposedVRFCoordinatorV2_5.sol +++ b/contracts/src/v0.8/vrf/dev/testhelpers/ExposedVRFCoordinatorV2_5.sol @@ -50,19 +50,19 @@ contract ExposedVRFCoordinatorV2_5 is VRFCoordinatorV2_5 { s_totalNativeBalance = newBalance; } - function setWithdrawableTokensTestingOnlyXXX(address oracle, uint96 newBalance) external { - s_withdrawableTokens[oracle] = newBalance; + function setWithdrawableTokensTestingOnlyXXX(uint96 newBalance) external { + s_withdrawableTokens = newBalance; } - function getWithdrawableTokensTestingOnlyXXX(address oracle) external view returns (uint96) { - return s_withdrawableTokens[oracle]; + function getWithdrawableTokensTestingOnlyXXX() external view returns (uint96) { + return s_withdrawableTokens; } - function setWithdrawableNativeTestingOnlyXXX(address oracle, uint96 newBalance) external { - s_withdrawableNative[oracle] = newBalance; + function setWithdrawableNativeTestingOnlyXXX(uint96 newBalance) external { + s_withdrawableNative = newBalance; } - function getWithdrawableNativeTestingOnlyXXX(address oracle) external view returns (uint96) { - return s_withdrawableNative[oracle]; + function getWithdrawableNativeTestingOnlyXXX() external view returns (uint96) { + return s_withdrawableNative; } } diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol index 4837411955c..ca29adac87c 100644 --- a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol +++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol @@ -56,11 +56,11 @@ contract VRFCoordinatorV2PlusUpgradedVersion is bytes extraArgs; } - mapping(bytes32 => address) /* keyHash */ /* oracle */ internal s_provingKeys; + mapping(bytes32 => bool) /* keyHash */ /* exists */ internal s_provingKeys; bytes32[] public s_provingKeyHashes; mapping(uint256 => bytes32) /* requestID */ /* commitment */ public s_requestCommitments; - event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); + event ProvingKeyRegistered(bytes32 keyHash); event RandomWordsRequested( bytes32 indexed keyHash, uint256 requestId, @@ -108,17 +108,16 @@ contract VRFCoordinatorV2PlusUpgradedVersion is /** * @notice Registers a proving key to an oracle. - * @param oracle address of the oracle * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ - function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { + function registerProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); - if (s_provingKeys[kh] != address(0)) { + if (s_provingKeys[kh]) { revert ProvingKeyAlreadyRegistered(kh); } - s_provingKeys[kh] = oracle; + s_provingKeys[kh] = true; s_provingKeyHashes.push(kh); - emit ProvingKeyRegistered(kh, oracle); + emit ProvingKeyRegistered(kh); } /** @@ -346,8 +345,7 @@ contract VRFCoordinatorV2PlusUpgradedVersion is ) internal view returns (Output memory) { bytes32 keyHash = hashOfKey(proof.pk); // Only registered proving keys are permitted. - address oracle = s_provingKeys[keyHash]; - if (oracle == address(0)) { + if (!s_provingKeys[keyHash]) { revert NoSuchProvingKey(keyHash); } uint256 requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); @@ -426,13 +424,13 @@ contract VRFCoordinatorV2PlusUpgradedVersion is revert InsufficientBalance(); } s_subscriptions[rc.subId].nativeBalance -= payment; - s_withdrawableNative[s_provingKeys[output.keyHash]] += payment; + s_withdrawableNative += payment; } else { if (s_subscriptions[rc.subId].balance < payment) { revert InsufficientBalance(); } s_subscriptions[rc.subId].balance -= payment; - s_withdrawableTokens[s_provingKeys[output.keyHash]] += payment; + s_withdrawableTokens += payment; } // Include payment in the event for tracking costs. diff --git a/contracts/test/v0.8/foundry/vrf/VRFCoordinatorV2Plus_Migration.t.sol b/contracts/test/v0.8/foundry/vrf/VRFCoordinatorV2Plus_Migration.t.sol index d7a54d6223c..b6056286261 100644 --- a/contracts/test/v0.8/foundry/vrf/VRFCoordinatorV2Plus_Migration.t.sol +++ b/contracts/test/v0.8/foundry/vrf/VRFCoordinatorV2Plus_Migration.t.sol @@ -331,8 +331,8 @@ contract VRFCoordinatorV2Plus_Migration is BaseTest { function registerProvingKey() public { uint256[2] memory uncompressedKeyParts = this.getProvingKeyParts(UNCOMPRESSED_PUBLIC_KEY); - v1Coordinator.registerProvingKey(OWNER, uncompressedKeyParts); - v1Coordinator_noLink.registerProvingKey(OWNER, uncompressedKeyParts); + v1Coordinator.registerProvingKey(uncompressedKeyParts); + v1Coordinator_noLink.registerProvingKey(uncompressedKeyParts); } // note: Call this function via this.getProvingKeyParts to be able to pass memory as calldata and diff --git a/contracts/test/v0.8/foundry/vrf/VRFV2Plus.t.sol b/contracts/test/v0.8/foundry/vrf/VRFV2Plus.t.sol index e2734f17288..62c08533d3f 100644 --- a/contracts/test/v0.8/foundry/vrf/VRFV2Plus.t.sol +++ b/contracts/test/v0.8/foundry/vrf/VRFV2Plus.t.sol @@ -124,12 +124,12 @@ contract VRFV2Plus is BaseTest { // Should revert when already registered. uint256[2] memory uncompressedKeyParts = this.getProvingKeyParts(vrfUncompressedPublicKey); vm.expectRevert(abi.encodeWithSelector(VRFCoordinatorV2_5.ProvingKeyAlreadyRegistered.selector, vrfKeyHash)); - s_testCoordinator.registerProvingKey(LINK_WHALE, uncompressedKeyParts); + s_testCoordinator.registerProvingKey(uncompressedKeyParts); } function registerProvingKey() public { uint256[2] memory uncompressedKeyParts = this.getProvingKeyParts(vrfUncompressedPublicKey); - s_testCoordinator.registerProvingKey(LINK_WHALE, uncompressedKeyParts); + s_testCoordinator.registerProvingKey(uncompressedKeyParts); } // note: Call this function via this.getProvingKeyParts to be able to pass memory as calldata and diff --git a/contracts/test/v0.8/foundry/vrf/VRFV2PlusSubscriptionAPI.t.sol b/contracts/test/v0.8/foundry/vrf/VRFV2PlusSubscriptionAPI.t.sol index 335e64ff7ef..0f34003d9ae 100644 --- a/contracts/test/v0.8/foundry/vrf/VRFV2PlusSubscriptionAPI.t.sol +++ b/contracts/test/v0.8/foundry/vrf/VRFV2PlusSubscriptionAPI.t.sol @@ -315,25 +315,25 @@ contract VRFV2PlusSubscriptionAPITest is BaseTest { assertEq(address(s_subscriptionAPI).balance, s_subscriptionAPI.s_totalNativeBalance()); } - function testOracleWithdrawNoLink() public { + function testWithdrawNoLink() public { // CASE: no link token set vm.expectRevert(SubscriptionAPI.LinkNotSet.selector); - s_subscriptionAPI.oracleWithdraw(OWNER, 1 ether); + s_subscriptionAPI.withdraw(OWNER); } - function testOracleWithdrawInsufficientBalance() public { + function testWithdrawInsufficientBalance() public { // CASE: link token set, trying to withdraw // more than balance MockLinkToken linkToken = new MockLinkToken(); s_subscriptionAPI.setLINKAndLINKNativeFeed(address(linkToken), address(0)); assertEq(address(s_subscriptionAPI.LINK()), address(linkToken)); - // call oracleWithdraw + // call withdraw vm.expectRevert(SubscriptionAPI.InsufficientBalance.selector); - s_subscriptionAPI.oracleWithdraw(OWNER, 1 ether); + s_subscriptionAPI.withdraw(OWNER); } - function testOracleWithdrawSufficientBalanceLinkSet() public { + function testWithdrawSufficientBalanceLinkSet() public { // CASE: link token set, trying to withdraw // less than balance MockLinkToken linkToken = new MockLinkToken(); @@ -344,58 +344,72 @@ contract VRFV2PlusSubscriptionAPITest is BaseTest { bool success = linkToken.transfer(address(s_subscriptionAPI), 10 ether); assertTrue(success, "failed link transfer"); - // set the withdrawable tokens of the oracle to be 1 ether - address oracle = makeAddr("oracle"); - s_subscriptionAPI.setWithdrawableTokensTestingOnlyXXX(oracle, 1 ether); - assertEq(s_subscriptionAPI.getWithdrawableTokensTestingOnlyXXX(oracle), 1 ether); + // set the withdrawable tokens of the contract to be 1 ether + s_subscriptionAPI.setWithdrawableTokensTestingOnlyXXX(1 ether); + assertEq(s_subscriptionAPI.getWithdrawableTokensTestingOnlyXXX(), 1 ether); // set the total balance to be the same as the link balance for consistency // (this is not necessary for the test, but just to be sane) s_subscriptionAPI.setTotalBalanceTestingOnlyXXX(10 ether); - // call oracleWithdraw from oracle address - changePrank(oracle); - s_subscriptionAPI.oracleWithdraw(oracle, 1 ether); - // assert link balance of oracle - assertEq(linkToken.balanceOf(oracle), 1 ether, "oracle link balance incorrect"); + // call Withdraw from owner address + uint256 ownerBalance = linkToken.balanceOf(OWNER); + changePrank(OWNER); + s_subscriptionAPI.withdraw(OWNER); + // assert link balance of owner + assertEq(linkToken.balanceOf(OWNER) - ownerBalance, 1 ether, "owner link balance incorrect"); // assert state of subscription api - assertEq(s_subscriptionAPI.getWithdrawableTokensTestingOnlyXXX(oracle), 0, "oracle withdrawable tokens incorrect"); + assertEq(s_subscriptionAPI.getWithdrawableTokensTestingOnlyXXX(), 0, "owner withdrawable tokens incorrect"); // assert that total balance is changed by the withdrawn amount assertEq(s_subscriptionAPI.s_totalBalance(), 9 ether, "total balance incorrect"); } - function testOracleWithdrawNativeInsufficientBalance() public { + function testWithdrawNativeInsufficientBalance() public { // CASE: trying to withdraw more than balance // should revert with InsufficientBalance - // call oracleWithdrawNative + // call WithdrawNative + changePrank(OWNER); vm.expectRevert(SubscriptionAPI.InsufficientBalance.selector); - s_subscriptionAPI.oracleWithdrawNative(payable(OWNER), 1 ether); + s_subscriptionAPI.withdrawNative(payable(OWNER)); + } + + function testWithdrawLinkInvalidOwner() public { + address invalidAddress = makeAddr("invalidAddress"); + changePrank(invalidAddress); + vm.expectRevert("Only callable by owner"); + s_subscriptionAPI.withdraw(payable(OWNER)); } - function testOracleWithdrawNativeSufficientBalance() public { + function testWithdrawNativeInvalidOwner() public { + address invalidAddress = makeAddr("invalidAddress"); + changePrank(invalidAddress); + vm.expectRevert("Only callable by owner"); + s_subscriptionAPI.withdrawNative(payable(OWNER)); + } + + function testWithdrawNativeSufficientBalance() public { // CASE: trying to withdraw less than balance // should withdraw successfully // transfer 10 ether to the contract to withdraw vm.deal(address(s_subscriptionAPI), 10 ether); - // set the withdrawable eth of the oracle to be 1 ether - address oracle = makeAddr("oracle"); - s_subscriptionAPI.setWithdrawableNativeTestingOnlyXXX(oracle, 1 ether); - assertEq(s_subscriptionAPI.getWithdrawableNativeTestingOnlyXXX(oracle), 1 ether); + // set the withdrawable eth of the contract to be 1 ether + s_subscriptionAPI.setWithdrawableNativeTestingOnlyXXX(1 ether); + assertEq(s_subscriptionAPI.getWithdrawableNativeTestingOnlyXXX(), 1 ether); // set the total balance to be the same as the eth balance for consistency // (this is not necessary for the test, but just to be sane) s_subscriptionAPI.setTotalNativeBalanceTestingOnlyXXX(10 ether); - // call oracleWithdrawNative from oracle address - changePrank(oracle); - s_subscriptionAPI.oracleWithdrawNative(payable(oracle), 1 ether); - // assert native balance of oracle - assertEq(address(oracle).balance, 1 ether, "oracle native balance incorrect"); + // call WithdrawNative from owner address + changePrank(OWNER); + s_subscriptionAPI.withdrawNative(payable(OWNER)); + // assert native balance + assertEq(address(OWNER).balance, 1 ether, "owner native balance incorrect"); // assert state of subscription api - assertEq(s_subscriptionAPI.getWithdrawableNativeTestingOnlyXXX(oracle), 0, "oracle withdrawable native incorrect"); + assertEq(s_subscriptionAPI.getWithdrawableNativeTestingOnlyXXX(), 0, "owner withdrawable native incorrect"); // assert that total balance is changed by the withdrawn amount assertEq(s_subscriptionAPI.s_totalNativeBalance(), 9 ether, "total native balance incorrect"); } diff --git a/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go b/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go index 62e8b9f0de3..1475c56499f 100644 --- a/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go +++ b/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go @@ -66,8 +66,8 @@ type VRFV2PlusClientRandomWordsRequest struct { } var VRFCoordinatorV25MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"internalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"externalBalance\",\"type\":\"uint256\"}],\"name\":\"BalanceInvariantViolated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"}],\"name\":\"BlockhashNotInStore\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorNotRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToSendNative\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToTransferLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"have\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"want\",\"type\":\"uint256\"}],\"name\":\"InsufficientGasForConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"have\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"min\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"max\",\"type\":\"uint16\"}],\"name\":\"InvalidRequestConfirmations\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkAlreadySet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeRequestedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCorrespondingRequest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"NoSuchProvingKey\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"NumWordsTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingRequestExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Reentrant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structVRFCoordinatorV2_5.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"MigrationCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"NativeFundsRecovered\",\"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\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountLink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountNative\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldNativeBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newNativeBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFundedWithNative\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_NATIVE_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"deregisterMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"deregisterProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFCoordinatorV2_5.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"fundSubscriptionWithNative\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveSubscriptionIds\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRequestConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"}],\"name\":\"migrate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"migrationVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdrawNative\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverNativeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"registerMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFV2PlusClient.RandomWordsRequest\",\"name\":\"req\",\"type\":\"tuple\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_config\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"reentrancyLock\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_currentSubNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fallbackWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_feeConfig\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_provingKeyHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"s_provingKeys\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requestCommitments\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalNativeBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"internalType\":\"structVRFCoordinatorV2_5.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"}],\"name\":\"setLINKAndLINKNativeFeed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a06040523480156200001157600080fd5b506040516200615d3803806200615d833981016040819052620000349162000183565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d7565b50505060601b6001600160601b031916608052620001b5565b6001600160a01b038116331415620001325760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156200019657600080fd5b81516001600160a01b0381168114620001ae57600080fd5b9392505050565b60805160601c615f82620001db60003960008181610566015261384f0152615f826000f3fe60806040526004361061023c5760003560e01c806379ba50971161012f578063b08c8795116100b1578063b08c87951461076d578063b2a7cac51461078d578063bec4c08c146107ad578063caf70c4a146107cd578063cb631797146107ed578063d98e620e1461080d578063da2f26101461082d578063dac83d2914610863578063dc311dd314610883578063e72f6e30146108b4578063ee9d2d38146108d4578063f2fde38b1461090157600080fd5b806379ba50971461060d5780638402595e1461062257806386fe91c7146106425780638da5cb5b1461066257806395b55cfc146106805780639b1c385e146106935780639d40a6fd146106b3578063a21a23e4146106eb578063a4c0ed3614610700578063aa433aff14610720578063aefb212f1461074057600080fd5b8063330987b3116101c3578063330987b314610444578063405b84fa1461046457806340d6bb821461048457806341af6c87146104af5780635d06b4ab146104df57806364d51a2a146104ff578063659827441461051457806366316d8d14610534578063689c4517146105545780636b6feccc146105885780636f64f03f146105cd57806372e9d565146105ed57600080fd5b80620122911461024157806304104edb1461026e578063043bd6ae14610290578063088070f5146102b457806308821d58146103345780630ae095401461035457806315c48b841461037457806318e3dd271461039c5780631b6b6d23146103db5780632949265714610408578063294daa4914610428575b600080fd5b34801561024d57600080fd5b50610256610921565b60405161026593929190615a61565b60405180910390f35b34801561027a57600080fd5b5061028e61028936600461533c565b61099d565b005b34801561029c57600080fd5b506102a660115481565b604051908152602001610265565b3480156102c057600080fd5b50600d546102fc9061ffff81169063ffffffff62010000820481169160ff600160301b82041691600160381b8204811691600160581b90041685565b6040805161ffff909616865263ffffffff9485166020870152921515928501929092528216606084015216608082015260a001610265565b34801561034057600080fd5b5061028e61034f36600461547c565b610b53565b34801561036057600080fd5b5061028e61036f36600461571e565b610ce7565b34801561038057600080fd5b5061038960c881565b60405161ffff9091168152602001610265565b3480156103a857600080fd5b50600a546103c390600160601b90046001600160601b031681565b6040516001600160601b039091168152602001610265565b3480156103e757600080fd5b506002546103fb906001600160a01b031681565b604051610265919061594f565b34801561041457600080fd5b5061028e610423366004615359565b610dac565b34801561043457600080fd5b5060405160018152602001610265565b34801561045057600080fd5b506103c361045f36600461554e565b610f29565b34801561047057600080fd5b5061028e61047f36600461571e565b61140d565b34801561049057600080fd5b5061049a6101f481565b60405163ffffffff9091168152602001610265565b3480156104bb57600080fd5b506104cf6104ca3660046154d1565b6117f8565b6040519015158152602001610265565b3480156104eb57600080fd5b5061028e6104fa36600461533c565b611999565b34801561050b57600080fd5b50610389606481565b34801561052057600080fd5b5061028e61052f36600461538e565b611a50565b34801561054057600080fd5b5061028e61054f366004615359565b611ab0565b34801561056057600080fd5b506103fb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561059457600080fd5b506012546105b09063ffffffff80821691600160201b90041682565b6040805163ffffffff938416815292909116602083015201610265565b3480156105d957600080fd5b5061028e6105e83660046153c7565b611c78565b3480156105f957600080fd5b506003546103fb906001600160a01b031681565b34801561061957600080fd5b5061028e611d77565b34801561062e57600080fd5b5061028e61063d36600461533c565b611e21565b34801561064e57600080fd5b50600a546103c3906001600160601b031681565b34801561066e57600080fd5b506000546001600160a01b03166103fb565b61028e61068e3660046154d1565b611f33565b34801561069f57600080fd5b506102a66106ae36600461562b565b61207a565b3480156106bf57600080fd5b506007546106d3906001600160401b031681565b6040516001600160401b039091168152602001610265565b3480156106f757600080fd5b506102a661240c565b34801561070c57600080fd5b5061028e61071b3660046153f4565b61265a565b34801561072c57600080fd5b5061028e61073b3660046154d1565b6127fa565b34801561074c57600080fd5b5061076061075b366004615743565b61285a565b60405161026591906159c6565b34801561077957600080fd5b5061028e610788366004615680565b61295b565b34801561079957600080fd5b5061028e6107a83660046154d1565b612ade565b3480156107b957600080fd5b5061028e6107c836600461571e565b612c02565b3480156107d957600080fd5b506102a66107e8366004615498565b612d99565b3480156107f957600080fd5b5061028e61080836600461571e565b612dc9565b34801561081957600080fd5b506102a66108283660046154d1565b6130b6565b34801561083957600080fd5b506103fb6108483660046154d1565b600e602052600090815260409020546001600160a01b031681565b34801561086f57600080fd5b5061028e61087e36600461571e565b6130d7565b34801561088f57600080fd5b506108a361089e3660046154d1565b6131e7565b604051610265959493929190615be3565b3480156108c057600080fd5b5061028e6108cf36600461533c565b6132e2565b3480156108e057600080fd5b506102a66108ef3660046154d1565b60106020526000908152604090205481565b34801561090d57600080fd5b5061028e61091c36600461533c565b6134c3565b600d54600f805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff1693919283919083018282801561098b57602002820191906000526020600020905b815481526020019060010190808311610977575b50505050509050925092509250909192565b6109a56134d4565b60135460005b81811015610b2b57826001600160a01b0316601382815481106109d0576109d0615ede565b6000918252602090912001546001600160a01b03161415610b195760136109f8600184615dab565b81548110610a0857610a08615ede565b600091825260209091200154601380546001600160a01b039092169183908110610a3457610a34615ede565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055826013610a6b600185615dab565b81548110610a7b57610a7b615ede565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506013805480610aba57610aba615ec8565b600082815260209020810160001990810180546001600160a01b03191690550190556040517ff80a1a97fd42251f3c33cda98635e7399253033a6774fe37cd3f650b5282af3790610b0c90859061594f565b60405180910390a1505050565b80610b2381615e46565b9150506109ab565b5081604051635428d44960e01b8152600401610b47919061594f565b60405180910390fd5b50565b610b5b6134d4565b604080518082018252600091610b8a919084906002908390839080828437600092019190915250612d99915050565b6000818152600e60205260409020549091506001600160a01b031680610bc657604051631dfd6e1360e21b815260048101839052602401610b47565b6000828152600e6020526040812080546001600160a01b03191690555b600f54811015610c9e5782600f8281548110610c0157610c01615ede565b90600052602060002001541415610c8c57600f805460009190610c2690600190615dab565b81548110610c3657610c36615ede565b9060005260206000200154905080600f8381548110610c5757610c57615ede565b600091825260209091200155600f805480610c7457610c74615ec8565b60019003818190600052602060002001600090559055505b80610c9681615e46565b915050610be3565b50806001600160a01b03167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610cda91815260200190565b60405180910390a2505050565b60008281526005602052604090205482906001600160a01b031680610d1f57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614610d4a5780604051636c51fda960e11b8152600401610b47919061594f565b600d54600160301b900460ff1615610d755760405163769dd35360e11b815260040160405180910390fd5b610d7e846117f8565b15610d9c57604051631685ecdd60e31b815260040160405180910390fd5b610da68484613529565b50505050565b600d54600160301b900460ff1615610dd75760405163769dd35360e11b815260040160405180910390fd5b336000908152600c60205260409020546001600160601b0380831691161015610e1357604051631e9acf1760e31b815260040160405180910390fd5b336000908152600c602052604081208054839290610e3b9084906001600160601b0316615dc2565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a600c8282829054906101000a90046001600160601b0316610e839190615dc2565b92506101000a8154816001600160601b0302191690836001600160601b031602179055506000826001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114610efd576040519150601f19603f3d011682016040523d82523d6000602084013e610f02565b606091505b5050905080610f245760405163950b247960e01b815260040160405180910390fd5b505050565b600d54600090600160301b900460ff1615610f575760405163769dd35360e11b815260040160405180910390fd5b60005a90506000610f6885856136e4565b90506000846060015163ffffffff166001600160401b03811115610f8e57610f8e615ef4565b604051908082528060200260200182016040528015610fb7578160200160208202803683370190505b50905060005b856060015163ffffffff1681101561103757826040015181604051602001610fef929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c82828151811061101a5761101a615ede565b60209081029190910101528061102f81615e46565b915050610fbd565b5060208083018051600090815260109092526040808320839055905190518291631fe543e360e01b9161106f91908690602401615aeb565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600d805460ff60301b1916600160301b1790559088015160808901519192506000916110d49163ffffffff169084613971565b600d805460ff60301b19169055602089810151600090815260069091526040902054909150600160c01b90046001600160401b0316611114816001615d34565b6020808b0151600090815260069091526040812080546001600160401b0393909316600160c01b026001600160c01b039093169290921790915560a08a0151805161116190600190615dab565b8151811061117157611171615ede565b602091010151600d5460f89190911c60011491506000906111a2908a90600160581b900463ffffffff163a856139bf565b905081156112ab576020808c01516000908152600690915260409020546001600160601b03808316600160601b9092041610156111f257604051631e9acf1760e31b815260040160405180910390fd5b60208b81015160009081526006909152604090208054829190600c90611229908490600160601b90046001600160601b0316615dc2565b82546101009290920a6001600160601b0381810219909316918316021790915589516000908152600e60209081526040808320546001600160a01b03168352600c90915281208054859450909261128291859116615d56565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550611397565b6020808c01516000908152600690915260409020546001600160601b03808316911610156112ec57604051631e9acf1760e31b815260040160405180910390fd5b6020808c0151600090815260069091526040812080548392906113199084906001600160601b0316615dc2565b82546101009290920a6001600160601b0381810219909316918316021790915589516000908152600e60209081526040808320546001600160a01b03168352600b90915281208054859450909261137291859116615d56565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b8a6020015188602001517f49580fdfd9497e1ed5c1b1cec0495087ae8e3f1267470ec2fb015db32e3d6aa78a6040015184886040516113f4939291909283526001600160601b039190911660208301521515604082015260600190565b60405180910390a3985050505050505050505b92915050565b600d54600160301b900460ff16156114385760405163769dd35360e11b815260040160405180910390fd5b61144181613a0e565b6114605780604051635428d44960e01b8152600401610b47919061594f565b60008060008061146f866131e7565b945094505093509350336001600160a01b0316826001600160a01b0316146114d25760405162461bcd60e51b81526020600482015260166024820152752737ba1039bab139b1b934b83a34b7b71037bbb732b960511b6044820152606401610b47565b6114db866117f8565b156115215760405162461bcd60e51b815260206004820152601660248201527550656e64696e6720726571756573742065786973747360501b6044820152606401610b47565b60006040518060c00160405280611536600190565b60ff168152602001888152602001846001600160a01b03168152602001838152602001866001600160601b03168152602001856001600160601b0316815250905060008160405160200161158a91906159ec565b60405160208183030381529060405290506115a488613a78565b505060405163ce3f471960e01b81526001600160a01b0388169063ce3f4719906001600160601b038816906115dd9085906004016159d9565b6000604051808303818588803b1580156115f657600080fd5b505af115801561160a573d6000803e3d6000fd5b50506002546001600160a01b031615801593509150611633905057506001600160601b03861615155b156116fd5760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb9061166a908a908a90600401615996565b602060405180830381600087803b15801561168457600080fd5b505af1158015611698573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116bc91906154b4565b6116fd5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742066756e647360701b6044820152606401610b47565b600d805460ff60301b1916600160301b17905560005b83518110156117a65783818151811061172e5761172e615ede565b60200260200101516001600160a01b0316638ea98117896040518263ffffffff1660e01b8152600401611761919061594f565b600060405180830381600087803b15801561177b57600080fd5b505af115801561178f573d6000803e3d6000fd5b50505050808061179e90615e46565b915050611713565b50600d805460ff60301b191690556040517fd63ca8cb945956747ee69bfdc3ea754c24a4caf7418db70e46052f7850be4187906117e69089908b90615963565b60405180910390a15050505050505050565b6000818152600560209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561188257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611864575b505050505081525050905060005b81604001515181101561198f5760005b600f5481101561197c576000611945600f83815481106118c2576118c2615ede565b9060005260206000200154856040015185815181106118e3576118e3615ede565b602002602001015188600460008960400151898151811061190657611906615ede565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081208d82529092529020546001600160401b0316613cc6565b50600081815260106020526040902054909150156119695750600195945050505050565b508061197481615e46565b9150506118a0565b508061198781615e46565b915050611890565b5060009392505050565b6119a16134d4565b6119aa81613a0e565b156119ca578060405163ac8a27ef60e01b8152600401610b47919061594f565b601380546001810182556000919091527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0900180546001600160a01b0319166001600160a01b0383161790556040517fb7cabbfc11e66731fc77de0444614282023bcbd41d16781c753a431d0af0162590611a4590839061594f565b60405180910390a150565b611a586134d4565b6002546001600160a01b031615611a8257604051631688c53760e11b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b600d54600160301b900460ff1615611adb5760405163769dd35360e11b815260040160405180910390fd5b6002546001600160a01b0316611b045760405163c1f0c0a160e01b815260040160405180910390fd5b336000908152600b60205260409020546001600160601b0380831691161015611b4057604051631e9acf1760e31b815260040160405180910390fd5b336000908152600b602052604081208054839290611b689084906001600160601b0316615dc2565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a60008282829054906101000a90046001600160601b0316611bb09190615dc2565b82546001600160601b039182166101009390930a92830291909202199091161790555060025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90611c059085908590600401615996565b602060405180830381600087803b158015611c1f57600080fd5b505af1158015611c33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5791906154b4565b611c7457604051631e9acf1760e31b815260040160405180910390fd5b5050565b611c806134d4565b604080518082018252600091611caf919084906002908390839080828437600092019190915250612d99915050565b6000818152600e60205260409020549091506001600160a01b031615611ceb57604051634a0b8fa760e01b815260048101829052602401610b47565b6000818152600e6020908152604080832080546001600160a01b0319166001600160a01b038816908117909155600f805460018101825594527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac802909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610cda565b6001546001600160a01b03163314611dca5760405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606401610b47565b60008054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611e296134d4565b600a544790600160601b90046001600160601b031681811115611e69576040516354ced18160e11b81526004810182905260248101839052604401610b47565b81811015610f24576000611e7d8284615dab565b90506000846001600160a01b03168260405160006040518083038185875af1925050503d8060008114611ecc576040519150601f19603f3d011682016040523d82523d6000602084013e611ed1565b606091505b5050905080611ef35760405163950b247960e01b815260040160405180910390fd5b7f4aed7c8eed0496c8c19ea2681fcca25741c1602342e38b045d9f1e8e905d2e9c8583604051611f24929190615963565b60405180910390a15050505050565b600d54600160301b900460ff1615611f5e5760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b0316611f9357604051630fb532db60e11b815260040160405180910390fd5b60008181526006602052604090208054600160601b90046001600160601b0316903490600c611fc28385615d56565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555034600a600c8282829054906101000a90046001600160601b031661200a9190615d56565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f7603b205d03651ee812f803fccde89f1012e545a9c99f0abfea9cedd0fd8e90282348461205d9190615d1c565b604080519283526020830191909152015b60405180910390a25050565b600d54600090600160301b900460ff16156120a85760405163769dd35360e11b815260040160405180910390fd5b6020808301356000908152600590915260409020546001600160a01b03166120e357604051630fb532db60e11b815260040160405180910390fd5b3360009081526004602090815260408083208583013584529091529020546001600160401b031680612130578260200135336040516379bfd40160e01b8152600401610b47929190615ac0565b600d5461ffff166121476060850160408601615665565b61ffff16108061216a575060c86121646060850160408601615665565b61ffff16115b156121b05761217f6060840160408501615665565b600d5460405163539c34bb60e11b815261ffff92831660048201529116602482015260c86044820152606401610b47565b600d5462010000900463ffffffff166121cf6080850160608601615765565b63ffffffff16111561221f576121eb6080840160608501615765565b600d54604051637aebf00f60e11b815263ffffffff9283166004820152620100009091049091166024820152604401610b47565b6101f461223260a0850160808601615765565b63ffffffff1611156122785761224e60a0840160808501615765565b6040516311ce1afb60e21b815263ffffffff90911660048201526101f46024820152604401610b47565b6000612285826001615d34565b905060008061229b863533602089013586613cc6565b909250905060006122b76122b260a0890189615c38565b613d3f565b905060006122c482613dbc565b9050836122cf613e2d565b60208a01356122e460808c0160608d01615765565b6122f460a08d0160808e01615765565b338660405160200161230c9796959493929190615b43565b604051602081830303815290604052805190602001206010600086815260200190815260200160002081905550336001600160a01b0316886020013589600001357feb0e3652e0f44f417695e6e90f2f42c99b65cd7169074c5a654b16b9748c3a4e87878d60400160208101906123839190615665565b8e60600160208101906123969190615765565b8f60800160208101906123a99190615765565b896040516123bc96959493929190615b04565b60405180910390a45050336000908152600460209081526040808320898301358452909152902080546001600160401b0319166001600160401b039490941693909317909255925050505b919050565b600d54600090600160301b900460ff161561243a5760405163769dd35360e11b815260040160405180910390fd5b600033612448600143615dab565b600754604051606093841b6001600160601b03199081166020830152924060348201523090931b909116605483015260c01b6001600160c01b031916606882015260700160408051601f198184030181529190528051602090910120600780549192506001600160401b039091169060006124c283615e61565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550506000806001600160401b0381111561250157612501615ef4565b60405190808252806020026020018201604052801561252a578160200160208202803683370190505b506040805160608082018352600080835260208084018281528486018381528984526006835286842095518654925191516001600160601b039182166001600160c01b031990941693909317600160601b9190921602176001600160c01b0316600160c01b6001600160401b039092169190910217909355835191820184523382528183018181528285018681528883526005855294909120825181546001600160a01b03199081166001600160a01b03928316178355925160018301805490941691161790915592518051949550909361260b9260028501920190615052565b5061261b91506008905083613ebd565b50817f1d3015d7ba850fa198dc7b1a3f5d42779313a681035f77c8c03764c61005518d3360405161264c919061594f565b60405180910390a250905090565b600d54600160301b900460ff16156126855760405163769dd35360e11b815260040160405180910390fd5b6002546001600160a01b031633146126b0576040516344b0e3c360e01b815260040160405180910390fd5b602081146126d157604051638129bbcd60e01b815260040160405180910390fd5b60006126df828401846154d1565b6000818152600560205260409020549091506001600160a01b031661271757604051630fb532db60e11b815260040160405180910390fd5b600081815260066020526040812080546001600160601b03169186919061273e8385615d56565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600a60008282829054906101000a90046001600160601b03166127869190615d56565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f1ced9348ff549fceab2ac57cd3a9de38edaaab274b725ee82c23e8fc8c4eec7a8287846127d99190615d1c565b604080519283526020830191909152015b60405180910390a2505050505050565b6128026134d4565b6000818152600560205260409020546001600160a01b031661283757604051630fb532db60e11b815260040160405180910390fd5b600081815260056020526040902054610b509082906001600160a01b0316613529565b606060006128686008613ec9565b905080841061288a57604051631390f2a160e01b815260040160405180910390fd5b60006128968486615d1c565b9050818111806128a4575083155b6128ae57806128b0565b815b905060006128be8683615dab565b6001600160401b038111156128d5576128d5615ef4565b6040519080825280602002602001820160405280156128fe578160200160208202803683370190505b50905060005b81518110156129515761292261291a8883615d1c565b600890613ed3565b82828151811061293457612934615ede565b60209081029190910101528061294981615e46565b915050612904565b5095945050505050565b6129636134d4565b60c861ffff8716111561299d5760405163539c34bb60e11b815261ffff871660048201819052602482015260c86044820152606401610b47565b600082136129c1576040516321ea67b360e11b815260048101839052602401610b47565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600d805465ffffffffffff191688176201000087021768ffffffffffffffffff60301b1916600160381b850263ffffffff60581b191617600160581b83021790558a51601280548d8701519289166001600160401b031990911617600160201b92891692909202919091179081905560118d90558a519788528785019590955298860191909152840196909652938201879052838116928201929092529190921c90911660c08201527f777357bb93f63d088f18112d3dba38457aec633eb8f1341e1d418380ad328e789060e00160405180910390a1505050505050565b600d54600160301b900460ff1615612b095760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b0316612b3e57604051630fb532db60e11b815260040160405180910390fd5b6000818152600560205260409020600101546001600160a01b03163314612b95576000818152600560205260409081902060010154905163d084e97560e01b8152610b47916001600160a01b03169060040161594f565b6000818152600560205260409081902080546001600160a01b031980821633908117845560019093018054909116905591516001600160a01b039092169183917fd4114ab6e9af9f597c52041f32d62dc57c5c4e4c0d4427006069635e216c93869161206e91859161597c565b60008281526005602052604090205482906001600160a01b031680612c3a57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614612c655780604051636c51fda960e11b8152600401610b47919061594f565b600d54600160301b900460ff1615612c905760405163769dd35360e11b815260040160405180910390fd5b60008481526005602052604090206002015460641415612cc3576040516305a48e0f60e01b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b031615612cfa57610da6565b6001600160a01b0383166000818152600460209081526040808320888452825280832080546001600160401b031916600190811790915560058352818420600201805491820181558452919092200180546001600160a01b0319169092179091555184907f1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e190612d8b90869061594f565b60405180910390a250505050565b600081604051602001612dac91906159b8565b604051602081830303815290604052805190602001209050919050565b60008281526005602052604090205482906001600160a01b031680612e0157604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614612e2c5780604051636c51fda960e11b8152600401610b47919061594f565b600d54600160301b900460ff1615612e575760405163769dd35360e11b815260040160405180910390fd5b612e60846117f8565b15612e7e57604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b0316612ecc5783836040516379bfd40160e01b8152600401610b47929190615ac0565b600084815260056020908152604080832060020180548251818502810185019093528083529192909190830182828015612f2f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612f11575b50505050509050600060018251612f469190615dab565b905060005b825181101561305257856001600160a01b0316838281518110612f7057612f70615ede565b60200260200101516001600160a01b03161415613040576000838381518110612f9b57612f9b615ede565b6020026020010151905080600560008a81526020019081526020016000206002018381548110612fcd57612fcd615ede565b600091825260208083209190910180546001600160a01b0319166001600160a01b03949094169390931790925589815260059091526040902060020180548061301857613018615ec8565b600082815260209020810160001990810180546001600160a01b031916905501905550613052565b8061304a81615e46565b915050612f4b565b506001600160a01b03851660009081526004602090815260408083208984529091529081902080546001600160401b03191690555186907f32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a7906127ea90889061594f565b600f81815481106130c657600080fd5b600091825260209091200154905081565b60008281526005602052604090205482906001600160a01b03168061310f57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461313a5780604051636c51fda960e11b8152600401610b47919061594f565b600d54600160301b900460ff16156131655760405163769dd35360e11b815260040160405180910390fd5b6000848152600560205260409020600101546001600160a01b03848116911614610da6576000848152600560205260409081902060010180546001600160a01b0319166001600160a01b0386161790555184907f21a4dad170a6bf476c31bbcf4a16628295b0e450672eec25d7c93308e05344a190612d8b903390879061597c565b6000818152600560205260408120548190819081906060906001600160a01b031661322557604051630fb532db60e11b815260040160405180910390fd5b60008681526006602090815260408083205460058352928190208054600290910180548351818602810186019094528084526001600160601b0380871696600160601b810490911695600160c01b9091046001600160401b0316946001600160a01b03909416939183918301828280156132c857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116132aa575b505050505090509450945094509450945091939590929450565b6132ea6134d4565b6002546001600160a01b03166133135760405163c1f0c0a160e01b815260040160405180910390fd5b6002546040516370a0823160e01b81526000916001600160a01b0316906370a082319061334490309060040161594f565b60206040518083038186803b15801561335c57600080fd5b505afa158015613370573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061339491906154ea565b600a549091506001600160601b0316818111156133ce576040516354ced18160e11b81526004810182905260248101839052604401610b47565b81811015610f245760006133e28284615dab565b60025460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb906134159087908590600401615963565b602060405180830381600087803b15801561342f57600080fd5b505af1158015613443573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061346791906154b4565b61348457604051631f01ff1360e21b815260040160405180910390fd5b7f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b43660084826040516134b5929190615963565b60405180910390a150505050565b6134cb6134d4565b610b5081613edf565b6000546001600160a01b031633146135275760405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606401610b47565b565b60008061353584613a78565b60025491935091506001600160a01b03161580159061355c57506001600160601b03821615155b1561360b5760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb9061359c9086906001600160601b03871690600401615963565b602060405180830381600087803b1580156135b657600080fd5b505af11580156135ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ee91906154b4565b61360b57604051631e9acf1760e31b815260040160405180910390fd5b6000836001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114613661576040519150601f19603f3d011682016040523d82523d6000602084013e613666565b606091505b50509050806136885760405163950b247960e01b815260040160405180910390fd5b604080516001600160a01b03861681526001600160601b038581166020830152841681830152905186917f8c74ce8b8cf87f5eb001275c8be27eb34ea2b62bfab6814fcc62192bb63e81c4919081900360600190a25050505050565b604080516060810182526000808252602082018190529181019190915260006137108460000151612d99565b6000818152600e60205260409020549091506001600160a01b03168061374c57604051631dfd6e1360e21b815260048101839052602401610b47565b600082866080015160405160200161376e929190918252602082015260400190565b60408051601f19818403018152918152815160209283012060008181526010909352912054909150806137b457604051631b44092560e11b815260040160405180910390fd5b85516020808801516040808a015160608b015160808c015160a08d015193516137e3978a979096959101615b8f565b6040516020818303038152906040528051906020012081146138185760405163354a450b60e21b815260040160405180910390fd5b60006138278760000151613f83565b9050806138ff578651604051631d2827a760e31b81526001600160401b0390911660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e9413d389060240160206040518083038186803b15801561389957600080fd5b505afa1580156138ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138d191906154ea565b9050806138ff57865160405163175dadad60e01b81526001600160401b039091166004820152602401610b47565b6000886080015182604051602001613921929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c905060006139488a83614065565b604080516060810182529889526020890196909652948701949094525093979650505050505050565b60005a61138881101561398357600080fd5b61138881039050846040820482031161399b57600080fd5b50823b6139a757600080fd5b60008083516020850160008789f190505b9392505050565b600081156139ec576012546139e59086908690600160201b900463ffffffff16866140d0565b9050613a06565b601254613a03908690869063ffffffff1686614172565b90505b949350505050565b6000805b601354811015613a6f57826001600160a01b031660138281548110613a3957613a39615ede565b6000918252602090912001546001600160a01b03161415613a5d5750600192915050565b80613a6781615e46565b915050613a12565b50600092915050565b6000818152600560209081526040808320815160608101835281546001600160a01b03908116825260018301541681850152600282018054845181870281018701865281815287968796949594860193919290830182828015613b0457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613ae6575b505050919092525050506000858152600660209081526040808320815160608101835290546001600160601b03808216808452600160601b8304909116948301859052600160c01b9091046001600160401b0316928201929092529096509094509192505b826040015151811015613be0576004600084604001518381518110613b9057613b90615ede565b6020908102919091018101516001600160a01b031682528181019290925260409081016000908120898252909252902080546001600160401b031916905580613bd881615e46565b915050613b69565b50600085815260056020526040812080546001600160a01b03199081168255600182018054909116905590613c1860028301826150b7565b5050600085815260066020526040812055613c34600886614298565b50600a8054859190600090613c539084906001600160601b0316615dc2565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555082600a600c8282829054906101000a90046001600160601b0316613c9b9190615dc2565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505050915091565b6040805160208082018790526001600160a01b03959095168183015260608101939093526001600160401b03919091166080808401919091528151808403909101815260a08301825280519084012060c083019490945260e0808301859052815180840390910181526101009092019052805191012091565b60408051602081019091526000815281613d685750604080516020810190915260008152611407565b63125fa26760e31b613d7a8385615dea565b6001600160e01b03191614613da257604051632923fee760e11b815260040160405180910390fd5b613daf8260048186615cf2565b8101906139b89190615503565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401613df591511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b600046613e39816142a4565b15613eb65760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e7857600080fd5b505afa158015613e8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613eb091906154ea565b91505090565b4391505090565b60006139b883836142c7565b6000611407825490565b60006139b88383614316565b6001600160a01b038116331415613f325760405162461bcd60e51b815260206004820152601760248201527621b0b73737ba103a3930b739b332b9103a379039b2b63360491b6044820152606401610b47565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600046613f8f816142a4565b1561405657610100836001600160401b0316613fa9613e2d565b613fb39190615dab565b1180613fcf5750613fc2613e2d565b836001600160401b031610155b15613fdd5750600092915050565b6040516315a03d4160e11b81526001600160401b0384166004820152606490632b407a82906024015b60206040518083038186803b15801561401e57600080fd5b505afa158015614032573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139b891906154ea565b50506001600160401b03164090565b60006140998360000151846020015185604001518660600151868860a001518960c001518a60e001518b6101000151614340565b600383602001516040516020016140b1929190615ad7565b60408051601f1981840301815291905280516020909101209392505050565b6000806141136000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061455c92505050565b905060005a6141228888615d1c565b61412c9190615dab565b6141369085615d8c565b9050600061414f63ffffffff871664e8d4a51000615d8c565b90508261415c8284615d1c565b6141669190615d1c565b98975050505050505050565b60008061417d614621565b9050600081136141a3576040516321ea67b360e11b815260048101829052602401610b47565b60006141e56000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061455c92505050565b9050600082825a6141f68b8b615d1c565b6142009190615dab565b61420a9088615d8c565b6142149190615d1c565b61422690670de0b6b3a7640000615d8c565b6142309190615d78565b9050600061424963ffffffff881664e8d4a51000615d8c565b9050614261816b033b2e3c9fd0803ce8000000615dab565b8211156142815760405163e80fa38160e01b815260040160405180910390fd5b61428b8183615d1c565b9998505050505050505050565b60006139b883836146ec565b600061a4b18214806142b8575062066eed82145b8061140757505062066eee1490565b600081815260018301602052604081205461430e57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611407565b506000611407565b600082600001828154811061432d5761432d615ede565b9060005260206000200154905092915050565b614349896147df565b6143925760405162461bcd60e51b815260206004820152601a6024820152797075626c6963206b6579206973206e6f74206f6e20637572766560301b6044820152606401610b47565b61439b886147df565b6143df5760405162461bcd60e51b815260206004820152601560248201527467616d6d61206973206e6f74206f6e20637572766560581b6044820152606401610b47565b6143e8836147df565b6144345760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610b47565b61443d826147df565b6144895760405162461bcd60e51b815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610b47565b614495878a88876148a2565b6144dd5760405162461bcd60e51b81526020600482015260196024820152786164647228632a706b2b732a6729213d5f755769746e65737360381b6044820152606401610b47565b60006144e98a876149c5565b905060006144fc898b878b868989614a29565b9050600061450d838d8d8a86614b3c565b9050808a1461454e5760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b6044820152606401610b47565b505050505050505050505050565b600046614568816142a4565b156145a757606c6001600160a01b031663c6f7de0e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561401e57600080fd5b6145b081614b7c565b15613a6f57600f602160991b016001600160a01b03166349948e0e84604051806080016040528060488152602001615f2e604891396040516020016145f69291906158a5565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161400691906159d9565b600d5460035460408051633fabe5a360e21b81529051600093600160381b900463ffffffff169283151592859283926001600160a01b03169163feaf968c9160048083019260a0929190829003018186803b15801561467f57600080fd5b505afa158015614693573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146b79190615780565b5094509092508491505080156146db57506146d28242615dab565b8463ffffffff16105b15613a065750601154949350505050565b600081815260018301602052604081205480156147d5576000614710600183615dab565b855490915060009061472490600190615dab565b905081811461478957600086600001828154811061474457614744615ede565b906000526020600020015490508087600001848154811061476757614767615ede565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061479a5761479a615ec8565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611407565b6000915050611407565b80516000906401000003d0191161482d5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420782d6f7264696e61746560701b6044820152606401610b47565b60208201516401000003d0191161487b5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420792d6f7264696e61746560701b6044820152606401610b47565b60208201516401000003d01990800961489b8360005b6020020151614bb6565b1492915050565b60006001600160a01b0382166148e85760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b6044820152606401610b47565b6020840151600090600116156148ff57601c614902565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe19918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa15801561499d573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b6149cd6150d5565b6149fa600184846040516020016149e69392919061592e565b604051602081830303815290604052614bda565b90505b614a06816147df565b611407578051604080516020810192909252614a2291016149e6565b90506149fd565b614a316150d5565b825186516401000003d0199081900691061415614a905760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610b47565b614a9b878988614c28565b614ae05760405162461bcd60e51b8152602060048201526016602482015275119a5c9cdd081b5d5b0818da1958dac819985a5b195960521b6044820152606401610b47565b614aeb848685614c28565b614b315760405162461bcd60e51b815260206004820152601760248201527614d958dbdb99081b5d5b0818da1958dac819985a5b1959604a1b6044820152606401610b47565b614166868484614d50565b600060028686868587604051602001614b5a969594939291906158d4565b60408051601f1981840301815291905280516020909101209695505050505050565b6000600a821480614b8e57506101a482145b80614b9b575062aa37dc82145b80614ba7575061210582145b8061140757505062014a331490565b6000806401000003d01980848509840990506401000003d019600782089392505050565b614be26150d5565b614beb82614e13565b8152614c00614bfb826000614891565b614e4e565b602082018190526002900660011415612407576020810180516401000003d019039052919050565b600082614c655760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b6044820152606401610b47565b83516020850151600090614c7b90600290615e88565b15614c8757601c614c8a565b601b5b9050600070014551231950b75fc4402da1732fc9bebe198387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614cfc573d6000803e3d6000fd5b505050602060405103519050600086604051602001614d1b9190615893565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b614d586150d5565b835160208086015185519186015160009384938493614d7993909190614e6e565b919450925090506401000003d019858209600114614dd55760405162461bcd60e51b815260206004820152601960248201527834b73b2d1036bab9ba1031329034b73b32b939b29037b3103d60391b6044820152606401610b47565b60405180604001604052806401000003d01980614df457614df4615eb2565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d019811061240757604080516020808201939093528151808203840181529082019091528051910120614e1b565b6000611407826002614e676401000003d0196001615d1c565b901c614f4e565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a0890506000614eae83838585614fe5565b9098509050614ebf88828e88615009565b9098509050614ed088828c87615009565b90985090506000614ee38d878b85615009565b9098509050614ef488828686614fe5565b9098509050614f0588828e89615009565b9098509050818114614f3a576401000003d019818a0998506401000003d01982890997506401000003d0198183099650614f3e565b8196505b5050505050509450945094915050565b600080614f596150f3565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152614f8b615111565b60208160c0846005600019fa925082614fdb5760405162461bcd60e51b81526020600482015260126024820152716269674d6f64457870206661696c7572652160701b6044820152606401610b47565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b8280548282559060005260206000209081019282156150a7579160200282015b828111156150a757825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615072565b506150b392915061512f565b5090565b5080546000825590600052602060002090810190610b50919061512f565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b808211156150b35760008155600101615130565b803561240781615f0a565b806040810183101561140757600080fd5b600082601f83011261517157600080fd5b615179615c85565b80838560408601111561518b57600080fd5b60005b60028110156151ad57813584526020938401939091019060010161518e565b509095945050505050565b600082601f8301126151c957600080fd5b81356001600160401b03808211156151e3576151e3615ef4565b604051601f8301601f19908116603f0116810190828211818310171561520b5761520b615ef4565b8160405283815286602085880101111561522457600080fd5b836020870160208301376000602085830101528094505050505092915050565b600060c0828403121561525657600080fd5b61525e615cad565b905081356001600160401b03808216821461527857600080fd5b81835260208401356020840152615291604085016152f7565b60408401526152a2606085016152f7565b60608401526152b360808501615144565b608084015260a08401359150808211156152cc57600080fd5b506152d9848285016151b8565b60a08301525092915050565b803561ffff8116811461240757600080fd5b803563ffffffff8116811461240757600080fd5b805169ffffffffffffffffffff8116811461240757600080fd5b80356001600160601b038116811461240757600080fd5b60006020828403121561534e57600080fd5b81356139b881615f0a565b6000806040838503121561536c57600080fd5b823561537781615f0a565b915061538560208401615325565b90509250929050565b600080604083850312156153a157600080fd5b82356153ac81615f0a565b915060208301356153bc81615f0a565b809150509250929050565b600080606083850312156153da57600080fd5b82356153e581615f0a565b9150615385846020850161514f565b6000806000806060858703121561540a57600080fd5b843561541581615f0a565b93506020850135925060408501356001600160401b038082111561543857600080fd5b818701915087601f83011261544c57600080fd5b81358181111561545b57600080fd5b88602082850101111561546d57600080fd5b95989497505060200194505050565b60006040828403121561548e57600080fd5b6139b8838361514f565b6000604082840312156154aa57600080fd5b6139b88383615160565b6000602082840312156154c657600080fd5b81516139b881615f1f565b6000602082840312156154e357600080fd5b5035919050565b6000602082840312156154fc57600080fd5b5051919050565b60006020828403121561551557600080fd5b604051602081018181106001600160401b038211171561553757615537615ef4565b604052823561554581615f1f565b81529392505050565b6000808284036101c081121561556357600080fd5b6101a08082121561557357600080fd5b61557b615ccf565b91506155878686615160565b82526155968660408701615160565b60208301526080850135604083015260a0850135606083015260c085013560808301526155c560e08601615144565b60a08301526101006155d987828801615160565b60c08401526155ec876101408801615160565b60e0840152610180860135908301529092508301356001600160401b0381111561561557600080fd5b61562185828601615244565b9150509250929050565b60006020828403121561563d57600080fd5b81356001600160401b0381111561565357600080fd5b820160c081850312156139b857600080fd5b60006020828403121561567757600080fd5b6139b8826152e5565b60008060008060008086880360e081121561569a57600080fd5b6156a3886152e5565b96506156b1602089016152f7565b95506156bf604089016152f7565b94506156cd606089016152f7565b9350608088013592506040609f19820112156156e857600080fd5b506156f1615c85565b6156fd60a089016152f7565b815261570b60c089016152f7565b6020820152809150509295509295509295565b6000806040838503121561573157600080fd5b8235915060208301356153bc81615f0a565b6000806040838503121561575657600080fd5b50508035926020909101359150565b60006020828403121561577757600080fd5b6139b8826152f7565b600080600080600060a0868803121561579857600080fd5b6157a18661530b565b94506020860151935060408601519250606086015191506157c46080870161530b565b90509295509295909350565b600081518084526020808501945080840160005b838110156158095781516001600160a01b0316875295820195908201906001016157e4565b509495945050505050565b8060005b6002811015610da6578151845260209384019390910190600101615818565b600081518084526020808501945080840160005b838110156158095781518752958201959082019060010161584b565b6000815180845261587f816020860160208601615e1a565b601f01601f19169290920160200192915050565b61589d8183615814565b604001919050565b600083516158b7818460208801615e1a565b8351908301906158cb818360208801615e1a565b01949350505050565b8681526158e46020820187615814565b6158f16060820186615814565b6158fe60a0820185615814565b61590b60e0820184615814565b60609190911b6001600160601b0319166101208201526101340195945050505050565b83815261593e6020820184615814565b606081019190915260800192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682526001600160601b0316602082015260400190565b604081016114078284615814565b6020815260006139b86020830184615837565b6020815260006139b86020830184615867565b6020815260ff82511660208201526020820151604082015260018060a01b0360408301511660608201526000606083015160c06080840152615a3160e08401826157d0565b60808501516001600160601b0390811660a0868101919091529095015190941660c0909301929092525090919050565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b81811015615ab257845183529383019391830191600101615a96565b509098975050505050505050565b9182526001600160a01b0316602082015260400190565b828152606081016139b86020830184615814565b828152604060208201526000613a066040830184615837565b86815285602082015261ffff85166040820152600063ffffffff808616606084015280851660808401525060c060a083015261416660c0830184615867565b878152602081018790526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c0820181905260009061428b90830184615867565b8781526001600160401b03871660208201526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c0820181905260009061428b90830184615867565b6001600160601b038681168252851660208201526001600160401b03841660408201526001600160a01b038316606082015260a060808201819052600090615c2d908301846157d0565b979650505050505050565b6000808335601e19843603018112615c4f57600080fd5b8301803591506001600160401b03821115615c6957600080fd5b602001915036819003821315615c7e57600080fd5b9250929050565b604080519081016001600160401b0381118282101715615ca757615ca7615ef4565b60405290565b60405160c081016001600160401b0381118282101715615ca757615ca7615ef4565b60405161012081016001600160401b0381118282101715615ca757615ca7615ef4565b60008085851115615d0257600080fd5b83861115615d0f57600080fd5b5050820193919092039150565b60008219821115615d2f57615d2f615e9c565b500190565b60006001600160401b038083168185168083038211156158cb576158cb615e9c565b60006001600160601b038281168482168083038211156158cb576158cb615e9c565b600082615d8757615d87615eb2565b500490565b6000816000190483118215151615615da657615da6615e9c565b500290565b600082821015615dbd57615dbd615e9c565b500390565b60006001600160601b0383811690831681811015615de257615de2615e9c565b039392505050565b6001600160e01b03198135818116916004851015615e125780818660040360031b1b83161692505b505092915050565b60005b83811015615e35578181015183820152602001615e1d565b83811115610da65750506000910152565b6000600019821415615e5a57615e5a615e9c565b5060010190565b60006001600160401b0380831681811415615e7e57615e7e615e9c565b6001019392505050565b600082615e9757615e97615eb2565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610b5057600080fd5b8015158114610b5057600080fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000806000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"internalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"externalBalance\",\"type\":\"uint256\"}],\"name\":\"BalanceInvariantViolated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"}],\"name\":\"BlockhashNotInStore\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorNotRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToSendNative\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToTransferLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"have\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"want\",\"type\":\"uint256\"}],\"name\":\"InsufficientGasForConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"have\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"min\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"max\",\"type\":\"uint16\"}],\"name\":\"InvalidRequestConfirmations\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkAlreadySet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeRequestedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCorrespondingRequest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"NoSuchProvingKey\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"NumWordsTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingRequestExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Reentrant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structVRFCoordinatorV2_5.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"MigrationCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"NativeFundsRecovered\",\"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\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountLink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountNative\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldNativeBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newNativeBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFundedWithNative\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_NATIVE_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"deregisterMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"deregisterProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFCoordinatorV2_5.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"fundSubscriptionWithNative\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveSubscriptionIds\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRequestConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"}],\"name\":\"migrate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"migrationVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverNativeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"registerMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFV2PlusClient.RandomWordsRequest\",\"name\":\"req\",\"type\":\"tuple\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_config\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"reentrancyLock\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_currentSubNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fallbackWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_feeConfig\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_provingKeyHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"s_provingKeys\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requestCommitments\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalNativeBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"internalType\":\"structVRFCoordinatorV2_5.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"}],\"name\":\"setLINKAndLINKNativeFeed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"withdrawNative\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b506040516200606038038062006060833981016040819052620000349162000183565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d7565b50505060601b6001600160601b031916608052620001b5565b6001600160a01b038116331415620001325760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156200019657600080fd5b81516001600160a01b0381168114620001ae57600080fd5b9392505050565b60805160601c615e85620001db6000396000818161056601526137d10152615e856000f3fe60806040526004361061023c5760003560e01c80637bce14d11161012f578063b08c8795116100b1578063b08c87951461076d578063b2a7cac51461078d578063bec4c08c146107ad578063caf70c4a146107cd578063cb631797146107ed578063d98e620e1461080d578063da2f26101461082d578063dac83d291461085d578063dc311dd31461087d578063e72f6e30146108ae578063ee9d2d38146108ce578063f2fde38b146108fb57600080fd5b80637bce14d1146106025780638402595e1461062257806386fe91c7146106425780638da5cb5b1461066257806395b55cfc146106805780639b1c385e146106935780639d40a6fd146106b3578063a21a23e4146106eb578063a4c0ed3614610700578063aa433aff14610720578063aefb212f1461074057600080fd5b8063330987b3116101c3578063330987b314610444578063405b84fa1461046457806340d6bb821461048457806341af6c87146104af57806351cff8d9146104df5780635d06b4ab146104ff57806364d51a2a1461051f5780636598274414610534578063689c4517146105545780636b6feccc1461058857806372e9d565146105cd57806379ba5097146105ed57600080fd5b80620122911461024157806304104edb1461026e578063043bd6ae14610290578063088070f5146102b457806308821d58146103345780630ae095401461035457806315c48b841461037457806318e3dd271461039c5780631b6b6d23146103db578063294daa49146104085780632f622e6b14610424575b600080fd5b34801561024d57600080fd5b5061025661091b565b60405161026593929190615964565b60405180910390f35b34801561027a57600080fd5b5061028e610289366004615295565b610997565b005b34801561029c57600080fd5b506102a660105481565b604051908152602001610265565b3480156102c057600080fd5b50600c546102fc9061ffff81169063ffffffff62010000820481169160ff600160301b82041691600160381b8204811691600160581b90041685565b6040805161ffff909616865263ffffffff9485166020870152921515928501929092528216606084015216608082015260a001610265565b34801561034057600080fd5b5061028e61034f366004615373565b610b4d565b34801561036057600080fd5b5061028e61036f366004615621565b610cc4565b34801561038057600080fd5b5061038960c881565b60405161ffff9091168152602001610265565b3480156103a857600080fd5b50600a546103c390600160601b90046001600160601b031681565b6040516001600160601b039091168152602001610265565b3480156103e757600080fd5b506002546103fb906001600160a01b031681565b6040516102659190615852565b34801561041457600080fd5b5060405160018152602001610265565b34801561043057600080fd5b5061028e61043f366004615295565b610d89565b34801561045057600080fd5b506103c361045f366004615451565b610efd565b34801561047057600080fd5b5061028e61047f366004615621565b6113bf565b34801561049057600080fd5b5061049a6101f481565b60405163ffffffff9091168152602001610265565b3480156104bb57600080fd5b506104cf6104ca3660046153d4565b6117aa565b6040519015158152602001610265565b3480156104eb57600080fd5b5061028e6104fa366004615295565b61194b565b34801561050b57600080fd5b5061028e61051a366004615295565b611afc565b34801561052b57600080fd5b50610389606481565b34801561054057600080fd5b5061028e61054f3660046152b2565b611bb3565b34801561056057600080fd5b506103fb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561059457600080fd5b506011546105b09063ffffffff80821691600160201b90041682565b6040805163ffffffff938416815292909116602083015201610265565b3480156105d957600080fd5b506003546103fb906001600160a01b031681565b3480156105f957600080fd5b5061028e611c13565b34801561060e57600080fd5b5061028e61061d366004615373565b611cbd565b34801561062e57600080fd5b5061028e61063d366004615295565b611daa565b34801561064e57600080fd5b50600a546103c3906001600160601b031681565b34801561066e57600080fd5b506000546001600160a01b03166103fb565b61028e61068e3660046153d4565b611ebc565b34801561069f57600080fd5b506102a66106ae36600461552e565b612003565b3480156106bf57600080fd5b506007546106d3906001600160401b031681565b6040516001600160401b039091168152602001610265565b3480156106f757600080fd5b506102a6612395565b34801561070c57600080fd5b5061028e61071b3660046152eb565b6125e3565b34801561072c57600080fd5b5061028e61073b3660046153d4565b612783565b34801561074c57600080fd5b5061076061075b366004615646565b6127e3565b60405161026591906158c9565b34801561077957600080fd5b5061028e610788366004615583565b6128e4565b34801561079957600080fd5b5061028e6107a83660046153d4565b612a67565b3480156107b957600080fd5b5061028e6107c8366004615621565b612b8b565b3480156107d957600080fd5b506102a66107e836600461539b565b612d22565b3480156107f957600080fd5b5061028e610808366004615621565b612d52565b34801561081957600080fd5b506102a66108283660046153d4565b61303f565b34801561083957600080fd5b506104cf6108483660046153d4565b600d6020526000908152604090205460ff1681565b34801561086957600080fd5b5061028e610878366004615621565b613060565b34801561088957600080fd5b5061089d6108983660046153d4565b613170565b604051610265959493929190615ae6565b3480156108ba57600080fd5b5061028e6108c9366004615295565b61326b565b3480156108da57600080fd5b506102a66108e93660046153d4565b600f6020526000908152604090205481565b34801561090757600080fd5b5061028e610916366004615295565b61344c565b600c54600e805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff1693919283919083018282801561098557602002820191906000526020600020905b815481526020019060010190808311610971575b50505050509050925092509250909192565b61099f61345d565b60125460005b81811015610b2557826001600160a01b0316601282815481106109ca576109ca615de1565b6000918252602090912001546001600160a01b03161415610b135760126109f2600184615cae565b81548110610a0257610a02615de1565b600091825260209091200154601280546001600160a01b039092169183908110610a2e57610a2e615de1565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055826012610a65600185615cae565b81548110610a7557610a75615de1565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506012805480610ab457610ab4615dcb565b600082815260209020810160001990810180546001600160a01b03191690550190556040517ff80a1a97fd42251f3c33cda98635e7399253033a6774fe37cd3f650b5282af3790610b06908590615852565b60405180910390a1505050565b80610b1d81615d49565b9150506109a5565b5081604051635428d44960e01b8152600401610b419190615852565b60405180910390fd5b50565b610b5561345d565b604080518082018252600091610b84919084906002908390839080828437600092019190915250612d22915050565b6000818152600d602052604090205490915060ff16610bb957604051631dfd6e1360e21b815260048101829052602401610b41565b6000818152600d60205260408120805460ff191690555b600e54811015610c8b5781600e8281548110610bee57610bee615de1565b90600052602060002001541415610c7957600e805460009190610c1390600190615cae565b81548110610c2357610c23615de1565b9060005260206000200154905080600e8381548110610c4457610c44615de1565b600091825260209091200155600e805480610c6157610c61615dcb565b60019003818190600052602060002001600090559055505b80610c8381615d49565b915050610bd0565b506040518181527fbd242ec01625c15ecbc02cf700ac8b02c86f7346fa91a08e186810221ae509d0906020015b60405180910390a15050565b60008281526005602052604090205482906001600160a01b031680610cfc57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614610d275780604051636c51fda960e11b8152600401610b419190615852565b600c54600160301b900460ff1615610d525760405163769dd35360e11b815260040160405180910390fd5b610d5b846117aa565b15610d7957604051631685ecdd60e31b815260040160405180910390fd5b610d8384846134b2565b50505050565b600c54600160301b900460ff1615610db45760405163769dd35360e11b815260040160405180910390fd5b610dbc61345d565b600b54600160601b90046001600160601b0316610dec57604051631e9acf1760e31b815260040160405180910390fd5b600b8054600160601b90046001600160601b0316908190600c610e0f8380615cc5565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a600c8282829054906101000a90046001600160601b0316610e579190615cc5565b92506101000a8154816001600160601b0302191690836001600160601b031602179055506000826001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114610ed1576040519150601f19603f3d011682016040523d82523d6000602084013e610ed6565b606091505b5050905080610ef85760405163950b247960e01b815260040160405180910390fd5b505050565b600c54600090600160301b900460ff1615610f2b5760405163769dd35360e11b815260040160405180910390fd5b60005a90506000610f3c858561366d565b90506000846060015163ffffffff166001600160401b03811115610f6257610f62615df7565b604051908082528060200260200182016040528015610f8b578160200160208202803683370190505b50905060005b856060015163ffffffff1681101561100b57826040015181604051602001610fc3929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c828281518110610fee57610fee615de1565b60209081029190910101528061100381615d49565b915050610f91565b50602080830180516000908152600f9092526040808320839055905190518291631fe543e360e01b91611043919086906024016159ee565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600c805460ff60301b1916600160301b1790559088015160808901519192506000916110a89163ffffffff1690846138f2565b600c805460ff60301b19169055602089810151600090815260069091526040902054909150600160c01b90046001600160401b03166110e8816001615c37565b6020808b0151600090815260069091526040812080546001600160401b0393909316600160c01b026001600160c01b039093169290921790915560a08a0151805161113590600190615cae565b8151811061114557611145615de1565b602091010151600c5460f89190911c6001149150600090611176908a90600160581b900463ffffffff163a85613940565b9050811561126e576020808c01516000908152600690915260409020546001600160601b03808316600160601b9092041610156111c657604051631e9acf1760e31b815260040160405180910390fd5b60208b81015160009081526006909152604090208054829190600c906111fd908490600160601b90046001600160601b0316615cc5565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600b600c8282829054906101000a90046001600160601b03166112459190615c59565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550611349565b6020808c01516000908152600690915260409020546001600160601b03808316911610156112af57604051631e9acf1760e31b815260040160405180910390fd5b6020808c0151600090815260069091526040812080548392906112dc9084906001600160601b0316615cc5565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600b60008282829054906101000a90046001600160601b03166113249190615c59565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b8a6020015188602001517f49580fdfd9497e1ed5c1b1cec0495087ae8e3f1267470ec2fb015db32e3d6aa78a6040015184886040516113a6939291909283526001600160601b039190911660208301521515604082015260600190565b60405180910390a3985050505050505050505b92915050565b600c54600160301b900460ff16156113ea5760405163769dd35360e11b815260040160405180910390fd5b6113f38161398f565b6114125780604051635428d44960e01b8152600401610b419190615852565b60008060008061142186613170565b945094505093509350336001600160a01b0316826001600160a01b0316146114845760405162461bcd60e51b81526020600482015260166024820152752737ba1039bab139b1b934b83a34b7b71037bbb732b960511b6044820152606401610b41565b61148d866117aa565b156114d35760405162461bcd60e51b815260206004820152601660248201527550656e64696e6720726571756573742065786973747360501b6044820152606401610b41565b60006040518060c001604052806114e8600190565b60ff168152602001888152602001846001600160a01b03168152602001838152602001866001600160601b03168152602001856001600160601b0316815250905060008160405160200161153c91906158ef565b6040516020818303038152906040529050611556886139f9565b505060405163ce3f471960e01b81526001600160a01b0388169063ce3f4719906001600160601b0388169061158f9085906004016158dc565b6000604051808303818588803b1580156115a857600080fd5b505af11580156115bc573d6000803e3d6000fd5b50506002546001600160a01b0316158015935091506115e5905057506001600160601b03861615155b156116af5760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb9061161c908a908a90600401615899565b602060405180830381600087803b15801561163657600080fd5b505af115801561164a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166e91906153b7565b6116af5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742066756e647360701b6044820152606401610b41565b600c805460ff60301b1916600160301b17905560005b8351811015611758578381815181106116e0576116e0615de1565b60200260200101516001600160a01b0316638ea98117896040518263ffffffff1660e01b81526004016117139190615852565b600060405180830381600087803b15801561172d57600080fd5b505af1158015611741573d6000803e3d6000fd5b50505050808061175090615d49565b9150506116c5565b50600c805460ff60301b191690556040517fd63ca8cb945956747ee69bfdc3ea754c24a4caf7418db70e46052f7850be4187906117989089908b90615866565b60405180910390a15050505050505050565b6000818152600560209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561183457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611816575b505050505081525050905060005b8160400151518110156119415760005b600e5481101561192e5760006118f7600e838154811061187457611874615de1565b90600052602060002001548560400151858151811061189557611895615de1565b60200260200101518860046000896040015189815181106118b8576118b8615de1565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081208d82529092529020546001600160401b0316613c47565b506000818152600f60205260409020549091501561191b5750600195945050505050565b508061192681615d49565b915050611852565b508061193981615d49565b915050611842565b5060009392505050565b600c54600160301b900460ff16156119765760405163769dd35360e11b815260040160405180910390fd5b61197e61345d565b6002546001600160a01b03166119a75760405163c1f0c0a160e01b815260040160405180910390fd5b600b546001600160601b03166119d057604051631e9acf1760e31b815260040160405180910390fd5b600b80546001600160601b031690819060006119ec8380615cc5565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a60008282829054906101000a90046001600160601b0316611a349190615cc5565b82546001600160601b039182166101009390930a92830291909202199091161790555060025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90611a899085908590600401615899565b602060405180830381600087803b158015611aa357600080fd5b505af1158015611ab7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611adb91906153b7565b611af857604051631e9acf1760e31b815260040160405180910390fd5b5050565b611b0461345d565b611b0d8161398f565b15611b2d578060405163ac8a27ef60e01b8152600401610b419190615852565b601280546001810182556000919091527fbb8a6a4669ba250d26cd7a459eca9d215f8307e33aebe50379bc5a3617ec34440180546001600160a01b0319166001600160a01b0383161790556040517fb7cabbfc11e66731fc77de0444614282023bcbd41d16781c753a431d0af0162590611ba8908390615852565b60405180910390a150565b611bbb61345d565b6002546001600160a01b031615611be557604051631688c53760e11b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b6001546001600160a01b03163314611c665760405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606401610b41565b60008054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611cc561345d565b604080518082018252600091611cf4919084906002908390839080828437600092019190915250612d22915050565b6000818152600d602052604090205490915060ff1615611d2a57604051634a0b8fa760e01b815260048101829052602401610b41565b6000818152600d6020526040808220805460ff19166001908117909155600e805491820181559092527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909101829055517fc9583fd3afa3d7f16eb0b88d0268e7d05c09bafa4b21e092cbd1320e1bc8089d90610cb89083815260200190565b611db261345d565b600a544790600160601b90046001600160601b031681811115611df2576040516354ced18160e11b81526004810182905260248101839052604401610b41565b81811015610ef8576000611e068284615cae565b90506000846001600160a01b03168260405160006040518083038185875af1925050503d8060008114611e55576040519150601f19603f3d011682016040523d82523d6000602084013e611e5a565b606091505b5050905080611e7c5760405163950b247960e01b815260040160405180910390fd5b7f4aed7c8eed0496c8c19ea2681fcca25741c1602342e38b045d9f1e8e905d2e9c8583604051611ead929190615866565b60405180910390a15050505050565b600c54600160301b900460ff1615611ee75760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b0316611f1c57604051630fb532db60e11b815260040160405180910390fd5b60008181526006602052604090208054600160601b90046001600160601b0316903490600c611f4b8385615c59565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555034600a600c8282829054906101000a90046001600160601b0316611f939190615c59565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f7603b205d03651ee812f803fccde89f1012e545a9c99f0abfea9cedd0fd8e902823484611fe69190615c1f565b604080519283526020830191909152015b60405180910390a25050565b600c54600090600160301b900460ff16156120315760405163769dd35360e11b815260040160405180910390fd5b6020808301356000908152600590915260409020546001600160a01b031661206c57604051630fb532db60e11b815260040160405180910390fd5b3360009081526004602090815260408083208583013584529091529020546001600160401b0316806120b9578260200135336040516379bfd40160e01b8152600401610b419291906159c3565b600c5461ffff166120d06060850160408601615568565b61ffff1610806120f3575060c86120ed6060850160408601615568565b61ffff16115b15612139576121086060840160408501615568565b600c5460405163539c34bb60e11b815261ffff92831660048201529116602482015260c86044820152606401610b41565b600c5462010000900463ffffffff166121586080850160608601615668565b63ffffffff1611156121a8576121746080840160608501615668565b600c54604051637aebf00f60e11b815263ffffffff9283166004820152620100009091049091166024820152604401610b41565b6101f46121bb60a0850160808601615668565b63ffffffff161115612201576121d760a0840160808501615668565b6040516311ce1afb60e21b815263ffffffff90911660048201526101f46024820152604401610b41565b600061220e826001615c37565b9050600080612224863533602089013586613c47565b9092509050600061224061223b60a0890189615b3b565b613cc0565b9050600061224d82613d3d565b905083612258613dae565b60208a013561226d60808c0160608d01615668565b61227d60a08d0160808e01615668565b33866040516020016122959796959493929190615a46565b60405160208183030381529060405280519060200120600f600086815260200190815260200160002081905550336001600160a01b0316886020013589600001357feb0e3652e0f44f417695e6e90f2f42c99b65cd7169074c5a654b16b9748c3a4e87878d604001602081019061230c9190615568565b8e606001602081019061231f9190615668565b8f60800160208101906123329190615668565b8960405161234596959493929190615a07565b60405180910390a45050336000908152600460209081526040808320898301358452909152902080546001600160401b0319166001600160401b039490941693909317909255925050505b919050565b600c54600090600160301b900460ff16156123c35760405163769dd35360e11b815260040160405180910390fd5b6000336123d1600143615cae565b600754604051606093841b6001600160601b03199081166020830152924060348201523090931b909116605483015260c01b6001600160c01b031916606882015260700160408051601f198184030181529190528051602090910120600780549192506001600160401b0390911690600061244b83615d64565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550506000806001600160401b0381111561248a5761248a615df7565b6040519080825280602002602001820160405280156124b3578160200160208202803683370190505b506040805160608082018352600080835260208084018281528486018381528984526006835286842095518654925191516001600160601b039182166001600160c01b031990941693909317600160601b9190921602176001600160c01b0316600160c01b6001600160401b039092169190910217909355835191820184523382528183018181528285018681528883526005855294909120825181546001600160a01b03199081166001600160a01b0392831617835592516001830180549094169116179091559251805194955090936125949260028501920190614fd3565b506125a491506008905083613e3e565b50817f1d3015d7ba850fa198dc7b1a3f5d42779313a681035f77c8c03764c61005518d336040516125d59190615852565b60405180910390a250905090565b600c54600160301b900460ff161561260e5760405163769dd35360e11b815260040160405180910390fd5b6002546001600160a01b03163314612639576040516344b0e3c360e01b815260040160405180910390fd5b6020811461265a57604051638129bbcd60e01b815260040160405180910390fd5b6000612668828401846153d4565b6000818152600560205260409020549091506001600160a01b03166126a057604051630fb532db60e11b815260040160405180910390fd5b600081815260066020526040812080546001600160601b0316918691906126c78385615c59565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600a60008282829054906101000a90046001600160601b031661270f9190615c59565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f1ced9348ff549fceab2ac57cd3a9de38edaaab274b725ee82c23e8fc8c4eec7a8287846127629190615c1f565b604080519283526020830191909152015b60405180910390a2505050505050565b61278b61345d565b6000818152600560205260409020546001600160a01b03166127c057604051630fb532db60e11b815260040160405180910390fd5b600081815260056020526040902054610b4a9082906001600160a01b03166134b2565b606060006127f16008613e4a565b905080841061281357604051631390f2a160e01b815260040160405180910390fd5b600061281f8486615c1f565b90508181118061282d575083155b6128375780612839565b815b905060006128478683615cae565b6001600160401b0381111561285e5761285e615df7565b604051908082528060200260200182016040528015612887578160200160208202803683370190505b50905060005b81518110156128da576128ab6128a38883615c1f565b600890613e54565b8282815181106128bd576128bd615de1565b6020908102919091010152806128d281615d49565b91505061288d565b5095945050505050565b6128ec61345d565b60c861ffff871611156129265760405163539c34bb60e11b815261ffff871660048201819052602482015260c86044820152606401610b41565b6000821361294a576040516321ea67b360e11b815260048101839052602401610b41565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600c805465ffffffffffff191688176201000087021768ffffffffffffffffff60301b1916600160381b850263ffffffff60581b191617600160581b83021790558a51601180548d8701519289166001600160401b031990911617600160201b92891692909202919091179081905560108d90558a519788528785019590955298860191909152840196909652938201879052838116928201929092529190921c90911660c08201527f777357bb93f63d088f18112d3dba38457aec633eb8f1341e1d418380ad328e789060e00160405180910390a1505050505050565b600c54600160301b900460ff1615612a925760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b0316612ac757604051630fb532db60e11b815260040160405180910390fd5b6000818152600560205260409020600101546001600160a01b03163314612b1e576000818152600560205260409081902060010154905163d084e97560e01b8152610b41916001600160a01b031690600401615852565b6000818152600560205260409081902080546001600160a01b031980821633908117845560019093018054909116905591516001600160a01b039092169183917fd4114ab6e9af9f597c52041f32d62dc57c5c4e4c0d4427006069635e216c938691611ff791859161587f565b60008281526005602052604090205482906001600160a01b031680612bc357604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614612bee5780604051636c51fda960e11b8152600401610b419190615852565b600c54600160301b900460ff1615612c195760405163769dd35360e11b815260040160405180910390fd5b60008481526005602052604090206002015460641415612c4c576040516305a48e0f60e01b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b031615612c8357610d83565b6001600160a01b0383166000818152600460209081526040808320888452825280832080546001600160401b031916600190811790915560058352818420600201805491820181558452919092200180546001600160a01b0319169092179091555184907f1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e190612d14908690615852565b60405180910390a250505050565b600081604051602001612d3591906158bb565b604051602081830303815290604052805190602001209050919050565b60008281526005602052604090205482906001600160a01b031680612d8a57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614612db55780604051636c51fda960e11b8152600401610b419190615852565b600c54600160301b900460ff1615612de05760405163769dd35360e11b815260040160405180910390fd5b612de9846117aa565b15612e0757604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b0316612e555783836040516379bfd40160e01b8152600401610b419291906159c3565b600084815260056020908152604080832060020180548251818502810185019093528083529192909190830182828015612eb857602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612e9a575b50505050509050600060018251612ecf9190615cae565b905060005b8251811015612fdb57856001600160a01b0316838281518110612ef957612ef9615de1565b60200260200101516001600160a01b03161415612fc9576000838381518110612f2457612f24615de1565b6020026020010151905080600560008a81526020019081526020016000206002018381548110612f5657612f56615de1565b600091825260208083209190910180546001600160a01b0319166001600160a01b039490941693909317909255898152600590915260409020600201805480612fa157612fa1615dcb565b600082815260209020810160001990810180546001600160a01b031916905501905550612fdb565b80612fd381615d49565b915050612ed4565b506001600160a01b03851660009081526004602090815260408083208984529091529081902080546001600160401b03191690555186907f32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a790612773908890615852565b600e818154811061304f57600080fd5b600091825260209091200154905081565b60008281526005602052604090205482906001600160a01b03168061309857604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146130c35780604051636c51fda960e11b8152600401610b419190615852565b600c54600160301b900460ff16156130ee5760405163769dd35360e11b815260040160405180910390fd5b6000848152600560205260409020600101546001600160a01b03848116911614610d83576000848152600560205260409081902060010180546001600160a01b0319166001600160a01b0386161790555184907f21a4dad170a6bf476c31bbcf4a16628295b0e450672eec25d7c93308e05344a190612d14903390879061587f565b6000818152600560205260408120548190819081906060906001600160a01b03166131ae57604051630fb532db60e11b815260040160405180910390fd5b60008681526006602090815260408083205460058352928190208054600290910180548351818602810186019094528084526001600160601b0380871696600160601b810490911695600160c01b9091046001600160401b0316946001600160a01b039094169391839183018282801561325157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613233575b505050505090509450945094509450945091939590929450565b61327361345d565b6002546001600160a01b031661329c5760405163c1f0c0a160e01b815260040160405180910390fd5b6002546040516370a0823160e01b81526000916001600160a01b0316906370a08231906132cd903090600401615852565b60206040518083038186803b1580156132e557600080fd5b505afa1580156132f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061331d91906153ed565b600a549091506001600160601b031681811115613357576040516354ced18160e11b81526004810182905260248101839052604401610b41565b81811015610ef857600061336b8284615cae565b60025460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb9061339e9087908590600401615866565b602060405180830381600087803b1580156133b857600080fd5b505af11580156133cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f091906153b7565b61340d57604051631f01ff1360e21b815260040160405180910390fd5b7f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600848260405161343e929190615866565b60405180910390a150505050565b61345461345d565b610b4a81613e60565b6000546001600160a01b031633146134b05760405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606401610b41565b565b6000806134be846139f9565b60025491935091506001600160a01b0316158015906134e557506001600160601b03821615155b156135945760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906135259086906001600160601b03871690600401615866565b602060405180830381600087803b15801561353f57600080fd5b505af1158015613553573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061357791906153b7565b61359457604051631e9acf1760e31b815260040160405180910390fd5b6000836001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d80600081146135ea576040519150601f19603f3d011682016040523d82523d6000602084013e6135ef565b606091505b50509050806136115760405163950b247960e01b815260040160405180910390fd5b604080516001600160a01b03861681526001600160601b038581166020830152841681830152905186917f8c74ce8b8cf87f5eb001275c8be27eb34ea2b62bfab6814fcc62192bb63e81c4919081900360600190a25050505050565b604080516060810182526000808252602082018190529181019190915260006136998460000151612d22565b6000818152600d602052604090205490915060ff166136ce57604051631dfd6e1360e21b815260048101829052602401610b41565b60008185608001516040516020016136f0929190918252602082015260400190565b60408051601f1981840301815291815281516020928301206000818152600f9093529120549091508061373657604051631b44092560e11b815260040160405180910390fd5b845160208087015160408089015160608a015160808b015160a08c01519351613765978a979096959101615a92565b60405160208183030381529060405280519060200120811461379a5760405163354a450b60e21b815260040160405180910390fd5b60006137a98660000151613f04565b905080613881578551604051631d2827a760e31b81526001600160401b0390911660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e9413d389060240160206040518083038186803b15801561381b57600080fd5b505afa15801561382f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061385391906153ed565b90508061388157855160405163175dadad60e01b81526001600160401b039091166004820152602401610b41565b60008760800151826040516020016138a3929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c905060006138ca8983613fe6565b6040805160608101825297885260208801969096529486019490945250929695505050505050565b60005a61138881101561390457600080fd5b61138881039050846040820482031161391c57600080fd5b50823b61392857600080fd5b60008083516020850160008789f190505b9392505050565b6000811561396d576011546139669086908690600160201b900463ffffffff1686614051565b9050613987565b601154613984908690869063ffffffff16866140f3565b90505b949350505050565b6000805b6012548110156139f057826001600160a01b0316601282815481106139ba576139ba615de1565b6000918252602090912001546001600160a01b031614156139de5750600192915050565b806139e881615d49565b915050613993565b50600092915050565b6000818152600560209081526040808320815160608101835281546001600160a01b03908116825260018301541681850152600282018054845181870281018701865281815287968796949594860193919290830182828015613a8557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a67575b505050919092525050506000858152600660209081526040808320815160608101835290546001600160601b03808216808452600160601b8304909116948301859052600160c01b9091046001600160401b0316928201929092529096509094509192505b826040015151811015613b61576004600084604001518381518110613b1157613b11615de1565b6020908102919091018101516001600160a01b031682528181019290925260409081016000908120898252909252902080546001600160401b031916905580613b5981615d49565b915050613aea565b50600085815260056020526040812080546001600160a01b03199081168255600182018054909116905590613b996002830182615038565b5050600085815260066020526040812055613bb5600886614219565b50600a8054859190600090613bd49084906001600160601b0316615cc5565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555082600a600c8282829054906101000a90046001600160601b0316613c1c9190615cc5565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505050915091565b6040805160208082018790526001600160a01b03959095168183015260608101939093526001600160401b03919091166080808401919091528151808403909101815260a08301825280519084012060c083019490945260e0808301859052815180840390910181526101009092019052805191012091565b60408051602081019091526000815281613ce957506040805160208101909152600081526113b9565b63125fa26760e31b613cfb8385615ced565b6001600160e01b03191614613d2357604051632923fee760e11b815260040160405180910390fd5b613d308260048186615bf5565b8101906139399190615406565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401613d7691511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b600046613dba81614225565b15613e375760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613df957600080fd5b505afa158015613e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e3191906153ed565b91505090565b4391505090565b60006139398383614248565b60006113b9825490565b60006139398383614297565b6001600160a01b038116331415613eb35760405162461bcd60e51b815260206004820152601760248201527621b0b73737ba103a3930b739b332b9103a379039b2b63360491b6044820152606401610b41565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600046613f1081614225565b15613fd757610100836001600160401b0316613f2a613dae565b613f349190615cae565b1180613f505750613f43613dae565b836001600160401b031610155b15613f5e5750600092915050565b6040516315a03d4160e11b81526001600160401b0384166004820152606490632b407a82906024015b60206040518083038186803b158015613f9f57600080fd5b505afa158015613fb3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061393991906153ed565b50506001600160401b03164090565b600061401a8360000151846020015185604001518660600151868860a001518960c001518a60e001518b61010001516142c1565b600383602001516040516020016140329291906159da565b60408051601f1981840301815291905280516020909101209392505050565b6000806140946000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506144dd92505050565b905060005a6140a38888615c1f565b6140ad9190615cae565b6140b79085615c8f565b905060006140d063ffffffff871664e8d4a51000615c8f565b9050826140dd8284615c1f565b6140e79190615c1f565b98975050505050505050565b6000806140fe6145a2565b905060008113614124576040516321ea67b360e11b815260048101829052602401610b41565b60006141666000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506144dd92505050565b9050600082825a6141778b8b615c1f565b6141819190615cae565b61418b9088615c8f565b6141959190615c1f565b6141a790670de0b6b3a7640000615c8f565b6141b19190615c7b565b905060006141ca63ffffffff881664e8d4a51000615c8f565b90506141e2816b033b2e3c9fd0803ce8000000615cae565b8211156142025760405163e80fa38160e01b815260040160405180910390fd5b61420c8183615c1f565b9998505050505050505050565b6000613939838361466d565b600061a4b1821480614239575062066eed82145b806113b957505062066eee1490565b600081815260018301602052604081205461428f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556113b9565b5060006113b9565b60008260000182815481106142ae576142ae615de1565b9060005260206000200154905092915050565b6142ca89614760565b6143135760405162461bcd60e51b815260206004820152601a6024820152797075626c6963206b6579206973206e6f74206f6e20637572766560301b6044820152606401610b41565b61431c88614760565b6143605760405162461bcd60e51b815260206004820152601560248201527467616d6d61206973206e6f74206f6e20637572766560581b6044820152606401610b41565b61436983614760565b6143b55760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610b41565b6143be82614760565b61440a5760405162461bcd60e51b815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610b41565b614416878a8887614823565b61445e5760405162461bcd60e51b81526020600482015260196024820152786164647228632a706b2b732a6729213d5f755769746e65737360381b6044820152606401610b41565b600061446a8a87614946565b9050600061447d898b878b8689896149aa565b9050600061448e838d8d8a86614abd565b9050808a146144cf5760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b6044820152606401610b41565b505050505050505050505050565b6000466144e981614225565b1561452857606c6001600160a01b031663c6f7de0e6040518163ffffffff1660e01b815260040160206040518083038186803b158015613f9f57600080fd5b61453181614afd565b156139f057600f602160991b016001600160a01b03166349948e0e84604051806080016040528060488152602001615e31604891396040516020016145779291906157a8565b6040516020818303038152906040526040518263ffffffff1660e01b8152600401613f8791906158dc565b600c5460035460408051633fabe5a360e21b81529051600093600160381b900463ffffffff169283151592859283926001600160a01b03169163feaf968c9160048083019260a0929190829003018186803b15801561460057600080fd5b505afa158015614614573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146389190615683565b50945090925084915050801561465c57506146538242615cae565b8463ffffffff16105b156139875750601054949350505050565b60008181526001830160205260408120548015614756576000614691600183615cae565b85549091506000906146a590600190615cae565b905081811461470a5760008660000182815481106146c5576146c5615de1565b90600052602060002001549050808760000184815481106146e8576146e8615de1565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061471b5761471b615dcb565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506113b9565b60009150506113b9565b80516000906401000003d019116147ae5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420782d6f7264696e61746560701b6044820152606401610b41565b60208201516401000003d019116147fc5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420792d6f7264696e61746560701b6044820152606401610b41565b60208201516401000003d01990800961481c8360005b6020020151614b37565b1492915050565b60006001600160a01b0382166148695760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b6044820152606401610b41565b60208401516000906001161561488057601c614883565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe19918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa15801561491e573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b61494e615056565b61497b6001848460405160200161496793929190615831565b604051602081830303815290604052614b5b565b90505b61498781614760565b6113b95780516040805160208101929092526149a39101614967565b905061497e565b6149b2615056565b825186516401000003d0199081900691061415614a115760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610b41565b614a1c878988614ba9565b614a615760405162461bcd60e51b8152602060048201526016602482015275119a5c9cdd081b5d5b0818da1958dac819985a5b195960521b6044820152606401610b41565b614a6c848685614ba9565b614ab25760405162461bcd60e51b815260206004820152601760248201527614d958dbdb99081b5d5b0818da1958dac819985a5b1959604a1b6044820152606401610b41565b6140e7868484614cd1565b600060028686868587604051602001614adb969594939291906157d7565b60408051601f1981840301815291905280516020909101209695505050505050565b6000600a821480614b0f57506101a482145b80614b1c575062aa37dc82145b80614b28575061210582145b806113b957505062014a331490565b6000806401000003d01980848509840990506401000003d019600782089392505050565b614b63615056565b614b6c82614d94565b8152614b81614b7c826000614812565b614dcf565b602082018190526002900660011415612390576020810180516401000003d019039052919050565b600082614be65760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b6044820152606401610b41565b83516020850151600090614bfc90600290615d8b565b15614c0857601c614c0b565b601b5b9050600070014551231950b75fc4402da1732fc9bebe198387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614c7d573d6000803e3d6000fd5b505050602060405103519050600086604051602001614c9c9190615796565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b614cd9615056565b835160208086015185519186015160009384938493614cfa93909190614def565b919450925090506401000003d019858209600114614d565760405162461bcd60e51b815260206004820152601960248201527834b73b2d1036bab9ba1031329034b73b32b939b29037b3103d60391b6044820152606401610b41565b60405180604001604052806401000003d01980614d7557614d75615db5565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d019811061239057604080516020808201939093528151808203840181529082019091528051910120614d9c565b60006113b9826002614de86401000003d0196001615c1f565b901c614ecf565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a0890506000614e2f83838585614f66565b9098509050614e4088828e88614f8a565b9098509050614e5188828c87614f8a565b90985090506000614e648d878b85614f8a565b9098509050614e7588828686614f66565b9098509050614e8688828e89614f8a565b9098509050818114614ebb576401000003d019818a0998506401000003d01982890997506401000003d0198183099650614ebf565b8196505b5050505050509450945094915050565b600080614eda615074565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152614f0c615092565b60208160c0846005600019fa925082614f5c5760405162461bcd60e51b81526020600482015260126024820152716269674d6f64457870206661696c7572652160701b6044820152606401610b41565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215615028579160200282015b8281111561502857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614ff3565b506150349291506150b0565b5090565b5080546000825590600052602060002090810190610b4a91906150b0565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b8082111561503457600081556001016150b1565b803561239081615e0d565b600082601f8301126150e157600080fd5b6150e9615b88565b8083856040860111156150fb57600080fd5b60005b600281101561511d5781358452602093840193909101906001016150fe565b509095945050505050565b600082601f83011261513957600080fd5b81356001600160401b038082111561515357615153615df7565b604051601f8301601f19908116603f0116810190828211818310171561517b5761517b615df7565b8160405283815286602085880101111561519457600080fd5b836020870160208301376000602085830101528094505050505092915050565b600060c082840312156151c657600080fd5b6151ce615bb0565b905081356001600160401b0380821682146151e857600080fd5b8183526020840135602084015261520160408501615267565b604084015261521260608501615267565b6060840152615223608085016150c5565b608084015260a084013591508082111561523c57600080fd5b5061524984828501615128565b60a08301525092915050565b803561ffff8116811461239057600080fd5b803563ffffffff8116811461239057600080fd5b805169ffffffffffffffffffff8116811461239057600080fd5b6000602082840312156152a757600080fd5b813561393981615e0d565b600080604083850312156152c557600080fd5b82356152d081615e0d565b915060208301356152e081615e0d565b809150509250929050565b6000806000806060858703121561530157600080fd5b843561530c81615e0d565b93506020850135925060408501356001600160401b038082111561532f57600080fd5b818701915087601f83011261534357600080fd5b81358181111561535257600080fd5b88602082850101111561536457600080fd5b95989497505060200194505050565b60006040828403121561538557600080fd5b8260408301111561539557600080fd5b50919050565b6000604082840312156153ad57600080fd5b61393983836150d0565b6000602082840312156153c957600080fd5b815161393981615e22565b6000602082840312156153e657600080fd5b5035919050565b6000602082840312156153ff57600080fd5b5051919050565b60006020828403121561541857600080fd5b604051602081018181106001600160401b038211171561543a5761543a615df7565b604052823561544881615e22565b81529392505050565b6000808284036101c081121561546657600080fd5b6101a08082121561547657600080fd5b61547e615bd2565b915061548a86866150d0565b825261549986604087016150d0565b60208301526080850135604083015260a0850135606083015260c085013560808301526154c860e086016150c5565b60a08301526101006154dc878288016150d0565b60c08401526154ef8761014088016150d0565b60e0840152610180860135908301529092508301356001600160401b0381111561551857600080fd5b615524858286016151b4565b9150509250929050565b60006020828403121561554057600080fd5b81356001600160401b0381111561555657600080fd5b820160c0818503121561393957600080fd5b60006020828403121561557a57600080fd5b61393982615255565b60008060008060008086880360e081121561559d57600080fd5b6155a688615255565b96506155b460208901615267565b95506155c260408901615267565b94506155d060608901615267565b9350608088013592506040609f19820112156155eb57600080fd5b506155f4615b88565b61560060a08901615267565b815261560e60c08901615267565b6020820152809150509295509295509295565b6000806040838503121561563457600080fd5b8235915060208301356152e081615e0d565b6000806040838503121561565957600080fd5b50508035926020909101359150565b60006020828403121561567a57600080fd5b61393982615267565b600080600080600060a0868803121561569b57600080fd5b6156a48661527b565b94506020860151935060408601519250606086015191506156c76080870161527b565b90509295509295909350565b600081518084526020808501945080840160005b8381101561570c5781516001600160a01b0316875295820195908201906001016156e7565b509495945050505050565b8060005b6002811015610d8357815184526020938401939091019060010161571b565b600081518084526020808501945080840160005b8381101561570c5781518752958201959082019060010161574e565b60008151808452615782816020860160208601615d1d565b601f01601f19169290920160200192915050565b6157a08183615717565b604001919050565b600083516157ba818460208801615d1d565b8351908301906157ce818360208801615d1d565b01949350505050565b8681526157e76020820187615717565b6157f46060820186615717565b61580160a0820185615717565b61580e60e0820184615717565b60609190911b6001600160601b0319166101208201526101340195945050505050565b8381526158416020820184615717565b606081019190915260800192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682526001600160601b0316602082015260400190565b604081016113b98284615717565b602081526000613939602083018461573a565b602081526000613939602083018461576a565b6020815260ff82511660208201526020820151604082015260018060a01b0360408301511660608201526000606083015160c0608084015261593460e08401826156d3565b60808501516001600160601b0390811660a0868101919091529095015190941660c0909301929092525090919050565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b818110156159b557845183529383019391830191600101615999565b509098975050505050505050565b9182526001600160a01b0316602082015260400190565b828152606081016139396020830184615717565b828152604060208201526000613987604083018461573a565b86815285602082015261ffff85166040820152600063ffffffff808616606084015280851660808401525060c060a08301526140e760c083018461576a565b878152602081018790526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c0820181905260009061420c9083018461576a565b8781526001600160401b03871660208201526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c0820181905260009061420c9083018461576a565b6001600160601b038681168252851660208201526001600160401b03841660408201526001600160a01b038316606082015260a060808201819052600090615b30908301846156d3565b979650505050505050565b6000808335601e19843603018112615b5257600080fd5b8301803591506001600160401b03821115615b6c57600080fd5b602001915036819003821315615b8157600080fd5b9250929050565b604080519081016001600160401b0381118282101715615baa57615baa615df7565b60405290565b60405160c081016001600160401b0381118282101715615baa57615baa615df7565b60405161012081016001600160401b0381118282101715615baa57615baa615df7565b60008085851115615c0557600080fd5b83861115615c1257600080fd5b5050820193919092039150565b60008219821115615c3257615c32615d9f565b500190565b60006001600160401b038083168185168083038211156157ce576157ce615d9f565b60006001600160601b038281168482168083038211156157ce576157ce615d9f565b600082615c8a57615c8a615db5565b500490565b6000816000190483118215151615615ca957615ca9615d9f565b500290565b600082821015615cc057615cc0615d9f565b500390565b60006001600160601b0383811690831681811015615ce557615ce5615d9f565b039392505050565b6001600160e01b03198135818116916004851015615d155780818660040360031b1b83161692505b505092915050565b60005b83811015615d38578181015183820152602001615d20565b83811115610d835750506000910152565b6000600019821415615d5d57615d5d615d9f565b5060010190565b60006001600160401b0380831681811415615d8157615d81615d9f565b6001019392505050565b600082615d9a57615d9a615db5565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610b4a57600080fd5b8015158114610b4a57600080fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000806000a", } var VRFCoordinatorV25ABI = VRFCoordinatorV25MetaData.ABI @@ -634,25 +634,25 @@ func (_VRFCoordinatorV25 *VRFCoordinatorV25CallerSession) SProvingKeyHashes(arg0 return _VRFCoordinatorV25.Contract.SProvingKeyHashes(&_VRFCoordinatorV25.CallOpts, arg0) } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Caller) SProvingKeys(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) { +func (_VRFCoordinatorV25 *VRFCoordinatorV25Caller) SProvingKeys(opts *bind.CallOpts, arg0 [32]byte) (bool, error) { var out []interface{} err := _VRFCoordinatorV25.contract.Call(opts, &out, "s_provingKeys", arg0) if err != nil { - return *new(common.Address), err + return *new(bool), err } - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) return out0, err } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) SProvingKeys(arg0 [32]byte) (common.Address, error) { +func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) SProvingKeys(arg0 [32]byte) (bool, error) { return _VRFCoordinatorV25.Contract.SProvingKeys(&_VRFCoordinatorV25.CallOpts, arg0) } -func (_VRFCoordinatorV25 *VRFCoordinatorV25CallerSession) SProvingKeys(arg0 [32]byte) (common.Address, error) { +func (_VRFCoordinatorV25 *VRFCoordinatorV25CallerSession) SProvingKeys(arg0 [32]byte) (bool, error) { return _VRFCoordinatorV25.Contract.SProvingKeys(&_VRFCoordinatorV25.CallOpts, arg0) } @@ -854,30 +854,6 @@ func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) OnTokenTransfer(ar return _VRFCoordinatorV25.Contract.OnTokenTransfer(&_VRFCoordinatorV25.TransactOpts, arg0, amount, data) } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.contract.Transact(opts, "oracleWithdraw", recipient, amount) -} - -func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) OracleWithdraw(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.Contract.OracleWithdraw(&_VRFCoordinatorV25.TransactOpts, recipient, amount) -} - -func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) OracleWithdraw(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.Contract.OracleWithdraw(&_VRFCoordinatorV25.TransactOpts, recipient, amount) -} - -func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) OracleWithdrawNative(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.contract.Transact(opts, "oracleWithdrawNative", recipient, amount) -} - -func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) OracleWithdrawNative(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.Contract.OracleWithdrawNative(&_VRFCoordinatorV25.TransactOpts, recipient, amount) -} - -func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) OracleWithdrawNative(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.Contract.OracleWithdrawNative(&_VRFCoordinatorV25.TransactOpts, recipient, amount) -} - func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) OwnerCancelSubscription(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) { return _VRFCoordinatorV25.contract.Transact(opts, "ownerCancelSubscription", subId) } @@ -926,16 +902,16 @@ func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) RegisterMigratable return _VRFCoordinatorV25.Contract.RegisterMigratableCoordinator(&_VRFCoordinatorV25.TransactOpts, target) } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.contract.Transact(opts, "registerProvingKey", oracle, publicProvingKey) +func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) RegisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorV25.contract.Transact(opts, "registerProvingKey", publicProvingKey) } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) RegisterProvingKey(oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.Contract.RegisterProvingKey(&_VRFCoordinatorV25.TransactOpts, oracle, publicProvingKey) +func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) RegisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorV25.Contract.RegisterProvingKey(&_VRFCoordinatorV25.TransactOpts, publicProvingKey) } -func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) RegisterProvingKey(oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV25.Contract.RegisterProvingKey(&_VRFCoordinatorV25.TransactOpts, oracle, publicProvingKey) +func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) RegisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorV25.Contract.RegisterProvingKey(&_VRFCoordinatorV25.TransactOpts, publicProvingKey) } func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) { @@ -1010,6 +986,30 @@ func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) TransferOwnership( return _VRFCoordinatorV25.Contract.TransferOwnership(&_VRFCoordinatorV25.TransactOpts, to) } +func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV25.contract.Transact(opts, "withdraw", recipient) +} + +func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) Withdraw(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV25.Contract.Withdraw(&_VRFCoordinatorV25.TransactOpts, recipient) +} + +func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) Withdraw(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV25.Contract.Withdraw(&_VRFCoordinatorV25.TransactOpts, recipient) +} + +func (_VRFCoordinatorV25 *VRFCoordinatorV25Transactor) WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV25.contract.Transact(opts, "withdrawNative", recipient) +} + +func (_VRFCoordinatorV25 *VRFCoordinatorV25Session) WithdrawNative(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV25.Contract.WithdrawNative(&_VRFCoordinatorV25.TransactOpts, recipient) +} + +func (_VRFCoordinatorV25 *VRFCoordinatorV25TransactorSession) WithdrawNative(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV25.Contract.WithdrawNative(&_VRFCoordinatorV25.TransactOpts, recipient) +} + type VRFCoordinatorV25ConfigSetIterator struct { Event *VRFCoordinatorV25ConfigSet @@ -2054,32 +2054,21 @@ func (it *VRFCoordinatorV25ProvingKeyDeregisteredIterator) Close() error { type VRFCoordinatorV25ProvingKeyDeregistered struct { KeyHash [32]byte - Oracle common.Address Raw types.Log } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) FilterProvingKeyDeregistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorV25ProvingKeyDeregisteredIterator, error) { - - var oracleRule []interface{} - for _, oracleItem := range oracle { - oracleRule = append(oracleRule, oracleItem) - } +func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) FilterProvingKeyDeregistered(opts *bind.FilterOpts) (*VRFCoordinatorV25ProvingKeyDeregisteredIterator, error) { - logs, sub, err := _VRFCoordinatorV25.contract.FilterLogs(opts, "ProvingKeyDeregistered", oracleRule) + logs, sub, err := _VRFCoordinatorV25.contract.FilterLogs(opts, "ProvingKeyDeregistered") if err != nil { return nil, err } return &VRFCoordinatorV25ProvingKeyDeregisteredIterator{contract: _VRFCoordinatorV25.contract, event: "ProvingKeyDeregistered", logs: logs, sub: sub}, nil } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyDeregistered, oracle []common.Address) (event.Subscription, error) { +func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyDeregistered) (event.Subscription, error) { - var oracleRule []interface{} - for _, oracleItem := range oracle { - oracleRule = append(oracleRule, oracleItem) - } - - logs, sub, err := _VRFCoordinatorV25.contract.WatchLogs(opts, "ProvingKeyDeregistered", oracleRule) + logs, sub, err := _VRFCoordinatorV25.contract.WatchLogs(opts, "ProvingKeyDeregistered") if err != nil { return nil, err } @@ -2182,32 +2171,21 @@ func (it *VRFCoordinatorV25ProvingKeyRegisteredIterator) Close() error { type VRFCoordinatorV25ProvingKeyRegistered struct { KeyHash [32]byte - Oracle common.Address Raw types.Log } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorV25ProvingKeyRegisteredIterator, error) { +func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) FilterProvingKeyRegistered(opts *bind.FilterOpts) (*VRFCoordinatorV25ProvingKeyRegisteredIterator, error) { - var oracleRule []interface{} - for _, oracleItem := range oracle { - oracleRule = append(oracleRule, oracleItem) - } - - logs, sub, err := _VRFCoordinatorV25.contract.FilterLogs(opts, "ProvingKeyRegistered", oracleRule) + logs, sub, err := _VRFCoordinatorV25.contract.FilterLogs(opts, "ProvingKeyRegistered") if err != nil { return nil, err } return &VRFCoordinatorV25ProvingKeyRegisteredIterator{contract: _VRFCoordinatorV25.contract, event: "ProvingKeyRegistered", logs: logs, sub: sub}, nil } -func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) { - - var oracleRule []interface{} - for _, oracleItem := range oracle { - oracleRule = append(oracleRule, oracleItem) - } +func (_VRFCoordinatorV25 *VRFCoordinatorV25Filterer) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyRegistered) (event.Subscription, error) { - logs, sub, err := _VRFCoordinatorV25.contract.WatchLogs(opts, "ProvingKeyRegistered", oracleRule) + logs, sub, err := _VRFCoordinatorV25.contract.WatchLogs(opts, "ProvingKeyRegistered") if err != nil { return nil, err } @@ -3674,11 +3652,11 @@ func (VRFCoordinatorV25OwnershipTransferred) Topic() common.Hash { } func (VRFCoordinatorV25ProvingKeyDeregistered) Topic() common.Hash { - return common.HexToHash("0x72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d") + return common.HexToHash("0xbd242ec01625c15ecbc02cf700ac8b02c86f7346fa91a08e186810221ae509d0") } func (VRFCoordinatorV25ProvingKeyRegistered) Topic() common.Hash { - return common.HexToHash("0xe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b8") + return common.HexToHash("0xc9583fd3afa3d7f16eb0b88d0268e7d05c09bafa4b21e092cbd1320e1bc8089d") } func (VRFCoordinatorV25RandomWordsFulfilled) Topic() common.Hash { @@ -3768,7 +3746,7 @@ type VRFCoordinatorV25Interface interface { SProvingKeyHashes(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) - SProvingKeys(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) + SProvingKeys(opts *bind.CallOpts, arg0 [32]byte) (bool, error) SRequestCommitments(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) @@ -3798,10 +3776,6 @@ type VRFCoordinatorV25Interface interface { OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) - OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) - - OracleWithdrawNative(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) - OwnerCancelSubscription(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) @@ -3810,7 +3784,7 @@ type VRFCoordinatorV25Interface interface { RegisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) - RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) + RegisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) @@ -3824,6 +3798,10 @@ type VRFCoordinatorV25Interface interface { TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + + WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + FilterConfigSet(opts *bind.FilterOpts) (*VRFCoordinatorV25ConfigSetIterator, error) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ConfigSet) (event.Subscription, error) @@ -3872,15 +3850,15 @@ type VRFCoordinatorV25Interface interface { ParseOwnershipTransferred(log types.Log) (*VRFCoordinatorV25OwnershipTransferred, error) - FilterProvingKeyDeregistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorV25ProvingKeyDeregisteredIterator, error) + FilterProvingKeyDeregistered(opts *bind.FilterOpts) (*VRFCoordinatorV25ProvingKeyDeregisteredIterator, error) - WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyDeregistered, oracle []common.Address) (event.Subscription, error) + WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyDeregistered) (event.Subscription, error) ParseProvingKeyDeregistered(log types.Log) (*VRFCoordinatorV25ProvingKeyDeregistered, error) - FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorV25ProvingKeyRegisteredIterator, error) + FilterProvingKeyRegistered(opts *bind.FilterOpts) (*VRFCoordinatorV25ProvingKeyRegisteredIterator, error) - WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) + WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV25ProvingKeyRegistered) (event.Subscription, error) ParseProvingKeyRegistered(log types.Log) (*VRFCoordinatorV25ProvingKeyRegistered, error) diff --git a/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go b/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go index 7aae3d37770..ae3b764b3ce 100644 --- a/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go +++ b/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go @@ -66,8 +66,8 @@ type VRFV2PlusClientRandomWordsRequest struct { } var VRFCoordinatorV2PlusUpgradedVersionMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"internalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"externalBalance\",\"type\":\"uint256\"}],\"name\":\"BalanceInvariantViolated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"}],\"name\":\"BlockhashNotInStore\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorNotRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToSendNative\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToTransferLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"transferredValue\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"expectedValue\",\"type\":\"uint96\"}],\"name\":\"InvalidNativeBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"have\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"min\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"max\",\"type\":\"uint16\"}],\"name\":\"InvalidRequestConfirmations\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"requestVersion\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"expectedVersion\",\"type\":\"uint8\"}],\"name\":\"InvalidVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkAlreadySet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeRequestedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCorrespondingRequest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"NoSuchProvingKey\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"NumWordsTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingRequestExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Reentrant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SubscriptionIDCollisionFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structVRFCoordinatorV2PlusUpgradedVersion.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"MigrationCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"NativeFundsRecovered\",\"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\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountLink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountNative\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldNativeBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newNativeBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFundedWithNative\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_NATIVE_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFCoordinatorV2PlusUpgradedVersion.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"fundSubscriptionWithNative\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveSubscriptionIds\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRequestConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"}],\"name\":\"migrate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"migrationVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedData\",\"type\":\"bytes\"}],\"name\":\"onMigration\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdrawNative\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverNativeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"registerMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFV2PlusClient.RandomWordsRequest\",\"name\":\"req\",\"type\":\"tuple\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_config\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"reentrancyLock\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_currentSubNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_provingKeyHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requestCommitments\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalNativeBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"internalType\":\"structVRFCoordinatorV2PlusUpgradedVersion.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"}],\"name\":\"setLINKAndLINKNativeFeed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a06040523480156200001157600080fd5b50604051620061a6380380620061a6833981016040819052620000349162000183565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d7565b50505060601b6001600160601b031916608052620001b5565b6001600160a01b038116331415620001325760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156200019657600080fd5b81516001600160a01b0381168114620001ae57600080fd5b9392505050565b60805160601c615fcb620001db600039600081816104a601526136530152615fcb6000f3fe6080604052600436106101e05760003560e01c8062012291146101e5578063088070f5146102125780630ae095401461029257806315c48b84146102b457806318e3dd27146102dc5780631b6b6d231461031b5780632949265714610348578063294daa4914610368578063330987b314610384578063405b84fa146103a457806340d6bb82146103c457806341af6c87146103ef5780635d06b4ab1461041f57806364d51a2a1461043f578063659827441461045457806366316d8d14610474578063689c4517146104945780636f64f03f146104c857806372e9d565146104e857806379ba5097146105085780638402595e1461051d57806386fe91c71461053d5780638da5cb5b1461055d57806395b55cfc1461057b5780639b1c385e1461058e5780639d40a6fd146105bc578063a21a23e4146105e9578063a4c0ed36146105fe578063aa433aff1461061e578063aefb212f1461063e578063b08c87951461066b578063b2a7cac51461068b578063bec4c08c146106ab578063caf70c4a146106cb578063cb631797146106eb578063ce3f47191461070b578063d98e620e1461071e578063dac83d291461073e578063dc311dd31461075e578063e72f6e301461078f578063ee9d2d38146107af578063f2fde38b146107dc575b600080fd5b3480156101f157600080fd5b506101fa6107fc565b60405161020993929190615a56565b60405180910390f35b34801561021e57600080fd5b50600d5461025a9061ffff81169063ffffffff62010000820481169160ff600160301b82041691600160381b8204811691600160581b90041685565b6040805161ffff909616865263ffffffff9485166020870152921515928501929092528216606084015216608082015260a001610209565b34801561029e57600080fd5b506102b26102ad3660046156c9565b610878565b005b3480156102c057600080fd5b506102c960c881565b60405161ffff9091168152602001610209565b3480156102e857600080fd5b50600a5461030390600160601b90046001600160601b031681565b6040516001600160601b039091168152602001610209565b34801561032757600080fd5b5060025461033b906001600160a01b031681565b60405161020991906158fa565b34801561035457600080fd5b506102b261036336600461523b565b610946565b34801561037457600080fd5b5060405160028152602001610209565b34801561039057600080fd5b5061030361039f36600461541e565b610ac3565b3480156103b057600080fd5b506102b26103bf3660046156c9565b610f9e565b3480156103d057600080fd5b506103da6101f481565b60405163ffffffff9091168152602001610209565b3480156103fb57600080fd5b5061040f61040a3660046156b0565b611389565b6040519015158152602001610209565b34801561042b57600080fd5b506102b261043a36600461521e565b61152a565b34801561044b57600080fd5b506102c9606481565b34801561046057600080fd5b506102b261046f366004615270565b6115e1565b34801561048057600080fd5b506102b261048f36600461523b565b611641565b3480156104a057600080fd5b5061033b7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104d457600080fd5b506102b26104e33660046152a9565b611809565b3480156104f457600080fd5b5060035461033b906001600160a01b031681565b34801561051457600080fd5b506102b2611910565b34801561052957600080fd5b506102b261053836600461521e565b6119ba565b34801561054957600080fd5b50600a54610303906001600160601b031681565b34801561056957600080fd5b506000546001600160a01b031661033b565b6102b26105893660046156b0565b611ac6565b34801561059a57600080fd5b506105ae6105a93660046154fb565b611c0a565b604051908152602001610209565b3480156105c857600080fd5b506007546105dc906001600160401b031681565b6040516102099190615bef565b3480156105f557600080fd5b506105ae611f7a565b34801561060a57600080fd5b506102b26106193660046152e5565b6121c8565b34801561062a57600080fd5b506102b26106393660046156b0565b612365565b34801561064a57600080fd5b5061065e6106593660046156ee565b6123c8565b6040516102099190615971565b34801561067757600080fd5b506102b2610686366004615612565b6124c9565b34801561069757600080fd5b506102b26106a63660046156b0565b61263d565b3480156106b757600080fd5b506102b26106c63660046156c9565b612761565b3480156106d757600080fd5b506105ae6106e6366004615340565b6128f8565b3480156106f757600080fd5b506102b26107063660046156c9565b612928565b6102b2610719366004615392565b612c15565b34801561072a57600080fd5b506105ae6107393660046156b0565b612f26565b34801561074a57600080fd5b506102b26107593660046156c9565b612f47565b34801561076a57600080fd5b5061077e6107793660046156b0565b613057565b604051610209959493929190615c03565b34801561079b57600080fd5b506102b26107aa36600461521e565b613152565b3480156107bb57600080fd5b506105ae6107ca3660046156b0565b60106020526000908152604090205481565b3480156107e857600080fd5b506102b26107f736600461521e565b61332d565b600d54600f805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff1693919283919083018282801561086657602002820191906000526020600020905b815481526020019060010190808311610852575b50505050509050925092509250909192565b60008281526005602052604090205482906001600160a01b0316806108b057604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146108e45780604051636c51fda960e11b81526004016108db91906158fa565b60405180910390fd5b600d54600160301b900460ff161561090f5760405163769dd35360e11b815260040160405180910390fd5b61091884611389565b1561093657604051631685ecdd60e31b815260040160405180910390fd5b610940848461333e565b50505050565b600d54600160301b900460ff16156109715760405163769dd35360e11b815260040160405180910390fd5b336000908152600c60205260409020546001600160601b03808316911610156109ad57604051631e9acf1760e31b815260040160405180910390fd5b336000908152600c6020526040812080548392906109d59084906001600160601b0316615e0b565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a600c8282829054906101000a90046001600160601b0316610a1d9190615e0b565b92506101000a8154816001600160601b0302191690836001600160601b031602179055506000826001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114610a97576040519150601f19603f3d011682016040523d82523d6000602084013e610a9c565b606091505b5050905080610abe5760405163950b247960e01b815260040160405180910390fd5b505050565b600d54600090600160301b900460ff1615610af15760405163769dd35360e11b815260040160405180910390fd5b60005a90506000610b0285856134f9565b90506000846060015163ffffffff166001600160401b03811115610b2857610b28615f3d565b604051908082528060200260200182016040528015610b51578160200160208202803683370190505b50905060005b856060015163ffffffff16811015610bc857826040015181604051602001610b80929190615984565b6040516020818303038152906040528051906020012060001c828281518110610bab57610bab615f27565b602090810291909101015280610bc081615e8f565b915050610b57565b5060208083018051600090815260109092526040808320839055905190518291631fe543e360e01b91610c0091908690602401615ae0565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600d805460ff60301b1916600160301b179055908801516080890151919250600091610c659163ffffffff16908461376c565b600d805460ff60301b19169055602089810151600090815260069091526040902054909150600160c01b90046001600160401b0316610ca5816001615d7d565b6020808b0151600090815260069091526040812080546001600160401b0393909316600160c01b026001600160c01b039093169290921790915560a08a01518051610cf290600190615df4565b81518110610d0257610d02615f27565b602091010151600d5460f89190911c6001149150600090610d33908a90600160581b900463ffffffff163a856137ba565b90508115610e3c576020808c01516000908152600690915260409020546001600160601b03808316600160601b909204161015610d8357604051631e9acf1760e31b815260040160405180910390fd5b60208b81015160009081526006909152604090208054829190600c90610dba908490600160601b90046001600160601b0316615e0b565b82546101009290920a6001600160601b0381810219909316918316021790915589516000908152600e60209081526040808320546001600160a01b03168352600c909152812080548594509092610e1391859116615d9f565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550610f28565b6020808c01516000908152600690915260409020546001600160601b0380831691161015610e7d57604051631e9acf1760e31b815260040160405180910390fd5b6020808c015160009081526006909152604081208054839290610eaa9084906001600160601b0316615e0b565b82546101009290920a6001600160601b0381810219909316918316021790915589516000908152600e60209081526040808320546001600160a01b03168352600b909152812080548594509092610f0391859116615d9f565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b8a6020015188602001517f49580fdfd9497e1ed5c1b1cec0495087ae8e3f1267470ec2fb015db32e3d6aa78a604001518488604051610f85939291909283526001600160601b039190911660208301521515604082015260600190565b60405180910390a3985050505050505050505b92915050565b600d54600160301b900460ff1615610fc95760405163769dd35360e11b815260040160405180910390fd5b610fd281613809565b610ff15780604051635428d44960e01b81526004016108db91906158fa565b60008060008061100086613057565b945094505093509350336001600160a01b0316826001600160a01b0316146110635760405162461bcd60e51b81526020600482015260166024820152752737ba1039bab139b1b934b83a34b7b71037bbb732b960511b60448201526064016108db565b61106c86611389565b156110b25760405162461bcd60e51b815260206004820152601660248201527550656e64696e6720726571756573742065786973747360501b60448201526064016108db565b60006040518060c001604052806110c7600290565b60ff168152602001888152602001846001600160a01b03168152602001838152602001866001600160601b03168152602001856001600160601b0316815250905060008160405160200161111b91906159c3565b604051602081830303815290604052905061113588613873565b505060405163ce3f471960e01b81526001600160a01b0388169063ce3f4719906001600160601b0388169061116e9085906004016159b0565b6000604051808303818588803b15801561118757600080fd5b505af115801561119b573d6000803e3d6000fd5b50506002546001600160a01b0316158015935091506111c4905057506001600160601b03861615155b1561128e5760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906111fb908a908a90600401615941565b602060405180830381600087803b15801561121557600080fd5b505af1158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d919061535c565b61128e5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742066756e647360701b60448201526064016108db565b600d805460ff60301b1916600160301b17905560005b8351811015611337578381815181106112bf576112bf615f27565b60200260200101516001600160a01b0316638ea98117896040518263ffffffff1660e01b81526004016112f291906158fa565b600060405180830381600087803b15801561130c57600080fd5b505af1158015611320573d6000803e3d6000fd5b50505050808061132f90615e8f565b9150506112a4565b50600d805460ff60301b191690556040517fd63ca8cb945956747ee69bfdc3ea754c24a4caf7418db70e46052f7850be4187906113779089908b9061590e565b60405180910390a15050505050505050565b6000818152600560209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561141357602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113f5575b505050505081525050905060005b8160400151518110156115205760005b600f5481101561150d5760006114d6600f838154811061145357611453615f27565b90600052602060002001548560400151858151811061147457611474615f27565b602002602001015188600460008960400151898151811061149757611497615f27565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081208d82529092529020546001600160401b0316613ac1565b50600081815260106020526040902054909150156114fa5750600195945050505050565b508061150581615e8f565b915050611431565b508061151881615e8f565b915050611421565b5060009392505050565b611532613b4a565b61153b81613809565b1561155b578060405163ac8a27ef60e01b81526004016108db91906158fa565b601380546001810182556000919091527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0900180546001600160a01b0319166001600160a01b0383161790556040517fb7cabbfc11e66731fc77de0444614282023bcbd41d16781c753a431d0af01625906115d69083906158fa565b60405180910390a150565b6115e9613b4a565b6002546001600160a01b03161561161357604051631688c53760e11b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b600d54600160301b900460ff161561166c5760405163769dd35360e11b815260040160405180910390fd5b6002546001600160a01b03166116955760405163c1f0c0a160e01b815260040160405180910390fd5b336000908152600b60205260409020546001600160601b03808316911610156116d157604051631e9acf1760e31b815260040160405180910390fd5b336000908152600b6020526040812080548392906116f99084906001600160601b0316615e0b565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a60008282829054906101000a90046001600160601b03166117419190615e0b565b82546001600160601b039182166101009390930a92830291909202199091161790555060025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906117969085908590600401615941565b602060405180830381600087803b1580156117b057600080fd5b505af11580156117c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e8919061535c565b61180557604051631e9acf1760e31b815260040160405180910390fd5b5050565b611811613b4a565b6040805180820182526000916118409190849060029083908390808284376000920191909152506128f8915050565b6000818152600e60205260409020549091506001600160a01b03161561187c57604051634a0b8fa760e01b8152600481018290526024016108db565b6000818152600e6020908152604080832080546001600160a01b0319166001600160a01b038816908117909155600f805460018101825594527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac802909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b8910160405180910390a2505050565b6001546001600160a01b031633146119635760405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b60448201526064016108db565b60008054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6119c2613b4a565b600a544790600160601b90046001600160601b0316818111156119fc5780826040516354ced18160e11b81526004016108db929190615984565b81811015610abe576000611a108284615df4565b90506000846001600160a01b03168260405160006040518083038185875af1925050503d8060008114611a5f576040519150601f19603f3d011682016040523d82523d6000602084013e611a64565b606091505b5050905080611a865760405163950b247960e01b815260040160405180910390fd5b7f4aed7c8eed0496c8c19ea2681fcca25741c1602342e38b045d9f1e8e905d2e9c8583604051611ab792919061590e565b60405180910390a15050505050565b600d54600160301b900460ff1615611af15760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b0316611b2657604051630fb532db60e11b815260040160405180910390fd5b60008181526006602052604090208054600160601b90046001600160601b0316903490600c611b558385615d9f565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555034600a600c8282829054906101000a90046001600160601b0316611b9d9190615d9f565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f7603b205d03651ee812f803fccde89f1012e545a9c99f0abfea9cedd0fd8e902823484611bf09190615d65565b604051611bfe929190615984565b60405180910390a25050565b600d54600090600160301b900460ff1615611c385760405163769dd35360e11b815260040160405180910390fd5b6020808301356000908152600590915260409020546001600160a01b0316611c7357604051630fb532db60e11b815260040160405180910390fd5b3360009081526004602090815260408083208583013584529091529020546001600160401b031680611cc0578260200135336040516379bfd40160e01b81526004016108db929190615ab5565b600d5461ffff16611cd760608501604086016155f7565b61ffff161080611cfa575060c8611cf460608501604086016155f7565b61ffff16115b15611d3457611d0f60608401604085016155f7565b600d5460405163539c34bb60e11b81526108db929161ffff169060c890600401615a38565b600d5462010000900463ffffffff16611d536080850160608601615710565b63ffffffff161115611d9957611d6f6080840160608501615710565b600d54604051637aebf00f60e11b81526108db929162010000900463ffffffff1690600401615bd8565b6101f4611dac60a0850160808601615710565b63ffffffff161115611de657611dc860a0840160808501615710565b6101f46040516311ce1afb60e21b81526004016108db929190615bd8565b6000611df3826001615d7d565b9050600080611e09863533602089013586613ac1565b90925090506000611e25611e2060a0890189615c58565b613b9f565b90506000611e3282613c1c565b905083611e3d613c8d565b60208a0135611e5260808c0160608d01615710565b611e6260a08d0160808e01615710565b3386604051602001611e7a9796959493929190615b38565b604051602081830303815290604052805190602001206010600086815260200190815260200160002081905550336001600160a01b0316886020013589600001357feb0e3652e0f44f417695e6e90f2f42c99b65cd7169074c5a654b16b9748c3a4e87878d6040016020810190611ef191906155f7565b8e6060016020810190611f049190615710565b8f6080016020810190611f179190615710565b89604051611f2a96959493929190615af9565b60405180910390a45050336000908152600460209081526040808320898301358452909152902080546001600160401b0319166001600160401b039490941693909317909255925050505b919050565b600d54600090600160301b900460ff1615611fa85760405163769dd35360e11b815260040160405180910390fd5b600033611fb6600143615df4565b600754604051606093841b6001600160601b03199081166020830152924060348201523090931b909116605483015260c01b6001600160c01b031916606882015260700160408051601f198184030181529190528051602090910120600780549192506001600160401b0390911690600061203083615eaa565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550506000806001600160401b0381111561206f5761206f615f3d565b604051908082528060200260200182016040528015612098578160200160208202803683370190505b506040805160608082018352600080835260208084018281528486018381528984526006835286842095518654925191516001600160601b039182166001600160c01b031990941693909317600160601b9190921602176001600160c01b0316600160c01b6001600160401b039092169190910217909355835191820184523382528183018181528285018681528883526005855294909120825181546001600160a01b03199081166001600160a01b0392831617835592516001830180549094169116179091559251805194955090936121799260028501920190614e8f565b5061218991506008905083613d1d565b50817f1d3015d7ba850fa198dc7b1a3f5d42779313a681035f77c8c03764c61005518d336040516121ba91906158fa565b60405180910390a250905090565b600d54600160301b900460ff16156121f35760405163769dd35360e11b815260040160405180910390fd5b6002546001600160a01b0316331461221e576040516344b0e3c360e01b815260040160405180910390fd5b6020811461223f57604051638129bbcd60e01b815260040160405180910390fd5b600061224d828401846156b0565b6000818152600560205260409020549091506001600160a01b031661228557604051630fb532db60e11b815260040160405180910390fd5b600081815260066020526040812080546001600160601b0316918691906122ac8385615d9f565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600a60008282829054906101000a90046001600160601b03166122f49190615d9f565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f1ced9348ff549fceab2ac57cd3a9de38edaaab274b725ee82c23e8fc8c4eec7a8287846123479190615d65565b604051612355929190615984565b60405180910390a2505050505050565b61236d613b4a565b6000818152600560205260409020546001600160a01b03166123a257604051630fb532db60e11b815260040160405180910390fd5b6000818152600560205260409020546123c59082906001600160a01b031661333e565b50565b606060006123d66008613d29565b90508084106123f857604051631390f2a160e01b815260040160405180910390fd5b60006124048486615d65565b905081811180612412575083155b61241c578061241e565b815b9050600061242c8683615df4565b6001600160401b0381111561244357612443615f3d565b60405190808252806020026020018201604052801561246c578160200160208202803683370190505b50905060005b81518110156124bf576124906124888883615d65565b600890613d33565b8282815181106124a2576124a2615f27565b6020908102919091010152806124b781615e8f565b915050612472565b5095945050505050565b6124d1613b4a565b60c861ffff871611156124fe57858660c860405163539c34bb60e11b81526004016108db93929190615a38565b60008213612522576040516321ea67b360e11b8152600481018390526024016108db565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600d805465ffffffffffff1916881762010000870217600160301b600160781b031916600160381b850263ffffffff60581b191617600160581b83021790558a51601280548d8701519289166001600160401b031990911617600160201b92891692909202919091179081905560118d90558a519788528785019590955298860191909152840196909652938201879052838116928201929092529190921c90911660c08201527f777357bb93f63d088f18112d3dba38457aec633eb8f1341e1d418380ad328e789060e00160405180910390a1505050505050565b600d54600160301b900460ff16156126685760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b031661269d57604051630fb532db60e11b815260040160405180910390fd5b6000818152600560205260409020600101546001600160a01b031633146126f4576000818152600560205260409081902060010154905163d084e97560e01b81526108db916001600160a01b0316906004016158fa565b6000818152600560205260409081902080546001600160a01b031980821633908117845560019093018054909116905591516001600160a01b039092169183917fd4114ab6e9af9f597c52041f32d62dc57c5c4e4c0d4427006069635e216c938691611bfe918591615927565b60008281526005602052604090205482906001600160a01b03168061279957604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146127c45780604051636c51fda960e11b81526004016108db91906158fa565b600d54600160301b900460ff16156127ef5760405163769dd35360e11b815260040160405180910390fd5b60008481526005602052604090206002015460641415612822576040516305a48e0f60e01b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b03161561285957610940565b6001600160a01b0383166000818152600460209081526040808320888452825280832080546001600160401b031916600190811790915560058352818420600201805491820181558452919092200180546001600160a01b0319169092179091555184907f1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e1906128ea9086906158fa565b60405180910390a250505050565b60008160405160200161290b9190615963565b604051602081830303815290604052805190602001209050919050565b60008281526005602052604090205482906001600160a01b03168061296057604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461298b5780604051636c51fda960e11b81526004016108db91906158fa565b600d54600160301b900460ff16156129b65760405163769dd35360e11b815260040160405180910390fd5b6129bf84611389565b156129dd57604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b0316612a2b5783836040516379bfd40160e01b81526004016108db929190615ab5565b600084815260056020908152604080832060020180548251818502810185019093528083529192909190830182828015612a8e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a70575b50505050509050600060018251612aa59190615df4565b905060005b8251811015612bb157856001600160a01b0316838281518110612acf57612acf615f27565b60200260200101516001600160a01b03161415612b9f576000838381518110612afa57612afa615f27565b6020026020010151905080600560008a81526020019081526020016000206002018381548110612b2c57612b2c615f27565b600091825260208083209190910180546001600160a01b0319166001600160a01b039490941693909317909255898152600590915260409020600201805480612b7757612b77615f11565b600082815260209020810160001990810180546001600160a01b031916905501905550612bb1565b80612ba981615e8f565b915050612aaa565b506001600160a01b03851660009081526004602090815260408083208984529091529081902080546001600160401b03191690555186907f32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a7906123559088906158fa565b6000612c2382840184615535565b9050806000015160ff16600114612c5c57805160405163237d181f60e21b815260ff9091166004820152600160248201526044016108db565b8060a001516001600160601b03163414612ca05760a08101516040516306acf13560e41b81523460048201526001600160601b0390911660248201526044016108db565b6020808201516000908152600590915260409020546001600160a01b031615612cdc576040516326afa43560e11b815260040160405180910390fd5b60005b816060015151811015612d7c5760016004600084606001518481518110612d0857612d08615f27565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008460200151815260200190815260200160002060006101000a8154816001600160401b0302191690836001600160401b031602179055508080612d7490615e8f565b915050612cdf565b50604080516060808201835260808401516001600160601b03908116835260a0850151811660208085019182526000858701818152828901805183526006845288832097518854955192516001600160401b0316600160c01b026001600160c01b03938816600160601b026001600160c01b0319909716919097161794909417169390931790945584518084018652868601516001600160a01b03908116825281860184815294880151828801908152925184526005865295909220825181549087166001600160a01b0319918216178255935160018201805491909716941693909317909455925180519192612e7b92600285019290910190614e8f565b5050506080810151600a8054600090612e9e9084906001600160601b0316615d9f565b92506101000a8154816001600160601b0302191690836001600160601b031602179055508060a00151600a600c8282829054906101000a90046001600160601b0316612eea9190615d9f565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555061094081602001516008613d1d90919063ffffffff16565b600f8181548110612f3657600080fd5b600091825260209091200154905081565b60008281526005602052604090205482906001600160a01b031680612f7f57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614612faa5780604051636c51fda960e11b81526004016108db91906158fa565b600d54600160301b900460ff1615612fd55760405163769dd35360e11b815260040160405180910390fd5b6000848152600560205260409020600101546001600160a01b03848116911614610940576000848152600560205260409081902060010180546001600160a01b0319166001600160a01b0386161790555184907f21a4dad170a6bf476c31bbcf4a16628295b0e450672eec25d7c93308e05344a1906128ea9033908790615927565b6000818152600560205260408120548190819081906060906001600160a01b031661309557604051630fb532db60e11b815260040160405180910390fd5b60008681526006602090815260408083205460058352928190208054600290910180548351818602810186019094528084526001600160601b0380871696600160601b810490911695600160c01b9091046001600160401b0316946001600160a01b039094169391839183018282801561313857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161311a575b505050505090509450945094509450945091939590929450565b61315a613b4a565b6002546001600160a01b03166131835760405163c1f0c0a160e01b815260040160405180910390fd5b6002546040516370a0823160e01b81526000916001600160a01b0316906370a08231906131b49030906004016158fa565b60206040518083038186803b1580156131cc57600080fd5b505afa1580156131e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132049190615379565b600a549091506001600160601b0316818111156132385780826040516354ced18160e11b81526004016108db929190615984565b81811015610abe57600061324c8284615df4565b60025460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb9061327f908790859060040161590e565b602060405180830381600087803b15801561329957600080fd5b505af11580156132ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132d1919061535c565b6132ee57604051631f01ff1360e21b815260040160405180910390fd5b7f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600848260405161331f92919061590e565b60405180910390a150505050565b613335613b4a565b6123c581613d3f565b60008061334a84613873565b60025491935091506001600160a01b03161580159061337157506001600160601b03821615155b156134205760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906133b19086906001600160601b0387169060040161590e565b602060405180830381600087803b1580156133cb57600080fd5b505af11580156133df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613403919061535c565b61342057604051631e9acf1760e31b815260040160405180910390fd5b6000836001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114613476576040519150601f19603f3d011682016040523d82523d6000602084013e61347b565b606091505b505090508061349d5760405163950b247960e01b815260040160405180910390fd5b604080516001600160a01b03861681526001600160601b038581166020830152841681830152905186917f8c74ce8b8cf87f5eb001275c8be27eb34ea2b62bfab6814fcc62192bb63e81c4919081900360600190a25050505050565b6040805160608101825260008082526020820181905291810191909152600061352584600001516128f8565b6000818152600e60205260409020549091506001600160a01b03168061356157604051631dfd6e1360e21b8152600481018390526024016108db565b600082866080015160405160200161357a929190615984565b60408051601f19818403018152918152815160209283012060008181526010909352912054909150806135c057604051631b44092560e11b815260040160405180910390fd5b85516020808801516040808a015160608b015160808c015160a08d015193516135ef978a979096959101615b84565b6040516020818303038152906040528051906020012081146136245760405163354a450b60e21b815260040160405180910390fd5b60006136338760000151613de3565b9050806136fa578651604051631d2827a760e31b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163e9413d38916136879190600401615bef565b60206040518083038186803b15801561369f57600080fd5b505afa1580156136b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136d79190615379565b9050806136fa57865160405163175dadad60e01b81526108db9190600401615bef565b600088608001518260405160200161371c929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c905060006137438a83613ec0565b604080516060810182529889526020890196909652948701949094525093979650505050505050565b60005a61138881101561377e57600080fd5b61138881039050846040820482031161379657600080fd5b50823b6137a257600080fd5b60008083516020850160008789f190505b9392505050565b600081156137e7576012546137e09086908690600160201b900463ffffffff1686613f2b565b9050613801565b6012546137fe908690869063ffffffff1686613fcd565b90505b949350505050565b6000805b60135481101561386a57826001600160a01b03166013828154811061383457613834615f27565b6000918252602090912001546001600160a01b031614156138585750600192915050565b8061386281615e8f565b91505061380d565b50600092915050565b6000818152600560209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879687969495948601939192908301828280156138ff57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116138e1575b505050919092525050506000858152600660209081526040808320815160608101835290546001600160601b03808216808452600160601b8304909116948301859052600160c01b9091046001600160401b0316928201929092529096509094509192505b8260400151518110156139db57600460008460400151838151811061398b5761398b615f27565b6020908102919091018101516001600160a01b031682528181019290925260409081016000908120898252909252902080546001600160401b0319169055806139d381615e8f565b915050613964565b50600085815260056020526040812080546001600160a01b03199081168255600182018054909116905590613a136002830182614ef4565b5050600085815260066020526040812055613a2f6008866140f2565b50600a8054859190600090613a4e9084906001600160601b0316615e0b565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555082600a600c8282829054906101000a90046001600160601b0316613a969190615e0b565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505050915091565b60408051602081018690526001600160a01b03851691810191909152606081018390526001600160401b03821660808201526000908190819060a00160408051601f198184030181529082905280516020918201209250613b26918991849101615984565b60408051808303601f19018152919052805160209091012097909650945050505050565b6000546001600160a01b03163314613b9d5760405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b60448201526064016108db565b565b60408051602081019091526000815281613bc85750604080516020810190915260008152610f98565b63125fa26760e31b613bda8385615e33565b6001600160e01b03191614613c0257604051632923fee760e11b815260040160405180910390fd5b613c0f8260048186615d3b565b8101906137b391906153d3565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401613c5591511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b600046613c99816140fe565b15613d165760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613cd857600080fd5b505afa158015613cec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d109190615379565b91505090565b4391505090565b60006137b38383614121565b6000610f98825490565b60006137b38383614170565b6001600160a01b038116331415613d925760405162461bcd60e51b815260206004820152601760248201527621b0b73737ba103a3930b739b332b9103a379039b2b63360491b60448201526064016108db565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600046613def816140fe565b15613eb157610100836001600160401b0316613e09613c8d565b613e139190615df4565b1180613e2f5750613e22613c8d565b836001600160401b031610155b15613e3d5750600092915050565b6040516315a03d4160e11b8152606490632b407a8290613e61908690600401615bef565b60206040518083038186803b158015613e7957600080fd5b505afa158015613e8d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137b39190615379565b50506001600160401b03164090565b6000613ef48360000151846020015185604001518660600151868860a001518960c001518a60e001518b610100015161419a565b60038360200151604051602001613f0c929190615acc565b60408051601f1981840301815291905280516020909101209392505050565b600080613f6e6000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506143b592505050565b905060005a613f7d8888615d65565b613f879190615df4565b613f919085615dd5565b90506000613faa63ffffffff871664e8d4a51000615dd5565b905082613fb78284615d65565b613fc19190615d65565b98975050505050505050565b600080613fd861447a565b905060008113613ffe576040516321ea67b360e11b8152600481018290526024016108db565b60006140406000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506143b592505050565b9050600082825a6140518b8b615d65565b61405b9190615df4565b6140659088615dd5565b61406f9190615d65565b61408190670de0b6b3a7640000615dd5565b61408b9190615dc1565b905060006140a463ffffffff881664e8d4a51000615dd5565b90506140bb81676765c793fa10079d601b1b615df4565b8211156140db5760405163e80fa38160e01b815260040160405180910390fd5b6140e58183615d65565b9998505050505050505050565b60006137b38383614545565b600061a4b1821480614112575062066eed82145b80610f9857505062066eee1490565b600081815260018301602052604081205461416857508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610f98565b506000610f98565b600082600001828154811061418757614187615f27565b9060005260206000200154905092915050565b6141a389614638565b6141ec5760405162461bcd60e51b815260206004820152601a6024820152797075626c6963206b6579206973206e6f74206f6e20637572766560301b60448201526064016108db565b6141f588614638565b6142395760405162461bcd60e51b815260206004820152601560248201527467616d6d61206973206e6f74206f6e20637572766560581b60448201526064016108db565b61424283614638565b61428e5760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e20637572766500000060448201526064016108db565b61429782614638565b6142e25760405162461bcd60e51b815260206004820152601c60248201527b73486173685769746e657373206973206e6f74206f6e20637572766560201b60448201526064016108db565b6142ee878a88876146fb565b6143365760405162461bcd60e51b81526020600482015260196024820152786164647228632a706b2b732a6729213d5f755769746e65737360381b60448201526064016108db565b60006143428a8761480f565b90506000614355898b878b868989614873565b90506000614366838d8d8a86614986565b9050808a146143a75760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b60448201526064016108db565b505050505050505050505050565b6000466143c1816140fe565b1561440057606c6001600160a01b031663c6f7de0e6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e7957600080fd5b614409816149c6565b1561386a57600f602160991b016001600160a01b03166349948e0e84604051806080016040528060488152602001615f776048913960405160200161444f929190615850565b6040516020818303038152906040526040518263ffffffff1660e01b8152600401613e6191906159b0565b600d5460035460408051633fabe5a360e21b81529051600093600160381b900463ffffffff169283151592859283926001600160a01b03169163feaf968c9160048083019260a0929190829003018186803b1580156144d857600080fd5b505afa1580156144ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614510919061572b565b509450909250849150508015614534575061452b8242615df4565b8463ffffffff16105b156138015750601154949350505050565b6000818152600183016020526040812054801561462e576000614569600183615df4565b855490915060009061457d90600190615df4565b90508181146145e257600086600001828154811061459d5761459d615f27565b90600052602060002001549050808760000184815481106145c0576145c0615f27565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806145f3576145f3615f11565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610f98565b6000915050610f98565b80516000906401000003d019116146865760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420782d6f7264696e61746560701b60448201526064016108db565b60208201516401000003d019116146d45760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420792d6f7264696e61746560701b60448201526064016108db565b60208201516401000003d0199080096146f48360005b6020020151614a00565b1492915050565b60006001600160a01b0382166147415760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b60448201526064016108db565b60208401516000906001161561475857601c61475b565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe19918203925060009190890987516040805160008082526020909101918290529293506001916147c591869188918790615992565b6020604051602081039080840390855afa1580156147e7573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b614817614f12565b61484460018484604051602001614830939291906158d9565b604051602081830303815290604052614a24565b90505b61485081614638565b610f9857805160408051602081019290925261486c9101614830565b9050614847565b61487b614f12565b825186516401000003d01990819006910614156148da5760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e6374000060448201526064016108db565b6148e5878988614a72565b61492a5760405162461bcd60e51b8152602060048201526016602482015275119a5c9cdd081b5d5b0818da1958dac819985a5b195960521b60448201526064016108db565b614935848685614a72565b61497b5760405162461bcd60e51b815260206004820152601760248201527614d958dbdb99081b5d5b0818da1958dac819985a5b1959604a1b60448201526064016108db565b613fc1868484614b8d565b6000600286868685876040516020016149a49695949392919061587f565b60408051601f1981840301815291905280516020909101209695505050505050565b6000600a8214806149d857506101a482145b806149e5575062aa37dc82145b806149f1575061210582145b80610f9857505062014a331490565b6000806401000003d01980848509840990506401000003d019600782089392505050565b614a2c614f12565b614a3582614c50565b8152614a4a614a458260006146ea565b614c8b565b602082018190526002900660011415611f75576020810180516401000003d019039052919050565b600082614aaf5760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b60448201526064016108db565b83516020850151600090614ac590600290615ed1565b15614ad157601c614ad4565b601b5b9050600070014551231950b75fc4402da1732fc9bebe19838709604080516000808252602090910191829052919250600190614b17908390869088908790615992565b6020604051602081039080840390855afa158015614b39573d6000803e3d6000fd5b505050602060405103519050600086604051602001614b58919061583e565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b614b95614f12565b835160208086015185519186015160009384938493614bb693909190614cab565b919450925090506401000003d019858209600114614c125760405162461bcd60e51b815260206004820152601960248201527834b73b2d1036bab9ba1031329034b73b32b939b29037b3103d60391b60448201526064016108db565b60405180604001604052806401000003d01980614c3157614c31615efb565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d0198110611f7557604080516020808201939093528151808203840181529082019091528051910120614c58565b6000610f98826002614ca46401000003d0196001615d65565b901c614d8b565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a0890506000614ceb83838585614e22565b9098509050614cfc88828e88614e46565b9098509050614d0d88828c87614e46565b90985090506000614d208d878b85614e46565b9098509050614d3188828686614e22565b9098509050614d4288828e89614e46565b9098509050818114614d77576401000003d019818a0998506401000003d01982890997506401000003d0198183099650614d7b565b8196505b5050505050509450945094915050565b600080614d96614f30565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152614dc8614f4e565b60208160c0846005600019fa925082614e185760405162461bcd60e51b81526020600482015260126024820152716269674d6f64457870206661696c7572652160701b60448201526064016108db565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215614ee4579160200282015b82811115614ee457825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614eaf565b50614ef0929150614f6c565b5090565b50805460008255906000526020600020908101906123c59190614f6c565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b80821115614ef05760008155600101614f6d565b8035611f7581615f53565b600082601f830112614f9d57600080fd5b813560206001600160401b03821115614fb857614fb8615f3d565b8160051b614fc7828201615d0b565b838152828101908684018388018501891015614fe257600080fd5b600093505b8584101561500e578035614ffa81615f53565b835260019390930192918401918401614fe7565b50979650505050505050565b600082601f83011261502b57600080fd5b615033615c9e565b80838560408601111561504557600080fd5b60005b6002811015615067578135845260209384019390910190600101615048565b509095945050505050565b60008083601f84011261508457600080fd5b5081356001600160401b0381111561509b57600080fd5b6020830191508360208285010111156150b357600080fd5b9250929050565b600082601f8301126150cb57600080fd5b81356001600160401b038111156150e4576150e4615f3d565b6150f7601f8201601f1916602001615d0b565b81815284602083860101111561510c57600080fd5b816020850160208301376000918101602001919091529392505050565b600060c0828403121561513b57600080fd5b615143615cc6565b905081356001600160401b03808216821461515d57600080fd5b81835260208401356020840152615176604085016151dc565b6040840152615187606085016151dc565b606084015261519860808501614f81565b608084015260a08401359150808211156151b157600080fd5b506151be848285016150ba565b60a08301525092915050565b803561ffff81168114611f7557600080fd5b803563ffffffff81168114611f7557600080fd5b80516001600160501b0381168114611f7557600080fd5b80356001600160601b0381168114611f7557600080fd5b60006020828403121561523057600080fd5b81356137b381615f53565b6000806040838503121561524e57600080fd5b823561525981615f53565b915061526760208401615207565b90509250929050565b6000806040838503121561528357600080fd5b823561528e81615f53565b9150602083013561529e81615f53565b809150509250929050565b600080606083850312156152bc57600080fd5b82356152c781615f53565b9150606083018410156152d957600080fd5b50926020919091019150565b600080600080606085870312156152fb57600080fd5b843561530681615f53565b93506020850135925060408501356001600160401b0381111561532857600080fd5b61533487828801615072565b95989497509550505050565b60006040828403121561535257600080fd5b6137b3838361501a565b60006020828403121561536e57600080fd5b81516137b381615f68565b60006020828403121561538b57600080fd5b5051919050565b600080602083850312156153a557600080fd5b82356001600160401b038111156153bb57600080fd5b6153c785828601615072565b90969095509350505050565b6000602082840312156153e557600080fd5b604051602081016001600160401b038111828210171561540757615407615f3d565b604052823561541581615f68565b81529392505050565b6000808284036101c081121561543357600080fd5b6101a08082121561544357600080fd5b61544b615ce8565b9150615457868661501a565b8252615466866040870161501a565b60208301526080850135604083015260a0850135606083015260c0850135608083015261549560e08601614f81565b60a08301526101006154a98782880161501a565b60c08401526154bc87610140880161501a565b60e0840152610180860135908301529092508301356001600160401b038111156154e557600080fd5b6154f185828601615129565b9150509250929050565b60006020828403121561550d57600080fd5b81356001600160401b0381111561552357600080fd5b820160c081850312156137b357600080fd5b60006020828403121561554757600080fd5b81356001600160401b038082111561555e57600080fd5b9083019060c0828603121561557257600080fd5b61557a615cc6565b823560ff8116811461558b57600080fd5b8152602083810135908201526155a360408401614f81565b60408201526060830135828111156155ba57600080fd5b6155c687828601614f8c565b6060830152506155d860808401615207565b60808201526155e960a08401615207565b60a082015295945050505050565b60006020828403121561560957600080fd5b6137b3826151ca565b60008060008060008086880360e081121561562c57600080fd5b615635886151ca565b9650615643602089016151dc565b9550615651604089016151dc565b945061565f606089016151dc565b9350608088013592506040609f198201121561567a57600080fd5b50615683615c9e565b61568f60a089016151dc565b815261569d60c089016151dc565b6020820152809150509295509295509295565b6000602082840312156156c257600080fd5b5035919050565b600080604083850312156156dc57600080fd5b82359150602083013561529e81615f53565b6000806040838503121561570157600080fd5b50508035926020909101359150565b60006020828403121561572257600080fd5b6137b3826151dc565b600080600080600060a0868803121561574357600080fd5b61574c866151f0565b945060208601519350604086015192506060860151915061576f608087016151f0565b90509295509295909350565b600081518084526020808501945080840160005b838110156157b45781516001600160a01b03168752958201959082019060010161578f565b509495945050505050565b8060005b60028110156109405781518452602093840193909101906001016157c3565b600081518084526020808501945080840160005b838110156157b4578151875295820195908201906001016157f6565b6000815180845261582a816020860160208601615e63565b601f01601f19169290920160200192915050565b61584881836157bf565b604001919050565b60008351615862818460208801615e63565b835190830190615876818360208801615e63565b01949350505050565b86815261588f60208201876157bf565b61589c60608201866157bf565b6158a960a08201856157bf565b6158b660e08201846157bf565b60609190911b6001600160601b0319166101208201526101340195945050505050565b8381526158e960208201846157bf565b606081019190915260800192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682526001600160601b0316602082015260400190565b60408101610f9882846157bf565b6020815260006137b360208301846157e2565b918252602082015260400190565b93845260ff9290921660208401526040830152606082015260800190565b6020815260006137b36020830184615812565b6020815260ff82511660208201526020820151604082015260018060a01b0360408301511660608201526000606083015160c06080840152615a0860e084018261577b565b60808501516001600160601b0390811660a0868101919091529095015190941660c0909301929092525090919050565b61ffff93841681529183166020830152909116604082015260600190565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b81811015615aa757845183529383019391830191600101615a8b565b509098975050505050505050565b9182526001600160a01b0316602082015260400190565b828152606081016137b360208301846157bf565b82815260406020820152600061380160408301846157e2565b86815285602082015261ffff85166040820152600063ffffffff808616606084015280851660808401525060c060a0830152613fc160c0830184615812565b878152602081018790526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c082018190526000906140e590830184615812565b8781526001600160401b03871660208201526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c082018190526000906140e590830184615812565b63ffffffff92831681529116602082015260400190565b6001600160401b0391909116815260200190565b6001600160601b038681168252851660208201526001600160401b03841660408201526001600160a01b038316606082015260a060808201819052600090615c4d9083018461577b565b979650505050505050565b6000808335601e19843603018112615c6f57600080fd5b8301803591506001600160401b03821115615c8957600080fd5b6020019150368190038213156150b357600080fd5b604080519081016001600160401b0381118282101715615cc057615cc0615f3d565b60405290565b60405160c081016001600160401b0381118282101715615cc057615cc0615f3d565b60405161012081016001600160401b0381118282101715615cc057615cc0615f3d565b604051601f8201601f191681016001600160401b0381118282101715615d3357615d33615f3d565b604052919050565b60008085851115615d4b57600080fd5b83861115615d5857600080fd5b5050820193919092039150565b60008219821115615d7857615d78615ee5565b500190565b60006001600160401b0382811684821680830382111561587657615876615ee5565b60006001600160601b0382811684821680830382111561587657615876615ee5565b600082615dd057615dd0615efb565b500490565b6000816000190483118215151615615def57615def615ee5565b500290565b600082821015615e0657615e06615ee5565b500390565b60006001600160601b0383811690831681811015615e2b57615e2b615ee5565b039392505050565b6001600160e01b03198135818116916004851015615e5b5780818660040360031b1b83161692505b505092915050565b60005b83811015615e7e578181015183820152602001615e66565b838111156109405750506000910152565b6000600019821415615ea357615ea3615ee5565b5060010190565b60006001600160401b0382811680821415615ec757615ec7615ee5565b6001019392505050565b600082615ee057615ee0615efb565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146123c557600080fd5b80151581146123c557600080fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000806000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"internalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"externalBalance\",\"type\":\"uint256\"}],\"name\":\"BalanceInvariantViolated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"}],\"name\":\"BlockhashNotInStore\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorNotRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToSendNative\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToTransferLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"transferredValue\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"expectedValue\",\"type\":\"uint96\"}],\"name\":\"InvalidNativeBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"have\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"min\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"max\",\"type\":\"uint16\"}],\"name\":\"InvalidRequestConfirmations\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"requestVersion\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"expectedVersion\",\"type\":\"uint8\"}],\"name\":\"InvalidVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkAlreadySet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeRequestedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCorrespondingRequest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"NoSuchProvingKey\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"NumWordsTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingRequestExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Reentrant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SubscriptionIDCollisionFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structVRFCoordinatorV2PlusUpgradedVersion.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"MigrationCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"NativeFundsRecovered\",\"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\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountLink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountNative\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldNativeBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newNativeBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFundedWithNative\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_NATIVE_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFCoordinatorV2PlusUpgradedVersion.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"fundSubscriptionWithNative\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveSubscriptionIds\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRequestConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"}],\"name\":\"migrate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"migrationVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedData\",\"type\":\"bytes\"}],\"name\":\"onMigration\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverNativeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"registerMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFV2PlusClient.RandomWordsRequest\",\"name\":\"req\",\"type\":\"tuple\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_config\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"reentrancyLock\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_currentSubNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_provingKeyHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requestCommitments\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalNativeBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"}],\"internalType\":\"structVRFCoordinatorV2PlusUpgradedVersion.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"}],\"name\":\"setLINKAndLINKNativeFeed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"withdrawNative\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b506040516200610538038062006105833981016040819052620000349162000183565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d7565b50505060601b6001600160601b031916608052620001b5565b6001600160a01b038116331415620001325760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156200019657600080fd5b81516001600160a01b0381168114620001ae57600080fd5b9392505050565b60805160601c615f2a620001db600039600081816104a601526136510152615f2a6000f3fe6080604052600436106101e05760003560e01c8062012291146101e5578063088070f5146102125780630ae095401461029257806315c48b84146102b457806318e3dd27146102dc5780631b6b6d231461031b578063294daa49146103485780632f622e6b14610364578063330987b314610384578063405b84fa146103a457806340d6bb82146103c457806341af6c87146103ef57806351cff8d91461041f5780635d06b4ab1461043f57806364d51a2a1461045f5780636598274414610474578063689c45171461049457806372e9d565146104c857806379ba5097146104e85780637bce14d1146104fd5780638402595e1461051d57806386fe91c71461053d5780638da5cb5b1461055d57806395b55cfc1461057b5780639b1c385e1461058e5780639d40a6fd146105bc578063a21a23e4146105e9578063a4c0ed36146105fe578063aa433aff1461061e578063aefb212f1461063e578063b08c87951461066b578063b2a7cac51461068b578063bec4c08c146106ab578063caf70c4a146106cb578063cb631797146106eb578063ce3f47191461070b578063d98e620e1461071e578063dac83d291461073e578063dc311dd31461075e578063e72f6e301461078f578063ee9d2d38146107af578063f2fde38b146107dc575b600080fd5b3480156101f157600080fd5b506101fa6107fc565b604051610209939291906159b5565b60405180910390f35b34801561021e57600080fd5b50600c5461025a9061ffff81169063ffffffff62010000820481169160ff600160301b82041691600160381b8204811691600160581b90041685565b6040805161ffff909616865263ffffffff9485166020870152921515928501929092528216606084015216608082015260a001610209565b34801561029e57600080fd5b506102b26102ad366004615628565b610878565b005b3480156102c057600080fd5b506102c960c881565b60405161ffff9091168152602001610209565b3480156102e857600080fd5b50600a5461030390600160601b90046001600160601b031681565b6040516001600160601b039091168152602001610209565b34801561032757600080fd5b5060025461033b906001600160a01b031681565b6040516102099190615859565b34801561035457600080fd5b5060405160028152602001610209565b34801561037057600080fd5b506102b261037f3660046151c6565b610946565b34801561039057600080fd5b5061030361039f36600461537d565b610aba565b3480156103b057600080fd5b506102b26103bf366004615628565b610f73565b3480156103d057600080fd5b506103da6101f481565b60405163ffffffff9091168152602001610209565b3480156103fb57600080fd5b5061040f61040a36600461560f565b61135e565b6040519015158152602001610209565b34801561042b57600080fd5b506102b261043a3660046151c6565b6114ff565b34801561044b57600080fd5b506102b261045a3660046151c6565b6116b0565b34801561046b57600080fd5b506102c9606481565b34801561048057600080fd5b506102b261048f3660046151e3565b611767565b3480156104a057600080fd5b5061033b7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104d457600080fd5b5060035461033b906001600160a01b031681565b3480156104f457600080fd5b506102b26117c7565b34801561050957600080fd5b506102b2610518366004615277565b611871565b34801561052957600080fd5b506102b26105383660046151c6565b61196a565b34801561054957600080fd5b50600a54610303906001600160601b031681565b34801561056957600080fd5b506000546001600160a01b031661033b565b6102b261058936600461560f565b611a76565b34801561059a57600080fd5b506105ae6105a936600461545a565b611bba565b604051908152602001610209565b3480156105c857600080fd5b506007546105dc906001600160401b031681565b6040516102099190615b4e565b3480156105f557600080fd5b506105ae611f2a565b34801561060a57600080fd5b506102b261061936600461521c565b612178565b34801561062a57600080fd5b506102b261063936600461560f565b612315565b34801561064a57600080fd5b5061065e61065936600461564d565b612378565b60405161020991906158d0565b34801561067757600080fd5b506102b2610686366004615571565b612479565b34801561069757600080fd5b506102b26106a636600461560f565b6125ed565b3480156106b757600080fd5b506102b26106c6366004615628565b612711565b3480156106d757600080fd5b506105ae6106e636600461529f565b6128a8565b3480156106f757600080fd5b506102b2610706366004615628565b6128d8565b6102b26107193660046152f1565b612bc5565b34801561072a57600080fd5b506105ae61073936600461560f565b612ed6565b34801561074a57600080fd5b506102b2610759366004615628565b612ef7565b34801561076a57600080fd5b5061077e61077936600461560f565b613007565b604051610209959493929190615b62565b34801561079b57600080fd5b506102b26107aa3660046151c6565b613102565b3480156107bb57600080fd5b506105ae6107ca36600461560f565b600f6020526000908152604090205481565b3480156107e857600080fd5b506102b26107f73660046151c6565b6132dd565b600c54600e805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff1693919283919083018282801561086657602002820191906000526020600020905b815481526020019060010190808311610852575b50505050509050925092509250909192565b60008281526005602052604090205482906001600160a01b0316806108b057604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146108e45780604051636c51fda960e11b81526004016108db9190615859565b60405180910390fd5b600c54600160301b900460ff161561090f5760405163769dd35360e11b815260040160405180910390fd5b6109188461135e565b1561093657604051631685ecdd60e31b815260040160405180910390fd5b61094084846132ee565b50505050565b600c54600160301b900460ff16156109715760405163769dd35360e11b815260040160405180910390fd5b6109796134a9565b600b54600160601b90046001600160601b03166109a957604051631e9acf1760e31b815260040160405180910390fd5b600b8054600160601b90046001600160601b0316908190600c6109cc8380615d6a565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a600c8282829054906101000a90046001600160601b0316610a149190615d6a565b92506101000a8154816001600160601b0302191690836001600160601b031602179055506000826001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114610a8e576040519150601f19603f3d011682016040523d82523d6000602084013e610a93565b606091505b5050905080610ab55760405163950b247960e01b815260040160405180910390fd5b505050565b600c54600090600160301b900460ff1615610ae85760405163769dd35360e11b815260040160405180910390fd5b60005a90506000610af985856134fe565b90506000846060015163ffffffff166001600160401b03811115610b1f57610b1f615e9c565b604051908082528060200260200182016040528015610b48578160200160208202803683370190505b50905060005b856060015163ffffffff16811015610bbf57826040015181604051602001610b779291906158e3565b6040516020818303038152906040528051906020012060001c828281518110610ba257610ba2615e86565b602090810291909101015280610bb781615dee565b915050610b4e565b50602080830180516000908152600f9092526040808320839055905190518291631fe543e360e01b91610bf791908690602401615a3f565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600c805460ff60301b1916600160301b179055908801516080890151919250600091610c5c9163ffffffff169084613769565b600c805460ff60301b19169055602089810151600090815260069091526040902054909150600160c01b90046001600160401b0316610c9c816001615cdc565b6020808b0151600090815260069091526040812080546001600160401b0393909316600160c01b026001600160c01b039093169290921790915560a08a01518051610ce990600190615d53565b81518110610cf957610cf9615e86565b602091010151600c5460f89190911c6001149150600090610d2a908a90600160581b900463ffffffff163a856137b7565b90508115610e22576020808c01516000908152600690915260409020546001600160601b03808316600160601b909204161015610d7a57604051631e9acf1760e31b815260040160405180910390fd5b60208b81015160009081526006909152604090208054829190600c90610db1908490600160601b90046001600160601b0316615d6a565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600b600c8282829054906101000a90046001600160601b0316610df99190615cfe565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550610efd565b6020808c01516000908152600690915260409020546001600160601b0380831691161015610e6357604051631e9acf1760e31b815260040160405180910390fd5b6020808c015160009081526006909152604081208054839290610e909084906001600160601b0316615d6a565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600b60008282829054906101000a90046001600160601b0316610ed89190615cfe565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b8a6020015188602001517f49580fdfd9497e1ed5c1b1cec0495087ae8e3f1267470ec2fb015db32e3d6aa78a604001518488604051610f5a939291909283526001600160601b039190911660208301521515604082015260600190565b60405180910390a3985050505050505050505b92915050565b600c54600160301b900460ff1615610f9e5760405163769dd35360e11b815260040160405180910390fd5b610fa781613806565b610fc65780604051635428d44960e01b81526004016108db9190615859565b600080600080610fd586613007565b945094505093509350336001600160a01b0316826001600160a01b0316146110385760405162461bcd60e51b81526020600482015260166024820152752737ba1039bab139b1b934b83a34b7b71037bbb732b960511b60448201526064016108db565b6110418661135e565b156110875760405162461bcd60e51b815260206004820152601660248201527550656e64696e6720726571756573742065786973747360501b60448201526064016108db565b60006040518060c0016040528061109c600290565b60ff168152602001888152602001846001600160a01b03168152602001838152602001866001600160601b03168152602001856001600160601b031681525090506000816040516020016110f09190615922565b604051602081830303815290604052905061110a88613870565b505060405163ce3f471960e01b81526001600160a01b0388169063ce3f4719906001600160601b0388169061114390859060040161590f565b6000604051808303818588803b15801561115c57600080fd5b505af1158015611170573d6000803e3d6000fd5b50506002546001600160a01b031615801593509150611199905057506001600160601b03861615155b156112635760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906111d0908a908a906004016158a0565b602060405180830381600087803b1580156111ea57600080fd5b505af11580156111fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122291906152bb565b6112635760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742066756e647360701b60448201526064016108db565b600c805460ff60301b1916600160301b17905560005b835181101561130c5783818151811061129457611294615e86565b60200260200101516001600160a01b0316638ea98117896040518263ffffffff1660e01b81526004016112c79190615859565b600060405180830381600087803b1580156112e157600080fd5b505af11580156112f5573d6000803e3d6000fd5b50505050808061130490615dee565b915050611279565b50600c805460ff60301b191690556040517fd63ca8cb945956747ee69bfdc3ea754c24a4caf7418db70e46052f7850be41879061134c9089908b9061586d565b60405180910390a15050505050505050565b6000818152600560209081526040808320815160608101835281546001600160a01b03908116825260018301541681850152600282018054845181870281018701865281815287969395860193909291908301828280156113e857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113ca575b505050505081525050905060005b8160400151518110156114f55760005b600e548110156114e25760006114ab600e838154811061142857611428615e86565b90600052602060002001548560400151858151811061144957611449615e86565b602002602001015188600460008960400151898151811061146c5761146c615e86565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081208d82529092529020546001600160401b0316613abe565b506000818152600f6020526040902054909150156114cf5750600195945050505050565b50806114da81615dee565b915050611406565b50806114ed81615dee565b9150506113f6565b5060009392505050565b600c54600160301b900460ff161561152a5760405163769dd35360e11b815260040160405180910390fd5b6115326134a9565b6002546001600160a01b031661155b5760405163c1f0c0a160e01b815260040160405180910390fd5b600b546001600160601b031661158457604051631e9acf1760e31b815260040160405180910390fd5b600b80546001600160601b031690819060006115a08380615d6a565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555080600a60008282829054906101000a90046001600160601b03166115e89190615d6a565b82546001600160601b039182166101009390930a92830291909202199091161790555060025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb9061163d90859085906004016158a0565b602060405180830381600087803b15801561165757600080fd5b505af115801561166b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168f91906152bb565b6116ac57604051631e9acf1760e31b815260040160405180910390fd5b5050565b6116b86134a9565b6116c181613806565b156116e1578060405163ac8a27ef60e01b81526004016108db9190615859565b601280546001810182556000919091527fbb8a6a4669ba250d26cd7a459eca9d215f8307e33aebe50379bc5a3617ec34440180546001600160a01b0319166001600160a01b0383161790556040517fb7cabbfc11e66731fc77de0444614282023bcbd41d16781c753a431d0af016259061175c908390615859565b60405180910390a150565b61176f6134a9565b6002546001600160a01b03161561179957604051631688c53760e11b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b6001546001600160a01b0316331461181a5760405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b60448201526064016108db565b60008054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6118796134a9565b6040805180820182526000916118a89190849060029083908390808284376000920191909152506128a8915050565b6000818152600d602052604090205490915060ff16156118de57604051634a0b8fa760e01b8152600481018290526024016108db565b6000818152600d6020526040808220805460ff19166001908117909155600e805491820181559092527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909101829055517fc9583fd3afa3d7f16eb0b88d0268e7d05c09bafa4b21e092cbd1320e1bc8089d9061195e9083815260200190565b60405180910390a15050565b6119726134a9565b600a544790600160601b90046001600160601b0316818111156119ac5780826040516354ced18160e11b81526004016108db9291906158e3565b81811015610ab55760006119c08284615d53565b90506000846001600160a01b03168260405160006040518083038185875af1925050503d8060008114611a0f576040519150601f19603f3d011682016040523d82523d6000602084013e611a14565b606091505b5050905080611a365760405163950b247960e01b815260040160405180910390fd5b7f4aed7c8eed0496c8c19ea2681fcca25741c1602342e38b045d9f1e8e905d2e9c8583604051611a6792919061586d565b60405180910390a15050505050565b600c54600160301b900460ff1615611aa15760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b0316611ad657604051630fb532db60e11b815260040160405180910390fd5b60008181526006602052604090208054600160601b90046001600160601b0316903490600c611b058385615cfe565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555034600a600c8282829054906101000a90046001600160601b0316611b4d9190615cfe565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f7603b205d03651ee812f803fccde89f1012e545a9c99f0abfea9cedd0fd8e902823484611ba09190615cc4565b604051611bae9291906158e3565b60405180910390a25050565b600c54600090600160301b900460ff1615611be85760405163769dd35360e11b815260040160405180910390fd5b6020808301356000908152600590915260409020546001600160a01b0316611c2357604051630fb532db60e11b815260040160405180910390fd5b3360009081526004602090815260408083208583013584529091529020546001600160401b031680611c70578260200135336040516379bfd40160e01b81526004016108db929190615a14565b600c5461ffff16611c876060850160408601615556565b61ffff161080611caa575060c8611ca46060850160408601615556565b61ffff16115b15611ce457611cbf6060840160408501615556565b600c5460405163539c34bb60e11b81526108db929161ffff169060c890600401615997565b600c5462010000900463ffffffff16611d03608085016060860161566f565b63ffffffff161115611d4957611d1f608084016060850161566f565b600c54604051637aebf00f60e11b81526108db929162010000900463ffffffff1690600401615b37565b6101f4611d5c60a085016080860161566f565b63ffffffff161115611d9657611d7860a084016080850161566f565b6101f46040516311ce1afb60e21b81526004016108db929190615b37565b6000611da3826001615cdc565b9050600080611db9863533602089013586613abe565b90925090506000611dd5611dd060a0890189615bb7565b613b47565b90506000611de282613bc4565b905083611ded613c35565b60208a0135611e0260808c0160608d0161566f565b611e1260a08d0160808e0161566f565b3386604051602001611e2a9796959493929190615a97565b60405160208183030381529060405280519060200120600f600086815260200190815260200160002081905550336001600160a01b0316886020013589600001357feb0e3652e0f44f417695e6e90f2f42c99b65cd7169074c5a654b16b9748c3a4e87878d6040016020810190611ea19190615556565b8e6060016020810190611eb4919061566f565b8f6080016020810190611ec7919061566f565b89604051611eda96959493929190615a58565b60405180910390a45050336000908152600460209081526040808320898301358452909152902080546001600160401b0319166001600160401b039490941693909317909255925050505b919050565b600c54600090600160301b900460ff1615611f585760405163769dd35360e11b815260040160405180910390fd5b600033611f66600143615d53565b600754604051606093841b6001600160601b03199081166020830152924060348201523090931b909116605483015260c01b6001600160c01b031916606882015260700160408051601f198184030181529190528051602090910120600780549192506001600160401b03909116906000611fe083615e09565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550506000806001600160401b0381111561201f5761201f615e9c565b604051908082528060200260200182016040528015612048578160200160208202803683370190505b506040805160608082018352600080835260208084018281528486018381528984526006835286842095518654925191516001600160601b039182166001600160c01b031990941693909317600160601b9190921602176001600160c01b0316600160c01b6001600160401b039092169190910217909355835191820184523382528183018181528285018681528883526005855294909120825181546001600160a01b03199081166001600160a01b0392831617835592516001830180549094169116179091559251805194955090936121299260028501920190614e37565b5061213991506008905083613cc5565b50817f1d3015d7ba850fa198dc7b1a3f5d42779313a681035f77c8c03764c61005518d3360405161216a9190615859565b60405180910390a250905090565b600c54600160301b900460ff16156121a35760405163769dd35360e11b815260040160405180910390fd5b6002546001600160a01b031633146121ce576040516344b0e3c360e01b815260040160405180910390fd5b602081146121ef57604051638129bbcd60e01b815260040160405180910390fd5b60006121fd8284018461560f565b6000818152600560205260409020549091506001600160a01b031661223557604051630fb532db60e11b815260040160405180910390fd5b600081815260066020526040812080546001600160601b03169186919061225c8385615cfe565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600a60008282829054906101000a90046001600160601b03166122a49190615cfe565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f1ced9348ff549fceab2ac57cd3a9de38edaaab274b725ee82c23e8fc8c4eec7a8287846122f79190615cc4565b6040516123059291906158e3565b60405180910390a2505050505050565b61231d6134a9565b6000818152600560205260409020546001600160a01b031661235257604051630fb532db60e11b815260040160405180910390fd5b6000818152600560205260409020546123759082906001600160a01b03166132ee565b50565b606060006123866008613cd1565b90508084106123a857604051631390f2a160e01b815260040160405180910390fd5b60006123b48486615cc4565b9050818111806123c2575083155b6123cc57806123ce565b815b905060006123dc8683615d53565b6001600160401b038111156123f3576123f3615e9c565b60405190808252806020026020018201604052801561241c578160200160208202803683370190505b50905060005b815181101561246f576124406124388883615cc4565b600890613cdb565b82828151811061245257612452615e86565b60209081029190910101528061246781615dee565b915050612422565b5095945050505050565b6124816134a9565b60c861ffff871611156124ae57858660c860405163539c34bb60e11b81526004016108db93929190615997565b600082136124d2576040516321ea67b360e11b8152600481018390526024016108db565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600c805465ffffffffffff1916881762010000870217600160301b600160781b031916600160381b850263ffffffff60581b191617600160581b83021790558a51601180548d8701519289166001600160401b031990911617600160201b92891692909202919091179081905560108d90558a519788528785019590955298860191909152840196909652938201879052838116928201929092529190921c90911660c08201527f777357bb93f63d088f18112d3dba38457aec633eb8f1341e1d418380ad328e789060e00160405180910390a1505050505050565b600c54600160301b900460ff16156126185760405163769dd35360e11b815260040160405180910390fd5b6000818152600560205260409020546001600160a01b031661264d57604051630fb532db60e11b815260040160405180910390fd5b6000818152600560205260409020600101546001600160a01b031633146126a4576000818152600560205260409081902060010154905163d084e97560e01b81526108db916001600160a01b031690600401615859565b6000818152600560205260409081902080546001600160a01b031980821633908117845560019093018054909116905591516001600160a01b039092169183917fd4114ab6e9af9f597c52041f32d62dc57c5c4e4c0d4427006069635e216c938691611bae918591615886565b60008281526005602052604090205482906001600160a01b03168061274957604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146127745780604051636c51fda960e11b81526004016108db9190615859565b600c54600160301b900460ff161561279f5760405163769dd35360e11b815260040160405180910390fd5b600084815260056020526040902060020154606414156127d2576040516305a48e0f60e01b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b03161561280957610940565b6001600160a01b0383166000818152600460209081526040808320888452825280832080546001600160401b031916600190811790915560058352818420600201805491820181558452919092200180546001600160a01b0319169092179091555184907f1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e19061289a908690615859565b60405180910390a250505050565b6000816040516020016128bb91906158c2565b604051602081830303815290604052805190602001209050919050565b60008281526005602052604090205482906001600160a01b03168061291057604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461293b5780604051636c51fda960e11b81526004016108db9190615859565b600c54600160301b900460ff16156129665760405163769dd35360e11b815260040160405180910390fd5b61296f8461135e565b1561298d57604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b03831660009081526004602090815260408083208784529091529020546001600160401b03166129db5783836040516379bfd40160e01b81526004016108db929190615a14565b600084815260056020908152604080832060020180548251818502810185019093528083529192909190830182828015612a3e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a20575b50505050509050600060018251612a559190615d53565b905060005b8251811015612b6157856001600160a01b0316838281518110612a7f57612a7f615e86565b60200260200101516001600160a01b03161415612b4f576000838381518110612aaa57612aaa615e86565b6020026020010151905080600560008a81526020019081526020016000206002018381548110612adc57612adc615e86565b600091825260208083209190910180546001600160a01b0319166001600160a01b039490941693909317909255898152600590915260409020600201805480612b2757612b27615e70565b600082815260209020810160001990810180546001600160a01b031916905501905550612b61565b80612b5981615dee565b915050612a5a565b506001600160a01b03851660009081526004602090815260408083208984529091529081902080546001600160401b03191690555186907f32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a790612305908890615859565b6000612bd382840184615494565b9050806000015160ff16600114612c0c57805160405163237d181f60e21b815260ff9091166004820152600160248201526044016108db565b8060a001516001600160601b03163414612c505760a08101516040516306acf13560e41b81523460048201526001600160601b0390911660248201526044016108db565b6020808201516000908152600590915260409020546001600160a01b031615612c8c576040516326afa43560e11b815260040160405180910390fd5b60005b816060015151811015612d2c5760016004600084606001518481518110612cb857612cb8615e86565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008460200151815260200190815260200160002060006101000a8154816001600160401b0302191690836001600160401b031602179055508080612d2490615dee565b915050612c8f565b50604080516060808201835260808401516001600160601b03908116835260a0850151811660208085019182526000858701818152828901805183526006845288832097518854955192516001600160401b0316600160c01b026001600160c01b03938816600160601b026001600160c01b0319909716919097161794909417169390931790945584518084018652868601516001600160a01b03908116825281860184815294880151828801908152925184526005865295909220825181549087166001600160a01b0319918216178255935160018201805491909716941693909317909455925180519192612e2b92600285019290910190614e37565b5050506080810151600a8054600090612e4e9084906001600160601b0316615cfe565b92506101000a8154816001600160601b0302191690836001600160601b031602179055508060a00151600a600c8282829054906101000a90046001600160601b0316612e9a9190615cfe565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555061094081602001516008613cc590919063ffffffff16565b600e8181548110612ee657600080fd5b600091825260209091200154905081565b60008281526005602052604090205482906001600160a01b031680612f2f57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614612f5a5780604051636c51fda960e11b81526004016108db9190615859565b600c54600160301b900460ff1615612f855760405163769dd35360e11b815260040160405180910390fd5b6000848152600560205260409020600101546001600160a01b03848116911614610940576000848152600560205260409081902060010180546001600160a01b0319166001600160a01b0386161790555184907f21a4dad170a6bf476c31bbcf4a16628295b0e450672eec25d7c93308e05344a19061289a9033908790615886565b6000818152600560205260408120548190819081906060906001600160a01b031661304557604051630fb532db60e11b815260040160405180910390fd5b60008681526006602090815260408083205460058352928190208054600290910180548351818602810186019094528084526001600160601b0380871696600160601b810490911695600160c01b9091046001600160401b0316946001600160a01b03909416939183918301828280156130e857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116130ca575b505050505090509450945094509450945091939590929450565b61310a6134a9565b6002546001600160a01b03166131335760405163c1f0c0a160e01b815260040160405180910390fd5b6002546040516370a0823160e01b81526000916001600160a01b0316906370a0823190613164903090600401615859565b60206040518083038186803b15801561317c57600080fd5b505afa158015613190573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131b491906152d8565b600a549091506001600160601b0316818111156131e85780826040516354ced18160e11b81526004016108db9291906158e3565b81811015610ab55760006131fc8284615d53565b60025460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb9061322f908790859060040161586d565b602060405180830381600087803b15801561324957600080fd5b505af115801561325d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061328191906152bb565b61329e57604051631f01ff1360e21b815260040160405180910390fd5b7f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b43660084826040516132cf92919061586d565b60405180910390a150505050565b6132e56134a9565b61237581613ce7565b6000806132fa84613870565b60025491935091506001600160a01b03161580159061332157506001600160601b03821615155b156133d05760025460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906133619086906001600160601b0387169060040161586d565b602060405180830381600087803b15801561337b57600080fd5b505af115801561338f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133b391906152bb565b6133d057604051631e9acf1760e31b815260040160405180910390fd5b6000836001600160a01b0316826001600160601b031660405160006040518083038185875af1925050503d8060008114613426576040519150601f19603f3d011682016040523d82523d6000602084013e61342b565b606091505b505090508061344d5760405163950b247960e01b815260040160405180910390fd5b604080516001600160a01b03861681526001600160601b038581166020830152841681830152905186917f8c74ce8b8cf87f5eb001275c8be27eb34ea2b62bfab6814fcc62192bb63e81c4919081900360600190a25050505050565b6000546001600160a01b031633146134fc5760405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b60448201526064016108db565b565b6040805160608101825260008082526020820181905291810191909152600061352a84600001516128a8565b6000818152600d602052604090205490915060ff1661355f57604051631dfd6e1360e21b8152600481018290526024016108db565b60008185608001516040516020016135789291906158e3565b60408051601f1981840301815291815281516020928301206000818152600f909352912054909150806135be57604051631b44092560e11b815260040160405180910390fd5b845160208087015160408089015160608a015160808b015160a08c015193516135ed978a979096959101615ae3565b6040516020818303038152906040528051906020012081146136225760405163354a450b60e21b815260040160405180910390fd5b60006136318660000151613d8b565b9050806136f8578551604051631d2827a760e31b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163e9413d38916136859190600401615b4e565b60206040518083038186803b15801561369d57600080fd5b505afa1580156136b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136d591906152d8565b9050806136f857855160405163175dadad60e01b81526108db9190600401615b4e565b600087608001518260405160200161371a929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c905060006137418983613e68565b6040805160608101825297885260208801969096529486019490945250929695505050505050565b60005a61138881101561377b57600080fd5b61138881039050846040820482031161379357600080fd5b50823b61379f57600080fd5b60008083516020850160008789f190505b9392505050565b600081156137e4576011546137dd9086908690600160201b900463ffffffff1686613ed3565b90506137fe565b6011546137fb908690869063ffffffff1686613f75565b90505b949350505050565b6000805b60125481101561386757826001600160a01b03166012828154811061383157613831615e86565b6000918252602090912001546001600160a01b031614156138555750600192915050565b8061385f81615dee565b91505061380a565b50600092915050565b6000818152600560209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879687969495948601939192908301828280156138fc57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116138de575b505050919092525050506000858152600660209081526040808320815160608101835290546001600160601b03808216808452600160601b8304909116948301859052600160c01b9091046001600160401b0316928201929092529096509094509192505b8260400151518110156139d857600460008460400151838151811061398857613988615e86565b6020908102919091018101516001600160a01b031682528181019290925260409081016000908120898252909252902080546001600160401b0319169055806139d081615dee565b915050613961565b50600085815260056020526040812080546001600160a01b03199081168255600182018054909116905590613a106002830182614e9c565b5050600085815260066020526040812055613a2c60088661409a565b50600a8054859190600090613a4b9084906001600160601b0316615d6a565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555082600a600c8282829054906101000a90046001600160601b0316613a939190615d6a565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505050915091565b60408051602081018690526001600160a01b03851691810191909152606081018390526001600160401b03821660808201526000908190819060a00160408051601f198184030181529082905280516020918201209250613b239189918491016158e3565b60408051808303601f19018152919052805160209091012097909650945050505050565b60408051602081019091526000815281613b705750604080516020810190915260008152610f6d565b63125fa26760e31b613b828385615d92565b6001600160e01b03191614613baa57604051632923fee760e11b815260040160405180910390fd5b613bb78260048186615c9a565b8101906137b09190615332565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401613bfd91511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b600046613c41816140a6565b15613cbe5760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613c8057600080fd5b505afa158015613c94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cb891906152d8565b91505090565b4391505090565b60006137b083836140c9565b6000610f6d825490565b60006137b08383614118565b6001600160a01b038116331415613d3a5760405162461bcd60e51b815260206004820152601760248201527621b0b73737ba103a3930b739b332b9103a379039b2b63360491b60448201526064016108db565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600046613d97816140a6565b15613e5957610100836001600160401b0316613db1613c35565b613dbb9190615d53565b1180613dd75750613dca613c35565b836001600160401b031610155b15613de55750600092915050565b6040516315a03d4160e11b8152606490632b407a8290613e09908690600401615b4e565b60206040518083038186803b158015613e2157600080fd5b505afa158015613e35573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137b091906152d8565b50506001600160401b03164090565b6000613e9c8360000151846020015185604001518660600151868860a001518960c001518a60e001518b6101000151614142565b60038360200151604051602001613eb4929190615a2b565b60408051601f1981840301815291905280516020909101209392505050565b600080613f166000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061435d92505050565b905060005a613f258888615cc4565b613f2f9190615d53565b613f399085615d34565b90506000613f5263ffffffff871664e8d4a51000615d34565b905082613f5f8284615cc4565b613f699190615cc4565b98975050505050505050565b600080613f80614422565b905060008113613fa6576040516321ea67b360e11b8152600481018290526024016108db565b6000613fe86000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061435d92505050565b9050600082825a613ff98b8b615cc4565b6140039190615d53565b61400d9088615d34565b6140179190615cc4565b61402990670de0b6b3a7640000615d34565b6140339190615d20565b9050600061404c63ffffffff881664e8d4a51000615d34565b905061406381676765c793fa10079d601b1b615d53565b8211156140835760405163e80fa38160e01b815260040160405180910390fd5b61408d8183615cc4565b9998505050505050505050565b60006137b083836144ed565b600061a4b18214806140ba575062066eed82145b80610f6d57505062066eee1490565b600081815260018301602052604081205461411057508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610f6d565b506000610f6d565b600082600001828154811061412f5761412f615e86565b9060005260206000200154905092915050565b61414b896145e0565b6141945760405162461bcd60e51b815260206004820152601a6024820152797075626c6963206b6579206973206e6f74206f6e20637572766560301b60448201526064016108db565b61419d886145e0565b6141e15760405162461bcd60e51b815260206004820152601560248201527467616d6d61206973206e6f74206f6e20637572766560581b60448201526064016108db565b6141ea836145e0565b6142365760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e20637572766500000060448201526064016108db565b61423f826145e0565b61428a5760405162461bcd60e51b815260206004820152601c60248201527b73486173685769746e657373206973206e6f74206f6e20637572766560201b60448201526064016108db565b614296878a88876146a3565b6142de5760405162461bcd60e51b81526020600482015260196024820152786164647228632a706b2b732a6729213d5f755769746e65737360381b60448201526064016108db565b60006142ea8a876147b7565b905060006142fd898b878b86898961481b565b9050600061430e838d8d8a8661492e565b9050808a1461434f5760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b60448201526064016108db565b505050505050505050505050565b600046614369816140a6565b156143a857606c6001600160a01b031663c6f7de0e6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e2157600080fd5b6143b18161496e565b1561386757600f602160991b016001600160a01b03166349948e0e84604051806080016040528060488152602001615ed6604891396040516020016143f79291906157af565b6040516020818303038152906040526040518263ffffffff1660e01b8152600401613e09919061590f565b600c5460035460408051633fabe5a360e21b81529051600093600160381b900463ffffffff169283151592859283926001600160a01b03169163feaf968c9160048083019260a0929190829003018186803b15801561448057600080fd5b505afa158015614494573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144b8919061568a565b5094509092508491505080156144dc57506144d38242615d53565b8463ffffffff16105b156137fe5750601054949350505050565b600081815260018301602052604081205480156145d6576000614511600183615d53565b855490915060009061452590600190615d53565b905081811461458a57600086600001828154811061454557614545615e86565b906000526020600020015490508087600001848154811061456857614568615e86565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061459b5761459b615e70565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610f6d565b6000915050610f6d565b80516000906401000003d0191161462e5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420782d6f7264696e61746560701b60448201526064016108db565b60208201516401000003d0191161467c5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420792d6f7264696e61746560701b60448201526064016108db565b60208201516401000003d01990800961469c8360005b60200201516149a8565b1492915050565b60006001600160a01b0382166146e95760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b60448201526064016108db565b60208401516000906001161561470057601c614703565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe199182039250600091908909875160408051600080825260209091019182905292935060019161476d918691889187906158f1565b6020604051602081039080840390855afa15801561478f573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b6147bf614eba565b6147ec600184846040516020016147d893929190615838565b6040516020818303038152906040526149cc565b90505b6147f8816145e0565b610f6d57805160408051602081019290925261481491016147d8565b90506147ef565b614823614eba565b825186516401000003d01990819006910614156148825760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e6374000060448201526064016108db565b61488d878988614a1a565b6148d25760405162461bcd60e51b8152602060048201526016602482015275119a5c9cdd081b5d5b0818da1958dac819985a5b195960521b60448201526064016108db565b6148dd848685614a1a565b6149235760405162461bcd60e51b815260206004820152601760248201527614d958dbdb99081b5d5b0818da1958dac819985a5b1959604a1b60448201526064016108db565b613f69868484614b35565b60006002868686858760405160200161494c969594939291906157de565b60408051601f1981840301815291905280516020909101209695505050505050565b6000600a82148061498057506101a482145b8061498d575062aa37dc82145b80614999575061210582145b80610f6d57505062014a331490565b6000806401000003d01980848509840990506401000003d019600782089392505050565b6149d4614eba565b6149dd82614bf8565b81526149f26149ed826000614692565b614c33565b602082018190526002900660011415611f25576020810180516401000003d019039052919050565b600082614a575760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b60448201526064016108db565b83516020850151600090614a6d90600290615e30565b15614a7957601c614a7c565b601b5b9050600070014551231950b75fc4402da1732fc9bebe19838709604080516000808252602090910191829052919250600190614abf9083908690889087906158f1565b6020604051602081039080840390855afa158015614ae1573d6000803e3d6000fd5b505050602060405103519050600086604051602001614b00919061579d565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b614b3d614eba565b835160208086015185519186015160009384938493614b5e93909190614c53565b919450925090506401000003d019858209600114614bba5760405162461bcd60e51b815260206004820152601960248201527834b73b2d1036bab9ba1031329034b73b32b939b29037b3103d60391b60448201526064016108db565b60405180604001604052806401000003d01980614bd957614bd9615e5a565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d0198110611f2557604080516020808201939093528151808203840181529082019091528051910120614c00565b6000610f6d826002614c4c6401000003d0196001615cc4565b901c614d33565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a0890506000614c9383838585614dca565b9098509050614ca488828e88614dee565b9098509050614cb588828c87614dee565b90985090506000614cc88d878b85614dee565b9098509050614cd988828686614dca565b9098509050614cea88828e89614dee565b9098509050818114614d1f576401000003d019818a0998506401000003d01982890997506401000003d0198183099650614d23565b8196505b5050505050509450945094915050565b600080614d3e614ed8565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152614d70614ef6565b60208160c0846005600019fa925082614dc05760405162461bcd60e51b81526020600482015260126024820152716269674d6f64457870206661696c7572652160701b60448201526064016108db565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215614e8c579160200282015b82811115614e8c57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614e57565b50614e98929150614f14565b5090565b50805460008255906000526020600020908101906123759190614f14565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b80821115614e985760008155600101614f15565b8035611f2581615eb2565b600082601f830112614f4557600080fd5b813560206001600160401b03821115614f6057614f60615e9c565b8160051b614f6f828201615c6a565b838152828101908684018388018501891015614f8a57600080fd5b600093505b85841015614fb6578035614fa281615eb2565b835260019390930192918401918401614f8f565b50979650505050505050565b600082601f830112614fd357600080fd5b614fdb615bfd565b808385604086011115614fed57600080fd5b60005b600281101561500f578135845260209384019390910190600101614ff0565b509095945050505050565b60008083601f84011261502c57600080fd5b5081356001600160401b0381111561504357600080fd5b60208301915083602082850101111561505b57600080fd5b9250929050565b600082601f83011261507357600080fd5b81356001600160401b0381111561508c5761508c615e9c565b61509f601f8201601f1916602001615c6a565b8181528460208386010111156150b457600080fd5b816020850160208301376000918101602001919091529392505050565b600060c082840312156150e357600080fd5b6150eb615c25565b905081356001600160401b03808216821461510557600080fd5b8183526020840135602084015261511e60408501615184565b604084015261512f60608501615184565b606084015261514060808501614f29565b608084015260a084013591508082111561515957600080fd5b5061516684828501615062565b60a08301525092915050565b803561ffff81168114611f2557600080fd5b803563ffffffff81168114611f2557600080fd5b80516001600160501b0381168114611f2557600080fd5b80356001600160601b0381168114611f2557600080fd5b6000602082840312156151d857600080fd5b81356137b081615eb2565b600080604083850312156151f657600080fd5b823561520181615eb2565b9150602083013561521181615eb2565b809150509250929050565b6000806000806060858703121561523257600080fd5b843561523d81615eb2565b93506020850135925060408501356001600160401b0381111561525f57600080fd5b61526b8782880161501a565b95989497509550505050565b60006040828403121561528957600080fd5b8260408301111561529957600080fd5b50919050565b6000604082840312156152b157600080fd5b6137b08383614fc2565b6000602082840312156152cd57600080fd5b81516137b081615ec7565b6000602082840312156152ea57600080fd5b5051919050565b6000806020838503121561530457600080fd5b82356001600160401b0381111561531a57600080fd5b6153268582860161501a565b90969095509350505050565b60006020828403121561534457600080fd5b604051602081016001600160401b038111828210171561536657615366615e9c565b604052823561537481615ec7565b81529392505050565b6000808284036101c081121561539257600080fd5b6101a0808212156153a257600080fd5b6153aa615c47565b91506153b68686614fc2565b82526153c58660408701614fc2565b60208301526080850135604083015260a0850135606083015260c085013560808301526153f460e08601614f29565b60a083015261010061540887828801614fc2565b60c084015261541b876101408801614fc2565b60e0840152610180860135908301529092508301356001600160401b0381111561544457600080fd5b615450858286016150d1565b9150509250929050565b60006020828403121561546c57600080fd5b81356001600160401b0381111561548257600080fd5b820160c081850312156137b057600080fd5b6000602082840312156154a657600080fd5b81356001600160401b03808211156154bd57600080fd5b9083019060c082860312156154d157600080fd5b6154d9615c25565b823560ff811681146154ea57600080fd5b81526020838101359082015261550260408401614f29565b604082015260608301358281111561551957600080fd5b61552587828601614f34565b606083015250615537608084016151af565b608082015261554860a084016151af565b60a082015295945050505050565b60006020828403121561556857600080fd5b6137b082615172565b60008060008060008086880360e081121561558b57600080fd5b61559488615172565b96506155a260208901615184565b95506155b060408901615184565b94506155be60608901615184565b9350608088013592506040609f19820112156155d957600080fd5b506155e2615bfd565b6155ee60a08901615184565b81526155fc60c08901615184565b6020820152809150509295509295509295565b60006020828403121561562157600080fd5b5035919050565b6000806040838503121561563b57600080fd5b82359150602083013561521181615eb2565b6000806040838503121561566057600080fd5b50508035926020909101359150565b60006020828403121561568157600080fd5b6137b082615184565b600080600080600060a086880312156156a257600080fd5b6156ab86615198565b94506020860151935060408601519250606086015191506156ce60808701615198565b90509295509295909350565b600081518084526020808501945080840160005b838110156157135781516001600160a01b0316875295820195908201906001016156ee565b509495945050505050565b8060005b6002811015610940578151845260209384019390910190600101615722565b600081518084526020808501945080840160005b8381101561571357815187529582019590820190600101615755565b60008151808452615789816020860160208601615dc2565b601f01601f19169290920160200192915050565b6157a7818361571e565b604001919050565b600083516157c1818460208801615dc2565b8351908301906157d5818360208801615dc2565b01949350505050565b8681526157ee602082018761571e565b6157fb606082018661571e565b61580860a082018561571e565b61581560e082018461571e565b60609190911b6001600160601b0319166101208201526101340195945050505050565b838152615848602082018461571e565b606081019190915260800192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682526001600160601b0316602082015260400190565b60408101610f6d828461571e565b6020815260006137b06020830184615741565b918252602082015260400190565b93845260ff9290921660208401526040830152606082015260800190565b6020815260006137b06020830184615771565b6020815260ff82511660208201526020820151604082015260018060a01b0360408301511660608201526000606083015160c0608084015261596760e08401826156da565b60808501516001600160601b0390811660a0868101919091529095015190941660c0909301929092525090919050565b61ffff93841681529183166020830152909116604082015260600190565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b81811015615a06578451835293830193918301916001016159ea565b509098975050505050505050565b9182526001600160a01b0316602082015260400190565b828152606081016137b0602083018461571e565b8281526040602082015260006137fe6040830184615741565b86815285602082015261ffff85166040820152600063ffffffff808616606084015280851660808401525060c060a0830152613f6960c0830184615771565b878152602081018790526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c0820181905260009061408d90830184615771565b8781526001600160401b03871660208201526040810186905263ffffffff8581166060830152841660808201526001600160a01b03831660a082015260e060c0820181905260009061408d90830184615771565b63ffffffff92831681529116602082015260400190565b6001600160401b0391909116815260200190565b6001600160601b038681168252851660208201526001600160401b03841660408201526001600160a01b038316606082015260a060808201819052600090615bac908301846156da565b979650505050505050565b6000808335601e19843603018112615bce57600080fd5b8301803591506001600160401b03821115615be857600080fd5b60200191503681900382131561505b57600080fd5b604080519081016001600160401b0381118282101715615c1f57615c1f615e9c565b60405290565b60405160c081016001600160401b0381118282101715615c1f57615c1f615e9c565b60405161012081016001600160401b0381118282101715615c1f57615c1f615e9c565b604051601f8201601f191681016001600160401b0381118282101715615c9257615c92615e9c565b604052919050565b60008085851115615caa57600080fd5b83861115615cb757600080fd5b5050820193919092039150565b60008219821115615cd757615cd7615e44565b500190565b60006001600160401b038281168482168083038211156157d5576157d5615e44565b60006001600160601b038281168482168083038211156157d5576157d5615e44565b600082615d2f57615d2f615e5a565b500490565b6000816000190483118215151615615d4e57615d4e615e44565b500290565b600082821015615d6557615d65615e44565b500390565b60006001600160601b0383811690831681811015615d8a57615d8a615e44565b039392505050565b6001600160e01b03198135818116916004851015615dba5780818660040360031b1b83161692505b505092915050565b60005b83811015615ddd578181015183820152602001615dc5565b838111156109405750506000910152565b6000600019821415615e0257615e02615e44565b5060010190565b60006001600160401b0382811680821415615e2657615e26615e44565b6001019392505050565b600082615e3f57615e3f615e5a565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461237557600080fd5b801515811461237557600080fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000806000a", } var VRFCoordinatorV2PlusUpgradedVersionABI = VRFCoordinatorV2PlusUpgradedVersionMetaData.ABI @@ -768,30 +768,6 @@ func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionT return _VRFCoordinatorV2PlusUpgradedVersion.Contract.OnTokenTransfer(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, arg0, amount, data) } -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "oracleWithdraw", recipient, amount) -} - -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionSession) OracleWithdraw(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.Contract.OracleWithdraw(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient, amount) -} - -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactorSession) OracleWithdraw(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.Contract.OracleWithdraw(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient, amount) -} - -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) OracleWithdrawNative(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "oracleWithdrawNative", recipient, amount) -} - -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionSession) OracleWithdrawNative(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.Contract.OracleWithdrawNative(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient, amount) -} - -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactorSession) OracleWithdrawNative(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.Contract.OracleWithdrawNative(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient, amount) -} - func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) OwnerCancelSubscription(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) { return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "ownerCancelSubscription", subId) } @@ -840,16 +816,16 @@ func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionT return _VRFCoordinatorV2PlusUpgradedVersion.Contract.RegisterMigratableCoordinator(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, target) } -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "registerProvingKey", oracle, publicProvingKey) +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) RegisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "registerProvingKey", publicProvingKey) } -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionSession) RegisterProvingKey(oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.Contract.RegisterProvingKey(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, oracle, publicProvingKey) +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionSession) RegisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.Contract.RegisterProvingKey(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, publicProvingKey) } -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactorSession) RegisterProvingKey(oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2PlusUpgradedVersion.Contract.RegisterProvingKey(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, oracle, publicProvingKey) +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactorSession) RegisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.Contract.RegisterProvingKey(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, publicProvingKey) } func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) { @@ -924,6 +900,30 @@ func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionT return _VRFCoordinatorV2PlusUpgradedVersion.Contract.TransferOwnership(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, to) } +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "withdraw", recipient) +} + +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionSession) Withdraw(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.Contract.Withdraw(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient) +} + +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactorSession) Withdraw(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.Contract.Withdraw(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient) +} + +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactor) WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.contract.Transact(opts, "withdrawNative", recipient) +} + +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionSession) WithdrawNative(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.Contract.WithdrawNative(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient) +} + +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionTransactorSession) WithdrawNative(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorV2PlusUpgradedVersion.Contract.WithdrawNative(&_VRFCoordinatorV2PlusUpgradedVersion.TransactOpts, recipient) +} + type VRFCoordinatorV2PlusUpgradedVersionConfigSetIterator struct { Event *VRFCoordinatorV2PlusUpgradedVersionConfigSet @@ -1851,32 +1851,21 @@ func (it *VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegisteredIterator) Close type VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered struct { KeyHash [32]byte - Oracle common.Address Raw types.Log } -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionFilterer) FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegisteredIterator, error) { +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionFilterer) FilterProvingKeyRegistered(opts *bind.FilterOpts) (*VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegisteredIterator, error) { - var oracleRule []interface{} - for _, oracleItem := range oracle { - oracleRule = append(oracleRule, oracleItem) - } - - logs, sub, err := _VRFCoordinatorV2PlusUpgradedVersion.contract.FilterLogs(opts, "ProvingKeyRegistered", oracleRule) + logs, sub, err := _VRFCoordinatorV2PlusUpgradedVersion.contract.FilterLogs(opts, "ProvingKeyRegistered") if err != nil { return nil, err } return &VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegisteredIterator{contract: _VRFCoordinatorV2PlusUpgradedVersion.contract, event: "ProvingKeyRegistered", logs: logs, sub: sub}, nil } -func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionFilterer) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) { - - var oracleRule []interface{} - for _, oracleItem := range oracle { - oracleRule = append(oracleRule, oracleItem) - } +func (_VRFCoordinatorV2PlusUpgradedVersion *VRFCoordinatorV2PlusUpgradedVersionFilterer) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered) (event.Subscription, error) { - logs, sub, err := _VRFCoordinatorV2PlusUpgradedVersion.contract.WatchLogs(opts, "ProvingKeyRegistered", oracleRule) + logs, sub, err := _VRFCoordinatorV2PlusUpgradedVersion.contract.WatchLogs(opts, "ProvingKeyRegistered") if err != nil { return nil, err } @@ -3331,7 +3320,7 @@ func (VRFCoordinatorV2PlusUpgradedVersionOwnershipTransferred) Topic() common.Ha } func (VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered) Topic() common.Hash { - return common.HexToHash("0xe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b8") + return common.HexToHash("0xc9583fd3afa3d7f16eb0b88d0268e7d05c09bafa4b21e092cbd1320e1bc8089d") } func (VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled) Topic() common.Hash { @@ -3441,10 +3430,6 @@ type VRFCoordinatorV2PlusUpgradedVersionInterface interface { OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) - OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) - - OracleWithdrawNative(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) - OwnerCancelSubscription(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) @@ -3453,7 +3438,7 @@ type VRFCoordinatorV2PlusUpgradedVersionInterface interface { RegisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) - RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) + RegisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) @@ -3467,6 +3452,10 @@ type VRFCoordinatorV2PlusUpgradedVersionInterface interface { TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + + WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + FilterConfigSet(opts *bind.FilterOpts) (*VRFCoordinatorV2PlusUpgradedVersionConfigSetIterator, error) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV2PlusUpgradedVersionConfigSet) (event.Subscription, error) @@ -3509,9 +3498,9 @@ type VRFCoordinatorV2PlusUpgradedVersionInterface interface { ParseOwnershipTransferred(log types.Log) (*VRFCoordinatorV2PlusUpgradedVersionOwnershipTransferred, error) - FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegisteredIterator, error) + FilterProvingKeyRegistered(opts *bind.FilterOpts) (*VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegisteredIterator, error) - WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) + WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered) (event.Subscription, error) ParseProvingKeyRegistered(log types.Log) (*VRFCoordinatorV2PlusUpgradedVersionProvingKeyRegistered, error) diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 8a61f086148..7eb45aed46f 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -75,7 +75,7 @@ vrf_consumer_v2_plus_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsume vrf_consumer_v2_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.bin f1790a9a2f2a04c730593e483459709cb89e897f8a19d7a3ac0cfe6a97265e6e vrf_coordinator_mock: ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.bin 5c495cf8df1f46d8736b9150cdf174cce358cb8352f60f0d5bb9581e23920501 vrf_coordinator_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.bin 295f35ce282060317dfd01f45959f5a2b05ba26913e422fbd4fb6bf90b107006 -vrf_coordinator_v2_5: ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin b0e7c42a30b36d9d31fa9a3f26bad7937152e3dddee5bd8dd3d121390c879ab6 +vrf_coordinator_v2_5: ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin bcad64e1b41278998c94867370fd9c4809b1376ccc004955bc7ed33fe716408c vrf_coordinator_v2_plus_v2_example: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.bin 4a5b86701983b1b65f0a8dfa116b3f6d75f8f706fa274004b57bdf5992e4cec3 vrf_coordinator_v2plus_interface: ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.abi ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.bin 834a2ce0e83276372a0e1446593fd89798f4cf6dc95d4be0113e99fadf61558b vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.bin 14f888eb313930b50233a6f01ea31eba0206b7f41a41f6311670da8bb8a26963 @@ -93,7 +93,7 @@ vrf_v2_consumer_wrapper: ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer vrf_v2plus_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics/VRFV2PlusLoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics/VRFV2PlusLoadTestWithMetrics.bin 0a89cb7ed9dfb42f91e559b03dc351ccdbe14d281a7ab71c63bd3f47eeed7711 vrf_v2plus_single_consumer: ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample/VRFV2PlusSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample/VRFV2PlusSingleConsumerExample.bin 6226d05afa1664033b182bfbdde11d5dfb1d4c8e3eb0bd0448c8bfb76f5b96e4 vrf_v2plus_sub_owner: ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample/VRFV2PlusExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample/VRFV2PlusExternalSubOwnerExample.bin 7541f986571b8a5671a256edc27ae9b8df9bcdff45ac3b96e5609bbfcc320e4e -vrf_v2plus_upgraded_version: ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.bin c0793d86fb6e45342c4424184fe241c16da960c0b4de76816364b933344d0756 +vrf_v2plus_upgraded_version: ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.bin 3bcd359eddf2bc861dda86f9bb10b78bac1e0370badd15f67ae17b35c5d228d4 vrfv2_proxy_admin: ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.abi ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.bin 402b1103087ffe1aa598854a8f8b38f8cd3de2e3aaa86369e28017a9157f4980 vrfv2_reverting_example: ../../contracts/solc/v0.8.6/VRFV2RevertingExample/VRFV2RevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2RevertingExample/VRFV2RevertingExample.bin 1ae46f80351d428bd85ba58b9041b2a608a1845300d79a8fed83edf96606de87 vrfv2_transparent_upgradeable_proxy: ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.abi ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.bin fe1a8e6852fbd06d91f64315c5cede86d340891f5b5cc981fb5b86563f7eac3f diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index 43a0c65d7e6..97d987dac29 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -1071,7 +1071,6 @@ func main() { coordinatorReregisterKey := flag.NewFlagSet("coordinator-register-key", flag.ExitOnError) coordinatorAddress := coordinatorReregisterKey.String("coordinator-address", "", "coordinator address") uncompressedPubKey := coordinatorReregisterKey.String("pubkey", "", "uncompressed pubkey") - newOracleAddress := coordinatorReregisterKey.String("new-oracle-address", "", "oracle address") skipDeregister := coordinatorReregisterKey.Bool("skip-deregister", false, "if true, key will not be deregistered") helpers.ParseArgs(coordinatorReregisterKey, os.Args[2:], "coordinator-address", "pubkey", "new-oracle-address") @@ -1097,7 +1096,6 @@ func main() { // Use a higher gas price for the register call e.Owner.GasPrice.Mul(e.Owner.GasPrice, big.NewInt(2)) registerTx, err := coordinator.RegisterProvingKey(e.Owner, - common.HexToAddress(*newOracleAddress), [2]*big.Int{pk.X, pk.Y}) helpers.PanicErr(err) fmt.Println("Register transaction", helpers.ExplorerLink(e.ChainID, registerTx.Hash())) diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go index 21d76c42048..65e4cca2abf 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go @@ -196,7 +196,7 @@ func SmokeTestVRF(e helpers.Environment) { x, y := secp256k1.Coordinates(point) fmt.Println("proving key points x:", x, ", y:", y) fmt.Println("proving key points from unmarshal:", pk.X, pk.Y) - tx, err := coordinator.RegisterProvingKey(e.Owner, e.Owner.From, [2]*big.Int{x, y}) + tx, err := coordinator.RegisterProvingKey(e.Owner, [2]*big.Int{x, y}) helpers.PanicErr(err) registerReceipt := helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "register proving key on", coordinatorAddress.String()) var provingKeyRegisteredLog *vrf_coordinator_v2_5.VRFCoordinatorV25ProvingKeyRegistered diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go index 96f305213ea..48b196b409f 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go @@ -6,6 +6,8 @@ import ( "fmt" "math/big" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -17,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_sub_owner" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example" @@ -181,7 +182,6 @@ func RegisterCoordinatorProvingKey(e helpers.Environment, pk, err := crypto.UnmarshalPubkey(pubBytes) helpers.PanicErr(err) tx, err := coordinator.RegisterProvingKey(e.Owner, - common.HexToAddress(oracleAddress), [2]*big.Int{pk.X, pk.Y}) helpers.PanicErr(err) helpers.ConfirmTXMined( diff --git a/core/services/vrf/v2/coordinator_v2x_interface.go b/core/services/vrf/v2/coordinator_v2x_interface.go index e20500cca89..e88053ebc2c 100644 --- a/core/services/vrf/v2/coordinator_v2x_interface.go +++ b/core/services/vrf/v2/coordinator_v2x_interface.go @@ -36,9 +36,11 @@ type CoordinatorV2_X interface { GetConfig(opts *bind.CallOpts) (Config, error) ParseLog(log types.Log) (generated.AbigenLog, error) OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) + Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) LogsWithTopics(keyHash common.Hash) map[common.Hash][][]log.Topic Version() vrfcommon.Version - RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) + RegisterProvingKey(opts *bind.TransactOpts, oracle *common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) FilterSubscriptionCreated(opts *bind.FilterOpts, subID []*big.Int) (SubscriptionCreatedIterator, error) FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subID []*big.Int, sender []common.Address) (RandomWordsRequestedIterator, error) FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestID []*big.Int, subID []*big.Int) (RandomWordsFulfilledIterator, error) @@ -130,6 +132,14 @@ func (c *coordinatorV2) OracleWithdraw(opts *bind.TransactOpts, recipient common return c.coordinator.OracleWithdraw(opts, recipient, amount) } +func (c *coordinatorV2) Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return nil, errors.New("withdraw not implemented for v2") +} + +func (c *coordinatorV2) WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return nil, errors.New("withdrawNative not implemented for v2") +} + func (c *coordinatorV2) LogsWithTopics(keyHash common.Hash) map[common.Hash][][]log.Topic { return map[common.Hash][][]log.Topic{ vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested{}.Topic(): { @@ -144,8 +154,8 @@ func (c *coordinatorV2) Version() vrfcommon.Version { return c.vrfVersion } -func (c *coordinatorV2) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return c.coordinator.RegisterProvingKey(opts, oracle, publicProvingKey) +func (c *coordinatorV2) RegisterProvingKey(opts *bind.TransactOpts, oracle *common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return c.coordinator.RegisterProvingKey(opts, *oracle, publicProvingKey) } func (c *coordinatorV2) FilterSubscriptionCreated(opts *bind.FilterOpts, subID []*big.Int) (SubscriptionCreatedIterator, error) { @@ -281,7 +291,15 @@ func (c *coordinatorV2_5) ParseLog(log types.Log) (generated.AbigenLog, error) { } func (c *coordinatorV2_5) OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return c.coordinator.OracleWithdraw(opts, recipient, amount) + return nil, errors.New("oracle withdraw not implemented for v2.5") +} + +func (c *coordinatorV2_5) Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return c.coordinator.Withdraw(opts, recipient) +} + +func (c *coordinatorV2_5) WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return c.coordinator.WithdrawNative(opts, recipient) } func (c *coordinatorV2_5) LogsWithTopics(keyHash common.Hash) map[common.Hash][][]log.Topic { @@ -298,8 +316,11 @@ func (c *coordinatorV2_5) Version() vrfcommon.Version { return c.vrfVersion } -func (c *coordinatorV2_5) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { - return c.coordinator.RegisterProvingKey(opts, oracle, publicProvingKey) +func (c *coordinatorV2_5) RegisterProvingKey(opts *bind.TransactOpts, oracle *common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + if oracle != nil { + return nil, errors.New("oracle address not supported for registering proving key in v2.5") + } + return c.coordinator.RegisterProvingKey(opts, publicProvingKey) } func (c *coordinatorV2_5) FilterSubscriptionCreated(opts *bind.FilterOpts, subID []*big.Int) (SubscriptionCreatedIterator, error) { diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index 47b839c9b8a..b5f6a095cff 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -32,7 +32,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" @@ -1683,11 +1682,7 @@ func testMaliciousConsumer( time.Sleep(1 * time.Second) // Register a proving key associated with the VRF job. - p, err := vrfkey.PublicKey.Point() - require.NoError(t, err) - _, err = uni.rootContract.RegisterProvingKey( - uni.neil, uni.nallory.From, pair(secp256k1.Coordinates(p))) - require.NoError(t, err) + registerProvingKeyHelper(t, uni, uni.rootContract, vrfkey) subFunding := decimal.RequireFromString("1000000000000000000") _, err = uni.maliciousConsumerContract.CreateSubscriptionAndFund(carol, diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 927f0ff2939..fb47d84ed7a 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -45,7 +45,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" - "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/extraargs" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/proof" v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" @@ -850,12 +849,7 @@ func TestVRFV2PlusIntegration_RequestCost(t *testing.T) { vrfkey, err := app.GetKeyStore().VRF().Create() require.NoError(t, err) - p, err := vrfkey.PublicKey.Point() - require.NoError(t, err) - _, err = uni.rootContract.RegisterProvingKey( - uni.neil, uni.neil.From, pair(secp256k1.Coordinates(p))) - require.NoError(t, err) - uni.backend.Commit() + registerProvingKeyHelper(t, uni.coordinatorV2UniverseCommon, uni.rootContract, vrfkey) t.Run("non-proxied consumer", func(tt *testing.T) { carol := uni.vrfConsumers[0] carolContract := uni.consumerContracts[0] @@ -1010,12 +1004,7 @@ func TestVRFV2PlusIntegration_FulfillmentCost(t *testing.T) { vrfkey, err := app.GetKeyStore().VRF().Create() require.NoError(t, err) - p, err := vrfkey.PublicKey.Point() - require.NoError(t, err) - _, err = uni.rootContract.RegisterProvingKey( - uni.neil, uni.neil.From, pair(secp256k1.Coordinates(p))) - require.NoError(t, err) - uni.backend.Commit() + registerProvingKeyHelper(t, uni.coordinatorV2UniverseCommon, uni.rootContract, vrfkey) t.Run("non-proxied consumer", func(tt *testing.T) { carol := uni.vrfConsumers[0] diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index dffce9544d2..dee69c9abef 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -1485,8 +1485,13 @@ func registerProvingKeyHelper(t *testing.T, uni coordinatorV2UniverseCommon, coo // Register a proving key associated with the VRF job. p, err := vrfkey.PublicKey.Point() require.NoError(t, err) - _, err = coordinator.RegisterProvingKey( - uni.neil, uni.nallory.From, pair(secp256k1.Coordinates(p))) + if uni.rootContract.Version() == vrfcommon.V2Plus { + _, err = coordinator.RegisterProvingKey( + uni.neil, nil, pair(secp256k1.Coordinates(p))) + } else { + _, err = coordinator.RegisterProvingKey( + uni.neil, &uni.nallory.From, pair(secp256k1.Coordinates(p))) + } require.NoError(t, err) uni.backend.Commit() } @@ -1806,13 +1811,7 @@ func TestRequestCost(t *testing.T) { vrfkey, err := app.GetKeyStore().VRF().Create() require.NoError(t, err) - p, err := vrfkey.PublicKey.Point() - require.NoError(t, err) - _, err = uni.rootContract.RegisterProvingKey( - uni.neil, uni.neil.From, pair(secp256k1.Coordinates(p))) - require.NoError(t, err) - uni.backend.Commit() - + registerProvingKeyHelper(t, uni.coordinatorV2UniverseCommon, uni.rootContract, vrfkey) t.Run("non-proxied consumer", func(tt *testing.T) { carol := uni.vrfConsumers[0] carolContract := uni.consumerContracts[0] @@ -1915,18 +1914,10 @@ func TestFulfillmentCost(t *testing.T) { app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, cfg, uni.backend, key) require.NoError(t, app.Start(testutils.Context(t))) - var vrfkey vrfkey.KeyV2 - { - var err error - vrfkey, err = app.GetKeyStore().VRF().Create() - require.NoError(t, err) - p, err := vrfkey.PublicKey.Point() - require.NoError(t, err) - _, err = uni.rootContract.RegisterProvingKey( - uni.neil, uni.neil.From, pair(secp256k1.Coordinates(p))) - require.NoError(t, err) - uni.backend.Commit() - } + vrfkey, err := app.GetKeyStore().VRF().Create() + require.NoError(t, err) + registerProvingKeyHelper(t, uni.coordinatorV2UniverseCommon, uni.rootContract, vrfkey) + var ( nonProxiedConsumerGasEstimate uint64 proxiedConsumerGasEstimate uint64 diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 5db187cf932..d0c83e85841 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -147,7 +147,6 @@ func CreateVRFV2PlusJob( func VRFV2_5RegisterProvingKey( vrfKey *client.VRFKey, - oracleAddress string, coordinator contracts.VRFCoordinatorV2_5, ) (VRFV2PlusEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) @@ -155,7 +154,6 @@ func VRFV2_5RegisterProvingKey( return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( - oracleAddress, provingKey, ) if err != nil { @@ -166,7 +164,6 @@ func VRFV2_5RegisterProvingKey( func VRFV2PlusUpgradedVersionRegisterProvingKey( vrfKey *client.VRFKey, - oracleAddress string, coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, ) (VRFV2PlusEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) @@ -174,7 +171,6 @@ func VRFV2PlusUpgradedVersionRegisterProvingKey( return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( - oracleAddress, provingKey, ) if err != nil { @@ -207,7 +203,6 @@ func SetupVRFV2_5Environment( vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, - registerProvingKeyAgainstAddress string, numberOfTxKeysToCreate int, numberOfConsumers int, numberOfSubToCreate int, @@ -265,7 +260,7 @@ func SetupVRFV2_5Environment( pubKeyCompressed := vrfKey.Data.ID l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Registering Proving Key") - provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfv2_5Contracts.Coordinator) + provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, vrfv2_5Contracts.Coordinator) if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRegisteringProvingKey, err) } @@ -848,9 +843,8 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co Str("LINK amount", linkTotalBalance.String()). Str("Returning to", defaultWallet). Msg("Returning LINK for fulfilled requests") - err = coordinator.OracleWithdraw( + err = coordinator.Withdraw( common.HexToAddress(defaultWallet), - linkTotalBalance, ) if err != nil { return fmt.Errorf("Error withdrawing LINK from coordinator to default wallet, err: %w", err) @@ -860,12 +854,11 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co return fmt.Errorf("Error getting NATIVE total balance, err: %w", err) } l.Info(). - Str("Native Token amount", linkTotalBalance.String()). + Str("Native Token amount", nativeTotalBalance.String()). Str("Returning to", defaultWallet). Msg("Returning Native Token for fulfilled requests") - err = coordinator.OracleWithdrawNative( + err = coordinator.WithdrawNative( common.HexToAddress(defaultWallet), - nativeTotalBalance, ) if err != nil { return fmt.Errorf("Error withdrawing NATIVE from coordinator to default wallet, err: %w", err) diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 8a217e26766..898abb521b3 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -77,7 +77,6 @@ type VRFCoordinatorV2_5 interface { feeConfig vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig, ) error RegisterProvingKey( - oracleAddr string, publicProvingKey [2]*big.Int, ) error HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) @@ -92,8 +91,8 @@ type VRFCoordinatorV2_5 interface { GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2_5.GetSubscription, error) OwnerCancelSubscription(subID *big.Int) (*types.Transaction, error) CancelSubscription(subID *big.Int, to common.Address) (*types.Transaction, error) - OracleWithdraw(recipient common.Address, amount *big.Int) error - OracleWithdrawNative(recipient common.Address, amount *big.Int) error + Withdraw(recipient common.Address) error + WithdrawNative(recipient common.Address) error GetNativeTokenTotalBalance(ctx context.Context) (*big.Int, error) GetLinkTotalBalance(ctx context.Context) (*big.Int, error) FindSubscriptionID(subID *big.Int) (*big.Int, error) @@ -118,7 +117,6 @@ type VRFCoordinatorV2PlusUpgradedVersion interface { feeConfig vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionFeeConfig, ) error RegisterProvingKey( - oracleAddr string, publicProvingKey [2]*big.Int, ) error HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) diff --git a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go index 330166dc79d..31c7f1e4f42 100644 --- a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go @@ -180,15 +180,14 @@ func (v *EthereumVRFCoordinatorV2_5) CancelSubscription(subID *big.Int, to commo return tx, v.client.ProcessTransaction(tx) } -func (v *EthereumVRFCoordinatorV2_5) OracleWithdraw(recipient common.Address, amount *big.Int) error { +func (v *EthereumVRFCoordinatorV2_5) Withdraw(recipient common.Address) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err } - tx, err := v.coordinator.OracleWithdraw( + tx, err := v.coordinator.Withdraw( opts, recipient, - amount, ) if err != nil { return err @@ -196,15 +195,14 @@ func (v *EthereumVRFCoordinatorV2_5) OracleWithdraw(recipient common.Address, am return v.client.ProcessTransaction(tx) } -func (v *EthereumVRFCoordinatorV2_5) OracleWithdrawNative(recipient common.Address, amount *big.Int) error { +func (v *EthereumVRFCoordinatorV2_5) WithdrawNative(recipient common.Address) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err } - tx, err := v.coordinator.OracleWithdrawNative( + tx, err := v.coordinator.WithdrawNative( opts, recipient, - amount, ) if err != nil { return err @@ -249,14 +247,13 @@ func (v *EthereumVRFCoordinatorV2_5) SetLINKAndLINKNativeFeed(linkAddress string } func (v *EthereumVRFCoordinatorV2_5) RegisterProvingKey( - oracleAddr string, publicProvingKey [2]*big.Int, ) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err } - tx, err := v.coordinator.RegisterProvingKey(opts, common.HexToAddress(oracleAddr), publicProvingKey) + tx, err := v.coordinator.RegisterProvingKey(opts, publicProvingKey) if err != nil { return err } @@ -638,14 +635,13 @@ func (v *EthereumVRFCoordinatorV2PlusUpgradedVersion) SetLINKAndLINKNativeFeed(l } func (v *EthereumVRFCoordinatorV2PlusUpgradedVersion) RegisterProvingKey( - oracleAddr string, publicProvingKey [2]*big.Int, ) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err } - tx, err := v.coordinator.RegisterProvingKey(opts, common.HexToAddress(oracleAddr), publicProvingKey) + tx, err := v.coordinator.RegisterProvingKey(opts, publicProvingKey) if err != nil { return err } diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 6d298e075f0..069e3115aee 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -207,8 +207,6 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusConfig, linkToken, mockETHLinkFeed, - //register proving key against EOA address in order to return funds to this address - env.EVMClient.GetDefaultWallet().Address(), 0, 1, vrfv2PlusConfig.NumberOfSubToCreate, diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index f3f14ac7cee..9ddc87422be 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -54,7 +54,7 @@ func TestVRFv2Plus(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - // register proving key against oracle address (sending key) in order to test oracleWithdraw + // default wallet address is used to test Withdraw defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 2 @@ -63,7 +63,6 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusConfig, linkToken, mockETHLinkFeed, - defaultWalletAddress, numberOfTxKeysToCreate, 1, 1, @@ -523,9 +522,10 @@ func TestVRFv2Plus(t *testing.T) { "Active subscription ids should not contain sub id after sub cancellation", ) }) - t.Run("Oracle Withdraw", func(t *testing.T) { + + t.Run("Owner Withdraw", func(t *testing.T) { testConfig := vrfv2PlusConfig - subIDsForOracleWithDraw, err := vrfv2plus.CreateFundSubsAndAddConsumers( + subIDsForWithdraw, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, testConfig, linkToken, @@ -534,13 +534,13 @@ func TestVRFv2Plus(t *testing.T) { 1, ) require.NoError(t, err) - subIDForOracleWithdraw := subIDsForOracleWithDraw[0] + subIDForWithdraw := subIDsForWithdraw[0] fulfilledEventLink, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( vrfv2PlusContracts.LoadTestConsumers[0], vrfv2PlusContracts.Coordinator, vrfv2PlusData, - subIDForOracleWithdraw, + subIDForWithdraw, false, testConfig.RandomnessRequestCountPerRequest, testConfig, @@ -553,7 +553,7 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusContracts.LoadTestConsumers[0], vrfv2PlusContracts.Coordinator, vrfv2PlusData, - subIDForOracleWithdraw, + subIDForWithdraw, true, testConfig.RandomnessRequestCountPerRequest, testConfig, @@ -563,10 +563,10 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) amountToWithdrawLink := fulfilledEventLink.Payment - defaultWalletBalanceNativeBeforeOracleWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeBeforeWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + defaultWalletBalanceLinkBeforeWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) l.Info(). @@ -574,9 +574,8 @@ func TestVRFv2Plus(t *testing.T) { Str("Amount", amountToWithdrawLink.String()). Msg("Invoking Oracle Withdraw for LINK") - err = vrfv2PlusContracts.Coordinator.OracleWithdraw( + err = vrfv2PlusContracts.Coordinator.Withdraw( common.HexToAddress(defaultWalletAddress), - amountToWithdrawLink, ) require.NoError(t, err, "error withdrawing LINK from coordinator to default wallet") amountToWithdrawNative := fulfilledEventNative.Payment @@ -586,24 +585,23 @@ func TestVRFv2Plus(t *testing.T) { Str("Amount", amountToWithdrawNative.String()). Msg("Invoking Oracle Withdraw for Native") - err = vrfv2PlusContracts.Coordinator.OracleWithdrawNative( + err = vrfv2PlusContracts.Coordinator.WithdrawNative( common.HexToAddress(defaultWalletAddress), - amountToWithdrawNative, ) require.NoError(t, err, "error withdrawing Native tokens from coordinator to default wallet") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) - defaultWalletBalanceNativeAfterOracleWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeAfterWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + defaultWalletBalanceLinkAfterWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) //not possible to verify exact amount of Native/LINK returned as defaultWallet is used in other tests in parallel which might affect the balance - require.Equal(t, 1, defaultWalletBalanceNativeAfterOracleWithdraw.Cmp(defaultWalletBalanceNativeBeforeOracleWithdraw), "Native funds were not returned after oracle withdraw native") - require.Equal(t, 1, defaultWalletBalanceLinkAfterOracleWithdraw.Cmp(defaultWalletBalanceLinkBeforeOracleWithdraw), "LINK funds were not returned after oracle withdraw") + require.Equal(t, 1, defaultWalletBalanceNativeAfterWithdraw.Cmp(defaultWalletBalanceNativeBeforeWithdraw), "Native funds were not returned after oracle withdraw native") + require.Equal(t, 1, defaultWalletBalanceLinkAfterWithdraw.Cmp(defaultWalletBalanceLinkBeforeWithdraw), "LINK funds were not returned after oracle withdraw") }) } @@ -636,16 +634,12 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - // register proving key against oracle address (sending key) in order to test oracleWithdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() - numberOfTxKeysToCreate := 2 vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, vrfv2PlusConfig, linkToken, mockETHLinkFeed, - defaultWalletAddress, numberOfTxKeysToCreate, 1, 1, @@ -730,15 +724,11 @@ func TestVRFv2PlusMigration(t *testing.T) { linkAddress, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() - require.NoError(t, err, "error getting primary eth address") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, - nativeTokenPrimaryKeyAddress, 0, 2, 1, @@ -768,7 +758,7 @@ func TestVRFv2PlusMigration(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) - _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, vrfv2PlusData.PrimaryEthAddress, newCoordinator) + _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) require.NoError(t, err, fmt.Errorf("%s, err: %w", vrfv2plus.ErrRegisteringProvingKey, err)) err = newCoordinator.SetConfig( From 3c102bf78775f14b1fcc4da5d6fa72626b4e559e Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 3 Jan 2024 08:18:51 -0600 Subject: [PATCH 107/234] bump go-ethereum 1.12.2 (#10264) * update go-eth * change txpool to legacypool * run go generate * rerun go generate --------- Co-authored-by: James Walker --- core/chains/evm/config/toml/config.go | 6 ++--- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- core/scripts/go.mod | 10 +++++++- core/scripts/go.sum | 24 +++++++++++++++++-- go.mod | 11 ++++++++- go.sum | 24 +++++++++++++++++-- integration-tests/go.mod | 10 +++++++- integration-tests/go.sum | 24 +++++++++++++++++-- 12 files changed, 102 insertions(+), 17 deletions(-) diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index ee0492aafa8..292db56f817 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -6,7 +6,7 @@ import ( "slices" "strconv" - "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/pelletier/go-toml/v2" "github.com/shopspring/decimal" "go.uber.org/multierr" @@ -486,9 +486,9 @@ type GasEstimator struct { } func (e *GasEstimator) ValidateConfig() (err error) { - if uint64(*e.BumpPercent) < txpool.DefaultConfig.PriceBump { + if uint64(*e.BumpPercent) < legacypool.DefaultConfig.PriceBump { err = multierr.Append(err, commonconfig.ErrInvalid{Name: "BumpPercent", Value: *e.BumpPercent, - Msg: fmt.Sprintf("may not be less than Geth's default of %d", txpool.DefaultConfig.PriceBump)}) + Msg: fmt.Sprintf("may not be less than Geth's default of %d", legacypool.DefaultConfig.PriceBump)}) } if e.TipCapDefault.Cmp(e.TipCapMin) < 0 { err = multierr.Append(err, commonconfig.ErrInvalid{Name: "TipCapDefault", Value: e.TipCapDefault, diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 3cdd44cbc5c..0a77e57c88f 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.0 +GETH_VERSION: 1.12.2 functions: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.bin 3c972870b0afeb6d73a29ebb182f24956a2cebb127b21c4f867d1ecf19a762db functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.abi ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.bin 6beec092fbb3b619dfe69f1ad23392b0bbaf00327b335e4080f921c7122a57e4 functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 7eb45aed46f..dcc0a4dea37 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.0 +GETH_VERSION: 1.12.2 aggregator_v2v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.bin 95e8814b408bb05bf21742ef580d98698b7db6a9bac6a35c3de12b23aec4ee28 aggregator_v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.bin 351b55d3b0f04af67db6dfb5c92f1c64479400ca1fec77afc20bc0ce65cb49ab authorized_forwarder: ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.bin 8ea76c883d460f8353a45a493f2aebeb5a2d9a7b4619d1bc4fff5fb590bb3e10 diff --git a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 293defcfbe0..29f0deb7b2c 100644 --- a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.0 +GETH_VERSION: 1.12.2 errored_verifier: ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.abi ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.bin 510d18a58bfda646be35e46491baf73041eb333a349615465b20e2b5b41c5f73 exposed_verifier: ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.bin 6932cea8f2738e874d3ec9e1a4231d2421704030c071d9e15dd2f7f08482c246 fee_manager: ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.bin 1b852df75bfabcc2b57539e84309cd57f9e693a2bb6b25a50e4a6101ccf32c49 diff --git a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt index af907ce85eb..ff9bfa2fea4 100644 --- a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.0 +GETH_VERSION: 1.12.2 burn_mint_erc677: ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.abi ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.bin 405c9016171e614b17e10588653ef8d33dcea21dd569c3fddc596a46fcff68a3 erc20: ../../../contracts/solc/v0.8.19/ERC20/ERC20.abi ../../../contracts/solc/v0.8.19/ERC20/ERC20.bin 5b1a93d9b24f250e49a730c96335a8113c3f7010365cba578f313b483001d4fc link_token: ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.abi ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.bin c0ef9b507103aae541ebc31d87d051c2764ba9d843076b30ec505d37cdfffaba diff --git a/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt index a6d32bf0a85..cded1f0c5e5 100644 --- a/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.0 +GETH_VERSION: 1.12.2 entry_point: ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.abi ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.bin 2cb4bb2ba3efa8df3dfb0a57eb3727d17b68fe202682024fa7cfb4faf026833e greeter: ../../../contracts/solc/v0.8.15/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c greeter_wrapper: ../../../contracts/solc/v0.8.15/Greeter/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 60b05693ac8..98ab5cbfaf9 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -8,7 +8,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 - github.com/ethereum/go-ethereum v1.12.0 + github.com/ethereum/go-ethereum v1.12.2 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.4.0 github.com/jmoiron/sqlx v1.3.5 @@ -57,6 +57,7 @@ require ( github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect @@ -74,6 +75,8 @@ require ( github.com/cometbft/cometbft v0.37.2 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.10.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/cosmos-sdk v0.47.4 // indirect @@ -84,6 +87,7 @@ require ( github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -98,6 +102,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/esote/minmaxheap v1.0.0 // indirect + github.com/ethereum/c-kzg-4844 v0.3.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 // indirect @@ -202,6 +207,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect @@ -250,6 +256,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/status-im/keycard-go v0.2.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect @@ -310,6 +317,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect pgregory.net/rapid v0.5.5 // indirect + rsc.io/tmplfunc v0.0.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/core/scripts/go.sum b/core/scripts/go.sum index defd2e4b0bc..170e36ad2cc 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -164,6 +164,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= @@ -258,6 +260,10 @@ github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0 github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= +github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -296,6 +302,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= +github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -371,8 +379,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= -github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= +github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= +github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= +github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -613,6 +623,7 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -718,6 +729,8 @@ github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce h1:7UnVY3T/ZnHUrfv github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -951,6 +964,9 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1225,6 +1241,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -1954,5 +1972,7 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/go.mod b/go.mod index c812144ad6f..7fe535a425e 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/cosmos/cosmos-sdk v0.47.4 github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e github.com/esote/minmaxheap v1.0.0 - github.com/ethereum/go-ethereum v1.12.0 + github.com/ethereum/go-ethereum v1.12.2 github.com/fatih/color v1.16.0 github.com/fxamacker/cbor/v2 v2.5.0 github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 @@ -127,6 +127,7 @@ require ( github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect @@ -141,6 +142,8 @@ require ( github.com/cockroachdb/redact v1.1.3 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.10.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect @@ -150,6 +153,7 @@ require ( github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.3.0 // indirect @@ -161,6 +165,7 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect + github.com/ethereum/c-kzg-4844 v0.3.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gagliardetto/binary v0.7.1 // indirect @@ -211,6 +216,7 @@ require ( github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect + github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/huandu/skiplist v1.2.0 // indirect @@ -242,6 +248,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect @@ -269,6 +276,7 @@ require ( github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect @@ -312,6 +320,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect pgregory.net/rapid v0.5.5 // indirect + rsc.io/tmplfunc v0.0.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 49c889febbd..38eaa89993a 100644 --- a/go.sum +++ b/go.sum @@ -168,6 +168,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= @@ -244,6 +246,10 @@ github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0 github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= +github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -282,6 +288,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= +github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -353,8 +361,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= -github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= +github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= +github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= +github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -597,6 +607,7 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -703,6 +714,8 @@ github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce h1:7UnVY3T/ZnHUrfv github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -943,6 +956,9 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1212,6 +1228,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= @@ -1941,5 +1959,7 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 73e27848f09..673fbf4dbd5 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -9,7 +9,7 @@ require ( cosmossdk.io/errors v1.0.0 github.com/K-Phoen/grabana v0.21.17 github.com/cli/go-gh/v2 v2.0.0 - github.com/ethereum/go-ethereum v1.12.0 + github.com/ethereum/go-ethereum v1.12.2 github.com/go-resty/resty/v2 v2.7.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.4.0 @@ -95,6 +95,7 @@ require ( github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/buger/jsonparser v1.1.1 // indirect @@ -119,6 +120,8 @@ require ( github.com/cometbft/cometbft v0.37.2 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.10.0 // indirect github.com/containerd/containerd v1.7.7 // indirect github.com/containerd/log v0.1.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect @@ -133,6 +136,7 @@ require ( github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.3.0 // indirect @@ -152,6 +156,7 @@ require ( github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/esote/minmaxheap v1.0.0 // indirect + github.com/ethereum/c-kzg-4844 v0.3.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect @@ -301,6 +306,7 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect @@ -372,6 +378,7 @@ require ( github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect @@ -456,6 +463,7 @@ require ( k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect nhooyr.io/websocket v1.8.7 // indirect pgregory.net/rapid v0.5.5 // indirect + rsc.io/tmplfunc v0.0.3 // indirect sigs.k8s.io/controller-runtime v0.13.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index bdd5bcf9804..9977ed84b8b 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -214,6 +214,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= @@ -314,6 +316,10 @@ github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0 github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= +github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= github.com/containerd/containerd v1.7.7 h1:QOC2K4A42RQpcrZyptP6z9EJZnlHfHJUfZrAAHe15q4= github.com/containerd/containerd v1.7.7/go.mod h1:3c4XZv6VeT9qgf9GMTxNTMFxGJrGpI2vz1yk4ye+YY8= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= @@ -362,6 +368,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= +github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -445,8 +453,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= -github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= +github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= +github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= +github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -783,6 +793,7 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -936,6 +947,8 @@ github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTx github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= github.com/hetznercloud/hcloud-go/v2 v2.4.0 h1:MqlAE+w125PLvJRCpAJmEwrIxoVdUdOyuFUhE/Ukbok= github.com/hetznercloud/hcloud-go/v2 v2.4.0/go.mod h1:l7fA5xsncFBzQTyw29/dw5Yr88yEGKKdc6BHf24ONS0= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -1209,6 +1222,9 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -1553,6 +1569,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= @@ -2358,6 +2376,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= From 48002f273655c179b6254803c047f6ccbaf52c17 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Wed, 3 Jan 2024 15:33:04 +0100 Subject: [PATCH 108/234] remove network policies temporarily, force amd64 builds (#11673) --- charts/chainlink-cluster/devspace.yaml | 9 +-- .../templates/chainlink-db-networkpolicy.yaml | 27 --------- .../chainlink-node-networkpolicy.yaml | 57 ------------------- .../templates/geth-networkpolicy.yaml | 31 ---------- .../templates/mockserver-networkpolicy.yaml | 27 --------- .../templates/networkpolicy-default-deny.yaml | 9 --- 6 files changed, 5 insertions(+), 155 deletions(-) delete mode 100644 charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml delete mode 100644 charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml delete mode 100644 charts/chainlink-cluster/templates/geth-networkpolicy.yaml delete mode 100644 charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml delete mode 100644 charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index bd50a469ded..902925b278e 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -18,11 +18,12 @@ pipelines: # You can run this pipeline via `devspace deploy` (or `devspace run-pipeline deploy`) deploy: run: |- - run_dependencies --all # 1. Deploy any projects this project needs (see "dependencies") - ensure_pull_secrets --all # 2. Ensure pull secrets - build_images --all -t $(git rev-parse --short HEAD) # 3. Build, tag (git commit hash) and push all images (see "images") - create_deployments --all # 5. Deploy Helm charts and manifests specfied as "deployments" + run_dependencies --all + ensure_pull_secrets --all + build_images ---var DOCKER_DEFAULT_PLATFORM=linux/amd64 --all -t $(git rev-parse --short HEAD) kubectl annotate namespace ${DEVSPACE_NAMESPACE} janitor/ttl=${NS_TTL} + kubectl label namespace/${DEVSPACE_NAMESPACE} network=crib + create_deployments --all echo "Namespace ${DEVSPACE_NAMESPACE} will be deleted in ${NS_TTL}" purge: run: |- diff --git a/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml b/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml deleted file mode 100644 index bd989e8732b..00000000000 --- a/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ $.Release.Name }}-db -spec: - podSelector: - matchLabels: - app: {{ $.Release.Name }}-db - policyTypes: - - Ingress - ingress: - # Allow all node pods to access the database pods. - - from: - - podSelector: - matchLabels: - app: {{ $.Release.Name }} - ports: - - protocol: TCP - port: 5432 - # Allow all runner pods to access the database pods. - - from: - - podSelector: - matchLabels: - app: runner - ports: - - protocol: TCP - port: 5432 diff --git a/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml b/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml deleted file mode 100644 index 8ae02d7a46e..00000000000 --- a/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ $.Release.Name }}-node -spec: - podSelector: - matchLabels: - app: {{ $.Release.Name }} - policyTypes: - - Ingress - - Egress - ingress: - # Allow all ingress traffic between the node pods and from runner pod. - - from: - - podSelector: - matchLabels: - app: {{ $.Release.Name }} - - from: - - podSelector: - matchLabels: - app: runner - egress: - # Allow all egress traffic between the node pods and to runner pod. - - to: - - podSelector: - matchLabels: - app: {{ $.Release.Name }} - - to: - - podSelector: - matchLabels: - app: runner - # Allow all node pods to access the database pods. - - to: - - podSelector: - matchLabels: - app: {{ $.Release.Name }}-db - ports: - - protocol: TCP - port: 5432 - # Allow all node pods to access the geth pods. - - to: - - podSelector: - matchLabels: - app: geth - ports: - - protocol: TCP - port: 8544 - - protocol: TCP - port: 8546 - # Allow all node pods to access the mockserver pods. - - to: - - podSelector: - matchLabels: - app: mockserver - ports: - - protocol: TCP - port: 1080 diff --git a/charts/chainlink-cluster/templates/geth-networkpolicy.yaml b/charts/chainlink-cluster/templates/geth-networkpolicy.yaml deleted file mode 100644 index 87d6ac1c535..00000000000 --- a/charts/chainlink-cluster/templates/geth-networkpolicy.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ $.Release.Name }}-geth -spec: - podSelector: - matchLabels: - app: geth - policyTypes: - - Ingress - ingress: - # Allow http and websocket connections from the node pods. - - from: - - podSelector: - matchLabels: - app: {{ $.Release.Name }} - ports: - - protocol: TCP - port: 8544 - - protocol: TCP - port: 8546 - # Allow http and websocket connections from the runner pods. - - from: - - podSelector: - matchLabels: - app: runner - ports: - - protocol: TCP - port: 8544 - - protocol: TCP - port: 8546 diff --git a/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml b/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml deleted file mode 100644 index f5c56c79690..00000000000 --- a/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ $.Release.Name }}-mockserver -spec: - podSelector: - matchLabels: - app: mockserver - policyTypes: - - Ingress - ingress: - # Allow http traffic from the node pods. - - from: - - podSelector: - matchLabels: - app: {{ $.Release.Name }} - ports: - - protocol: TCP - port: 1080 - # Allow http traffic from the runner pods. - - from: - - podSelector: - matchLabels: - app: runner - ports: - - protocol: TCP - port: 1080 diff --git a/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml b/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml deleted file mode 100644 index 69f1da2e0b5..00000000000 --- a/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: default-deny-all -spec: - podSelector: {} - policyTypes: - - Ingress - - Egress From adfa4bd473e6b1c32e9a3d1a53437c42393b2ed4 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 3 Jan 2024 15:18:32 -0500 Subject: [PATCH 109/234] Refactoring Test_Client_LatestReport to parameterized test (#11670) --- .../relay/evm/mercury/wsrpc/client_test.go | 140 +++++++----------- 1 file changed, 51 insertions(+), 89 deletions(-) diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index 21accbf6d28..b4a3dae733d 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -120,97 +120,59 @@ func Test_Client_Transmit(t *testing.T) { func Test_Client_LatestReport(t *testing.T) { lggr := logger.TestLogger(t) ctx := testutils.Context(t) + cacheReads := 5 + + tests := []struct { + name string + ttl time.Duration + expectedCalls int + }{ + { + name: "with cache disabled", + ttl: 0, + expectedCalls: 5, + }, + { + name: "with cache enabled", + ttl: 1000 * time.Hour, //some large value that will never expire during a test + expectedCalls: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := &pb.LatestReportRequest{} + + cacheSet := cache.NewCacheSet(lggr, cache.Config{LatestReportTTL: tt.ttl}) + + resp := &pb.LatestReportResponse{} + + var calls int + wsrpcClient := &mocks.MockWSRPCClient{ + LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { + calls++ + assert.Equal(t, req, in) + return resp, nil + }, + } - t.Run("with nil cache", func(t *testing.T) { - req := &pb.LatestReportRequest{} - noopCacheSet := newNoopCacheSet() - resp := &pb.LatestReportResponse{} - - wsrpcClient := &mocks.MockWSRPCClient{ - LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { - assert.Equal(t, req, in) - return resp, nil - }, - } - - conn := &mocks.MockConn{ - Ready: true, - } - c := newClient(lggr, csakey.KeyV2{}, nil, "", noopCacheSet) - c.conn = conn - c.rawClient = wsrpcClient - require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) - - r, err := c.LatestReport(ctx, req) - - require.NoError(t, err) - assert.Equal(t, resp, r) - }) - - t.Run("with cache disabled", func(t *testing.T) { - req := &pb.LatestReportRequest{} - cacheSet := cache.NewCacheSet(lggr, cache.Config{LatestReportTTL: 0}) - resp := &pb.LatestReportResponse{} - - var calls int - wsrpcClient := &mocks.MockWSRPCClient{ - LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { - calls++ - assert.Equal(t, req, in) - return resp, nil - }, - } - - conn := &mocks.MockConn{ - Ready: true, - } - c := newClient(lggr, csakey.KeyV2{}, nil, "", cacheSet) - c.conn = conn - c.rawClient = wsrpcClient - - servicetest.Run(t, cacheSet) - simulateStart(ctx, t, c) - - for i := 0; i < 5; i++ { - r, err := c.LatestReport(ctx, req) - - require.NoError(t, err) - assert.Equal(t, resp, r) - } - assert.Equal(t, 5, calls, "expected 5 calls to LatestReport but it was called %d times", calls) - }) - - t.Run("with caching", func(t *testing.T) { - req := &pb.LatestReportRequest{} - const neverExpireTTL = 1000 * time.Hour // some massive value that will never expire during a test - cacheSet := cache.NewCacheSet(lggr, cache.Config{LatestReportTTL: neverExpireTTL}) - resp := &pb.LatestReportResponse{} - - var calls int - wsrpcClient := &mocks.MockWSRPCClient{ - LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { - calls++ - assert.Equal(t, req, in) - return resp, nil - }, - } - - conn := &mocks.MockConn{ - Ready: true, - } - c := newClient(lggr, csakey.KeyV2{}, nil, "", cacheSet) - c.conn = conn - c.rawClient = wsrpcClient + conn := &mocks.MockConn{ + Ready: true, + } + c := newClient(lggr, csakey.KeyV2{}, nil, "", cacheSet) + c.conn = conn + c.rawClient = wsrpcClient - servicetest.Run(t, cacheSet) - simulateStart(ctx, t, c) + servicetest.Run(t, cacheSet) + simulateStart(ctx, t, c) - for i := 0; i < 5; i++ { - r, err := c.LatestReport(ctx, req) + for i := 0; i < cacheReads; i++ { + r, err := c.LatestReport(ctx, req) - require.NoError(t, err) - assert.Equal(t, resp, r) - } - assert.Equal(t, 1, calls, "expected only 1 call to LatestReport but it was called %d times", calls) - }) + require.NoError(t, err) + assert.Equal(t, resp, r) + } + assert.Equal(t, tt.expectedCalls, calls, "expected %d calls to LatestReport but it was called %d times", tt.expectedCalls, calls) + }) + } } From 426ceddbcaaa9e8e78c800beacbfe3ba8a77fadc Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 3 Jan 2024 14:48:58 -0700 Subject: [PATCH 110/234] Bump slowest e2e job to larger runner to improve test times (#11681) --- integration-tests/smoke/automation_test.go_test_list.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/smoke/automation_test.go_test_list.json b/integration-tests/smoke/automation_test.go_test_list.json index da214a0d282..5bf941ed2ce 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -2,7 +2,7 @@ "tests": [ { "name": "TestAutomationBasic", - "label": "ubuntu-latest", + "label": "ubuntu-latest-32cores-128GB", "nodes": 6 }, { From 1446727b66c27f95636a441af7dc93699d7e28eb Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 3 Jan 2024 16:20:11 -0700 Subject: [PATCH 111/234] Remove duplicated ocr2 run in ci (#11682) --- .github/workflows/integration-tests.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e588ab509f2..813d4854563 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -326,18 +326,12 @@ jobs: nodes: 1 os: ubuntu-latest file: ocr - pyroscope_env: ci-smoke-ocr-evm-simulated + pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 nodes: 1 os: ubuntu-latest file: ocr2 - pyroscope_env: ci-smoke-ocr2-evm-simulated - - name: ocr2 - nodes: 1 - os: ubuntu-latest - run: -run TestOCRv2Request - file: ocr2 - pyroscope_env: ci-smoke-ocr2-evm-simulated + pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 nodes: 1 os: ubuntu-latest @@ -358,7 +352,7 @@ jobs: - name: vrfv2plus nodes: 1 os: ubuntu-latest - pyroscope_env: ci-smoke-vrf2plus-evm-simulated + pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr nodes: 1 os: ubuntu-latest From e3fe671aab6aba4c9c0259da0361d5d0a838abfc Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 4 Jan 2024 09:52:42 -0500 Subject: [PATCH 112/234] Add documentation for Mercury (#11680) * Add documentation for Mercury * Improve formatting --- docs/Mercury.md | 350 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 docs/Mercury.md diff --git a/docs/Mercury.md b/docs/Mercury.md new file mode 100644 index 00000000000..610605ddbc6 --- /dev/null +++ b/docs/Mercury.md @@ -0,0 +1,350 @@ +# Mercury Documentation + +## Contracts + +Use this tool to configure contracts: + +https://github.com/smartcontractkit/the-most-amazing-mercury-contract-configuration-tool + +TODO: updated process here @Austin Born + +[Reference contract](https://github.com/smartcontractkit/reference-data-directory/blob/master/ethereum-testnet-goerli-arbitrum-1/contracts/0x535051166466D159da8742167c9CA1eFe9e82613.json) + +[OCR2 config documentation](https://github.com/smartcontractkit/libocr/blob/master/offchainreporting2/internal/config/public_config.go) + +**🚨 Important config** + +`s` - transmission schedule. This should be set to the number of oracles on the feed - meaning that every oracle will attempt to transmit to the mercury server in the first stage of transmission. eg `[4]` if there are 4 node in the DON, excluding the bootstrap node. + +`f` - set this to `n//3` (where `//` denotes integer division), e.g. if you have 16 oracles, set `f` to 5. + +`deltaRound` - report generation frequency. This determines how frequently a new round should be started at most (if rounds take longer than this due to network latency, there will be fewer rounds per second than this parameter would suggest). `100ms` is a good starting point (10 rounds/s). + +`reportingPluginConfig.alphaAccept` - set this to `0`, because our mercury ContractTransmitter doesn't know the latest report that's been sent to the mercury server and we therefore always have a "pending" report which we compare against before accepting a report for transmission. + +`reportingPluginConfig.deltaC` - set this to `0` so every round will result in a report. + +
Example `verifier/<0xaddress>.json` + +```json +{ + "contractVersion": 1001, + "digests": { + "0x0006c67c0374ab0dcfa45c63b37df2ea8d16fb903c043caa98065033e9c15666": { + "feedId": "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c", + "proxyEnabled": true, + "status": "active" + } + }, + "feeds": { + "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c": { + "digests": [ + "0x0006c67c0374ab0dcfa45c63b37df2ea8d16fb903c043caa98065033e9c15666" + ], + "docs": { + "assetName": "Chainlink", + "feedCategory": "verified", + "feedType": "Crypto", + "hidden": true + }, + "externalAdapterRequestParams": { + "endpoint": "cryptolwba", + "from": "LINK", + "to": "USD" + }, + "feedId": "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c", + "latestConfig": { + "offchainConfig": { + "deltaGrace": "0", + "deltaProgress": "2s", + "deltaResend": "20s", + "deltaRound": "250ms", + "deltaStage": "60s", + "f": 1, + "maxDurationObservation": "250ms", + "maxDurationQuery": "0s", + "maxDurationReport": "250ms", + "maxDurationShouldAcceptFinalizedReport": "250ms", + "maxDurationShouldTransmitAcceptedReport": "250ms", + "rMax": 100, + "reportingPluginConfig": { + "alphaAcceptInfinite": false, + "alphaAcceptPpb": "0", + "alphaReportInfinite": false, + "alphaReportPpb": "0", + "deltaC": "0s" + }, + "s": [ + 4 + ] + }, + "offchainConfigVersion": 30, + "onchainConfig": { + "max": "99999999999999999999999999999", + "min": "1" + }, + "onchainConfigVersion": 1, + "oracles": [ + { + "api": [ + "coinmetrics", + "ncfx", + "tiingo-test" + ], + "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-0" + }, + { + "api": [ + "coinmetrics", + "ncfx", + "tiingo-test" + ], + "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-1" + }, + { + "api": [ + "coinmetrics", + "ncfx", + "tiingo-test" + ], + "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-2" + }, + { + "api": [ + "coinmetrics", + "ncfx", + "tiingo-test" + ], + "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-3" + } + ] + }, + "marketing": { + "category": "crypto", + "history": true, + "pair": [ + "LINK", + "USD" + ], + "path": "link-usd-verifier" + }, + "name": "LINK/USD-RefPricePlus-ArbitrumGoerli-002", + "reportFields": { + "ask": { + "decimals": 8, + "maxSubmissionValue": "99999999999999999999999999999", + "minSubmissionValue": "1", + "resultPath": "data,ask" + }, + "bid": { + "decimals": 8, + "maxSubmissionValue": "99999999999999999999999999999", + "minSubmissionValue": "1", + "resultPath": "data,bid" + }, + "median": { + "decimals": 8, + "maxSubmissionValue": "99999999999999999999999999999", + "minSubmissionValue": "1", + "resultPath": "data,mid" + } + }, + "status": "testing" + } + }, + "name": "Mercury v0.2 - Production Testnet Verifier (v1.0.0)", + "status": "testing" +} +``` +
+ +## Jobs + +### Bootstrap + +**🚨 Important config** + +`relayConfig.chainID` - target chain id. (the chain we pull block numbers from) + +`contractID` - the contract address of the verifier contract. + +
Example bootstrap TOML + +```toml +type = "bootstrap" +relay = "evm" +schemaVersion = 1 +name = "$feed_name" +contractID = "$verifier_contract_address" +feedID = "$feed_id" # IMPORTANT - DON'T FORGET THIS OR IT WON'T WORK +contractConfigTrackerPollInterval = "15s" + +[relayConfig] +chainID = $evm_chain_id +fromBlock = $from_block +``` +
+ +### OCR2 + +
Example OCR2 Mercury TOML + +```toml +type = "offchainreporting2" +schemaVersion = 1 +name = "$feed_name" +forwardingAllowed = false +maxTaskDuration = "1s" +contractID = "$verifier_contract_address" +feedID = "$feed_id" +contractConfigTrackerPollInterval = "15s" +ocrKeyBundleID = "$key_bundle_id" +p2pv2Bootstrappers = [ + "$bootstrapper_address>" +] +relay = "evm" +pluginType = "mercury" +transmitterID = "$csa_public_key" + +observationSource = """ + // ncfx + ds1_payload [type=bridge name="ncfx" timeout="50ms" requestData="{\\"data\\":{\\"endpoint\\":\\"crypto-lwba\\",\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + ds1_median [type=jsonparse path="data,mid"]; + ds1_bid [type=jsonparse path="data,bid"]; + ds1_ask [type=jsonparse path="data,ask"]; + + ds1_median_multiply [type=multiply times=100000000]; + ds1_bid_multiply [type=multiply times=100000000]; + ds1_ask_multiply [type=multiply times=100000000]; + + // tiingo + ds2_payload [type=bridge name="tiingo" timeout="50ms" requestData="{\\"data\\":{\\"endpoint\\":\\"crypto-lwba\\",\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + ds2_median [type=jsonparse path="data,mid"]; + ds2_bid [type=jsonparse path="data,bid"]; + ds2_ask [type=jsonparse path="data,ask"]; + + ds2_median_multiply [type=multiply times=100000000]; + ds2_bid_multiply [type=multiply times=100000000]; + ds2_ask_multiply [type=multiply times=100000000]; + + // coinmetrics + ds3_payload [type=bridge name="coinmetrics" timeout="50ms" requestData="{\\"data\\":{\\"endpoint\\":\\"crypto-lwba\\",\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + ds3_median [type=jsonparse path="data,mid"]; + ds3_bid [type=jsonparse path="data,bid"]; + ds3_ask [type=jsonparse path="data,ask"]; + + ds3_median_multiply [type=multiply times=100000000]; + ds3_bid_multiply [type=multiply times=100000000]; + ds3_ask_multiply [type=multiply times=100000000]; + + ds1_payload -> ds1_median -> ds1_median_multiply -> benchmark_price; + ds2_payload -> ds2_median -> ds2_median_multiply -> benchmark_price; + ds3_payload -> ds3_median -> ds3_median_multiply -> benchmark_price; + + benchmark_price [type=median allowedFaults=2 index=0]; + + ds1_payload -> ds1_bid -> ds1_bid_multiply -> bid_price; + ds2_payload -> ds2_bid -> ds2_bid_multiply -> bid_price; + ds3_payload -> ds3_bid -> ds3_bid_multiply -> bid_price; + + bid_price [type=median allowedFaults=2 index=1]; + + ds1_payload -> ds1_ask -> ds1_ask_multiply -> ask_price; + ds2_payload -> ds2_ask -> ds2_ask_multiply -> ask_price; + ds3_payload -> ds3_ask -> ds3_ask_multiply -> ask_price; + + ask_price [type=median allowedFaults=2 index=2]; +""" + +[pluginConfig] +serverURL = "$mercury_server_url" +serverPubKey = "$mercury_server_public_key" + +[relayConfig] +chainID = $evm_chain_id +fromBlock = $from_block +``` +
+ +## Nodes + +**🚨 Important config** + +`OCR2.Enabled` - must be `true` - Mercury uses OCR2. + +`P2P.V2.Enabled` - required in order for OCR2 to work. + +`Feature.LogPoller` - required in order for OCR2 to work. You will get fatal errors if not set. + +`JobPipeline.MaxSuccessfulRuns` - set to `0` to disable saving pipeline runs to reduce load on the db. Obviously this means you won’t see anything in the UI. + +`TelemetryIngress.SendInterval` - How frequently to send telemetry batches. Mercury generates a lot of telemetry data due to the throughput. `100ms` has been tested for a single feed with 5 nodes - this will need to be monitored (along with relevant config) as we add more feeds to a node. + +`Database` - **must** increase connection limits above the standard defaults + +
Example node config TOML + +```toml +RootDir = '$ROOT_DIR' + +[JobPipeline] +MaxSuccessfulRuns = 0 # you may set to some small value like '10' or similar if you like looking at job runs in the UI + +[Feature] +UICSAKeys = true # required +LogPoller = true # required + +[Log] +Level = 'info' # this should be 'debug' for chainlink internal deployments, nops may use 'info' to reduce log volume + +[Log.File] +< standard values > + +[WebServer] +< standard values > + +[WebServer.TLS] +< standard values > + +[[EVM]] +ChainID = '42161' # change as needed based on target chain + +[OCR] +Enabled = false # turn off OCR 1 + +[P2P] +TraceLogging = false # this should be 'true' for chainlink internal deployments, we may ask nops to set this to true for debugging +PeerID = '$PEERID' + +[P2P.V2] +Enabled = true # required +DefaultBootstrappers = < mercury bootstrap nodes > # Note that this should ideally be set in the job spec, this is just a fallback +# Make sure these IPs are properly configured in the firewall. May not be necessary for internal nodes +AnnounceAddresses = ['$EXTERNAL_IP:$EXTERNAL_PORT'] # Use whichever port you like, pls randomize, MAKE SURE ITS CONFIGURED IN THE FIREWALL +ListenAddresses = ['0.0.0.0:$INTERNAL_PORT'] # Use whichever port you like, pls randomize, MAKE SURE ITS CONFIGURED IN THE FIREWALL + +[OCR2] +Enabled = true # required +KeyBundleID = '$KEY_BUNDLE_ID' # Note that this should ideally be set in the job spec, this is just a fallback +CaptureEATelemetry = true + +[TelemetryIngress] +UniConn = false +SendInterval = '250ms' +BufferSize = 300 +MaxBatchSize = 100 + +[[TelemetryIngress.Endpoints]] +Network = 'EVM' +ChainID = '42161' # change as needed based on target chain +URL = '$TELEMETRY_ENDPOINT_URL' # Provided by Chainlink Labs RSTP team +ServerPubKey = '$TELEMETRY_PUB_KEY' # Provided by Chainlink Labs RSTP team + +[Database] +MaxIdleConns = 100 # should equal or greater than total number of mercury jobs +MaxOpenConns = 400 # caution! ensure postgres is configured to support this + +[[EVM.Nodes]] +< put RPC nodes here > +``` +
From 529d2cf8af1de371ba900b5f371496942ed52dc5 Mon Sep 17 00:00:00 2001 From: Gabriel Paradiso Date: Thu, 4 Jan 2024 17:05:25 +0100 Subject: [PATCH 113/234] [FUN-877] Persist subscriptions fetched from contracts (#11573) * feat: db schema to store data fetched from contracts * feat: build orm layer for subscriptions * feat: implement subscription cache layer * feat: load cached subscriptions concurrently * fix: lint issues * fix: protect NewORM from invalid parameters, NoopORM for gateway script * fix: address race condition on update subscription * chore: make db, cfg and lggr part of the handlerfactory * chore: removing allowlist migration from this pr * fix: update cache on new subscriptions, modify balance to be a text at the db layer * feat: store the router_contract_address * fix: set router contract address and subscription id as composite primary key * chore: make router contract address as part of orm properties * feat: update to db only in case of difference with current state * feat: filter by router_contract_address when GetSubscriptions * chore: have balance fields as bigint, add tests covering deleted subscriptions * fix: GetSubscriptions ASC * chore: improve redability --- core/scripts/gateway/run_gateway.go | 2 +- core/services/chainlink/application.go | 2 + core/services/gateway/delegate.go | 15 +- core/services/gateway/gateway_test.go | 12 +- core/services/gateway/handler_factory.go | 11 +- .../handlers/functions/handler.functions.go | 12 +- .../functions/handler.functions_test.go | 4 +- .../gateway/handlers/functions/mocks/orm.go | 91 +++++++ .../gateway/handlers/functions/orm.go | 132 ++++++++++ .../gateway/handlers/functions/orm_test.go | 246 ++++++++++++++++++ .../handlers/functions/subscriptions.go | 53 +++- .../handlers/functions/subscriptions_test.go | 67 ++++- .../handlers/functions/user_subscriptions.go | 44 +++- .../functions/user_subscriptions_test.go | 88 ++++++- .../gateway_integration_test.go | 2 +- .../services/ocr2/plugins/functions/plugin.go | 6 +- .../0215_functions_subscriptions.sql | 19 ++ 17 files changed, 762 insertions(+), 44 deletions(-) create mode 100644 core/services/gateway/handlers/functions/mocks/orm.go create mode 100644 core/services/gateway/handlers/functions/orm.go create mode 100644 core/services/gateway/handlers/functions/orm_test.go create mode 100644 core/store/migrate/migrations/0215_functions_subscriptions.sql diff --git a/core/scripts/gateway/run_gateway.go b/core/scripts/gateway/run_gateway.go index e30c43bb8af..5dbcd02bf56 100644 --- a/core/scripts/gateway/run_gateway.go +++ b/core/scripts/gateway/run_gateway.go @@ -48,7 +48,7 @@ func main() { lggr, _ := logger.NewLogger() - handlerFactory := gateway.NewHandlerFactory(nil, lggr) + handlerFactory := gateway.NewHandlerFactory(nil, nil, nil, lggr) gw, err := gateway.NewGatewayFromConfig(&cfg, handlerFactory, lggr) if err != nil { fmt.Println("error creating Gateway object:", err) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index ee109db2119..e94f51abdf5 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -344,6 +344,8 @@ func NewApplication(opts ApplicationOpts) (Application, error) { job.Gateway: gateway.NewDelegate( legacyEVMChains, keyStore.Eth(), + db, + cfg.Database(), globalLogger), } webhookJobRunner = delegates[job.Webhook].(*webhook.Delegate).WebhookJobRunner() diff --git a/core/services/gateway/delegate.go b/core/services/gateway/delegate.go index d4180184aca..8a97f68d1ea 100644 --- a/core/services/gateway/delegate.go +++ b/core/services/gateway/delegate.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/google/uuid" + "github.com/jmoiron/sqlx" "github.com/pelletier/go-toml" "github.com/pkg/errors" @@ -18,13 +19,21 @@ import ( type Delegate struct { legacyChains legacyevm.LegacyChainContainer ks keystore.Eth + db *sqlx.DB + cfg pg.QConfig lggr logger.Logger } var _ job.Delegate = (*Delegate)(nil) -func NewDelegate(legacyChains legacyevm.LegacyChainContainer, ks keystore.Eth, lggr logger.Logger) *Delegate { - return &Delegate{legacyChains: legacyChains, ks: ks, lggr: lggr} +func NewDelegate(legacyChains legacyevm.LegacyChainContainer, ks keystore.Eth, db *sqlx.DB, cfg pg.QConfig, lggr logger.Logger) *Delegate { + return &Delegate{ + legacyChains: legacyChains, + ks: ks, + db: db, + cfg: cfg, + lggr: lggr, + } } func (d *Delegate) JobType() job.Type { @@ -47,7 +56,7 @@ func (d *Delegate) ServicesForSpec(spec job.Job) (services []job.ServiceCtx, err if err2 != nil { return nil, errors.Wrap(err2, "unmarshal gateway config") } - handlerFactory := NewHandlerFactory(d.legacyChains, d.lggr) + handlerFactory := NewHandlerFactory(d.legacyChains, d.db, d.cfg, d.lggr) gateway, err := NewGatewayFromConfig(&gatewayConfig, handlerFactory, d.lggr) if err != nil { return nil, err diff --git a/core/services/gateway/gateway_test.go b/core/services/gateway/gateway_test.go index 74d689fffe1..1c31a643c80 100644 --- a/core/services/gateway/gateway_test.go +++ b/core/services/gateway/gateway_test.go @@ -57,7 +57,7 @@ Address = "0x0001020304050607080900010203040506070809" `) lggr := logger.TestLogger(t) - _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, lggr), lggr) + _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.NoError(t, err) } @@ -75,7 +75,7 @@ HandlerName = "dummy" `) lggr := logger.TestLogger(t) - _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, lggr), lggr) + _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.Error(t, err) } @@ -89,7 +89,7 @@ HandlerName = "no_such_handler" `) lggr := logger.TestLogger(t) - _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, lggr), lggr) + _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.Error(t, err) } @@ -103,7 +103,7 @@ SomeOtherField = "abcd" `) lggr := logger.TestLogger(t) - _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, lggr), lggr) + _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.Error(t, err) } @@ -121,7 +121,7 @@ Address = "0xnot_an_address" `) lggr := logger.TestLogger(t) - _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, lggr), lggr) + _, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, tomlConfig), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.Error(t, err) } @@ -129,7 +129,7 @@ func TestGateway_CleanStartAndClose(t *testing.T) { t.Parallel() lggr := logger.TestLogger(t) - gateway, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, buildConfig("")), gateway.NewHandlerFactory(nil, lggr), lggr) + gateway, err := gateway.NewGatewayFromConfig(parseTOMLConfig(t, buildConfig("")), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.NoError(t, err) servicetest.Run(t, gateway) } diff --git a/core/services/gateway/handler_factory.go b/core/services/gateway/handler_factory.go index 368bc64c872..8ccae8c7c4b 100644 --- a/core/services/gateway/handler_factory.go +++ b/core/services/gateway/handler_factory.go @@ -4,11 +4,14 @@ import ( "encoding/json" "fmt" + "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) const ( @@ -18,19 +21,21 @@ const ( type handlerFactory struct { legacyChains legacyevm.LegacyChainContainer + db *sqlx.DB + cfg pg.QConfig lggr logger.Logger } var _ HandlerFactory = (*handlerFactory)(nil) -func NewHandlerFactory(legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger) HandlerFactory { - return &handlerFactory{legacyChains, lggr} +func NewHandlerFactory(legacyChains legacyevm.LegacyChainContainer, db *sqlx.DB, cfg pg.QConfig, lggr logger.Logger) HandlerFactory { + return &handlerFactory{legacyChains, db, cfg, lggr} } func (hf *handlerFactory) NewHandler(handlerType HandlerType, handlerConfig json.RawMessage, donConfig *config.DONConfig, don handlers.DON) (handlers.Handler, error) { switch handlerType { case FunctionsHandlerType: - return functions.NewFunctionsHandlerFromConfig(handlerConfig, donConfig, don, hf.legacyChains, hf.lggr) + return functions.NewFunctionsHandlerFromConfig(handlerConfig, donConfig, don, hf.legacyChains, hf.db, hf.cfg, hf.lggr) case DummyHandlerType: return handlers.NewDummyHandler(donConfig, don, hf.lggr) default: diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 5277f9789d6..2872c72f761 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" @@ -22,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) var ( @@ -96,7 +98,7 @@ type PendingRequest struct { var _ handlers.Handler = (*functionsHandler)(nil) -func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *config.DONConfig, don handlers.DON, legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger) (handlers.Handler, error) { +func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *config.DONConfig, don handlers.DON, legacyChains legacyevm.LegacyChainContainer, db *sqlx.DB, qcfg pg.QConfig, lggr logger.Logger) (handlers.Handler, error) { var cfg FunctionsHandlerConfig err := json.Unmarshal(handlerConfig, &cfg) if err != nil { @@ -133,7 +135,13 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con if err2 != nil { return nil, err2 } - subscriptions, err2 = NewOnchainSubscriptions(chain.Client(), *cfg.OnchainSubscriptions, lggr) + + orm, err2 := NewORM(db, lggr, qcfg, cfg.OnchainSubscriptions.ContractAddress) + if err2 != nil { + return nil, err2 + } + + subscriptions, err2 = NewOnchainSubscriptions(chain.Client(), *cfg.OnchainSubscriptions, orm, lggr) if err2 != nil { return nil, err2 } diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 1d6dd109625..7e055815c88 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -83,7 +83,7 @@ func sendNodeReponses(t *testing.T, handler handlers.Handler, userRequestMsg api func TestFunctionsHandler_Minimal(t *testing.T) { t.Parallel() - handler, err := functions.NewFunctionsHandlerFromConfig(json.RawMessage("{}"), &config.DONConfig{}, nil, nil, logger.TestLogger(t)) + handler, err := functions.NewFunctionsHandlerFromConfig(json.RawMessage("{}"), &config.DONConfig{}, nil, nil, nil, nil, logger.TestLogger(t)) require.NoError(t, err) // empty message should always error out @@ -95,7 +95,7 @@ func TestFunctionsHandler_Minimal(t *testing.T) { func TestFunctionsHandler_CleanStartAndClose(t *testing.T) { t.Parallel() - handler, err := functions.NewFunctionsHandlerFromConfig(json.RawMessage("{}"), &config.DONConfig{}, nil, nil, logger.TestLogger(t)) + handler, err := functions.NewFunctionsHandlerFromConfig(json.RawMessage("{}"), &config.DONConfig{}, nil, nil, nil, nil, logger.TestLogger(t)) require.NoError(t, err) servicetest.Run(t, handler) diff --git a/core/services/gateway/handlers/functions/mocks/orm.go b/core/services/gateway/handlers/functions/mocks/orm.go new file mode 100644 index 00000000000..110675c8b55 --- /dev/null +++ b/core/services/gateway/handlers/functions/mocks/orm.go @@ -0,0 +1,91 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + functions "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + mock "github.com/stretchr/testify/mock" + + pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" +) + +// ORM is an autogenerated mock type for the ORM type +type ORM struct { + mock.Mock +} + +// GetSubscriptions provides a mock function with given fields: offset, limit, qopts +func (_m *ORM) GetSubscriptions(offset uint, limit uint, qopts ...pg.QOpt) ([]functions.CachedSubscription, error) { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, offset, limit) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetSubscriptions") + } + + var r0 []functions.CachedSubscription + var r1 error + if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) ([]functions.CachedSubscription, error)); ok { + return rf(offset, limit, qopts...) + } + if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) []functions.CachedSubscription); ok { + r0 = rf(offset, limit, qopts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]functions.CachedSubscription) + } + } + + if rf, ok := ret.Get(1).(func(uint, uint, ...pg.QOpt) error); ok { + r1 = rf(offset, limit, qopts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpsertSubscription provides a mock function with given fields: subscription, qopts +func (_m *ORM) UpsertSubscription(subscription functions.CachedSubscription, qopts ...pg.QOpt) error { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, subscription) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for UpsertSubscription") + } + + var r0 error + if rf, ok := ret.Get(0).(func(functions.CachedSubscription, ...pg.QOpt) error); ok { + r0 = rf(subscription, qopts...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewORM creates a new instance of ORM. 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 NewORM(t interface { + mock.TestingT + Cleanup(func()) +}) *ORM { + mock := &ORM{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/gateway/handlers/functions/orm.go b/core/services/gateway/handlers/functions/orm.go new file mode 100644 index 00000000000..b7ec8d865d1 --- /dev/null +++ b/core/services/gateway/handlers/functions/orm.go @@ -0,0 +1,132 @@ +package functions + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/lib/pq" + "github.com/pkg/errors" + + "github.com/jmoiron/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" +) + +//go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore + +type ORM interface { + GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]CachedSubscription, error) + UpsertSubscription(subscription CachedSubscription, qopts ...pg.QOpt) error +} + +type orm struct { + q pg.Q + routerContractAddress common.Address +} + +var _ ORM = (*orm)(nil) +var ( + ErrInvalidParameters = errors.New("invalid parameters provided to create a subscription cache ORM") +) + +const ( + tableName = "functions_subscriptions" +) + +type cachedSubscriptionRow struct { + SubscriptionID uint64 + Owner common.Address + Balance int64 + BlockedBalance int64 + ProposedOwner common.Address + Consumers pq.ByteaArray + Flags []uint8 + RouterContractAddress common.Address +} + +func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, routerContractAddress common.Address) (ORM, error) { + if db == nil || cfg == nil || lggr == nil || routerContractAddress == (common.Address{}) { + return nil, ErrInvalidParameters + } + + return &orm{ + q: pg.NewQ(db, lggr, cfg), + routerContractAddress: routerContractAddress, + }, nil +} + +func (o *orm) GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]CachedSubscription, error) { + var cacheSubscriptions []CachedSubscription + var cacheSubscriptionRows []cachedSubscriptionRow + stmt := fmt.Sprintf(` + SELECT subscription_id, owner, balance, blocked_balance, proposed_owner, consumers, flags, router_contract_address + FROM %s + WHERE router_contract_address = $1 + ORDER BY subscription_id ASC + OFFSET $2 + LIMIT $3; + `, tableName) + err := o.q.WithOpts(qopts...).Select(&cacheSubscriptionRows, stmt, o.routerContractAddress, offset, limit) + if err != nil { + return cacheSubscriptions, err + } + + for _, cs := range cacheSubscriptionRows { + cacheSubscriptions = append(cacheSubscriptions, cs.encode()) + } + + return cacheSubscriptions, nil +} + +// UpsertSubscription will update if a subscription exists or create if it does not. +// In case a subscription gets deleted we will update it with an owner address equal to 0x0. +func (o *orm) UpsertSubscription(subscription CachedSubscription, qopts ...pg.QOpt) error { + stmt := fmt.Sprintf(` + INSERT INTO %s (subscription_id, owner, balance, blocked_balance, proposed_owner, consumers, flags, router_contract_address) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT (subscription_id, router_contract_address) DO UPDATE + SET owner=$2, balance=$3, blocked_balance=$4, proposed_owner=$5, consumers=$6, flags=$7, router_contract_address=$8;`, tableName) + + if subscription.Balance == nil { + subscription.Balance = big.NewInt(0) + } + + if subscription.BlockedBalance == nil { + subscription.BlockedBalance = big.NewInt(0) + } + + _, err := o.q.WithOpts(qopts...).Exec( + stmt, + subscription.SubscriptionID, + subscription.Owner, + subscription.Balance.Int64(), + subscription.BlockedBalance.Int64(), + subscription.ProposedOwner, + subscription.Consumers, + subscription.Flags[:], + o.routerContractAddress, + ) + + return err +} + +func (cs *cachedSubscriptionRow) encode() CachedSubscription { + consumers := make([]common.Address, 0) + for _, csc := range cs.Consumers { + consumers = append(consumers, common.BytesToAddress(csc)) + } + + return CachedSubscription{ + SubscriptionID: cs.SubscriptionID, + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(cs.Balance), + Owner: cs.Owner, + BlockedBalance: big.NewInt(cs.BlockedBalance), + ProposedOwner: cs.ProposedOwner, + Consumers: consumers, + Flags: [32]byte(cs.Flags), + }, + } +} diff --git a/core/services/gateway/handlers/functions/orm_test.go b/core/services/gateway/handlers/functions/orm_test.go new file mode 100644 index 00000000000..37ec24ea0b1 --- /dev/null +++ b/core/services/gateway/handlers/functions/orm_test.go @@ -0,0 +1,246 @@ +package functions_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" + "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/gateway/handlers/functions" +) + +var ( + defaultFlags = [32]byte{0x1, 0x2, 0x3} +) + +func setupORM(t *testing.T) (functions.ORM, error) { + t.Helper() + + var ( + db = pgtest.NewSqlxDB(t) + lggr = logger.TestLogger(t) + ) + + return functions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) +} + +func createSubscriptions(t *testing.T, orm functions.ORM, amount int) []functions.CachedSubscription { + cachedSubscriptions := make([]functions.CachedSubscription, 0) + for i := amount; i > 0; i-- { + cs := functions.CachedSubscription{ + SubscriptionID: uint64(i), + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(10), + Owner: testutils.NewAddress(), + BlockedBalance: big.NewInt(20), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: defaultFlags, + }, + } + cachedSubscriptions = append(cachedSubscriptions, cs) + err := orm.UpsertSubscription(cs) + require.NoError(t, err) + } + return cachedSubscriptions +} + +func TestORM_GetSubscriptions(t *testing.T) { + t.Parallel() + t.Run("fetch first page", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + cachedSubscriptions := createSubscriptions(t, orm, 2) + results, err := orm.GetSubscriptions(0, 1) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, cachedSubscriptions[1], results[0]) + }) + + t.Run("fetch second page", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + cachedSubscriptions := createSubscriptions(t, orm, 2) + results, err := orm.GetSubscriptions(1, 5) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, cachedSubscriptions[0], results[0]) + }) +} + +func TestORM_UpsertSubscription(t *testing.T) { + t.Parallel() + + t.Run("create a subscription", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + expected := functions.CachedSubscription{ + SubscriptionID: uint64(1), + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(10), + Owner: testutils.NewAddress(), + BlockedBalance: big.NewInt(20), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: defaultFlags, + }, + } + err = orm.UpsertSubscription(expected) + require.NoError(t, err) + + results, err := orm.GetSubscriptions(0, 1) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, expected, results[0]) + }) + + t.Run("update a subscription", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + + expectedUpdated := functions.CachedSubscription{ + SubscriptionID: uint64(1), + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(10), + Owner: testutils.NewAddress(), + BlockedBalance: big.NewInt(20), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: defaultFlags, + }, + } + err = orm.UpsertSubscription(expectedUpdated) + require.NoError(t, err) + + expectedNotUpdated := functions.CachedSubscription{ + SubscriptionID: uint64(2), + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(10), + Owner: testutils.NewAddress(), + BlockedBalance: big.NewInt(20), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: defaultFlags, + }, + } + err = orm.UpsertSubscription(expectedNotUpdated) + require.NoError(t, err) + + // update the balance value + expectedUpdated.Balance = big.NewInt(20) + err = orm.UpsertSubscription(expectedUpdated) + require.NoError(t, err) + + results, err := orm.GetSubscriptions(0, 5) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, expectedNotUpdated, results[1]) + require.Equal(t, expectedUpdated, results[0]) + }) + + t.Run("update a deleted subscription", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + + subscription := functions.CachedSubscription{ + SubscriptionID: uint64(1), + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(10), + Owner: testutils.NewAddress(), + BlockedBalance: big.NewInt(20), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: defaultFlags, + }, + } + err = orm.UpsertSubscription(subscription) + require.NoError(t, err) + + // empty subscription + subscription.IFunctionsSubscriptionsSubscription = functions_router.IFunctionsSubscriptionsSubscription{ + Balance: big.NewInt(0), + Owner: common.Address{}, + BlockedBalance: big.NewInt(0), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: [32]byte{}, + } + + err = orm.UpsertSubscription(subscription) + require.NoError(t, err) + + results, err := orm.GetSubscriptions(0, 5) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, subscription, results[0]) + }) + + t.Run("create a subscription with same id but different router address", func(t *testing.T) { + var ( + db = pgtest.NewSqlxDB(t) + lggr = logger.TestLogger(t) + ) + + orm1, err := functions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) + require.NoError(t, err) + orm2, err := functions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) + require.NoError(t, err) + + subscription := functions.CachedSubscription{ + SubscriptionID: uint64(1), + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: assets.Ether(10).ToInt(), + Owner: testutils.NewAddress(), + BlockedBalance: assets.Ether(20).ToInt(), + ProposedOwner: common.Address{}, + Consumers: []common.Address{}, + Flags: defaultFlags, + }, + } + + err = orm1.UpsertSubscription(subscription) + require.NoError(t, err) + + // should update the existing subscription + subscription.Balance = assets.Ether(12).ToInt() + err = orm1.UpsertSubscription(subscription) + require.NoError(t, err) + + results, err := orm1.GetSubscriptions(0, 10) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + + // should create a new subscription because it comes from a different router contract + err = orm2.UpsertSubscription(subscription) + require.NoError(t, err) + + results, err = orm1.GetSubscriptions(0, 10) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + + results, err = orm2.GetSubscriptions(0, 10) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + }) +} + +func Test_NewORM(t *testing.T) { + t.Run("OK-create_ORM", func(t *testing.T) { + _, err := functions.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), testutils.NewAddress()) + require.NoError(t, err) + }) + t.Run("NOK-create_ORM_with_nil_fields", func(t *testing.T) { + _, err := functions.NewORM(nil, nil, nil, common.Address{}) + require.Error(t, err) + }) + t.Run("NOK-create_ORM_with_empty_address", func(t *testing.T) { + _, err := functions.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), common.Address{}) + require.Error(t, err) + }) +} diff --git a/core/services/gateway/handlers/functions/subscriptions.go b/core/services/gateway/handlers/functions/subscriptions.go index ebffbbdd206..8bc9cf09e7b 100644 --- a/core/services/gateway/handlers/functions/subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions.go @@ -19,12 +19,15 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +const defaultCacheBatchSize = 100 + type OnchainSubscriptionsConfig struct { ContractAddress common.Address `json:"contractAddress"` BlockConfirmations uint `json:"blockConfirmations"` UpdateFrequencySec uint `json:"updateFrequencySec"` UpdateTimeoutSec uint `json:"updateTimeoutSec"` UpdateRangeSize uint `json:"updateRangeSize"` + CacheBatchSize uint `json:"cacheBatchSize"` } // OnchainSubscriptions maintains a mirror of all subscriptions fetched from the blockchain (EVM-only). @@ -43,6 +46,7 @@ type onchainSubscriptions struct { config OnchainSubscriptionsConfig subscriptions UserSubscriptions + orm ORM client evmclient.Client router *functions_router.FunctionsRouter blockConfirmations *big.Int @@ -52,7 +56,7 @@ type onchainSubscriptions struct { stopCh services.StopChan } -func NewOnchainSubscriptions(client evmclient.Client, config OnchainSubscriptionsConfig, lggr logger.Logger) (OnchainSubscriptions, error) { +func NewOnchainSubscriptions(client evmclient.Client, config OnchainSubscriptionsConfig, orm ORM, lggr logger.Logger) (OnchainSubscriptions, error) { if client == nil { return nil, errors.New("client is nil") } @@ -63,9 +67,17 @@ func NewOnchainSubscriptions(client evmclient.Client, config OnchainSubscription if err != nil { return nil, fmt.Errorf("unexpected error during functions_router.NewFunctionsRouter: %s", err) } + + // if CacheBatchSize is not specified use the default value + if config.CacheBatchSize == 0 { + lggr.Info("CacheBatchSize not specified, using default size: ", defaultCacheBatchSize) + config.CacheBatchSize = defaultCacheBatchSize + } + return &onchainSubscriptions{ config: config, subscriptions: NewUserSubscriptions(), + orm: orm, client: client, router: router, blockConfirmations: big.NewInt(int64(config.BlockConfirmations)), @@ -87,6 +99,8 @@ func (s *onchainSubscriptions) Start(ctx context.Context) error { return errors.New("OnchainSubscriptionsConfig.UpdateRangeSize must be greater than 0") } + s.loadCachedSubscriptions() + s.closeWait.Add(1) go s.queryLoop() @@ -190,7 +204,15 @@ func (s *onchainSubscriptions) querySubscriptionsRange(ctx context.Context, bloc for i, subscription := range subscriptions { subscriptionId := start + uint64(i) subscription := subscription - s.subscriptions.UpdateSubscription(subscriptionId, &subscription) + updated := s.subscriptions.UpdateSubscription(subscriptionId, &subscription) + if updated { + if err = s.orm.UpsertSubscription(CachedSubscription{ + SubscriptionID: subscriptionId, + IFunctionsSubscriptionsSubscription: subscription, + }); err != nil { + s.lggr.Errorf("unexpected error updating subscription in the cache: %w", err) + } + } } return nil @@ -203,3 +225,30 @@ func (s *onchainSubscriptions) getSubscriptionsCount(ctx context.Context, blockN Context: ctx, }) } + +func (s *onchainSubscriptions) loadCachedSubscriptions() { + offset := uint(0) + for { + csBatch, err := s.orm.GetSubscriptions(offset, s.config.CacheBatchSize) + if err != nil { + break + } + + for _, cs := range csBatch { + _ = s.subscriptions.UpdateSubscription(cs.SubscriptionID, &functions_router.IFunctionsSubscriptionsSubscription{ + Balance: cs.Balance, + Owner: cs.Owner, + BlockedBalance: cs.BlockedBalance, + ProposedOwner: cs.ProposedOwner, + Consumers: cs.Consumers, + Flags: cs.Flags, + }) + } + s.lggr.Debugw("Loading cached subscriptions", "offset", offset, "batch_length", len(csBatch)) + + if len(csBatch) != int(s.config.CacheBatchSize) { + break + } + offset += s.config.CacheBatchSize + } +} diff --git a/core/services/gateway/handlers/functions/subscriptions_test.go b/core/services/gateway/handlers/functions/subscriptions_test.go index adbf637ad73..782dcc2332d 100644 --- a/core/services/gateway/handlers/functions/subscriptions_test.go +++ b/core/services/gateway/handlers/functions/subscriptions_test.go @@ -15,14 +15,17 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + fmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" ) const ( validUser = "0x9ED925d8206a4f88a2f643b28B3035B315753Cd6" invalidUser = "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C" + cachedUser = "0x3E2dc0F9DB014aE19888F539E59285D2Ea04233G" ) func TestSubscriptions_OnePass(t *testing.T) { @@ -47,7 +50,10 @@ func TestSubscriptions_OnePass(t *testing.T) { UpdateTimeoutSec: 1, UpdateRangeSize: 3, } - subscriptions, err := functions.NewOnchainSubscriptions(client, config, logger.TestLogger(t)) + orm := fmocks.NewORM(t) + orm.On("GetSubscriptions", uint(0), uint(100)).Return([]functions.CachedSubscription{}, nil) + orm.On("UpsertSubscription", mock.Anything).Return(nil) + subscriptions, err := functions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) require.NoError(t, err) err = subscriptions.Start(ctx) @@ -95,7 +101,10 @@ func TestSubscriptions_MultiPass(t *testing.T) { UpdateTimeoutSec: 1, UpdateRangeSize: 3, } - subscriptions, err := functions.NewOnchainSubscriptions(client, config, logger.TestLogger(t)) + orm := fmocks.NewORM(t) + orm.On("GetSubscriptions", uint(0), uint(100)).Return([]functions.CachedSubscription{}, nil) + orm.On("UpsertSubscription", mock.Anything).Return(nil) + subscriptions, err := functions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) require.NoError(t, err) err = subscriptions.Start(ctx) @@ -108,3 +117,57 @@ func TestSubscriptions_MultiPass(t *testing.T) { return currentCycle.Load() == ncycles }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) } + +func TestSubscriptions_Cached(t *testing.T) { + getSubscriptionCount := hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000003") + getSubscriptionsInRange := hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000109e6e1b12098cc8f3a1e9719a817ec53ab9b35c000000000000000000000000000000000000000000000000000034e23f515cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f5340f0968ee8b7dfd97e3327a6139273cc2c4fa000000000000000000000000000000000000000000000001158e460913d000000000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc14b92364c75e20000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005439e5881a529f3ccbffc0e82d49f9db3950aefe") + + ctx := testutils.Context(t) + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // getSubscriptionCount + To: &common.Address{}, + Data: hexutil.MustDecode("0x66419970"), + }, mock.Anything).Return(getSubscriptionCount, nil) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // GetSubscriptionsInRange + To: &common.Address{}, + Data: hexutil.MustDecode("0xec2454e500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003"), + }, mock.Anything).Return(getSubscriptionsInRange, nil) + config := functions.OnchainSubscriptionsConfig{ + ContractAddress: common.Address{}, + BlockConfirmations: 1, + UpdateFrequencySec: 1, + UpdateTimeoutSec: 1, + UpdateRangeSize: 3, + CacheBatchSize: 1, + } + + expectedBalance := big.NewInt(5) + orm := fmocks.NewORM(t) + orm.On("GetSubscriptions", uint(0), uint(1)).Return([]functions.CachedSubscription{ + { + SubscriptionID: 1, + IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ + Balance: expectedBalance, + Owner: common.HexToAddress(cachedUser), + BlockedBalance: big.NewInt(10), + }, + }, + }, nil) + orm.On("GetSubscriptions", uint(1), uint(1)).Return([]functions.CachedSubscription{}, nil) + orm.On("UpsertSubscription", mock.Anything).Return(nil) + + subscriptions, err := functions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) + require.NoError(t, err) + + err = subscriptions.Start(ctx) + require.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, subscriptions.Close()) + }) + + gomega.NewGomegaWithT(t).Eventually(func() bool { + actualBalance, err := subscriptions.GetMaxUserBalance(common.HexToAddress(cachedUser)) + return err == nil && assert.Equal(t, expectedBalance, actualBalance) + }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) +} diff --git a/core/services/gateway/handlers/functions/user_subscriptions.go b/core/services/gateway/handlers/functions/user_subscriptions.go index ff3dd753995..7e63720da71 100644 --- a/core/services/gateway/handlers/functions/user_subscriptions.go +++ b/core/services/gateway/handlers/functions/user_subscriptions.go @@ -12,8 +12,10 @@ import ( // Methods are NOT thread-safe. +var ErrUserHasNoSubscription = errors.New("user has no subscriptions") + type UserSubscriptions interface { - UpdateSubscription(subscriptionId uint64, subscription *functions_router.IFunctionsSubscriptionsSubscription) + UpdateSubscription(subscriptionId uint64, subscription *functions_router.IFunctionsSubscriptionsSubscription) bool GetMaxUserBalance(user common.Address) (*big.Int, error) } @@ -29,29 +31,45 @@ func NewUserSubscriptions() UserSubscriptions { } } -func (us *userSubscriptions) UpdateSubscription(subscriptionId uint64, subscription *functions_router.IFunctionsSubscriptionsSubscription) { +// CachedSubscription is used to populate the user subscription maps from a persistent layer like postgres. +type CachedSubscription struct { + SubscriptionID uint64 + functions_router.IFunctionsSubscriptionsSubscription +} + +// UpdateSubscription updates a subscription returning false in case there was no variation to the current state. +func (us *userSubscriptions) UpdateSubscription(subscriptionId uint64, subscription *functions_router.IFunctionsSubscriptionsSubscription) bool { if subscription == nil || subscription.Owner == utils.ZeroAddress { user, ok := us.subscriptionIdsMap[subscriptionId] - if ok { - delete(us.userSubscriptionsMap[user], subscriptionId) - if len(us.userSubscriptionsMap[user]) == 0 { - delete(us.userSubscriptionsMap, user) - } + if !ok { + return false } + + delete(us.userSubscriptionsMap[user], subscriptionId) delete(us.subscriptionIdsMap, subscriptionId) - } else { - us.subscriptionIdsMap[subscriptionId] = subscription.Owner - if _, ok := us.userSubscriptionsMap[subscription.Owner]; !ok { - us.userSubscriptionsMap[subscription.Owner] = make(map[uint64]*functions_router.IFunctionsSubscriptionsSubscription) + if len(us.userSubscriptionsMap[user]) == 0 { + delete(us.userSubscriptionsMap, user) } - us.userSubscriptionsMap[subscription.Owner][subscriptionId] = subscription + return true + } + + // there is no change to the subscription + if us.userSubscriptionsMap[subscription.Owner][subscriptionId] == subscription { + return false + } + + us.subscriptionIdsMap[subscriptionId] = subscription.Owner + if _, ok := us.userSubscriptionsMap[subscription.Owner]; !ok { + us.userSubscriptionsMap[subscription.Owner] = make(map[uint64]*functions_router.IFunctionsSubscriptionsSubscription) } + us.userSubscriptionsMap[subscription.Owner][subscriptionId] = subscription + return true } func (us *userSubscriptions) GetMaxUserBalance(user common.Address) (*big.Int, error) { subs, exists := us.userSubscriptionsMap[user] if !exists { - return nil, errors.New("user has no subscriptions") + return nil, ErrUserHasNoSubscription } maxBalance := big.NewInt(0) diff --git a/core/services/gateway/handlers/functions/user_subscriptions_test.go b/core/services/gateway/handlers/functions/user_subscriptions_test.go index e86399eb609..53827e07e1b 100644 --- a/core/services/gateway/handlers/functions/user_subscriptions_test.go +++ b/core/services/gateway/handlers/functions/user_subscriptions_test.go @@ -28,18 +28,23 @@ func TestUserSubscriptions(t *testing.T) { user2Balance1 := big.NewInt(50) user2Balance2 := big.NewInt(70) - us.UpdateSubscription(5, &functions_router.IFunctionsSubscriptionsSubscription{ + updated := us.UpdateSubscription(5, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: user1, Balance: user1Balance, }) - us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ + assert.True(t, updated) + + updated = us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: user2, Balance: user2Balance1, }) - us.UpdateSubscription(10, &functions_router.IFunctionsSubscriptionsSubscription{ + assert.True(t, updated) + + updated = us.UpdateSubscription(10, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: user2, Balance: user2Balance2, }) + assert.True(t, updated) balance, err := us.GetMaxUserBalance(user1) assert.NoError(t, err) @@ -49,29 +54,96 @@ func TestUserSubscriptions(t *testing.T) { assert.NoError(t, err) assert.Zero(t, balance.Cmp(user2Balance2)) }) +} - t.Run("UpdateSubscription to remove subscriptions", func(t *testing.T) { +func TestUserSubscriptions_UpdateSubscription(t *testing.T) { + t.Parallel() + + t.Run("update balance", func(t *testing.T) { + us := functions.NewUserSubscriptions() + owner := utils.RandomAddress() + + updated := us.UpdateSubscription(1, &functions_router.IFunctionsSubscriptionsSubscription{ + Owner: owner, + Balance: big.NewInt(10), + }) + assert.True(t, updated) + + updated = us.UpdateSubscription(1, &functions_router.IFunctionsSubscriptionsSubscription{ + Owner: owner, + Balance: big.NewInt(100), + }) + assert.True(t, updated) + }) + + t.Run("updated proposed owner", func(t *testing.T) { + us := functions.NewUserSubscriptions() + owner := utils.RandomAddress() + + updated := us.UpdateSubscription(1, &functions_router.IFunctionsSubscriptionsSubscription{ + Owner: owner, + Balance: big.NewInt(10), + }) + assert.True(t, updated) + + updated = us.UpdateSubscription(1, &functions_router.IFunctionsSubscriptionsSubscription{ + Owner: owner, + Balance: big.NewInt(10), + ProposedOwner: utils.RandomAddress(), + }) + assert.True(t, updated) + }) + t.Run("remove subscriptions", func(t *testing.T) { + us := functions.NewUserSubscriptions() user2 := utils.RandomAddress() user2Balance1 := big.NewInt(50) user2Balance2 := big.NewInt(70) - us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ + updated := us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: user2, Balance: user2Balance1, }) - us.UpdateSubscription(10, &functions_router.IFunctionsSubscriptionsSubscription{ + assert.True(t, updated) + + updated = us.UpdateSubscription(10, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: user2, Balance: user2Balance2, }) + assert.True(t, updated) - us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ + updated = us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: utils.ZeroAddress, }) - us.UpdateSubscription(10, &functions_router.IFunctionsSubscriptionsSubscription{ + assert.True(t, updated) + + updated = us.UpdateSubscription(10, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: utils.ZeroAddress, }) + assert.True(t, updated) _, err := us.GetMaxUserBalance(user2) assert.Error(t, err) }) + + t.Run("remove a non existing subscription", func(t *testing.T) { + us := functions.NewUserSubscriptions() + updated := us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ + Owner: utils.ZeroAddress, + }) + assert.False(t, updated) + }) + + t.Run("no actual changes", func(t *testing.T) { + us := functions.NewUserSubscriptions() + subscription := &functions_router.IFunctionsSubscriptionsSubscription{ + Owner: utils.RandomAddress(), + Balance: big.NewInt(25), + BlockedBalance: big.NewInt(25), + } + updated := us.UpdateSubscription(5, subscription) + assert.True(t, updated) + + updated = us.UpdateSubscription(5, subscription) + assert.False(t, updated) + }) } diff --git a/core/services/gateway/integration_tests/gateway_integration_test.go b/core/services/gateway/integration_tests/gateway_integration_test.go index 415a8f67cf8..9e4900efeee 100644 --- a/core/services/gateway/integration_tests/gateway_integration_test.go +++ b/core/services/gateway/integration_tests/gateway_integration_test.go @@ -118,7 +118,7 @@ func TestIntegration_Gateway_NoFullNodes_BasicConnectionAndMessage(t *testing.T) // Launch Gateway lggr := logger.TestLogger(t) gatewayConfig := fmt.Sprintf(gatewayConfigTemplate, nodeKeys.Address) - gateway, err := gateway.NewGatewayFromConfig(parseGatewayConfig(t, gatewayConfig), gateway.NewHandlerFactory(nil, lggr), lggr) + gateway, err := gateway.NewGatewayFromConfig(parseGatewayConfig(t, gatewayConfig), gateway.NewHandlerFactory(nil, nil, nil, lggr), lggr) require.NoError(t, err) servicetest.Run(t, gateway) userPort, nodePort := gateway.GetUserPort(), gateway.GetNodePort() diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 7e2b15bdccf..c6cfa946aba 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -143,7 +143,11 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs if err2 != nil { return nil, errors.Wrap(err, "failed to create a RateLimiter") } - subscriptions, err2 := gwFunctions.NewOnchainSubscriptions(conf.Chain.Client(), *pluginConfig.OnchainSubscriptions, conf.Logger) + gwFunctionsORM, err := gwFunctions.NewORM(conf.DB, conf.Logger, conf.QConfig, pluginConfig.OnchainSubscriptions.ContractAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to create functions ORM") + } + subscriptions, err2 := gwFunctions.NewOnchainSubscriptions(conf.Chain.Client(), *pluginConfig.OnchainSubscriptions, gwFunctionsORM, conf.Logger) if err2 != nil { return nil, errors.Wrap(err, "failed to create a OnchainSubscriptions") } diff --git a/core/store/migrate/migrations/0215_functions_subscriptions.sql b/core/store/migrate/migrations/0215_functions_subscriptions.sql new file mode 100644 index 00000000000..c3859d42f63 --- /dev/null +++ b/core/store/migrate/migrations/0215_functions_subscriptions.sql @@ -0,0 +1,19 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE functions_subscriptions( + router_contract_address bytea, + subscription_id bigint, + owner bytea CHECK (octet_length(owner) = 20) NOT NULL, + balance bigint, + blocked_balance bigint, + proposed_owner bytea, + consumers bytea[], + flags bytea, + PRIMARY KEY(router_contract_address, subscription_id) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS functions_subscriptions; +-- +goose StatementEnd From 2f10153ff81916a561f3035dee98f2530e6a17a6 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 4 Jan 2024 11:35:26 -0600 Subject: [PATCH 114/234] add chainlink health command; make DB avialable for testscript client/server tests (#11591) --- core/cmd/app.go | 11 + core/cmd/shell_local.go | 14 +- core/cmd/shell_remote.go | 18 ++ core/internal/cltest/heavyweight/orm.go | 71 ++--- core/web/health_controller.go | 2 +- core/web/health_controller_test.go | 4 +- docs/CHANGELOG.md | 4 + internal/testdb/testdb.go | 56 ++++ main_test.go | 94 ++++++- testdata/scripts/health/default.txtar | 129 +++++++++ testdata/scripts/health/help.txtar | 13 + testdata/scripts/health/multi-chain.txtar | 309 ++++++++++++++++++++++ testdata/scripts/help.txtar | 1 + testdata/scripts/node/db/migrate/db.txtar | 6 + 14 files changed, 658 insertions(+), 74 deletions(-) create mode 100644 internal/testdb/testdb.go create mode 100644 testdata/scripts/health/default.txtar create mode 100644 testdata/scripts/health/help.txtar create mode 100644 testdata/scripts/health/multi-chain.txtar create mode 100644 testdata/scripts/node/db/migrate/db.txtar diff --git a/core/cmd/app.go b/core/cmd/app.go index 17a4da85002..8e61380156c 100644 --- a/core/cmd/app.go +++ b/core/cmd/app.go @@ -162,6 +162,17 @@ func NewApp(s *Shell) *cli.App { Usage: "Commands for the node's configuration", Subcommands: initRemoteConfigSubCmds(s), }, + { + Name: "health", + Usage: "Prints a health report", + Action: s.Health, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "json, j", + Usage: "json output", + }, + }, + }, { Name: "jobs", Usage: "Commands for managing Jobs", diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index d4fb796c3e2..69b7373ed70 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -51,6 +51,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" webPresenters "github.com/smartcontractkit/chainlink/v2/core/web/presenters" + "github.com/smartcontractkit/chainlink/v2/internal/testdb" ) var ErrProfileTooLong = errors.New("requested profile duration too large") @@ -258,13 +259,6 @@ func initLocalSubCmds(s *Shell, safe bool) []cli.Command { // ownerPermsMask are the file permission bits reserved for owner. const ownerPermsMask = os.FileMode(0o700) -// PristineDBName is a clean copy of test DB with migrations. -// Used by heavyweight.FullTestDB* functions. -const ( - PristineDBName = "chainlink_test_pristine" - TestDBNamePrefix = "chainlink_test_" -) - // RunNode starts the Chainlink core. func (s *Shell) RunNode(c *cli.Context) error { if err := s.runNode(c); err != nil { @@ -815,7 +809,7 @@ func dropDanglingTestDBs(lggr logger.Logger, db *sqlx.DB) (err error) { }() } for _, dbname := range dbs { - if strings.HasPrefix(dbname, TestDBNamePrefix) && !strings.HasSuffix(dbname, "_pristine") { + if strings.HasPrefix(dbname, testdb.TestDBNamePrefix) && !strings.HasSuffix(dbname, "_pristine") { ch <- dbname } } @@ -1085,11 +1079,11 @@ func dropAndCreateDB(parsed url.URL) (err error) { } func dropAndCreatePristineDB(db *sqlx.DB, template string) (err error) { - _, err = db.Exec(fmt.Sprintf(`DROP DATABASE IF EXISTS "%s"`, PristineDBName)) + _, err = db.Exec(fmt.Sprintf(`DROP DATABASE IF EXISTS "%s"`, testdb.PristineDBName)) if err != nil { return fmt.Errorf("unable to drop postgres database: %v", err) } - _, err = db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH TEMPLATE "%s"`, PristineDBName, template)) + _, err = db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH TEMPLATE "%s"`, testdb.PristineDBName, template)) if err != nil { return fmt.Errorf("unable to create postgres database: %v", err) } diff --git a/core/cmd/shell_remote.go b/core/cmd/shell_remote.go index fa72d21ee6f..bc4620d0732 100644 --- a/core/cmd/shell_remote.go +++ b/core/cmd/shell_remote.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "github.com/gin-gonic/gin" "github.com/manyminds/api2go/jsonapi" "github.com/mitchellh/go-homedir" "github.com/pelletier/go-toml" @@ -511,6 +512,23 @@ func (s *Shell) checkRemoteBuildCompatibility(lggr logger.Logger, onlyWarn bool, return nil } +func (s *Shell) Health(c *cli.Context) error { + mime := gin.MIMEPlain + if c.Bool("json") { + mime = gin.MIMEJSON + } + resp, err := s.HTTP.Get("/health", map[string]string{"Accept": mime}) + if err != nil { + return s.errorOut(err) + } + b, err := parseResponse(resp) + if err != nil { + return s.errorOut(err) + } + fmt.Println(string(b)) + return nil +} + // ErrIncompatible is returned when the cli and remote versions are not compatible. type ErrIncompatible struct { CLIVersion, CLISha string diff --git a/core/internal/cltest/heavyweight/orm.go b/core/internal/cltest/heavyweight/orm.go index 5df28a49778..f49a94be05b 100644 --- a/core/internal/cltest/heavyweight/orm.go +++ b/core/internal/cltest/heavyweight/orm.go @@ -1,13 +1,8 @@ -package heavyweight - -// The heavyweight package contains cltest items that are costly and you should +// Package heavyweight contains test helpers that are costly and you should // think **real carefully** before using in your tests. +package heavyweight import ( - "database/sql" - "errors" - "fmt" - "net/url" "os" "path" "runtime" @@ -20,41 +15,45 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" "github.com/smartcontractkit/chainlink/v2/core/store/models" + "github.com/smartcontractkit/chainlink/v2/internal/testdb" ) // FullTestDBV2 creates a pristine DB which runs in a separate database than the normal // unit tests, so you can do things like use other Postgres connection types with it. func FullTestDBV2(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - return prepareFullTestDBV2(t, false, true, overrideFn) + return KindFixtures.PrepareDB(t, overrideFn) } // FullTestDBNoFixturesV2 is the same as FullTestDB, but it does not load fixtures. func FullTestDBNoFixturesV2(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - return prepareFullTestDBV2(t, false, false, overrideFn) + return KindTemplate.PrepareDB(t, overrideFn) } // FullTestDBEmptyV2 creates an empty DB (without migrations). func FullTestDBEmptyV2(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - return prepareFullTestDBV2(t, true, false, overrideFn) + return KindEmpty.PrepareDB(t, overrideFn) } func generateName() string { return strings.ReplaceAll(uuid.New().String(), "-", "") } -func prepareFullTestDBV2(t testing.TB, empty bool, loadFixtures bool, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - testutils.SkipShort(t, "FullTestDB") +type Kind int - if empty && loadFixtures { - t.Fatal("could not load fixtures into an empty DB") - } +const ( + KindEmpty Kind = iota + KindTemplate + KindFixtures +) + +func (c Kind) PrepareDB(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { + testutils.SkipShort(t, "FullTestDB") gcfg := configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres @@ -64,7 +63,7 @@ func prepareFullTestDBV2(t testing.TB, empty bool, loadFixtures bool, overrideFn }) require.NoError(t, os.MkdirAll(gcfg.RootDir(), 0700)) - migrationTestDBURL, err := dropAndCreateThrowawayTestDB(gcfg.Database().URL(), generateName(), empty) + migrationTestDBURL, err := testdb.CreateOrReplace(gcfg.Database().URL(), generateName(), c != KindEmpty) require.NoError(t, err) db, err := pg.NewConnection(migrationTestDBURL, dialects.Postgres, gcfg.Database()) require.NoError(t, err) @@ -81,7 +80,7 @@ func prepareFullTestDBV2(t testing.TB, empty bool, loadFixtures bool, overrideFn } }) - if loadFixtures { + if c == KindFixtures { _, filename, _, ok := runtime.Caller(1) if !ok { t.Fatal("could not get runtime.Caller(1)") @@ -95,39 +94,3 @@ func prepareFullTestDBV2(t testing.TB, empty bool, loadFixtures bool, overrideFn return gcfg, db } - -func dropAndCreateThrowawayTestDB(parsed url.URL, postfix string, empty bool) (string, error) { - if parsed.Path == "" { - return "", errors.New("path missing from database URL") - } - - // Match the naming schema that our dangling DB cleanup methods expect - dbname := cmd.TestDBNamePrefix + postfix - if l := len(dbname); l > 63 { - return "", fmt.Errorf("dbname %v too long (%d), max is 63 bytes. Try a shorter postfix", dbname, l) - } - // Cannot drop test database if we are connected to it, so we must connect - // to a different one. 'postgres' should be present on all postgres installations - parsed.Path = "/postgres" - db, err := sql.Open(string(dialects.Postgres), parsed.String()) - if err != nil { - return "", fmt.Errorf("In order to drop the test database, we need to connect to a separate database"+ - " called 'postgres'. But we are unable to open 'postgres' database: %+v\n", err) - } - defer db.Close() - - _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", dbname)) - if err != nil { - return "", fmt.Errorf("unable to drop postgres migrations test database: %v", err) - } - if empty { - _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", dbname)) - } else { - _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s", dbname, cmd.PristineDBName)) - } - if err != nil { - return "", fmt.Errorf("unable to create postgres test database with name '%s': %v", dbname, err) - } - parsed.Path = fmt.Sprintf("/%s", dbname) - return parsed.String(), nil -} diff --git a/core/web/health_controller.go b/core/web/health_controller.go index c8489fd6325..7ab07291b58 100644 --- a/core/web/health_controller.go +++ b/core/web/health_controller.go @@ -76,7 +76,7 @@ func (hc *HealthController) Health(c *gin.Context) { healthy, errors := checker.IsHealthy() if !healthy { - status = http.StatusServiceUnavailable + status = http.StatusMultiStatus } c.Status(status) diff --git a/core/web/health_controller_test.go b/core/web/health_controller_test.go index ae40a66bca9..547186e33fb 100644 --- a/core/web/health_controller_test.go +++ b/core/web/health_controller_test.go @@ -62,7 +62,7 @@ func TestHealthController_Health_status(t *testing.T) { { name: "not ready", ready: false, - status: http.StatusServiceUnavailable, + status: http.StatusMultiStatus, }, { name: "ready", @@ -118,7 +118,7 @@ func TestHealthController_Health_body(t *testing.T) { client := app.NewHTTPClient(nil) resp, cleanup := client.Get(tc.path, tc.headers) t.Cleanup(cleanup) - assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) + assert.Equal(t, http.StatusMultiStatus, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) if tc.expBody == bodyJSON { diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f18fe5369af..36ee68412ca 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] +### Added + +- `chainlink health` CLI command and HTML `/health` endpoint, to provide human-readable views of the underlying JSON health data. + ### Fixed - Fixed the encoding used for transactions when resending in batches diff --git a/internal/testdb/testdb.go b/internal/testdb/testdb.go new file mode 100644 index 00000000000..9b531166113 --- /dev/null +++ b/internal/testdb/testdb.go @@ -0,0 +1,56 @@ +package testdb + +import ( + "database/sql" + "errors" + "fmt" + "net/url" + + "github.com/smartcontractkit/chainlink/v2/core/store/dialects" +) + +const ( + // PristineDBName is a clean copy of test DB with migrations. + PristineDBName = "chainlink_test_pristine" + // TestDBNamePrefix is a common prefix that will be auto-removed by the dangling DB cleanup process. + TestDBNamePrefix = "chainlink_test_" +) + +// CreateOrReplace creates a database named with a common prefix and the given suffix, and returns the URL. +// If the database already exists, it will be dropped and re-created. +// If withTemplate is true, the pristine DB will be used as a template. +func CreateOrReplace(parsed url.URL, suffix string, withTemplate bool) (string, error) { + if parsed.Path == "" { + return "", errors.New("path missing from database URL") + } + + // Match the naming schema that our dangling DB cleanup methods expect + dbname := TestDBNamePrefix + suffix + if l := len(dbname); l > 63 { + return "", fmt.Errorf("dbname %v too long (%d), max is 63 bytes. Try a shorter suffix", dbname, l) + } + // Cannot drop test database if we are connected to it, so we must connect + // to a different one. 'postgres' should be present on all postgres installations + parsed.Path = "/postgres" + db, err := sql.Open(string(dialects.Postgres), parsed.String()) + if err != nil { + return "", fmt.Errorf("in order to drop the test database, we need to connect to a separate database"+ + " called 'postgres'. But we are unable to open 'postgres' database: %+v\n", err) + } + defer db.Close() + + _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", dbname)) + if err != nil { + return "", fmt.Errorf("unable to drop postgres migrations test database: %v", err) + } + if withTemplate { + _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s", dbname, PristineDBName)) + } else { + _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", dbname)) + } + if err != nil { + return "", fmt.Errorf("unable to create postgres test database with name '%s': %v", dbname, err) + } + parsed.Path = fmt.Sprintf("/%s", dbname) + return parsed.String(), nil +} diff --git a/main_test.go b/main_test.go index 032ec4718a2..15b17e32654 100644 --- a/main_test.go +++ b/main_test.go @@ -1,17 +1,41 @@ package main import ( + "fmt" + "net/url" "os" + "path/filepath" + "strconv" + "strings" "testing" + "github.com/google/uuid" + "github.com/hashicorp/consul/sdk/freeport" "github.com/rogpeppe/go-internal/testscript" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core" + "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/static" + "github.com/smartcontractkit/chainlink/v2/internal/testdb" "github.com/smartcontractkit/chainlink/v2/tools/txtar" ) +// special files can be included to allocate additional test resources +const ( + // testDBName triggers initializing of a test database. + // The URL will be set as the value of an env var named by the file. + // + // -- testdb.txt -- + // CL_DATABASE_URL + testDBName = "testdb.txt" + // testPortName triggers injection of a free port as the value of an env var named by the file. + // + // -- testport.txt -- + // PORT + testPortName = "testport.txt" +) + func TestMain(m *testing.M) { os.Exit(testscript.RunMain(m, map[string]func() int{ "chainlink": core.Main, @@ -22,11 +46,14 @@ func TestScripts(t *testing.T) { t.Parallel() visitor := txtar.NewDirVisitor("testdata/scripts", txtar.Recurse, func(path string) error { - t.Run(path, func(t *testing.T) { + t.Run(strings.TrimPrefix(path, "testdata/scripts/"), func(t *testing.T) { t.Parallel() + testscript.Run(t, testscript.Params{ - Dir: path, - Setup: commonEnv, + Dir: path, + Setup: commonEnv, + ContinueOnError: true, + //UpdateScripts: true, // uncomment to update golden files }) }) return nil @@ -35,9 +62,62 @@ func TestScripts(t *testing.T) { require.NoError(t, visitor.Walk()) } -func commonEnv(env *testscript.Env) error { - env.Setenv("HOME", "$WORK/home") - env.Setenv("VERSION", static.Version) - env.Setenv("COMMIT_SHA", static.Sha) +func commonEnv(te *testscript.Env) error { + te.Setenv("HOME", "$WORK/home") + te.Setenv("VERSION", static.Version) + te.Setenv("COMMIT_SHA", static.Sha) + + b, err := os.ReadFile(filepath.Join(te.WorkDir, testPortName)) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to read file %s: %w", testPortName, err) + } else if err == nil { + envVarName := strings.TrimSpace(string(b)) + te.T().Log("test port requested:", envVarName) + + port, ret, err2 := takeFreePort() + if err2 != nil { + return err2 + } + te.Defer(ret) + + te.Setenv(envVarName, strconv.Itoa(port)) + } + + b, err = os.ReadFile(filepath.Join(te.WorkDir, testDBName)) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to read file %s: %w", testDBName, err) + } else if err == nil { + envVarName := strings.TrimSpace(string(b)) + te.T().Log("test database requested:", envVarName) + + u2, err2 := initDB() + if err2 != nil { + return err2 + } + + te.Setenv(envVarName, u2) + } return nil } + +func takeFreePort() (int, func(), error) { + ports, err := freeport.Take(1) + if err != nil { + return 0, nil, fmt.Errorf("failed to get free port: %w", err) + } + return ports[0], func() { freeport.Return(ports) }, nil +} + +func initDB() (string, error) { + u, err := url.Parse(string(env.DatabaseURL.Get())) + if err != nil { + return "", fmt.Errorf("failed to parse url: %w", err) + } + + name := strings.ReplaceAll(uuid.NewString(), "-", "_") + "_test" + u2, err := testdb.CreateOrReplace(*u, name, true) + if err != nil { + return "", fmt.Errorf("failed to create DB: %w", err) + } + return u2, nil +} diff --git a/testdata/scripts/health/default.txtar b/testdata/scripts/health/default.txtar new file mode 100644 index 00000000000..c80faadfd13 --- /dev/null +++ b/testdata/scripts/health/default.txtar @@ -0,0 +1,129 @@ +# start node +exec sh -c 'eval "echo \"$(cat config.toml.tmpl)\" > config.toml"' +exec chainlink node -c config.toml start -p password -a creds & + +# initialize client +env NODEURL=http://localhost:$PORT +exec curl --retry 10 --retry-max-time 60 --retry-connrefused $NODEURL +exec chainlink --remote-node-url $NODEURL admin login -file creds --bypass-version-check + +exec chainlink --remote-node-url $NODEURL health +cmp stdout out.txt + +exec chainlink --remote-node-url $NODEURL health -json +cp stdout compact.json +exec jq . compact.json +cmp stdout out.json + +-- testdb.txt -- +CL_DATABASE_URL +-- testport.txt -- +PORT + +-- password -- +T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ +-- creds -- +notreal@fakeemail.ch +fj293fbBnlQ!f9vNs + +-- config.toml.tmpl -- +[Webserver] +HTTPPort = $PORT + +-- out.txt -- +-EventBroadcaster +-JobSpawner +-Mailbox.Monitor +-Mercury.WSRPCPool +-Mercury.WSRPCPool.CacheSet +-PipelineORM +-PipelineRunner +-PromReporter +-TelemetryManager + +-- out.json -- +{ + "data": [ + { + "type": "checks", + "id": "EventBroadcaster", + "attributes": { + "name": "EventBroadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "JobSpawner", + "attributes": { + "name": "JobSpawner", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mailbox.Monitor", + "attributes": { + "name": "Mailbox.Monitor", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mercury.WSRPCPool", + "attributes": { + "name": "Mercury.WSRPCPool", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mercury.WSRPCPool.CacheSet", + "attributes": { + "name": "Mercury.WSRPCPool.CacheSet", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PipelineORM", + "attributes": { + "name": "PipelineORM", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PipelineRunner", + "attributes": { + "name": "PipelineRunner", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PromReporter", + "attributes": { + "name": "PromReporter", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "TelemetryManager", + "attributes": { + "name": "TelemetryManager", + "status": "passing", + "output": "" + } + } + ] +} diff --git a/testdata/scripts/health/help.txtar b/testdata/scripts/health/help.txtar new file mode 100644 index 00000000000..07eb0509e73 --- /dev/null +++ b/testdata/scripts/health/help.txtar @@ -0,0 +1,13 @@ +exec chainlink health --help +cmp stdout out.txt + +-- out.txt -- +NAME: + chainlink health - Prints a health report + +USAGE: + chainlink health [command options] [arguments...] + +OPTIONS: + --json, -j json output + diff --git a/testdata/scripts/health/multi-chain.txtar b/testdata/scripts/health/multi-chain.txtar new file mode 100644 index 00000000000..72c5fd8e3f6 --- /dev/null +++ b/testdata/scripts/health/multi-chain.txtar @@ -0,0 +1,309 @@ +# start node +exec sh -c 'eval "echo \"$(cat config.toml.tmpl)\" > config.toml"' +exec chainlink node -c config.toml start -p password -a creds & + +# initialize client +env NODEURL=http://localhost:$PORT +exec curl --retry 10 --retry-max-time 60 --retry-connrefused $NODEURL +exec chainlink --remote-node-url $NODEURL admin login -file creds --bypass-version-check + +exec chainlink --remote-node-url $NODEURL health +cmp stdout out.txt + +exec chainlink --remote-node-url $NODEURL health -json +cp stdout compact.json +exec jq . compact.json +cmp stdout out.json + +-- testdb.txt -- +CL_DATABASE_URL +-- testport.txt -- +PORT + +-- password -- +T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ +-- creds -- +notreal@fakeemail.ch +fj293fbBnlQ!f9vNs + +-- config.toml.tmpl -- +[Webserver] +HTTPPort = $PORT + +[[Cosmos]] +ChainID = 'Foo' + +[[Cosmos.Nodes]] +Name = 'primary' +TendermintURL = 'http://tender.mint' + +[[EVM]] +ChainID = '1' + +[[EVM.Nodes]] +Name = 'fake' +WSURL = 'wss://foo.bar/ws' +HTTPURL = 'https://foo.bar' + +[[Solana]] +ChainID = 'Bar' + +[[Solana.Nodes]] +Name = 'primary' +URL = 'http://solana.web' + +[[Starknet]] +ChainID = 'Baz' + +[[Starknet.Nodes]] +Name = 'primary' +URL = 'http://stark.node' + +-- out.txt -- +-Cosmos.Foo.Chain +-Cosmos.Foo.Txm +-EVM.1 +-EVM.1.BalanceMonitor +-EVM.1.HeadBroadcaster +-EVM.1.HeadTracker +!EVM.1.HeadTracker.HeadListener + Listener is not connected +-EVM.1.LogBroadcaster +-EVM.1.Txm +-EVM.1.Txm.BlockHistoryEstimator +-EVM.1.Txm.Broadcaster +-EVM.1.Txm.Confirmer +-EVM.1.Txm.WrappedEvmEstimator +-EventBroadcaster +-JobSpawner +-Mailbox.Monitor +-Mercury.WSRPCPool +-Mercury.WSRPCPool.CacheSet +-PipelineORM +-PipelineRunner +-PromReporter +-Solana.Bar +-StarkNet.Baz +-TelemetryManager + +-- out.json -- +{ + "data": [ + { + "type": "checks", + "id": "Cosmos.Foo.Chain", + "attributes": { + "name": "Cosmos.Foo.Chain", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Cosmos.Foo.Txm", + "attributes": { + "name": "Cosmos.Foo.Txm", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1", + "attributes": { + "name": "EVM.1", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.BalanceMonitor", + "attributes": { + "name": "EVM.1.BalanceMonitor", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.HeadBroadcaster", + "attributes": { + "name": "EVM.1.HeadBroadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.HeadTracker", + "attributes": { + "name": "EVM.1.HeadTracker", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.HeadTracker.HeadListener", + "attributes": { + "name": "EVM.1.HeadTracker.HeadListener", + "status": "failing", + "output": "Listener is not connected" + } + }, + { + "type": "checks", + "id": "EVM.1.LogBroadcaster", + "attributes": { + "name": "EVM.1.LogBroadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.Txm", + "attributes": { + "name": "EVM.1.Txm", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.Txm.BlockHistoryEstimator", + "attributes": { + "name": "EVM.1.Txm.BlockHistoryEstimator", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.Txm.Broadcaster", + "attributes": { + "name": "EVM.1.Txm.Broadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.Txm.Confirmer", + "attributes": { + "name": "EVM.1.Txm.Confirmer", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EVM.1.Txm.WrappedEvmEstimator", + "attributes": { + "name": "EVM.1.Txm.WrappedEvmEstimator", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "EventBroadcaster", + "attributes": { + "name": "EventBroadcaster", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "JobSpawner", + "attributes": { + "name": "JobSpawner", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mailbox.Monitor", + "attributes": { + "name": "Mailbox.Monitor", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mercury.WSRPCPool", + "attributes": { + "name": "Mercury.WSRPCPool", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Mercury.WSRPCPool.CacheSet", + "attributes": { + "name": "Mercury.WSRPCPool.CacheSet", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PipelineORM", + "attributes": { + "name": "PipelineORM", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PipelineRunner", + "attributes": { + "name": "PipelineRunner", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "PromReporter", + "attributes": { + "name": "PromReporter", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "Solana.Bar", + "attributes": { + "name": "Solana.Bar", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "StarkNet.Baz", + "attributes": { + "name": "StarkNet.Baz", + "status": "passing", + "output": "" + } + }, + { + "type": "checks", + "id": "TelemetryManager", + "attributes": { + "name": "TelemetryManager", + "status": "passing", + "output": "" + } + } + ] +} diff --git a/testdata/scripts/help.txtar b/testdata/scripts/help.txtar index 1484aceb5df..e4c19f3987d 100644 --- a/testdata/scripts/help.txtar +++ b/testdata/scripts/help.txtar @@ -17,6 +17,7 @@ COMMANDS: blocks Commands for managing blocks bridges Commands for Bridges communicating with External Adapters config Commands for the node's configuration + health Prints a health report jobs Commands for managing Jobs keys Commands for managing various types of keys used by the Chainlink node node, local Commands for admin actions that must be run locally diff --git a/testdata/scripts/node/db/migrate/db.txtar b/testdata/scripts/node/db/migrate/db.txtar new file mode 100644 index 00000000000..f040a937fd0 --- /dev/null +++ b/testdata/scripts/node/db/migrate/db.txtar @@ -0,0 +1,6 @@ +exec chainlink node db migrate +! stdout . +stderr 'goose: no migrations to run. current version:' + +-- testdb.txt -- +CL_DATABASE_URL From 3ea324f91df1d4b0499de409537bd91e85acf0ad Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:47:17 +0100 Subject: [PATCH 115/234] Add ClNode.ExecGetVersion for E2E docker tests (#11691) * Add ExecGetVersion for Docker ClNode * Fix lint --- integration-tests/docker/test_env/cl_node.go | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index d7228b1ce8f..c28959dadc9 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -3,10 +3,12 @@ package test_env import ( "context" "fmt" + "io" "maps" "math/big" "net/url" "os" + "regexp" "strings" "testing" "time" @@ -15,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/pelletier/go-toml/v2" + "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" tc "github.com/testcontainers/testcontainers-go" @@ -361,6 +364,28 @@ func (n *ClNode) StartContainer() error { return nil } +func (n *ClNode) ExecGetVersion() (string, error) { + cmd := []string{"chainlink", "--version"} + _, output, err := n.Container.Exec(context.Background(), cmd) + if err != nil { + return "", errors.Wrapf(err, "could not execute cmd %s", cmd) + } + outputBytes, err := io.ReadAll(output) + if err != nil { + return "", err + } + outputString := strings.TrimSpace(string(outputBytes)) + + // Find version in cmd output + re := regexp.MustCompile("@(.*)") + matches := re.FindStringSubmatch(outputString) + + if len(matches) > 1 { + return matches[1], nil + } + return "", errors.Errorf("could not find chainlink version in command output '%'", output) +} + func (n *ClNode) getContainerRequest(secrets string) ( *tc.ContainerRequest, error) { configFile, err := os.CreateTemp("", "node_config") From 094b252395a3bce7e5f8f282742048d1f6f2e990 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 5 Jan 2024 11:20:56 -0500 Subject: [PATCH 116/234] Refrains From Explicitly Requesting New OCR Rounds (#11656) * Refrains From Explicitly Requesting New OCR Rounds * Fix Forwarder as Well --- integration-tests/actions/ocr_helpers.go | 19 +++++++++++++++++++ integration-tests/smoke/forwarder_ocr_test.go | 4 ++-- integration-tests/smoke/ocr_test.go | 10 +++++----- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/integration-tests/actions/ocr_helpers.go b/integration-tests/actions/ocr_helpers.go index 17e536ec839..dd0e6606e43 100644 --- a/integration-tests/actions/ocr_helpers.go +++ b/integration-tests/actions/ocr_helpers.go @@ -343,6 +343,25 @@ func StartNewRound( return nil } +// WatchNewRound watches for a new OCR round, similarly to StartNewRound, but it does not explicitly request a new +// round from the contract, as this can cause some odd behavior in some cases +func WatchNewRound( + roundNumber int64, + ocrInstances []contracts.OffchainAggregator, + client blockchain.EVMClient, + logger zerolog.Logger, +) error { + for i := 0; i < len(ocrInstances); i++ { + ocrRound := contracts.NewOffchainAggregatorRoundConfirmer(ocrInstances[i], big.NewInt(roundNumber), client.GetNetworkConfig().Timeout.Duration, logger) + client.AddHeaderEventSubscription(ocrInstances[i].Address(), ocrRound) + err := client.WaitForEvents() + if err != nil { + return fmt.Errorf("failed to wait for event subscriptions of OCR instance %d: %w", i+1, err) + } + } + return nil +} + // SetAdapterResponse sets a single adapter response that correlates with an ocr contract and a chainlink node func SetAdapterResponse( response int, diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index c71c6e31516..64128ed4a8c 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -68,7 +68,7 @@ func TestForwarderOCRBasic(t *testing.T) { err = actions.CreateOCRJobsWithForwarderLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) require.NoError(t, err, "failed to setup forwarder jobs") - err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") @@ -79,7 +79,7 @@ func TestForwarderOCRBasic(t *testing.T) { err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) require.NoError(t, err) - err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) require.NoError(t, err) err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index ba158923812..ebfbf698e98 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -47,7 +47,7 @@ func TestOCRBasic(t *testing.T) { err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) require.NoError(t, err) - err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -56,7 +56,7 @@ func TestOCRBasic(t *testing.T) { err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) require.NoError(t, err) - err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) require.NoError(t, err) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -94,7 +94,7 @@ func TestOCRJobReplacement(t *testing.T) { err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) require.NoError(t, err) - err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -103,7 +103,7 @@ func TestOCRJobReplacement(t *testing.T) { err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) require.NoError(t, err) - err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) require.NoError(t, err) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -120,7 +120,7 @@ func TestOCRJobReplacement(t *testing.T) { err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) require.NoError(t, err) - err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) From 3e944c91f7e32c8aea6f7998faf627e12077e9b1 Mon Sep 17 00:00:00 2001 From: Tate Date: Fri, 5 Jan 2024 11:53:26 -0700 Subject: [PATCH 117/234] Split automation-01 ci job out to multiple (#11694) --- .../scripts/buildTestMatrixList.sh | 24 +++++---- integration-tests/smoke/automation_test.go | 50 +++++++++---------- .../smoke/automation_test.go_test_list.json | 26 +++++++++- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/integration-tests/scripts/buildTestMatrixList.sh b/integration-tests/scripts/buildTestMatrixList.sh index 7f058b5b659..f02362aa47a 100755 --- a/integration-tests/scripts/buildTestMatrixList.sh +++ b/integration-tests/scripts/buildTestMatrixList.sh @@ -40,24 +40,26 @@ jq -c '.tests[]' ${JSONFILE} | while read -r test; do effective_node_count=${node_count:-$NODE_COUNT} subTests=$(echo ${test} | jq -r '.run[]?.name // empty') output="" + + if [ $COUNTER -ne 1 ]; then + echo -n "," + fi # Loop through subtests, if any, and print in the desired format if [ -n "$subTests" ]; then + subTestString="" + subTestCounter=1 for subTest in $subTests; do - if [ $COUNTER -ne 1 ]; then - echo -n "," + if [ $subTestCounter -ne 1 ]; then + subTestString+="|" fi - matrix_output $COUNTER $MATRIX_JOB_NAME "${testName}/${subTest}" ${effective_node_label} ${effective_node_count} - ((COUNTER++)) + subTestString+="${testName}\/${subTest}" + ((subTestCounter++)) done - else - if [ $COUNTER -ne 1 ]; then - echo -n "," - fi - matrix_output $COUNTER $MATRIX_JOB_NAME "${testName}" ${effective_node_label} ${effective_node_count} - ((COUNTER++)) + testName="${subTestString}" fi - + matrix_output $COUNTER $MATRIX_JOB_NAME "${testName}" ${effective_node_label} ${effective_node_count} + ((COUNTER++)) done > "./tmpout.json" OUTPUT=$(cat ./tmpout.json) echo "[${OUTPUT}]" diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 1dbfc78ec87..cb631eb8278 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -90,9 +90,9 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { "registry_2_1_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -168,7 +168,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { g.Expect(counter.Int64()).Should(gomega.BeNumerically(">=", int64(expect)), "Expected consumer counter to be greater than %d, but got %d", expect, counter.Int64()) } - }, "5m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~2m for performing each upkeep 5 times, ~2m buffer + }, "10m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~2m for performing each upkeep 5 times, ~2m buffer l.Info().Msgf("Total time taken to get 5 performs for each upkeep: %s", time.Since(startTime)) @@ -400,9 +400,9 @@ func TestAutomationAddFunds(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() a := setupAutomationTestDocker( @@ -557,9 +557,9 @@ func TestAutomationRegisterUpkeep(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -641,9 +641,9 @@ func TestAutomationPauseRegistry(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() a := setupAutomationTestDocker( @@ -710,9 +710,9 @@ func TestAutomationKeeperNodesDown(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -810,9 +810,9 @@ func TestAutomationPerformSimulation(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() a := setupAutomationTestDocker( @@ -873,9 +873,9 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -987,9 +987,9 @@ func TestUpdateCheckData(t *testing.T) { "registry_2_1": ethereum.RegistryVersion_2_1, } - for n, rv := range registryVersions { - name := n - registryVersion := rv + for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) diff --git a/integration-tests/smoke/automation_test.go_test_list.json b/integration-tests/smoke/automation_test.go_test_list.json index 5bf941ed2ce..b88684599c7 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -2,8 +2,30 @@ "tests": [ { "name": "TestAutomationBasic", - "label": "ubuntu-latest-32cores-128GB", - "nodes": 6 + "label": "ubuntu-latest", + "nodes": 2, + "run":[ + {"name":"registry_2_0"}, + {"name":"registry_2_1_conditional"} + ] + }, + { + "name": "TestAutomationBasic", + "label": "ubuntu-latest", + "nodes": 2, + "run":[ + {"name":"registry_2_1_logtrigger"}, + {"name":"registry_2_1_with_mercury_v02"} + ] + }, + { + "name": "TestAutomationBasic", + "label": "ubuntu-latest", + "nodes": 2, + "run":[ + {"name":"registry_2_1_with_mercury_v03"}, + {"name":"registry_2_1_with_logtrigger_and_mercury_v02"} + ] }, { "name": "TestSetUpkeepTriggerConfig" From 29140a20cb129f21bbf6d808f626ec212167a4c2 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 16:39:04 -0500 Subject: [PATCH 118/234] Adding guide for product teams + core instrumenting spans (#11684) * Adding guide for product teams implementing tracing * addressing comments * edits --- .github/tracing/README.md | 53 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/.github/tracing/README.md b/.github/tracing/README.md index feba31feb65..04f0216e25f 100644 --- a/.github/tracing/README.md +++ b/.github/tracing/README.md @@ -48,4 +48,55 @@ This folder contains the following config files: These config files are for an OTEL collector, grafana Tempo, and a grafana UI instance to run as containers on the same network. `otel-collector-dev.yaml` is the configuration for dev (i.e. your local machine) environments, and forwards traces from the otel collector to the grafana tempo instance on the same network. -`otel-collector-ci.yaml` is the configuration for the CI runs, and exports the trace data to the artifact from the github run. \ No newline at end of file +`otel-collector-ci.yaml` is the configuration for the CI runs, and exports the trace data to the artifact from the github run. + +## Adding Traces to Plugins and to core + +Adding traces requires identifying an observability gap in a related group of code executions or a critical path in your application. This is intuitive for the developer: + +- "What's the flow of component interaction in this distributed system?" +- "What's the behavior of the JobProcessorOne component when jobs with [x, y, z] attributes are processed?" +- "Is this critical path workflow behaving the way we expect?" + +The developer will measure a flow of execution from end to end in one trace. Each logically separate measure of this flow is called a span. Spans have either one or no parent span and multiple children span. The relationship between parent and child spans in agreggate will form a directed acyclic graph. The trace begins at the root of this graph. + +The most trivial application of a span is measuring top level performance in one critical path. There is much more you can do, including creating human readable and timestamped events within a span (useful for monitoring concurrent access to resources), recording errors, linking parent and children spans through large parts of an application, and even extending a span beyond a single process. + +Spans are created by `tracers` and passed through go applications by `Context`s. A tracer must be initialized first. Both core and plugin developers will initialize a tracer from the globally registered trace provider: + +``` +tracer := otel.GetTracerProvider().Tracer("example.com/foo") +``` + +The globally registered tracer provider is available for plugins after they are initialized, and available in core after configuration is processed (`initGlobals`). + +Add spans by: +``` + func interestingFunc() { + // Assuming there is an appropriate parentContext + ctx, span := tracer.Start(parentContext, "hello-span") + defer span.End() + + // do some work to track with hello-span + } +``` +As implied by the example, `span` is a child of its parent span captured by `parentContext`. + + +Note that in certain situations, there are 3rd party libraries that will setup spans. For instance: + +``` +import ( + "github.com/gin-gonic/gin" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" +) + +router := gin.Default() +router.Use(otelgin.Middleware("service-name")) +``` + +The developer aligns with best practices when they: +- Start with critical paths +- Measure paths from end to end (Context is wired all the way through) +- Emphasize broadness of measurement over depth +- Use automatic instrumentation if possible \ No newline at end of file From 25b55fdcd7a651a324303189e944a6620438ce14 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 7 Jan 2024 10:56:46 -0500 Subject: [PATCH 119/234] localhost --> 127.0.0.1 registry name update (#11696) --- integration-tests/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 5e34b059b68..d1e9bdd89ef 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -74,12 +74,12 @@ build_test_image: #Build a chainlink docker image for local testing and push to k3d registry .PHONY: build_push_docker_image build_push_docker_image: - docker build -f ../core/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ ; docker push localhost:5000/chainlink:develop + docker build -f ../core/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t 127.0.0.1:5000/chainlink:develop ../ ; docker push 127.0.0.1:5000/chainlink:develop #Build a chainlink docker image in plugin mode for local testing and push to k3d registry .PHONY: build_push_plugin_docker_image build_push_plugin_docker_image: - docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ ; docker push localhost:5000/chainlink:develop + docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t 127.0.0.1:5000/chainlink:develop ../ ; docker push 127.0.0.1:5000/chainlink:develop # Spins up containers needed to collect traces for local testing .PHONY: run_tracing @@ -197,7 +197,7 @@ build_docker_image: # example usage: make build_docker_image image=chainlink tag=latest .PHONY: build_plugin_docker_image build_plugin_docker_image: - docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ + docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t 127.0.0.1:5000/chainlink:develop ../ # image: the name for the chainlink image being built, example: image=chainlink # tag: the tag for the chainlink image being built, example: tag=latest From 7fe07103e796572f54dfcfc40c4777a0604eec07 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Mon, 8 Jan 2024 16:19:48 +0100 Subject: [PATCH 120/234] fix readme (#11689) --- integration-tests/load/ocr/README.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/integration-tests/load/ocr/README.md b/integration-tests/load/ocr/README.md index 3c231b50278..61951ba700f 100644 --- a/integration-tests/load/ocr/README.md +++ b/integration-tests/load/ocr/README.md @@ -1,21 +1,12 @@ -# OCR Load tests +### OCR Load tests ## Setup - These tests can connect to any cluster create with [chainlink-cluster](../../../charts/chainlink-cluster/README.md) -<<<<<<< HEAD -Create your cluster - -```sh -kubectl create ns my-cluster -devspace use namespace my-cluster -======= Create your cluster, if you already have one just use `kubefwd` ``` kubectl create ns cl-cluster devspace use namespace cl-cluster ->>>>>>> 06656fac80999d1539e16951a54b87c6df13a9c7 devspace deploy sudo kubefwd svc -n cl-cluster ``` @@ -26,7 +17,7 @@ If you haven't changed anything in [devspace.yaml](../../../charts/chainlink-clu ## Usage -```sh +``` export LOKI_TOKEN=... export LOKI_URL=... @@ -34,4 +25,4 @@ go test -v -run TestOCRLoad go test -v -run TestOCRVolume ``` -Check test configuration [here](config.toml) +Check test configuration [here](config.toml) \ No newline at end of file From fa00dd3620d3e69ac248b6ee481b69e5685093d0 Mon Sep 17 00:00:00 2001 From: Lei Date: Mon, 8 Jan 2024 16:03:39 -0800 Subject: [PATCH 121/234] add .env example for debugging script (#11693) --- core/scripts/chaincli/.env.debugging.example | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 core/scripts/chaincli/.env.debugging.example diff --git a/core/scripts/chaincli/.env.debugging.example b/core/scripts/chaincli/.env.debugging.example new file mode 100644 index 00000000000..6934bb107f5 --- /dev/null +++ b/core/scripts/chaincli/.env.debugging.example @@ -0,0 +1,15 @@ +# [Mandatory] http url of the archival node for your network +NODE_URL= +# [Mandatory] address of the KeeperRegistry contract for your upkeep +KEEPER_REGISTRY_ADDRESS= + +# [Optional] it is strongly recommended (not mandatory) to use tenderly for more debugging info +#TENDERLY_KEY= +#TENDERLY_ACCOUNT_NAME= +#TENDERLY_PROJECT_NAME= + +# [Optional] add mercury info only if your upkeep uses mercury +#MERCURY_ID= +#MERCURY_KEY= +#MERCURY_LEGACY_URL= +#MERCURY_URL= \ No newline at end of file From 8499fe8bae61d8cd35f26256a33cb988ed62b8ff Mon Sep 17 00:00:00 2001 From: Lei Date: Mon, 8 Jan 2024 16:03:50 -0800 Subject: [PATCH 122/234] show a more verbose message when perform gas is lower than gas used (#11686) --- core/scripts/chaincli/handler/debug.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 46b0e42e2a0..9782f5b72fe 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -25,6 +25,7 @@ import ( evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" + "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" @@ -362,7 +363,17 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if simulateResult.Success { resolveEligible() } else { - resolveIneligible("simulate perform upkeep unsuccessful") + // Convert performGas to *big.Int for comparison + performGasBigInt := new(big.Int).SetUint64(uint64(upkeepInfo.PerformGas)) + // Compare PerformGas and GasUsed + result := performGasBigInt.Cmp(simulateResult.GasUsed) + + if result < 0 { + // PerformGas is smaller than GasUsed + resolveIneligible(fmt.Sprintf("simulate perform upkeep unsuccessful, PerformGas (%d) is lower than GasUsed (%s)", upkeepInfo.PerformGas, simulateResult.GasUsed.String())) + } else { + resolveIneligible("simulate perform upkeep unsuccessful") + } } } From 61f42ce82fa06ae7c189475541573b96d47d4c20 Mon Sep 17 00:00:00 2001 From: krehermann Date: Mon, 8 Jan 2024 18:23:52 -0700 Subject: [PATCH 123/234] remove unused code from NewDelegate signature (#11708) --- core/services/chainlink/application.go | 1 - core/services/job/spawner_test.go | 2 +- core/services/ocr2/delegate.go | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index e94f51abdf5..71a8b0b33fe 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -416,7 +416,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { keyStore.Eth(), opts.RelayerChainInteroperators, mailMon, - eventBroadcaster, ) delegates[job.Bootstrap] = ocrbootstrap.NewDelegateBootstrap( db, diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index a46d9ca6c32..06e305c8057 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -306,7 +306,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { ocr2DelegateConfig := ocr2.NewDelegateConfig(config.OCR2(), config.Mercury(), config.Threshold(), config.Insecure(), config.JobPipeline(), config.Database(), processConfig) d := ocr2.NewDelegate(nil, orm, nil, nil, nil, nil, monitoringEndpoint, legacyChains, lggr, ocr2DelegateConfig, - keyStore.OCR2(), keyStore.DKGSign(), keyStore.DKGEncrypt(), ethKeyStore, testRelayGetter, mailMon, nil) + keyStore.OCR2(), keyStore.DKGSign(), keyStore.DKGEncrypt(), ethKeyStore, testRelayGetter, mailMon) delegateOCR2 := &delegate{jobOCR2VRF.Type, []job.ServiceCtx{}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 3136de44b8f..519a7a58f5d 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -227,7 +227,6 @@ func NewDelegate( ethKs keystore.Eth, relayers RelayGetter, mailMon *mailbox.Monitor, - eventBroadcaster pg.EventBroadcaster, ) *Delegate { return &Delegate{ db: db, From 80bc9f23d437ac7bc0c9c8f9df40e69b5ddd8675 Mon Sep 17 00:00:00 2001 From: krehermann Date: Tue, 9 Jan 2024 08:06:22 -0700 Subject: [PATCH 124/234] Replace mercury eventbroadcaster with polling (#11707) * POC example code to replace mercury eventbroadcaster with polling * remove Mercury Notify optimization * fix bad merge --- core/services/relay/evm/evm.go | 1 - .../relay/evm/mercury/config_poller.go | 60 +------------------ .../relay/evm/mercury/config_poller_test.go | 51 ---------------- .../relay/evm/mercury/helpers_test.go | 12 +--- 4 files changed, 5 insertions(+), 119 deletions(-) diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 37e6d64452e..8cdbfe76058 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -342,7 +342,6 @@ func newConfigProvider(lggr logger.Logger, chain legacyevm.Chain, opts *types.Re chain.LogPoller(), aggregatorAddress, *relayConfig.FeedID, - eventBroadcaster, // TODO: Does mercury need to support config contract? DF-19182 ) } else { diff --git a/core/services/relay/evm/mercury/config_poller.go b/core/services/relay/evm/mercury/config_poller.go index 8964a283049..98ef78020c7 100644 --- a/core/services/relay/evm/mercury/config_poller.go +++ b/core/services/relay/evm/mercury/config_poller.go @@ -91,8 +91,6 @@ type ConfigPoller struct { destChainLogPoller logpoller.LogPoller addr common.Address feedId common.Hash - notifyCh chan struct{} - subscription pg.Subscription } func FilterName(addr common.Address, feedID common.Hash) string { @@ -100,43 +98,30 @@ func FilterName(addr common.Address, feedID common.Hash) string { } // NewConfigPoller creates a new Mercury ConfigPoller -func NewConfigPoller(lggr logger.Logger, destChainPoller logpoller.LogPoller, addr common.Address, feedId common.Hash, eventBroadcaster pg.EventBroadcaster) (*ConfigPoller, error) { +func NewConfigPoller(lggr logger.Logger, destChainPoller logpoller.LogPoller, addr common.Address, feedId common.Hash) (*ConfigPoller, error) { err := destChainPoller.RegisterFilter(logpoller.Filter{Name: FilterName(addr, feedId), EventSigs: []common.Hash{FeedScopedConfigSet}, Addresses: []common.Address{addr}}) if err != nil { return nil, err } - subscription, err := eventBroadcaster.Subscribe(pg.ChannelInsertOnEVMLogs, "") - if err != nil { - return nil, err - } - cp := &ConfigPoller{ lggr: lggr, destChainLogPoller: destChainPoller, addr: addr, feedId: feedId, - notifyCh: make(chan struct{}, 1), - subscription: subscription, } return cp, nil } -// Start the subscription to Postgres' notify events. -func (cp *ConfigPoller) Start() { - go cp.startLogSubscription() -} +func (cp *ConfigPoller) Start() {} -// Close the subscription to Postgres' notify events. func (cp *ConfigPoller) Close() error { - cp.subscription.Close() return nil } -// Notify abstracts the logpoller.LogPoller Notify() implementation func (cp *ConfigPoller) Notify() <-chan struct{} { - return cp.notifyCh + return nil // rely on libocr's builtin config polling } // Replay abstracts the logpoller.LogPoller Replay() implementation @@ -190,42 +175,3 @@ func (cp *ConfigPoller) LatestBlockHeight(ctx context.Context) (blockHeight uint } return uint64(latest.BlockNumber), nil } - -func (cp *ConfigPoller) startLogSubscription() { - // trim the leading 0x to make it comparable to pg's hex encoding. - addressPgHex := cp.addr.Hex()[2:] - feedIdPgHex := cp.feedId.Hex()[2:] - - for { - event, ok := <-cp.subscription.Events() - if !ok { - cp.lggr.Debug("eventBroadcaster subscription closed, exiting notify loop") - return - } - - // Event payload should look like: "
:," - addressTopicValues := strings.Split(event.Payload, ":") - if len(addressTopicValues) < 2 { - cp.lggr.Warnf("invalid event from %s channel: %s", pg.ChannelInsertOnEVMLogs, event.Payload) - continue - } - - address := addressTopicValues[0] - if address != addressPgHex { - continue - } - - topicValues := strings.Split(addressTopicValues[1], ",") - if len(topicValues) <= feedIdTopicIndex { - continue - } - if topicValues[feedIdTopicIndex] != feedIdPgHex { - continue - } - - select { - case cp.notifyCh <- struct{}{}: - default: - } - } -} diff --git a/core/services/relay/evm/mercury/config_poller_test.go b/core/services/relay/evm/mercury/config_poller_test.go index 71e88d41a22..f828938f954 100644 --- a/core/services/relay/evm/mercury/config_poller_test.go +++ b/core/services/relay/evm/mercury/config_poller_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/onsi/gomega" "github.com/pkg/errors" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" @@ -18,7 +17,6 @@ import ( evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -27,7 +25,6 @@ func TestMercuryConfigPoller(t *testing.T) { feedIDBytes := [32]byte(feedID) th := SetupTH(t, feedID) - th.subscription.On("Events").Return(nil) notify := th.configPoller.Notify() assert.Empty(t, notify) @@ -115,54 +112,6 @@ func TestMercuryConfigPoller(t *testing.T) { assert.Equal(t, offchainConfig, newConfig.OffchainConfig) } -func TestNotify(t *testing.T) { - testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-2746") - feedIDStr := "8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1" - feedIDBytes, err := hexutil.Decode("0x" + feedIDStr) - require.NoError(t, err) - feedID := common.BytesToHash(feedIDBytes) - - eventCh := make(chan pg.Event) - - th := SetupTH(t, feedID) - th.subscription.On("Events").Return((<-chan pg.Event)(eventCh)) - - addressPgHex := th.verifierAddress.Hex()[2:] - - notify := th.configPoller.Notify() - assert.Empty(t, notify) - - eventCh <- pg.Event{} // Empty event - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: addressPgHex} // missing topic values - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: addressPgHex + ":val1"} // missing feedId topic value - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: addressPgHex + ":8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1,val2"} // wrong index - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: addressPgHex + ":val1,val2,8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1"} // wrong index - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: addressPgHex + ":val1,0x8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1"} // 0x prefix - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: "wrong_address:val1,8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1"} // wrong address - assert.Empty(t, notify) - - eventCh <- pg.Event{Payload: addressPgHex + ":val1,8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1"} // expected event to notify on - assert.Eventually(t, func() bool { <-notify; return true }, time.Second, 10*time.Millisecond) - - eventCh <- pg.Event{Payload: addressPgHex + ":val1,8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1"} // try second time - assert.Eventually(t, func() bool { <-notify; return true }, time.Second, 10*time.Millisecond) - - eventCh <- pg.Event{Payload: addressPgHex + ":val1,8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1:additional"} // additional colon separated parts - assert.Eventually(t, func() bool { <-notify; return true }, time.Second, 10*time.Millisecond) -} - func onchainPublicKeyToAddress(publicKeys []types.OnchainPublicKey) (addresses []common.Address, err error) { for _, signer := range publicKeys { if len(signer) != 20 { diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index 0703f878eed..f1686ee00c8 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -18,6 +18,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -26,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - pgmocks "github.com/smartcontractkit/chainlink/v2/core/services/pg/mocks" reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" reportcodecv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/reportcodec" reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" @@ -143,8 +143,6 @@ type TestHarness struct { verifierAddress common.Address verifierContract *verifier.Verifier logPoller logpoller.LogPoller - eventBroadcaster *pgmocks.EventBroadcaster - subscription *pgmocks.Subscription } func SetupTH(t *testing.T, feedID common.Hash) TestHarness { @@ -170,13 +168,9 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { lggr := logger.TestLogger(t) lorm := logpoller.NewORM(big.NewInt(1337), db, lggr, cfg) lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000) - eventBroadcaster := pgmocks.NewEventBroadcaster(t) - subscription := pgmocks.NewSubscription(t) servicetest.Run(t, lp) - eventBroadcaster.On("Subscribe", "evm.insert_on_logs", "").Return(subscription, nil) - - configPoller, err := NewConfigPoller(lggr, lp, verifierAddress, feedID, eventBroadcaster) + configPoller, err := NewConfigPoller(lggr, lp, verifierAddress, feedID) require.NoError(t, err) configPoller.Start() @@ -188,7 +182,5 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { verifierAddress: verifierAddress, verifierContract: verifierContract, logPoller: lp, - eventBroadcaster: eventBroadcaster, - subscription: subscription, } } From 1bb33a45d7f656c2f53f580dae6da933235915fd Mon Sep 17 00:00:00 2001 From: Vyzaldy Sanchez Date: Tue, 9 Jan 2024 12:14:27 -0400 Subject: [PATCH 125/234] Switches test to heavyweight DB (#11712) --- core/services/job/job_orm_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 768d16ddeb7..3590b526022 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -21,6 +21,7 @@ import ( evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" @@ -1386,7 +1387,7 @@ func Test_FindPipelineRunIDsByJobID(t *testing.T) { var jb job.Job config := configtest.NewTestGeneralConfig(t) - db := pgtest.NewSqlxDB(t) + _, db := heavyweight.FullTestDBV2(t, nil) keyStore := cltest.NewKeyStore(t, db, config.Database()) require.NoError(t, keyStore.OCR().Add(cltest.DefaultOCRKey)) From 0f82c97e015016fe08372ee147a26ebdcabb940b Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 9 Jan 2024 13:00:15 -0500 Subject: [PATCH 126/234] Adding Mercury.TLS field CertPath for node communication with web servers + load balancers over TLS (#11492) * feature/transmission_key: adding Transmission.TLS field CertPath * [Transmission.TLS] --> [Mercury.TLS] --- core/config/docs/core.toml | 5 +++ core/config/mercury_config.go | 5 +++ core/config/toml/types.go | 25 +++++++++++ core/config/toml/types_test.go | 45 +++++++++++++++++++ core/services/chainlink/config_mercury.go | 12 +++++ .../services/chainlink/config_mercury_test.go | 14 ++++++ core/services/chainlink/config_test.go | 6 +++ .../testdata/config-empty-effective.toml | 3 ++ .../chainlink/testdata/config-full.toml | 3 ++ .../config-multi-chain-effective.toml | 3 ++ core/services/ocr2/delegate.go | 1 + .../testdata/config-empty-effective.toml | 3 ++ core/web/resolver/testdata/config-full.toml | 3 ++ .../config-multi-chain-effective.toml | 3 ++ docs/CHANGELOG.md | 3 +- docs/CONFIG.md | 13 ++++++ testdata/scripts/node/validate/default.txtar | 3 ++ .../disk-based-logging-disabled.txtar | 3 ++ .../validate/disk-based-logging-no-dir.txtar | 3 ++ .../node/validate/disk-based-logging.txtar | 3 ++ testdata/scripts/node/validate/invalid.txtar | 3 ++ testdata/scripts/node/validate/valid.txtar | 3 ++ testdata/scripts/node/validate/warnings.txtar | 3 ++ 23 files changed, 167 insertions(+), 1 deletion(-) diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 953f1feabd8..3cb6bb0aaa7 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -574,3 +574,8 @@ MaxStaleAge = "1h" # Default # LatestReportDeadline controls how long to wait for a response from the # mercury server before retrying. Setting this to zero will wait indefinitely. LatestReportDeadline = "5s" # Default + +# Mercury.TLS controls client settings for when the node talks to traditional web servers or load balancers. +[Mercury.TLS] +# CertFile is the path to a PEM file of trusted root certificate authority certificates +CertFile = "/path/to/client/certs.pem" # Example diff --git a/core/config/mercury_config.go b/core/config/mercury_config.go index e530af6338f..6a483c593e3 100644 --- a/core/config/mercury_config.go +++ b/core/config/mercury_config.go @@ -12,7 +12,12 @@ type MercuryCache interface { LatestReportDeadline() time.Duration } +type MercuryTLS interface { + CertFile() string +} + type Mercury interface { Credentials(credName string) *ocr2models.MercuryCredentials Cache() MercuryCache + TLS() MercuryTLS } diff --git a/core/config/toml/types.go b/core/config/toml/types.go index e944b44853b..93fcef32611 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -1304,12 +1304,37 @@ func (mc *MercuryCache) setFrom(f *MercuryCache) { } } +type MercuryTLS struct { + CertFile *string +} + +func (m *MercuryTLS) setFrom(f *MercuryTLS) { + if v := f.CertFile; v != nil { + m.CertFile = v + } +} + +func (m *MercuryTLS) ValidateConfig() (err error) { + if *m.CertFile != "" { + if !isValidFilePath(*m.CertFile) { + err = multierr.Append(err, configutils.ErrInvalid{Name: "CertFile", Value: *m.CertFile, Msg: "must be a valid file path"}) + } + } + return +} + type Mercury struct { Cache MercuryCache `toml:",omitempty"` + TLS MercuryTLS `toml:",omitempty"` } func (m *Mercury) setFrom(f *Mercury) { m.Cache.setFrom(&f.Cache) + m.TLS.setFrom(&f.TLS) +} + +func (m *Mercury) ValidateConfig() (err error) { + return m.TLS.ValidateConfig() } type MercuryCredentials struct { diff --git a/core/config/toml/types_test.go b/core/config/toml/types_test.go index e16d3a864da..d29da688e33 100644 --- a/core/config/toml/types_test.go +++ b/core/config/toml/types_test.go @@ -531,5 +531,50 @@ func TestTracing_ValidateMode(t *testing.T) { } } +func TestMercuryTLS_ValidateTLSCertPath(t *testing.T) { + tests := []struct { + name string + tlsCertPath *string + wantErr bool + errMsg string + }{ + { + name: "valid file path", + tlsCertPath: ptr("/etc/ssl/certs/cert.pem"), + wantErr: false, + }, + { + name: "relative file path", + tlsCertPath: ptr("certs/cert.pem"), + wantErr: false, + }, + { + name: "excessively long file path", + tlsCertPath: ptr(strings.Repeat("z", 4097)), + wantErr: true, + errMsg: "CertFile: invalid value (" + strings.Repeat("z", 4097) + "): must be a valid file path", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mercury := &Mercury{ + TLS: MercuryTLS{ + CertFile: tt.tlsCertPath, + }, + } + + err := mercury.ValidateConfig() + + if tt.wantErr { + assert.Error(t, err) + assert.Equal(t, tt.errMsg, err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + // ptr is a utility function for converting a value to a pointer to the value. func ptr[T any](t T) *T { return &t } diff --git a/core/services/chainlink/config_mercury.go b/core/services/chainlink/config_mercury.go index 61e48d340e4..49f1cf0a5f4 100644 --- a/core/services/chainlink/config_mercury.go +++ b/core/services/chainlink/config_mercury.go @@ -24,6 +24,14 @@ func (m *mercuryCacheConfig) LatestReportDeadline() time.Duration { return m.c.LatestReportDeadline.Duration() } +type mercuryTLSConfig struct { + c toml.MercuryTLS +} + +func (m *mercuryTLSConfig) CertFile() string { + return *m.c.CertFile +} + type mercuryConfig struct { c toml.Mercury s toml.MercurySecrets @@ -47,3 +55,7 @@ func (m *mercuryConfig) Credentials(credName string) *models.MercuryCredentials func (m *mercuryConfig) Cache() config.MercuryCache { return &mercuryCacheConfig{c: m.c.Cache} } + +func (m *mercuryConfig) TLS() config.MercuryTLS { + return &mercuryTLSConfig{c: m.c.TLS} +} diff --git a/core/services/chainlink/config_mercury_test.go b/core/services/chainlink/config_mercury_test.go index 58019a91557..71dfb2a01ce 100644 --- a/core/services/chainlink/config_mercury_test.go +++ b/core/services/chainlink/config_mercury_test.go @@ -7,6 +7,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + + "github.com/smartcontractkit/chainlink/v2/core/config/toml" ) const ( @@ -34,3 +36,15 @@ func TestMercuryConfig(t *testing.T) { assert.Equal(t, &models.MercuryCredentials{URL: "https://chain1.link", Username: "username1", Password: "password1"}, m.Credentials("cred1")) assert.Equal(t, &models.MercuryCredentials{URL: "https://chain2.link", Username: "username2", Password: "password2"}, m.Credentials("cred2")) } + +func TestMercuryTLS(t *testing.T) { + certPath := "/path/to/cert.pem" + transmission := toml.Mercury{ + TLS: toml.MercuryTLS{ + CertFile: &certPath, + }, + } + cfg := mercuryConfig{c: transmission} + + assert.Equal(t, certPath, cfg.TLS().CertFile()) +} diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index f0c88db65fe..10ab00e8bd0 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -666,6 +666,9 @@ func TestConfig_Marshal(t *testing.T) { MaxStaleAge: models.MustNewDuration(101 * time.Second), LatestReportDeadline: models.MustNewDuration(102 * time.Second), }, + TLS: toml.MercuryTLS{ + CertFile: ptr("/path/to/cert.pem"), + }, } for _, tt := range []struct { @@ -1097,6 +1100,9 @@ URL = 'http://stark.node' LatestReportTTL = '1m40s' MaxStaleAge = '1m41s' LatestReportDeadline = '1m42s' + +[Mercury.TLS] +CertFile = '/path/to/cert.pem' `}, {"full", full, fullTOML}, {"multi-chain", multiChain, multiChainTOML}, diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index 9efad05c03a..cc4dddcf8af 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -227,3 +227,6 @@ TLSCertPath = '' LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' + +[Mercury.TLS] +CertFile = '' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index c6387e4ef67..da8b68cfdb1 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -238,6 +238,9 @@ LatestReportTTL = '1m40s' MaxStaleAge = '1m41s' LatestReportDeadline = '1m42s' +[Mercury.TLS] +CertFile = '/path/to/cert.pem' + [[EVM]] ChainID = '1' Enabled = false diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 40c18f28eb9..d5352a685c4 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -228,6 +228,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 519a7a58f5d..37235437de1 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -190,6 +190,7 @@ type jobPipelineConfig interface { type mercuryConfig interface { Credentials(credName string) *models.MercuryCredentials Cache() coreconfig.MercuryCache + TLS() coreconfig.MercuryTLS } type thresholdConfig interface { diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index 9efad05c03a..cc4dddcf8af 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -227,3 +227,6 @@ TLSCertPath = '' LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' + +[Mercury.TLS] +CertFile = '' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 45504d62596..957ffdc968e 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -238,6 +238,9 @@ LatestReportTTL = '1m40s' MaxStaleAge = '1m41s' LatestReportDeadline = '1m42s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' Enabled = false diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 40c18f28eb9..d5352a685c4 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -228,6 +228,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 36ee68412ca..2ba80927256 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -64,7 +64,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Two new prom metrics for mercury, nops should consider adding alerting on these: - `mercury_insufficient_blocks_count` - `mercury_zero_blocks_count` - +- Added new `Mercury.TLS` TOML config field `CertFile` for configuring transport credentials when the node acts as a client and initiates a TLS handshake. + ### Changed - `PromReporter` no longer directly reads txm related status from the db, and instead uses the txStore API. diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 2bb32b58252..421b0ea9dad 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1589,6 +1589,19 @@ LatestReportDeadline = "5s" # Default LatestReportDeadline controls how long to wait for a response from the mercury server before retrying. Setting this to zero will wait indefinitely. +## Mercury.TLS +```toml +[Mercury.TLS] +CertFile = "/path/to/client/certs.pem" # Example +``` +Mercury.TLS controls client settings for when the node talks to traditional web servers or load balancers. + +### CertFile +```toml +CertFile = "/path/to/client/certs.pem" # Example +``` +CertFile is the path to a PEM file of trusted root certificate authority certificates + ## EVM EVM defaults depend on ChainID: diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index c9f86e43b29..32a7e5f71b3 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -240,6 +240,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + Invalid configuration: invalid secrets: 2 errors: - Database.URL: empty: must be provided and non-empty - Password.Keystore: empty: must be provided and non-empty diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 1ae283c8a8a..9cca8764eed 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -284,6 +284,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index dcb2d05ce39..d1658a1772d 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -284,6 +284,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index fee173d3083..18ac99db785 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -284,6 +284,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 5acbad83f8c..f048e35547e 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -274,6 +274,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index b54fc899bbd..47c63e4c03e 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -281,6 +281,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index 710e29d4983..c6daa829ddb 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -263,6 +263,9 @@ LatestReportTTL = '1s' MaxStaleAge = '1h0m0s' LatestReportDeadline = '5s' +[Mercury.TLS] +CertFile = '' + # Configuration warning: Tracing.TLSCertPath: invalid value (something): must be empty when Tracing.Mode is 'unencrypted' Valid configuration. From 43a8eb6b6ce3fb354e20f89385f4153e9636898d Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:18:31 +0100 Subject: [PATCH 127/234] Add AlwaysPullImage option to docker core node (#11715) --- integration-tests/docker/test_env/cl_node.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index c28959dadc9..a00dea3a35a 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -51,6 +51,7 @@ type ClNode struct { PostgresDb *test_env.PostgresDb `json:"postgresDb"` UserEmail string `json:"userEmail"` UserPassword string `json:"userPassword"` + AlwaysPullImage bool t *testing.T l zerolog.Logger ls *logstream.LogStream @@ -435,9 +436,10 @@ func (n *ClNode) getContainerRequest(secrets string) ( apiCredsPath := "/home/api-credentials.txt" return &tc.ContainerRequest{ - Name: n.ContainerName, - Image: fmt.Sprintf("%s:%s", n.ContainerImage, n.ContainerVersion), - ExposedPorts: []string{"6688/tcp"}, + Name: n.ContainerName, + AlwaysPullImage: n.AlwaysPullImage, + Image: fmt.Sprintf("%s:%s", n.ContainerImage, n.ContainerVersion), + ExposedPorts: []string{"6688/tcp"}, Entrypoint: []string{"chainlink", "-c", configPath, "-s", secretsPath, From 7fc45b6bd3f9a41c8e69ad1d55d2d61ff1edbef2 Mon Sep 17 00:00:00 2001 From: Erik Burton Date: Tue, 9 Jan 2024 11:22:28 -0800 Subject: [PATCH 128/234] chore: bump github action versions (#11657) * Bump actions/download-artifact from 3 to 4 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Bump actions/stale from 8.0.0 to 9.0.0 Bumps [actions/stale](https://github.com/actions/stale) from 8.0.0 to 9.0.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/1160a2240286f5da8ec72b1c0816ce2481aabf84...28ca1036281a5e5922ead5184a1bbf96e5fc984e) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Bump actions/setup-node from 4.0.0 to 4.0.1 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/8f152de45cc393bb48ce5d89d36b731f54556e65...b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Bump actions/setup-go from 3 to 5 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Bump docker/build-push-action from 5.0.0 to 5.1.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/0565240e2d4ab88bba5387d719585280857ece09...4a13e500e55cf31b7a5d59a38ab2040ab0f42f56) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump helm/chart-releaser-action from 1.5.0 to 1.6.0 Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.5.0 to 1.6.0. - [Release notes](https://github.com/helm/chart-releaser-action/releases) - [Commits](https://github.com/helm/chart-releaser-action/compare/be16258da8010256c6e82849661221415f031968...a917fd15b20e8b64b94d9158ad54cd6345335584) --- updated-dependencies: - dependency-name: helm/chart-releaser-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Revert "Bump actions/download-artifact from 3 to 4" This reverts commit 8edfd2bde8f44e6eeca46aee821eb3a8dbb4ceda. * Revert "Bump actions/upload-artifact from 3 to 4" This reverts commit c75325c936ab616d82d22a6a9e05d2ee8cf98017. * chore: pin actions/download-artifact to commit SHA * chore: pin actions/upload-artifact to commit SHA --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: chainchad <96362174+chainchad@users.noreply.github.com> --- .github/workflows/ci-core.yml | 8 ++++---- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dependency-check.yml | 2 +- .github/workflows/helm-chart-publish.yml | 2 +- .github/workflows/integration-tests.yml | 2 +- .github/workflows/on-demand-log-poller.yml | 2 +- .github/workflows/performance-tests.yml | 4 ++-- .github/workflows/solidity-hardhat.yml | 4 ++-- .github/workflows/stale.yml | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index bac6f763892..0d9d2912718 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -50,7 +50,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs with: @@ -97,7 +97,7 @@ jobs: working-directory: ./.github/actions/setup-postgres - name: Store logs artifacts if: always() - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ matrix.cmd }}_logs path: | @@ -136,7 +136,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs with: @@ -154,7 +154,7 @@ jobs: - name: Setup DB run: ./chainlink.test local db preparetest - name: Load test outputs - uses: actions/download-artifact@v3 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: ./artifacts - name: Build flakey test runner diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8bc066f408c..5b846d8708d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Go if: ${{ matrix.language == 'go' }} - uses: actions/setup-go@v4 + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version-file: 'go.mod' diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index dbf08895757..0143042abd9 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -29,7 +29,7 @@ jobs: - name: Set up Go if: needs.changes.outputs.src == 'true' - uses: actions/setup-go@v4 + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version-file: 'go.mod' id: go diff --git a/.github/workflows/helm-chart-publish.yml b/.github/workflows/helm-chart-publish.yml index 6ea46e6a52d..156268d66b0 100644 --- a/.github/workflows/helm-chart-publish.yml +++ b/.github/workflows/helm-chart-publish.yml @@ -31,7 +31,7 @@ jobs: uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 - name: Run chart-releaser - uses: helm/chart-releaser-action@be16258da8010256c6e82849661221415f031968 # v1.5.0 + uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0 with: charts_dir: charts config: .github/cr.yaml diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 813d4854563..75184a47965 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -492,7 +492,7 @@ jobs: ls -l ./integration-tests/smoke/traces - name: Upload Trace Data if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: trace-data path: ./integration-tests/smoke/traces/trace-data.json diff --git a/.github/workflows/on-demand-log-poller.yml b/.github/workflows/on-demand-log-poller.yml index 42f901ec304..4658e188bac 100644 --- a/.github/workflows/on-demand-log-poller.yml +++ b/.github/workflows/on-demand-log-poller.yml @@ -76,7 +76,7 @@ jobs: with: ref: ${{ env.REF_NAME }} - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version-file: "integration-tests/go.mod" cache: true diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 4b6dd1a2280..7b9eef7fccc 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -31,7 +31,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Build and Push - uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 with: context: . file: core/chainlink.Dockerfile @@ -72,7 +72,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Publish pprof artifacts if: ${{ success() }} - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: pprof_results path: ./integration-tests/performance/logs diff --git a/.github/workflows/solidity-hardhat.yml b/.github/workflows/solidity-hardhat.yml index 129f37c0de6..9301c6f3967 100644 --- a/.github/workflows/solidity-hardhat.yml +++ b/.github/workflows/solidity-hardhat.yml @@ -84,7 +84,7 @@ jobs: - name: Rename coverage run: mv ./contracts/coverage.json ./contracts/coverage-${{ matrix.split.idx }}.json - name: Upload coverage - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: solidity-coverage-${{ matrix.split.idx }} path: ./contracts/coverage-${{ matrix.split.idx }}.json @@ -110,7 +110,7 @@ jobs: - name: Make coverage directory run: mkdir ./contracts/coverage-reports - name: Download coverage - uses: actions/download-artifact@v3 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: ./contracts/coverage-reports - name: Display structure of downloaded files diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6c5207e0f05..8eb95f4147c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} exempt-all-pr-assignees: true From b0da96535cb34f103b0cc3d0bd4e05ef240e837f Mon Sep 17 00:00:00 2001 From: krehermann Date: Tue, 9 Jan 2024 16:17:46 -0700 Subject: [PATCH 129/234] remove unused references to EventBroadcaster from evm relayer (#11720) * remove unused references to EventBroadcaster from evm relayer * fix test --- core/services/chainlink/relayer_factory.go | 9 +++-- core/services/job/spawner_test.go | 8 ++--- core/services/relay/evm/evm.go | 40 +++++++++------------- core/services/relay/evm/evm_test.go | 30 +++++++--------- 4 files changed, 36 insertions(+), 51 deletions(-) diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index b4bd530d080..49582e2d52a 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -67,11 +67,10 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m } relayerOpts := evmrelay.RelayerOpts{ - DB: ccOpts.DB, - QConfig: ccOpts.AppConfig.Database(), - CSAETHKeystore: config.CSAETHKeystore, - EventBroadcaster: ccOpts.EventBroadcaster, - MercuryPool: r.MercuryPool, + DB: ccOpts.DB, + QConfig: ccOpts.AppConfig.Database(), + CSAETHKeystore: config.CSAETHKeystore, + MercuryPool: r.MercuryPool, } relayer, err2 := evmrelay.NewRelayer(lggr.Named(relayID.ChainID), chain, relayerOpts) if err2 != nil { diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 06e305c8057..3e8ccbab848 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -31,7 +31,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" @@ -285,10 +284,9 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { chain := evmtest.MustGetDefaultChain(t, legacyChains) evmRelayer, err := evmrelayer.NewRelayer(lggr, chain, evmrelayer.RelayerOpts{ - DB: db, - QConfig: testopts.GeneralConfig.Database(), - CSAETHKeystore: keyStore, - EventBroadcaster: pg.NewNullEventBroadcaster(), + DB: db, + QConfig: testopts.GeneralConfig.Database(), + CSAETHKeystore: keyStore, }) assert.NoError(t, err) diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 8cdbfe76058..d6a8c584fab 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -45,14 +45,13 @@ import ( var _ commontypes.Relayer = &Relayer{} //nolint:staticcheck type Relayer struct { - db *sqlx.DB - chain legacyevm.Chain - lggr logger.Logger - ks CSAETHKeystore - mercuryPool wsrpc.Pool - eventBroadcaster pg.EventBroadcaster - pgCfg pg.QConfig - chainReader commontypes.ChainReader + db *sqlx.DB + chain legacyevm.Chain + lggr logger.Logger + ks CSAETHKeystore + mercuryPool wsrpc.Pool + pgCfg pg.QConfig + chainReader commontypes.ChainReader } type CSAETHKeystore interface { @@ -64,7 +63,6 @@ type RelayerOpts struct { *sqlx.DB pg.QConfig CSAETHKeystore - pg.EventBroadcaster MercuryPool wsrpc.Pool } @@ -79,9 +77,6 @@ func (c RelayerOpts) Validate() error { if c.CSAETHKeystore == nil { err = errors.Join(err, errors.New("nil Keystore")) } - if c.EventBroadcaster == nil { - err = errors.Join(err, errors.New("nil Eventbroadcaster")) - } if err != nil { err = fmt.Errorf("invalid RelayerOpts: %w", err) @@ -96,13 +91,12 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R } lggr = lggr.Named("Relayer") return &Relayer{ - db: opts.DB, - chain: chain, - lggr: lggr, - ks: opts.CSAETHKeystore, - mercuryPool: opts.MercuryPool, - eventBroadcaster: opts.EventBroadcaster, - pgCfg: opts.QConfig, + db: opts.DB, + chain: chain, + lggr: lggr, + ks: opts.CSAETHKeystore, + mercuryPool: opts.MercuryPool, + pgCfg: opts.QConfig, }, nil } @@ -151,7 +145,7 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty if relayConfig.ChainID.String() != r.chain.ID().String() { return nil, fmt.Errorf("internal error: chain id in spec does not match this relayer's chain: have %s expected %s", relayConfig.ChainID.String(), r.chain.ID().String()) } - cw, err := newConfigProvider(lggr, r.chain, relayOpts, r.eventBroadcaster) + cw, err := newConfigProvider(lggr, r.chain, relayOpts) if err != nil { return nil, pkgerrors.WithStack(err) } @@ -210,7 +204,7 @@ func (r *Relayer) NewConfigProvider(args commontypes.RelayArgs) (commontypes.Con return nil, fmt.Errorf("internal error: chain id in spec does not match this relayer's chain: have %s expected %s", relayConfig.ChainID.String(), r.chain.ID().String()) } - configProvider, err := newConfigProvider(lggr, r.chain, relayOpts, r.eventBroadcaster) + configProvider, err := newConfigProvider(lggr, r.chain, relayOpts) if err != nil { // Never return (*configProvider)(nil) return nil, err @@ -320,7 +314,7 @@ func (c *configWatcher) ContractConfigTracker() ocrtypes.ContractConfigTracker { return c.configPoller } -func newConfigProvider(lggr logger.Logger, chain legacyevm.Chain, opts *types.RelayOpts, eventBroadcaster pg.EventBroadcaster) (*configWatcher, error) { +func newConfigProvider(lggr logger.Logger, chain legacyevm.Chain, opts *types.RelayOpts) (*configWatcher, error) { if !common.IsHexAddress(opts.ContractID) { return nil, pkgerrors.Errorf("invalid contractID, expected hex address") } @@ -464,7 +458,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } contractID := common.HexToAddress(relayOpts.ContractID) - configWatcher, err := newConfigProvider(lggr, r.chain, relayOpts, r.eventBroadcaster) + configWatcher, err := newConfigProvider(lggr, r.chain, relayOpts) if err != nil { return nil, err } diff --git a/core/services/relay/evm/evm_test.go b/core/services/relay/evm/evm_test.go index 8f49128ff2d..41e51a7ab8f 100644 --- a/core/services/relay/evm/evm_test.go +++ b/core/services/relay/evm/evm_test.go @@ -15,10 +15,9 @@ import ( func TestRelayerOpts_Validate(t *testing.T) { cfg := configtest.NewTestGeneralConfig(t) type fields struct { - DB *sqlx.DB - QConfig pg.QConfig - CSAETHKeystore evm.CSAETHKeystore - EventBroadcaster pg.EventBroadcaster + DB *sqlx.DB + QConfig pg.QConfig + CSAETHKeystore evm.CSAETHKeystore } tests := []struct { name string @@ -28,23 +27,19 @@ func TestRelayerOpts_Validate(t *testing.T) { { name: "all invalid", fields: fields{ - DB: nil, - QConfig: nil, - CSAETHKeystore: nil, - EventBroadcaster: nil, + DB: nil, + QConfig: nil, + CSAETHKeystore: nil, }, wantErrContains: `nil DB nil QConfig -nil Keystore -nil Eventbroadcaster`, +nil Keystore`, }, { name: "missing db, keystore", fields: fields{ - DB: nil, - QConfig: cfg.Database(), - CSAETHKeystore: nil, - EventBroadcaster: pg.NewNullEventBroadcaster(), + DB: nil, + QConfig: cfg.Database(), }, wantErrContains: `nil DB nil Keystore`, @@ -53,10 +48,9 @@ nil Keystore`, for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := evm.RelayerOpts{ - DB: tt.fields.DB, - QConfig: tt.fields.QConfig, - CSAETHKeystore: tt.fields.CSAETHKeystore, - EventBroadcaster: tt.fields.EventBroadcaster, + DB: tt.fields.DB, + QConfig: tt.fields.QConfig, + CSAETHKeystore: tt.fields.CSAETHKeystore, } err := c.Validate() if tt.wantErrContains != "" { From 768edec1c80d5cac8ed9710ebd65a1e3e41ed94f Mon Sep 17 00:00:00 2001 From: krehermann Date: Tue, 9 Jan 2024 17:26:09 -0700 Subject: [PATCH 130/234] remove unused eventbroadcaster from legacy evm chains (#11722) --- core/chains/legacyevm/chain.go | 10 ++----- core/chains/legacyevm/chain_test.go | 29 ++++++++----------- core/cmd/shell.go | 2 +- core/cmd/shell_local_test.go | 15 ++++------ core/internal/cltest/cltest.go | 7 ++--- core/internal/testutils/evmtest/evmtest.go | 10 +++---- .../relayer_chain_interoperators_test.go | 16 +++++----- 7 files changed, 36 insertions(+), 53 deletions(-) diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index ef84573cd09..0e0e1e65aca 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -37,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) //go:generate mockery --quiet --name Chain --output ./mocks/ --case=underscore @@ -164,9 +163,8 @@ func (c ChainRelayExtenderConfig) Validate() error { type ChainOpts struct { AppConfig AppConfig - EventBroadcaster pg.EventBroadcaster - MailMon *mailbox.Monitor - GasEstimator gas.EvmFeeEstimator + MailMon *mailbox.Monitor + GasEstimator gas.EvmFeeEstimator *sqlx.DB @@ -185,9 +183,7 @@ func (o ChainOpts) Validate() error { if o.AppConfig == nil { err = errors.Join(err, errors.New("nil AppConfig")) } - if o.EventBroadcaster == nil { - err = errors.Join(err, errors.New("nil EventBroadcaster")) - } + if o.MailMon == nil { err = errors.Join(err, errors.New("nil MailMon")) } diff --git a/core/chains/legacyevm/chain_test.go b/core/chains/legacyevm/chain_test.go index 93332348aa0..e639db6e7cc 100644 --- a/core/chains/legacyevm/chain_test.go +++ b/core/chains/legacyevm/chain_test.go @@ -14,7 +14,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) func TestLegacyChains(t *testing.T) { @@ -34,10 +33,9 @@ func TestLegacyChains(t *testing.T) { func TestChainOpts_Validate(t *testing.T) { type fields struct { - AppConfig legacyevm.AppConfig - EventBroadcaster pg.EventBroadcaster - MailMon *mailbox.Monitor - DB *sqlx.DB + AppConfig legacyevm.AppConfig + MailMon *mailbox.Monitor + DB *sqlx.DB } tests := []struct { name string @@ -47,19 +45,17 @@ func TestChainOpts_Validate(t *testing.T) { { name: "valid", fields: fields{ - AppConfig: configtest.NewTestGeneralConfig(t), - EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &mailbox.Monitor{}, - DB: pgtest.NewSqlxDB(t), + AppConfig: configtest.NewTestGeneralConfig(t), + MailMon: &mailbox.Monitor{}, + DB: pgtest.NewSqlxDB(t), }, }, { name: "invalid", fields: fields{ - AppConfig: nil, - EventBroadcaster: nil, - MailMon: nil, - DB: nil, + AppConfig: nil, + MailMon: nil, + DB: nil, }, wantErr: true, }, @@ -67,10 +63,9 @@ func TestChainOpts_Validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { o := legacyevm.ChainOpts{ - AppConfig: tt.fields.AppConfig, - EventBroadcaster: tt.fields.EventBroadcaster, - MailMon: tt.fields.MailMon, - DB: tt.fields.DB, + AppConfig: tt.fields.AppConfig, + MailMon: tt.fields.MailMon, + DB: tt.fields.DB, } if err := o.Validate(); (err != nil) != tt.wantErr { t.Errorf("ChainOpts.Validate() error = %v, wantErr %v", err, tt.wantErr) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index e4711646cb4..f4386a4faab 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -176,7 +176,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G evmFactoryCfg := chainlink.EVMFactoryConfig{ CSAETHKeystore: keyStore, - ChainOpts: legacyevm.ChainOpts{AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon, DB: db}, + ChainOpts: legacyevm.ChainOpts{AppConfig: cfg, MailMon: mailMon, DB: db}, } // evm always enabled for backward compatibility // TODO BCF-2510 this needs to change in order to clear the path for EVM extraction diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 400a8e3e75a..5bdcbb180ef 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -25,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" chainlinkmocks "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" evmrelayer "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" @@ -89,10 +88,9 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { Logger: lggr, KeyStore: keyStore.Eth(), ChainOpts: legacyevm.ChainOpts{ - AppConfig: cfg, - EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &mailbox.Monitor{}, - DB: db, + AppConfig: cfg, + MailMon: &mailbox.Monitor{}, + DB: db, }, } testRelayers := genTestEVMRelayers(t, opts, keyStore) @@ -194,10 +192,9 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { Logger: lggr, KeyStore: keyStore.Eth(), ChainOpts: legacyevm.ChainOpts{ - AppConfig: cfg, - EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &mailbox.Monitor{}, - DB: db, + AppConfig: cfg, + MailMon: &mailbox.Monitor{}, + DB: db, }, } testRelayers := genTestEVMRelayers(t, opts, keyStore) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index fceb58ccdab..7bb2a76bc7b 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -360,10 +360,9 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn evmOpts := chainlink.EVMFactoryConfig{ ChainOpts: legacyevm.ChainOpts{ - AppConfig: cfg, - EventBroadcaster: eventBroadcaster, - MailMon: mailMon, - DB: db, + AppConfig: cfg, + MailMon: mailMon, + DB: db, }, CSAETHKeystore: keyStore, } diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 9397db53acb..cc56c3c9e9b 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -37,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) @@ -87,11 +86,10 @@ func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) legacyevm.ChainR Logger: lggr, KeyStore: testopts.KeyStore, ChainOpts: legacyevm.ChainOpts{ - AppConfig: testopts.GeneralConfig, - EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: testopts.MailMon, - GasEstimator: testopts.GasEstimator, - DB: testopts.DB, + AppConfig: testopts.GeneralConfig, + MailMon: testopts.MailMon, + GasEstimator: testopts.GasEstimator, + DB: testopts.DB, }, } opts.GenEthClient = func(*big.Int) evmclient.Client { diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index d89fbce12db..6f6e3afce31 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -25,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/plugins" @@ -206,10 +205,9 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { initFuncs: []chainlink.CoreRelayerChainInitFunc{ chainlink.InitEVM(testctx, factory, chainlink.EVMFactoryConfig{ ChainOpts: legacyevm.ChainOpts{ - AppConfig: cfg, - EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &mailbox.Monitor{}, - DB: db, + AppConfig: cfg, + MailMon: &mailbox.Monitor{}, + DB: db, }, CSAETHKeystore: keyStore, }), @@ -280,10 +278,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { TOMLConfigs: cfg.SolanaConfigs()}), chainlink.InitEVM(testctx, factory, chainlink.EVMFactoryConfig{ ChainOpts: legacyevm.ChainOpts{ - AppConfig: cfg, - EventBroadcaster: pg.NewNullEventBroadcaster(), - MailMon: &mailbox.Monitor{}, - DB: db, + AppConfig: cfg, + + MailMon: &mailbox.Monitor{}, + DB: db, }, CSAETHKeystore: keyStore, }), From 5b9abcf2a0653f9ec79ce62e0af54bae10c4bc37 Mon Sep 17 00:00:00 2001 From: David Cauchi <13139524+davidcauchi@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:08:24 +0100 Subject: [PATCH 131/234] Enable base sepolia on demand ocr soak (#11726) --- .github/workflows/on-demand-ocr-soak-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index a4289dd0129..32e080aafca 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -15,6 +15,7 @@ on: - "CELO_ALFAJORES" - "CELO_MAINNET" - "BASE_GOERLI" + - "BASE_SEPOLIA" - "BASE_MAINNET" - "BSC_MAINNET" - "BSC_TESTNET" From f8bc7c6f32c12bb9bc523dcac1d149b9d8185b12 Mon Sep 17 00:00:00 2001 From: jinhoonbang Date: Wed, 10 Jan 2024 06:05:16 -0800 Subject: [PATCH 132/234] add logging for vrf output (#11719) --- core/services/pipeline/task.vrfv2.go | 7 +++++-- core/services/pipeline/task.vrfv2plus.go | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/services/pipeline/task.vrfv2.go b/core/services/pipeline/task.vrfv2.go index a871e543496..2c2c85eaedf 100644 --- a/core/services/pipeline/task.vrfv2.go +++ b/core/services/pipeline/task.vrfv2.go @@ -39,7 +39,7 @@ func (t *VRFTaskV2) Type() TaskType { return TaskTypeVRFV2 } -func (t *VRFTaskV2) Run(_ context.Context, _ logger.Logger, vars Vars, inputs []Result) (result Result, runInfo RunInfo) { +func (t *VRFTaskV2) Run(_ context.Context, lggr logger.Logger, vars Vars, inputs []Result) (result Result, runInfo RunInfo) { if len(inputs) != 1 { return Result{Error: ErrWrongInputCardinality}, runInfo } @@ -134,7 +134,8 @@ func (t *VRFTaskV2) Run(_ context.Context, _ logger.Logger, vars Vars, inputs [] return Result{Error: err}, runInfo } results := make(map[string]interface{}) - results["output"] = hexutil.Encode(b) + output := hexutil.Encode(b) + results["output"] = output // RequestID needs to be a [32]byte for EvmTxMeta. results["requestID"] = hexutil.Encode(requestId.Bytes()) @@ -142,5 +143,7 @@ func (t *VRFTaskV2) Run(_ context.Context, _ logger.Logger, vars Vars, inputs [] results["proof"] = onChainProof results["requestCommitment"] = rc + lggr.Debugw("Completed VRF V2 task run", "reqID", requestId.String(), "output", output) + return Result{Value: results}, runInfo } diff --git a/core/services/pipeline/task.vrfv2plus.go b/core/services/pipeline/task.vrfv2plus.go index a596bfa3092..c029af68cbe 100644 --- a/core/services/pipeline/task.vrfv2plus.go +++ b/core/services/pipeline/task.vrfv2plus.go @@ -42,7 +42,7 @@ func (t *VRFTaskV2Plus) Type() TaskType { return TaskTypeVRFV2Plus } -func (t *VRFTaskV2Plus) Run(_ context.Context, _ logger.Logger, vars Vars, inputs []Result) (result Result, runInfo RunInfo) { +func (t *VRFTaskV2Plus) Run(_ context.Context, lggr logger.Logger, vars Vars, inputs []Result) (result Result, runInfo RunInfo) { if len(inputs) != 1 { return Result{Error: ErrWrongInputCardinality}, runInfo } @@ -142,7 +142,8 @@ func (t *VRFTaskV2Plus) Run(_ context.Context, _ logger.Logger, vars Vars, input return Result{Error: err}, runInfo } results := make(map[string]interface{}) - results["output"] = hexutil.Encode(b) + output := hexutil.Encode(b) + results["output"] = output // RequestID needs to be a [32]byte for EvmTxMeta. results["requestID"] = hexutil.Encode(requestId.Bytes()) @@ -150,5 +151,7 @@ func (t *VRFTaskV2Plus) Run(_ context.Context, _ logger.Logger, vars Vars, input results["proof"] = onChainProof results["requestCommitment"] = rc + lggr.Debugw("Completed VRF V2 task run", "reqID", requestId.String(), "output", output) + return Result{Value: results}, runInfo } From 89a8ba46d2b914f9c7fe7a97deebcdff80ad593a Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 10 Jan 2024 12:22:18 -0300 Subject: [PATCH 133/234] [TT-757] enable log stream in test env builder by default (#11706) * enable log stream in test env builder by default * remove one more usage of WithLogStream() --- integration-tests/docker/test_env/test_env_builder.go | 8 +++++--- integration-tests/load/vrfv2/vrfv2_test.go | 1 - integration-tests/load/vrfv2plus/vrfv2plus_test.go | 1 - integration-tests/smoke/automation_test.go | 1 - integration-tests/smoke/cron_test.go | 2 -- integration-tests/smoke/flux_test.go | 1 - integration-tests/smoke/forwarder_ocr_test.go | 1 - integration-tests/smoke/forwarders_ocr2_test.go | 1 - integration-tests/smoke/keeper_test.go | 1 - integration-tests/smoke/ocr2_test.go | 3 --- integration-tests/smoke/ocr_test.go | 1 - integration-tests/smoke/runlog_test.go | 1 - integration-tests/smoke/vrf_test.go | 2 -- integration-tests/smoke/vrfv2_test.go | 2 -- integration-tests/smoke/vrfv2plus_test.go | 3 --- integration-tests/universal/log_poller/helpers.go | 1 - 16 files changed, 5 insertions(+), 25 deletions(-) diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 7358d76f2af..bd73d33d9bf 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -59,7 +59,8 @@ type CLTestEnvBuilder struct { func NewCLTestEnvBuilder() *CLTestEnvBuilder { return &CLTestEnvBuilder{ - l: log.Logger, + l: log.Logger, + hasLogStream: true, } } @@ -101,8 +102,9 @@ func (b *CLTestEnvBuilder) WithTestInstance(t *testing.T) *CLTestEnvBuilder { return b } -func (b *CLTestEnvBuilder) WithLogStream() *CLTestEnvBuilder { - b.hasLogStream = true +// WithoutLogStream disables LogStream logging component +func (b *CLTestEnvBuilder) WithoutLogStream() *CLTestEnvBuilder { + b.hasLogStream = false return b } diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index ec0f945870d..c48f3488594 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -184,7 +184,6 @@ func TestVRFV2Performance(t *testing.T) { l.Error().Err(err).Msg("Error cleaning up test environment") } }). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 069e3115aee..74be537c753 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -189,7 +189,6 @@ func TestVRFV2PlusPerformance(t *testing.T) { l.Error().Err(err).Msg("Error cleaning up test environment") } }). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index cb631eb8278..70b6a8f6856 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1097,7 +1097,6 @@ func setupAutomationTestDocker( WithMockAdapter(). WithFunding(big.NewFloat(testConfig.ChainlinkNodeFunding)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "Error deploying test environment for Mercury") env.ParallelTransactions(true) diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index 751c2867676..df1b85882e9 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -25,7 +25,6 @@ func TestCronBasic(t *testing.T) { WithMockAdapter(). WithCLNodes(1). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) @@ -72,7 +71,6 @@ func TestCronJobReplacement(t *testing.T) { WithMockAdapter(). WithCLNodes(1). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 828350a9422..345971e8922 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -31,7 +31,6 @@ func TestFluxBasic(t *testing.T) { WithMockAdapter(). WithCLNodes(3). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 64128ed4a8c..fd8dfc780af 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -26,7 +26,6 @@ func TestForwarderOCRBasic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 71d35508175..90dc20a3898 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -35,7 +35,6 @@ func TestForwarderOCR2Basic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 1bc416f0456..330d041c8f6 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1113,7 +1113,6 @@ func setupKeeperTest(t *testing.T) ( WithCLNodeConfig(clNodeConfig). WithFunding(big.NewFloat(.5)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "Error deploying test environment") diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 266fcea6382..2603b6e9e6a 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -52,7 +52,6 @@ func TestOCRv2Basic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) @@ -134,7 +133,6 @@ func TestOCRv2Request(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) @@ -210,7 +208,6 @@ func TestOCRv2JobReplacement(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index ebfbf698e98..5f091835894 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -27,7 +27,6 @@ func TestOCRBasic(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.5)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index 5cc21a28c2c..dc08b19b16f 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -29,7 +29,6 @@ func TestRunLogBasic(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 0554bd34760..51ea3828cfd 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -29,7 +29,6 @@ func TestVRFBasic(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) env.ParallelTransactions(true) @@ -119,7 +118,6 @@ func TestVRFJobReplacement(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err) env.ParallelTransactions(true) diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 4167342c41f..6f69b579a4a 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -39,7 +39,6 @@ func TestVRFv2Basic(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") @@ -371,7 +370,6 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 9ddc87422be..6cc4a07237f 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -42,7 +42,6 @@ func TestVRFv2Plus(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") @@ -622,7 +621,6 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") @@ -713,7 +711,6 @@ func TestVRFv2PlusMigration(t *testing.T) { WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 1abd98632f1..40040fd0aee 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -1060,7 +1060,6 @@ func setupLogPollerTestDocker( WithChainOptions(logPolllerSettingsFn). EVMClientNetworkOptions(evmClientSettingsFn). WithStandardCleanup(). - WithLogStream(). Build() require.NoError(t, err, "Error deploying test environment") From 2a20248504932c557bef17b25bd87b71b49e37eb Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 10 Jan 2024 09:46:44 -0600 Subject: [PATCH 134/234] remove redundant operator-ui make dependencies (#11729) --- GNUmakefile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 09b1dfe87a3..a6e886b7f6c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,7 @@ GO_LDFLAGS := $(shell tools/bin/ldflags) GOFLAGS = -ldflags "$(GO_LDFLAGS)" .PHONY: install -install: operator-ui-autoinstall install-chainlink-autoinstall ## Install chainlink and all its dependencies. +install: install-chainlink-autoinstall ## Install chainlink and all its dependencies. .PHONY: install-git-hooks install-git-hooks: ## Install git hooks. @@ -14,8 +14,6 @@ install-git-hooks: ## Install git hooks. .PHONY: install-chainlink-autoinstall install-chainlink-autoinstall: | pnpmdep gomod install-chainlink ## Autoinstall chainlink. -.PHONY: operator-ui-autoinstall -operator-ui-autoinstall: | operator-ui ## Autoinstall frontend UI. .PHONY: pnpmdep pnpmdep: ## Install solidity contract dependencies through pnpm @@ -44,13 +42,13 @@ godoc: ## Install and run godoc install-chainlink: operator-ui ## Install the chainlink binary. go install $(GOFLAGS) . -chainlink: operator-ui ## Build the chainlink binary. +chainlink: ## Build the chainlink binary. go build $(GOFLAGS) . -chainlink-dev: operator-ui ## Build a dev build of chainlink binary. +chainlink-dev: ## Build a dev build of chainlink binary. go build -tags dev $(GOFLAGS) . -chainlink-test: operator-ui ## Build a test build of chainlink binary. +chainlink-test: ## Build a test build of chainlink binary. go build $(GOFLAGS) . .PHONY: chainlink-local-start From 1567a11874ab62846598959ce37d9b3dc1581081 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 10 Jan 2024 10:02:38 -0600 Subject: [PATCH 135/234] core/cmd: keys eth list: print Unknown/None instead of (#11724) Co-authored-by: Vyzaldy Sanchez --- core/cmd/eth_keys_commands.go | 18 +++++++-- core/cmd/eth_keys_commands_test.go | 4 +- testdata/scripts/keys/eth/create/help.txtar | 14 +++++++ testdata/scripts/keys/eth/list/help.txtar | 9 +++++ .../scripts/keys/eth/list/unavailable.txtar | 38 +++++++++++++++++++ 5 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 testdata/scripts/keys/eth/create/help.txtar create mode 100644 testdata/scripts/keys/eth/list/help.txtar create mode 100644 testdata/scripts/keys/eth/list/unavailable.txtar diff --git a/core/cmd/eth_keys_commands.go b/core/cmd/eth_keys_commands.go index ad76a7655ef..4fb3044ecb6 100644 --- a/core/cmd/eth_keys_commands.go +++ b/core/cmd/eth_keys_commands.go @@ -124,15 +124,27 @@ type EthKeyPresenter struct { } func (p *EthKeyPresenter) ToRow() []string { + eth := "Unknown" + if p.EthBalance != nil { + eth = p.EthBalance.String() + } + link := "Unknown" + if p.LinkBalance != nil { + link = p.LinkBalance.String() + } + gas := "None" + if p.MaxGasPriceWei != nil { + gas = p.MaxGasPriceWei.String() + } return []string{ p.Address, p.EVMChainID.String(), - p.EthBalance.String(), - p.LinkBalance.String(), + eth, + link, fmt.Sprintf("%v", p.Disabled), p.CreatedAt.String(), p.UpdatedAt.String(), - p.MaxGasPriceWei.String(), + gas, } } diff --git a/core/cmd/eth_keys_commands_test.go b/core/cmd/eth_keys_commands_test.go index e9121270191..de40a5bf873 100644 --- a/core/cmd/eth_keys_commands_test.go +++ b/core/cmd/eth_keys_commands_test.go @@ -161,8 +161,8 @@ func TestShell_ListETHKeys_Disabled(t *testing.T) { assert.Nil(t, balances[0].LinkBalance) assert.Nil(t, balances[0].MaxGasPriceWei) assert.Equal(t, []string{ - k.Address.String(), "0", "", "0", "false", - balances[0].UpdatedAt.String(), balances[0].CreatedAt.String(), "", + k.Address.String(), "0", "Unknown", "Unknown", "false", + balances[0].UpdatedAt.String(), balances[0].CreatedAt.String(), "None", }, balances[0].ToRow()) } diff --git a/testdata/scripts/keys/eth/create/help.txtar b/testdata/scripts/keys/eth/create/help.txtar new file mode 100644 index 00000000000..d089b220ae4 --- /dev/null +++ b/testdata/scripts/keys/eth/create/help.txtar @@ -0,0 +1,14 @@ +exec chainlink keys eth create --help +cmp stdout out.txt + +-- out.txt -- +NAME: + chainlink keys eth create - Create a key in the node's keystore alongside the existing key; to create an original key, just run the node + +USAGE: + chainlink keys eth create [command options] [arguments...] + +OPTIONS: + --evm-chain-id value, --evmChainID value Chain ID for the key. If left blank, default chain will be used. + --max-gas-price-gwei value, --maxGasPriceGWei value Optional maximum gas price (GWei) for the creating key. (default: 0) + diff --git a/testdata/scripts/keys/eth/list/help.txtar b/testdata/scripts/keys/eth/list/help.txtar new file mode 100644 index 00000000000..d7156fd3e69 --- /dev/null +++ b/testdata/scripts/keys/eth/list/help.txtar @@ -0,0 +1,9 @@ +exec chainlink keys eth list --help +cmp stdout out.txt + +-- out.txt -- +NAME: + chainlink keys eth list - List available Ethereum accounts with their ETH & LINK balances and other metadata + +USAGE: + chainlink keys eth list [arguments...] \ No newline at end of file diff --git a/testdata/scripts/keys/eth/list/unavailable.txtar b/testdata/scripts/keys/eth/list/unavailable.txtar new file mode 100644 index 00000000000..912ad75c1e4 --- /dev/null +++ b/testdata/scripts/keys/eth/list/unavailable.txtar @@ -0,0 +1,38 @@ +# start node +exec sh -c 'eval "echo \"$(cat config.toml.tmpl)\" > config.toml"' +exec chainlink node -c config.toml start -p password -a creds & + +# initialize client +env NODEURL=http://localhost:$PORT +exec curl --retry 10 --retry-max-time 60 --retry-connrefused $NODEURL +exec chainlink --remote-node-url $NODEURL admin login -file creds --bypass-version-check + +exec chainlink --remote-node-url $NODEURL keys eth list +! stdout 'ETH: ' +! stdout 'LINK: ' +! stdout '' +stdout 'ETH: Unknown' +stdout 'LINK: Unknown' + +-- testdb.txt -- +CL_DATABASE_URL +-- testport.txt -- +PORT + +-- password -- +T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ +-- creds -- +notreal@fakeemail.ch +fj293fbBnlQ!f9vNs + +-- config.toml.tmpl -- +[Webserver] +HTTPPort = $PORT + +[[EVM]] +ChainID = '99' + +[[EVM.Nodes]] +Name = 'fake' +WSURL = 'wss://foo.bar/ws' +HTTPURL = 'https://foo.bar' From 6594979d7604e9358566925b377af1e79fcba13d Mon Sep 17 00:00:00 2001 From: krehermann Date: Wed, 10 Jan 2024 09:21:52 -0700 Subject: [PATCH 136/234] Chore/rm eventbroadcaster entirely (#11727) * remove unused eventbroadcaster from legacy evm chains * rm eventbroadcaster completely * update txtar for health cmds --- core/cmd/shell.go | 4 - core/internal/cltest/cltest.go | 5 - core/internal/cltest/simulated_backend.go | 8 +- core/services/chainlink/application.go | 10 +- core/services/pg/event_broadcaster.go | 346 -------------------- core/services/pg/event_broadcaster_test.go | 239 -------------- core/services/pg/mocks/event_broadcaster.go | 169 ---------- core/services/pg/mocks/subscription.go | 93 ------ testdata/scripts/health/default.txtar | 10 - testdata/scripts/health/multi-chain.txtar | 10 - 10 files changed, 3 insertions(+), 891 deletions(-) delete mode 100644 core/services/pg/event_broadcaster.go delete mode 100644 core/services/pg/event_broadcaster_test.go delete mode 100644 core/services/pg/mocks/event_broadcaster.go delete mode 100644 core/services/pg/mocks/subscription.go diff --git a/core/cmd/shell.go b/core/cmd/shell.go index f4386a4faab..547de67210f 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -43,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/periodicbackup" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/versioning" @@ -156,8 +155,6 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G keyStore := keystore.New(db, utils.GetScryptParams(cfg), appLggr, cfg.Database()) mailMon := mailbox.NewMonitor(cfg.AppID().String(), appLggr.Named("Mailbox")) - dbListener := cfg.Database().Listener() - eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), dbListener.MinReconnectInterval(), dbListener.MaxReconnectDuration(), appLggr, cfg.AppID()) loopRegistry := plugins.NewLoopRegistry(appLggr, cfg.Tracing()) mercuryPool := wsrpc.NewPool(appLggr, cache.Config{ @@ -226,7 +223,6 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G SqlxDB: db, KeyStore: keyStore, RelayerChainInteroperators: relayChainInterops, - EventBroadcaster: eventBroadcaster, MailMon: mailMon, Logger: appLggr, AuditLogger: auditLogger, diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 7bb2a76bc7b..dcd16b8e59c 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -311,8 +311,6 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn auditLogger = audit.NoopLogger } - var eventBroadcaster pg.EventBroadcaster = pg.NewNullEventBroadcaster() - url := cfg.Database().URL() db, err := pg.NewConnection(url.String(), cfg.Database().Dialect(), cfg.Database()) require.NoError(t, err) @@ -329,8 +327,6 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn ethClient = dep case webhook.ExternalInitiatorManager: externalInitiatorManager = dep - case pg.EventBroadcaster: - eventBroadcaster = dep default: switch flag { case UseRealExternalInitiatorManager: @@ -415,7 +411,6 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn c := clhttptest.NewTestLocalOnlyHTTPClient() appInstance, err := chainlink.NewApplication(chainlink.ApplicationOpts{ Config: cfg, - EventBroadcaster: eventBroadcaster, MailMon: mailMon, SqlxDB: db, KeyStore: keyStore, diff --git a/core/internal/cltest/simulated_backend.go b/core/internal/cltest/simulated_backend.go index 0aecc7f8324..cde060d7f4a 100644 --- a/core/internal/cltest/simulated_backend.go +++ b/core/internal/cltest/simulated_backend.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/core" - "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -16,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) func NewSimulatedBackend(t *testing.T, alloc core.GenesisAlloc, gasLimit uint32) *backends.SimulatedBackend { @@ -41,9 +39,8 @@ func NewApplicationWithConfigV2OnSimulatedBlockchain( require.Zero(t, evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()).Cmp(testutils.SimulatedChainID)) chainID := big.New(testutils.SimulatedChainID) client := client.NewSimulatedBackendClient(t, backend, testutils.SimulatedChainID) - eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) - flagsAndDeps = append(flagsAndDeps, client, eventBroadcaster, chainID) + flagsAndDeps = append(flagsAndDeps, client, chainID) // app.Stop() will call client.Close on the simulated backend app := NewApplicationWithConfig(t, cfg, flagsAndDeps...) @@ -66,9 +63,8 @@ func NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain( require.Zero(t, evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()).Cmp(testutils.SimulatedChainID)) chainID := big.New(testutils.SimulatedChainID) client := client.NewSimulatedBackendClient(t, backend, testutils.SimulatedChainID) - eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) - flagsAndDeps = append(flagsAndDeps, client, eventBroadcaster, chainID) + flagsAndDeps = append(flagsAndDeps, client, chainID) // app.Stop() will call client.Close on the simulated backend return NewApplicationWithConfigAndKey(t, cfg, flagsAndDeps...) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 71a8b0b33fe..fae938c0db6 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -116,7 +116,6 @@ type Application interface { // in the services package, but the Store has its own package. type ChainlinkApplication struct { relayers *CoreRelayerChainInteroperators - EventBroadcaster pg.EventBroadcaster jobORM job.ORM jobSpawner job.Spawner pipelineORM pipeline.ORM @@ -150,7 +149,6 @@ type ChainlinkApplication struct { type ApplicationOpts struct { Config GeneralConfig Logger logger.Logger - EventBroadcaster pg.EventBroadcaster MailMon *mailbox.Monitor SqlxDB *sqlx.DB KeyStore keystore.Master @@ -178,7 +176,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { db := opts.SqlxDB cfg := opts.Config relayerChainInterops := opts.RelayerChainInteroperators - eventBroadcaster := opts.EventBroadcaster mailMon := opts.MailMon externalInitiatorManager := opts.ExternalInitiatorManager globalLogger := logger.Sugared(opts.Logger) @@ -254,7 +251,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, fmt.Errorf("no evm chains found") } - srvcs = append(srvcs, eventBroadcaster, mailMon) + srvcs = append(srvcs, mailMon) srvcs = append(srvcs, relayerChainInterops.Services()...) promReporter := promreporter.NewPromReporter(db.DB, legacyEVMChains, globalLogger) srvcs = append(srvcs, promReporter) @@ -480,7 +477,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return &ChainlinkApplication{ relayers: opts.RelayerChainInteroperators, - EventBroadcaster: eventBroadcaster, jobORM: jobORM, jobSpawner: jobSpawner, pipelineRunner: pipelineRunner, @@ -809,10 +805,6 @@ func (app *ChainlinkApplication) GetRelayers() RelayerChainInteroperators { return app.relayers } -func (app *ChainlinkApplication) GetEventBroadcaster() pg.EventBroadcaster { - return app.EventBroadcaster -} - func (app *ChainlinkApplication) GetSqlxDB() *sqlx.DB { return app.sqlxDB } diff --git a/core/services/pg/event_broadcaster.go b/core/services/pg/event_broadcaster.go deleted file mode 100644 index a575ab33489..00000000000 --- a/core/services/pg/event_broadcaster.go +++ /dev/null @@ -1,346 +0,0 @@ -package pg - -import ( - "context" - "database/sql" - "net/url" - "sync" - "time" - - "github.com/google/uuid" - "github.com/lib/pq" - "github.com/pkg/errors" - - "github.com/smartcontractkit/chainlink-common/pkg/services" - commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" - - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/static" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -//go:generate mockery --quiet --name EventBroadcaster --output ./mocks/ --case=underscore -//go:generate mockery --quiet --name Subscription --output ./mocks/ --case=underscore - -// EventBroadcaster opaquely manages a collection of Postgres event listeners -// and broadcasts events to subscribers (with an optional payload filter). -type EventBroadcaster interface { - services.Service - Subscribe(channel, payloadFilter string) (Subscription, error) - Notify(channel string, payload string) error -} - -type eventBroadcaster struct { - services.StateMachine - uri string - minReconnectInterval time.Duration - maxReconnectDuration time.Duration - db *sql.DB - listener *pq.Listener - subscriptions map[string]map[Subscription]struct{} - subscriptionsMu sync.RWMutex - chStop chan struct{} - chDone chan struct{} - lggr logger.Logger -} - -var _ EventBroadcaster = (*eventBroadcaster)(nil) - -type Event struct { - Channel string - Payload string -} - -func NewEventBroadcaster(uri url.URL, minReconnectInterval time.Duration, maxReconnectDuration time.Duration, lggr logger.Logger, appID uuid.UUID) *eventBroadcaster { - if minReconnectInterval == time.Duration(0) { - minReconnectInterval = 1 * time.Second - } - if maxReconnectDuration == time.Duration(0) { - maxReconnectDuration = 1 * time.Minute - } - static.SetConsumerName(&uri, "EventBroadcaster", &appID) - return &eventBroadcaster{ - uri: uri.String(), - minReconnectInterval: minReconnectInterval, - maxReconnectDuration: maxReconnectDuration, - subscriptions: make(map[string]map[Subscription]struct{}), - chStop: make(chan struct{}), - chDone: make(chan struct{}), - lggr: lggr.Named("EventBroadcaster"), - } -} - -// Start starts EventBroadcaster. -func (b *eventBroadcaster) Start(context.Context) error { - return b.StartOnce("Postgres event broadcaster", func() (err error) { - // Explicitly using the lib/pq for notifications so we use the postgres driverName - // and NOT pgx. - db, err := sql.Open("postgres", b.uri) - if err != nil { - return err - } - b.db = db - b.listener = pq.NewListener(b.uri, b.minReconnectInterval, b.maxReconnectDuration, func(ev pq.ListenerEventType, err error) { - // sanity check since these can still be called after closing the listener - select { - case <-b.chStop: - return - default: - } - // These are always connection-related events, and the pq library - // automatically handles reconnecting to the DB. Therefore, we do not - // need to terminate, but rather simply log these events for node - // operators' sanity. - switch ev { - case pq.ListenerEventConnected: - b.lggr.Debug("Postgres event broadcaster: connected") - case pq.ListenerEventDisconnected: - b.lggr.Warnw("Postgres event broadcaster: disconnected, trying to reconnect...", "err", err) - case pq.ListenerEventReconnected: - b.lggr.Debug("Postgres event broadcaster: reconnected") - case pq.ListenerEventConnectionAttemptFailed: - b.lggr.Warnw("Postgres event broadcaster: reconnect attempt failed, trying again...", "err", err) - } - }) - - go b.runLoop() - return nil - }) -} - -// Stop permanently destroys the EventBroadcaster. Calling this does not clean -// up any outstanding subscriptions. Subscribers must explicitly call `.Close()` -// or they will leak goroutines. -func (b *eventBroadcaster) Close() error { - return b.StopOnce("Postgres event broadcaster", func() (err error) { - b.subscriptionsMu.RLock() - defer b.subscriptionsMu.RUnlock() - b.subscriptions = nil - - err = services.CloseAll(b.db, b.listener) - close(b.chStop) - <-b.chDone - return err - }) -} - -func (b *eventBroadcaster) Name() string { - return b.lggr.Name() -} - -func (b *eventBroadcaster) HealthReport() map[string]error { - return map[string]error{b.Name(): b.Healthy()} -} - -func (b *eventBroadcaster) runLoop() { - defer close(b.chDone) - for { - select { - case <-b.chStop: - return - - case notification, open := <-b.listener.NotificationChannel(): - if !open { - return - } else if notification == nil { - continue - } - b.lggr.Debugw("Postgres event broadcaster: received notification", - "channel", notification.Channel, - "payload", notification.Extra, - ) - b.broadcast(notification) - } - } -} - -func (b *eventBroadcaster) Notify(channel string, payload string) error { - _, err := b.db.Exec(`SELECT pg_notify($1, $2)`, channel, payload) - return errors.Wrap(err, "Postgres event broadcaster could not notify") -} - -func (b *eventBroadcaster) Subscribe(channel, payloadFilter string) (Subscription, error) { - b.subscriptionsMu.Lock() - defer b.subscriptionsMu.Unlock() - - if _, exists := b.subscriptions[channel]; !exists { - err := b.listener.Listen(channel) - if err != nil { - return nil, errors.Wrap(err, "Postgres event broadcaster could not subscribe") - } - b.subscriptions[channel] = make(map[Subscription]struct{}) - } - - sub := &subscription{ - channel: channel, - payloadFilter: payloadFilter, - eventBroadcaster: b, - queue: utils.NewBoundedQueue[Event](1000), - chEvents: make(chan Event), - chDone: make(chan struct{}), - lggr: logger.Sugared(b.lggr), - } - sub.processQueueWorker = commonutils.NewSleeperTask( - commonutils.SleeperFuncTask(sub.processQueue, "SubscriptionQueueProcessor"), - ) - b.subscriptions[channel][sub] = struct{}{} - return sub, nil -} - -func (b *eventBroadcaster) removeSubscription(sub Subscription) { - b.subscriptionsMu.Lock() - defer b.subscriptionsMu.Unlock() - - // The following conditions can occur on shutdown when .Stop() is called - // before one or more subscriptions' .Close() methods are called - if b.subscriptions == nil { - return - } - subs, exists := b.subscriptions[sub.ChannelName()] - if !exists || subs == nil { - return - } - - delete(b.subscriptions[sub.ChannelName()], sub) - if len(b.subscriptions[sub.ChannelName()]) == 0 { - err := b.listener.Unlisten(sub.ChannelName()) - if err != nil { - b.lggr.Errorw("Postgres event broadcaster: failed to unsubscribe", "err", err) - } - delete(b.subscriptions, sub.ChannelName()) - } -} - -func (b *eventBroadcaster) broadcast(notification *pq.Notification) { - b.subscriptionsMu.RLock() - defer b.subscriptionsMu.RUnlock() - - event := Event{ - Channel: notification.Channel, - Payload: notification.Extra, - } - - var wg sync.WaitGroup - for sub := range b.subscriptions[event.Channel] { - if sub.InterestedIn(event) { - wg.Add(1) - go func(sub Subscription) { - defer wg.Done() - sub.Send(event) - }(sub) - } - } - wg.Wait() -} - -// Subscription represents a subscription to a Postgres event channel -type Subscription interface { - Events() <-chan Event - Close() - - ChannelName() string - InterestedIn(event Event) bool - Send(event Event) -} - -type subscription struct { - channel string - payloadFilter string - eventBroadcaster *eventBroadcaster - queue *utils.BoundedQueue[Event] - processQueueWorker *commonutils.SleeperTask - chEvents chan Event - chDone chan struct{} - lggr logger.SugaredLogger -} - -var _ Subscription = (*subscription)(nil) - -func (sub *subscription) InterestedIn(event Event) bool { - return sub.payloadFilter == event.Payload || sub.payloadFilter == "" -} - -func (sub *subscription) Send(event Event) { - sub.queue.Add(event) - sub.processQueueWorker.WakeUpIfStarted() -} - -const broadcastTimeout = 10 * time.Second - -func (sub *subscription) processQueue() { - deadline := time.Now().Add(broadcastTimeout) - for !sub.queue.Empty() { - event := sub.queue.Take() - select { - case sub.chEvents <- event: - case <-time.After(time.Until(deadline)): - sub.lggr.Warnf("Postgres event broadcaster: SLOW processQueue(), timed out after %s", broadcastTimeout) - return - case <-sub.chDone: - sub.lggr.Debugw("Postgres event broadcaster: request cancelled during processQueue()") - return - } - } -} - -func (sub *subscription) Events() <-chan Event { - return sub.chEvents -} - -func (sub *subscription) ChannelName() string { - return sub.channel -} - -func (sub *subscription) Close() { - sub.eventBroadcaster.removeSubscription(sub) - // Close chDone before stopping the SleeperTask to avoid deadlocks - close(sub.chDone) - err := sub.processQueueWorker.Stop() - if err != nil { - sub.lggr.Errorw("THIS NEVER RETURNS AN ERROR", "err", err) - } - close(sub.chEvents) -} - -// NullEventBroadcaster implements null pattern for event broadcaster -type NullEventBroadcaster struct { - Sub *NullSubscription -} - -func NewNullEventBroadcaster() *NullEventBroadcaster { - sub := &NullSubscription{make(chan (Event))} - return &NullEventBroadcaster{sub} -} - -var _ EventBroadcaster = &NullEventBroadcaster{} - -func (*NullEventBroadcaster) Name() string { return "NullEventBroadcaster" } - -// Start does no-op. -func (*NullEventBroadcaster) Start(context.Context) error { return nil } - -// Close does no-op. -func (*NullEventBroadcaster) Close() error { return nil } - -// Ready does no-op. -func (*NullEventBroadcaster) Ready() error { return nil } - -// HealthReport does no-op -func (*NullEventBroadcaster) HealthReport() map[string]error { return map[string]error{} } - -func (ne *NullEventBroadcaster) Subscribe(channel, payloadFilter string) (Subscription, error) { - return ne.Sub, nil -} -func (*NullEventBroadcaster) Notify(channel string, payload string) error { return nil } - -var _ Subscription = &NullSubscription{} - -type NullSubscription struct { - Ch chan (Event) -} - -func (ns *NullSubscription) Events() <-chan Event { return ns.Ch } -func (ns *NullSubscription) Close() {} -func (ns *NullSubscription) ChannelName() string { return "" } -func (ns *NullSubscription) InterestedIn(event Event) bool { return false } -func (ns *NullSubscription) Send(event Event) {} diff --git a/core/services/pg/event_broadcaster_test.go b/core/services/pg/event_broadcaster_test.go deleted file mode 100644 index e8a4a1086db..00000000000 --- a/core/services/pg/event_broadcaster_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package pg_test - -import ( - "sync" - "testing" - "time" - - "github.com/google/uuid" - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" -) - -func TestEventBroadcaster(t *testing.T) { - config, _ := heavyweight.FullTestDBNoFixturesV2(t, nil) - - eventBroadcaster := pg.NewEventBroadcaster(config.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) - servicetest.Run(t, eventBroadcaster) - - t.Run("doesn't broadcast unrelated events (no payload filter)", func(t *testing.T) { - sub, err := eventBroadcaster.Subscribe("foo", "") - require.NoError(t, err) - defer sub.Close() - - go func() { - err := eventBroadcaster.Notify("bar", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("fooo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("fo", "123") - require.NoError(t, err) - }() - - ch := sub.Events() - gomega.NewWithT(t).Consistently(ch).ShouldNot(gomega.Receive()) - }) - - t.Run("doesn't broadcast unrelated events (with payload filter)", func(t *testing.T) { - sub, err := eventBroadcaster.Subscribe("foo", "123") - require.NoError(t, err) - defer sub.Close() - - go func() { - err := eventBroadcaster.Notify("foo", "asdf") - require.NoError(t, err) - err = eventBroadcaster.Notify("bar", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("fooo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("fo", "123") - require.NoError(t, err) - }() - - ch := sub.Events() - gomega.NewWithT(t).Consistently(ch).ShouldNot(gomega.Receive()) - }) - - t.Run("does broadcast related events (no payload filter)", func(t *testing.T) { - sub, err := eventBroadcaster.Subscribe("foo", "") - require.NoError(t, err) - defer sub.Close() - - go func() { - err := eventBroadcaster.Notify("foo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "aslkdjslkdfj") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "true") - require.NoError(t, err) - }() - - ch := sub.Events() - gomega.NewWithT(t).Eventually(ch).Should(gomega.Receive()) - gomega.NewWithT(t).Eventually(ch).Should(gomega.Receive()) - gomega.NewWithT(t).Eventually(ch).Should(gomega.Receive()) - }) - - t.Run("does broadcast related events (with payload filter)", func(t *testing.T) { - sub, err := eventBroadcaster.Subscribe("foo", "123") - require.NoError(t, err) - defer sub.Close() - - go func() { - err := eventBroadcaster.Notify("foo", "asdf") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "true") - require.NoError(t, err) - }() - - ch := sub.Events() - gomega.NewWithT(t).Eventually(ch).Should(gomega.Receive()) - gomega.NewWithT(t).Eventually(ch).Should(gomega.Receive()) - gomega.NewWithT(t).Consistently(ch).ShouldNot(gomega.Receive()) - }) - - t.Run("broadcasts to the correct subscribers", func(t *testing.T) { - sub1, err := eventBroadcaster.Subscribe("foo", "") - require.NoError(t, err) - defer sub1.Close() - - sub2, err := eventBroadcaster.Subscribe("foo", "123") - require.NoError(t, err) - defer sub2.Close() - - sub3, err := eventBroadcaster.Subscribe("bar", "") - require.NoError(t, err) - defer sub3.Close() - - sub4, err := eventBroadcaster.Subscribe("bar", "asdf") - require.NoError(t, err) - defer sub4.Close() - - var wg sync.WaitGroup - wg.Add(5) - - recv := func(ch <-chan pg.Event) pg.Event { - select { - case e := <-ch: - return e - case <-time.After(5 * time.Second): - t.Fatal("did not receive") - } - return pg.Event{} - } - - go func() { - defer wg.Done() - err := eventBroadcaster.Notify("foo", "asdf") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("foo", "true") - require.NoError(t, err) - - err = eventBroadcaster.Notify("bar", "asdf") - require.NoError(t, err) - err = eventBroadcaster.Notify("bar", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("bar", "123") - require.NoError(t, err) - err = eventBroadcaster.Notify("bar", "true") - require.NoError(t, err) - }() - - go func() { - defer wg.Done() - e := recv(sub1.Events()) - require.Equal(t, "foo", e.Channel) - require.Equal(t, "asdf", e.Payload) - - e = recv(sub1.Events()) - require.Equal(t, "foo", e.Channel) - require.Equal(t, "123", e.Payload) - - e = recv(sub1.Events()) - require.Equal(t, "foo", e.Channel) - require.Equal(t, "123", e.Payload) - - e = recv(sub1.Events()) - require.Equal(t, "foo", e.Channel) - require.Equal(t, "true", e.Payload) - - gomega.NewWithT(t).Consistently(sub1.Events()).ShouldNot(gomega.Receive()) - }() - - go func() { - defer wg.Done() - e := recv(sub2.Events()) - require.Equal(t, "foo", e.Channel) - require.Equal(t, "123", e.Payload) - - e = recv(sub2.Events()) - require.Equal(t, "foo", e.Channel) - require.Equal(t, "123", e.Payload) - - gomega.NewWithT(t).Consistently(sub2.Events()).ShouldNot(gomega.Receive()) - }() - - go func() { - defer wg.Done() - e := recv(sub3.Events()) - require.Equal(t, "bar", e.Channel) - require.Equal(t, "asdf", e.Payload) - - e = recv(sub3.Events()) - require.Equal(t, "bar", e.Channel) - require.Equal(t, "123", e.Payload) - - e = recv(sub3.Events()) - require.Equal(t, "bar", e.Channel) - require.Equal(t, "123", e.Payload) - - e = recv(sub3.Events()) - require.Equal(t, "bar", e.Channel) - require.Equal(t, "true", e.Payload) - - gomega.NewWithT(t).Consistently(sub3.Events()).ShouldNot(gomega.Receive()) - }() - - go func() { - defer wg.Done() - e := recv(sub4.Events()) - require.Equal(t, "bar", e.Channel) - require.Equal(t, "asdf", e.Payload) - - gomega.NewWithT(t).Consistently(sub4.Events()).ShouldNot(gomega.Receive()) - }() - - wg.Wait() - }) - - t.Run("closes events channel on subscription close", func(t *testing.T) { - sub, err := eventBroadcaster.Subscribe("foo", "") - require.NoError(t, err) - - chEvents := sub.Events() - - sub.Close() - - select { - case _, ok := <-chEvents: - if ok { - t.Fatal("expected chEvents to be closed") - } - default: - t.Fatal("expected chEvents to not block") - } - }) -} diff --git a/core/services/pg/mocks/event_broadcaster.go b/core/services/pg/mocks/event_broadcaster.go deleted file mode 100644 index 63f06db494b..00000000000 --- a/core/services/pg/mocks/event_broadcaster.go +++ /dev/null @@ -1,169 +0,0 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" - mock "github.com/stretchr/testify/mock" -) - -// EventBroadcaster is an autogenerated mock type for the EventBroadcaster type -type EventBroadcaster struct { - mock.Mock -} - -// Close provides a mock function with given fields: -func (_m *EventBroadcaster) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// HealthReport provides a mock function with given fields: -func (_m *EventBroadcaster) HealthReport() map[string]error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for HealthReport") - } - - var r0 map[string]error - if rf, ok := ret.Get(0).(func() map[string]error); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]error) - } - } - - return r0 -} - -// Name provides a mock function with given fields: -func (_m *EventBroadcaster) Name() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Name") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// Notify provides a mock function with given fields: channel, payload -func (_m *EventBroadcaster) Notify(channel string, payload string) error { - ret := _m.Called(channel, payload) - - if len(ret) == 0 { - panic("no return value specified for Notify") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(channel, payload) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Ready provides a mock function with given fields: -func (_m *EventBroadcaster) Ready() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Ready") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Start provides a mock function with given fields: _a0 -func (_m *EventBroadcaster) Start(_a0 context.Context) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for Start") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Subscribe provides a mock function with given fields: channel, payloadFilter -func (_m *EventBroadcaster) Subscribe(channel string, payloadFilter string) (pg.Subscription, error) { - ret := _m.Called(channel, payloadFilter) - - if len(ret) == 0 { - panic("no return value specified for Subscribe") - } - - var r0 pg.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(string, string) (pg.Subscription, error)); ok { - return rf(channel, payloadFilter) - } - if rf, ok := ret.Get(0).(func(string, string) pg.Subscription); ok { - r0 = rf(channel, payloadFilter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(pg.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(channel, payloadFilter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewEventBroadcaster creates a new instance of EventBroadcaster. 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 NewEventBroadcaster(t interface { - mock.TestingT - Cleanup(func()) -}) *EventBroadcaster { - mock := &EventBroadcaster{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/services/pg/mocks/subscription.go b/core/services/pg/mocks/subscription.go deleted file mode 100644 index fcd194004de..00000000000 --- a/core/services/pg/mocks/subscription.go +++ /dev/null @@ -1,93 +0,0 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. - -package mocks - -import ( - pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" - mock "github.com/stretchr/testify/mock" -) - -// Subscription is an autogenerated mock type for the Subscription type -type Subscription struct { - mock.Mock -} - -// ChannelName provides a mock function with given fields: -func (_m *Subscription) ChannelName() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ChannelName") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// Close provides a mock function with given fields: -func (_m *Subscription) Close() { - _m.Called() -} - -// Events provides a mock function with given fields: -func (_m *Subscription) Events() <-chan pg.Event { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Events") - } - - var r0 <-chan pg.Event - if rf, ok := ret.Get(0).(func() <-chan pg.Event); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan pg.Event) - } - } - - return r0 -} - -// InterestedIn provides a mock function with given fields: event -func (_m *Subscription) InterestedIn(event pg.Event) bool { - ret := _m.Called(event) - - if len(ret) == 0 { - panic("no return value specified for InterestedIn") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(pg.Event) bool); ok { - r0 = rf(event) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// Send provides a mock function with given fields: event -func (_m *Subscription) Send(event pg.Event) { - _m.Called(event) -} - -// NewSubscription creates a new instance of Subscription. 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 NewSubscription(t interface { - mock.TestingT - Cleanup(func()) -}) *Subscription { - mock := &Subscription{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/testdata/scripts/health/default.txtar b/testdata/scripts/health/default.txtar index c80faadfd13..15be9da1fe6 100644 --- a/testdata/scripts/health/default.txtar +++ b/testdata/scripts/health/default.txtar @@ -31,7 +31,6 @@ fj293fbBnlQ!f9vNs HTTPPort = $PORT -- out.txt -- --EventBroadcaster -JobSpawner -Mailbox.Monitor -Mercury.WSRPCPool @@ -44,15 +43,6 @@ HTTPPort = $PORT -- out.json -- { "data": [ - { - "type": "checks", - "id": "EventBroadcaster", - "attributes": { - "name": "EventBroadcaster", - "status": "passing", - "output": "" - } - }, { "type": "checks", "id": "JobSpawner", diff --git a/testdata/scripts/health/multi-chain.txtar b/testdata/scripts/health/multi-chain.txtar index 72c5fd8e3f6..6a6adb895cb 100644 --- a/testdata/scripts/health/multi-chain.txtar +++ b/testdata/scripts/health/multi-chain.txtar @@ -74,7 +74,6 @@ URL = 'http://stark.node' -EVM.1.Txm.Broadcaster -EVM.1.Txm.Confirmer -EVM.1.Txm.WrappedEvmEstimator --EventBroadcaster -JobSpawner -Mailbox.Monitor -Mercury.WSRPCPool @@ -206,15 +205,6 @@ URL = 'http://stark.node' "output": "" } }, - { - "type": "checks", - "id": "EventBroadcaster", - "attributes": { - "name": "EventBroadcaster", - "status": "passing", - "output": "" - } - }, { "type": "checks", "id": "JobSpawner", From ba841a51666daeb8252d034c5f64da2f637f5f7d Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Wed, 10 Jan 2024 13:18:10 -0500 Subject: [PATCH 137/234] Extract core/store/models (#11626) * Extract URL * tidy * Extract duration tests * Generate * update imports * Extract duration to common * tidy * fix imports * tidy * remove unused import * Remove unused mock file * fix imports * Use commonconfig Duration * Use commonconfig * Use commonconfig * Use MustNewDuration * use commonconfig * fix imports * fix imports * fmt * Use common url * fix imports --- core/chains/evm/config/config_test.go | 6 +- core/chains/evm/config/toml/config.go | 31 +- .../evm/headtracker/head_broadcaster_test.go | 4 +- .../evm/headtracker/head_listener_test.go | 6 +- .../evm/headtracker/head_tracker_test.go | 6 +- core/chains/evm/txmgr/evm_tx_store_test.go | 4 +- core/chains/evm/txmgr/resender_test.go | 6 +- core/cmd/evm_node_commands_test.go | 10 +- core/cmd/evm_transaction_commands_test.go | 6 +- core/cmd/jobs_commands_test.go | 5 +- core/cmd/shell_local_test.go | 9 +- core/cmd/shell_remote_test.go | 4 +- core/config/audit_logger_config.go | 3 +- core/config/auto_pprof_config.go | 8 +- core/config/docs/docs_test.go | 4 +- core/config/job_pipeline_config.go | 4 +- core/config/p2p_v2_config.go | 8 +- core/config/toml/types.go | 103 +++--- core/config/toml/types_test.go | 7 +- core/config/web_config.go | 14 +- core/internal/features/features_test.go | 13 +- .../features/ocr2/features_ocr2_test.go | 7 +- .../testutils/configtest/general_config.go | 19 +- core/logger/audit/audit_logger.go | 4 +- core/logger/audit/audit_logger_test.go | 7 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/chainlink/config.go | 2 +- .../services/chainlink/config_audit_logger.go | 3 +- core/services/chainlink/config_auto_pprof.go | 12 +- core/services/chainlink/config_general.go | 10 +- .../services/chainlink/config_job_pipeline.go | 4 +- .../chainlink/config_job_pipeline_test.go | 4 +- core/services/chainlink/config_p2p.go | 14 +- core/services/chainlink/config_test.go | 117 +++---- core/services/chainlink/config_web_server.go | 20 +- .../chainlink/config_web_server_test.go | 8 +- .../relayer_chain_interoperators_test.go | 31 +- core/services/feeds/config.go | 4 +- core/services/feeds/mocks/config.go | 292 ------------------ core/services/feeds/service_test.go | 86 +++--- core/services/fluxmonitorv2/config.go | 4 +- .../fluxmonitorv2/integrations_test.go | 21 +- core/services/fluxmonitorv2/validate.go | 4 +- core/services/fluxmonitorv2/validate_test.go | 6 +- .../job/job_pipeline_orm_integration_test.go | 3 +- core/services/job/runner_integration_test.go | 9 +- core/services/keeper/integration_test.go | 16 +- core/services/nurse.go | 8 +- core/services/nurse_test.go | 20 +- core/services/ocr/validate_test.go | 4 +- .../v1/internal/testutils.go | 7 +- .../ocr2/plugins/mercury/helpers_test.go | 6 +- .../plugins/ocr2keeper/integration_test.go | 5 +- .../internal/ocr2vrf_integration_test.go | 10 +- core/services/ocr2/validate/validate_test.go | 4 +- core/services/ocrcommon/peer_wrapper_test.go | 6 +- core/services/pg/locked_db_test.go | 6 +- core/services/pipeline/common.go | 4 +- core/services/pipeline/mocks/config.go | 10 +- core/services/pipeline/task.bridge_test.go | 5 +- core/services/telemetry/manager_test.go | 4 +- core/services/vrf/v1/integration_test.go | 4 +- core/services/vrf/v2/bhs_feeder_test.go | 8 +- .../vrf/v2/integration_helpers_test.go | 30 +- .../vrf/v2/integration_v2_plus_test.go | 4 +- core/services/vrf/v2/integration_v2_test.go | 12 +- core/sessions/ldapauth/helpers_test.go | 18 +- core/sessions/localauth/reaper.go | 6 +- core/sessions/localauth/reaper_test.go | 19 +- core/store/models/common.go | 167 ---------- core/store/models/common_test.go | 64 ---- core/store/models/secrets.go | 12 +- core/web/evm_transfer_controller_test.go | 3 +- core/web/pipeline_runs_controller_test.go | 10 +- core/web/presenters/job.go | 17 +- core/web/resolver/node_test.go | 6 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 4 +- integration-tests/go.sum | 8 +- integration-tests/load/functions/config.go | 46 +-- integration-tests/load/ocr/config.go | 28 +- integration-tests/load/vrfv2/config.go | 10 +- integration-tests/load/vrfv2plus/config.go | 10 +- integration-tests/smoke/automation_test.go | 11 +- integration-tests/smoke/keeper_test.go | 5 +- integration-tests/types/config/node/core.go | 15 +- .../universal/log_poller/config.go | 14 +- .../universal/log_poller/helpers.go | 19 +- integration-tests/utils/common.go | 6 +- 91 files changed, 564 insertions(+), 1071 deletions(-) delete mode 100644 core/services/feeds/mocks/config.go diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index 0127328239a..a8cbf6667f3 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + configurl "github.com/smartcontractkit/chainlink-common/pkg/config" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -21,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestChainScopedConfig(t *testing.T) { @@ -383,8 +383,8 @@ func Test_chainScopedConfig_Validate(t *testing.T) { c.EVM[0] = &toml.EVMConfig{ChainID: chainID, Enabled: ptr(true), Chain: toml.Defaults(chainID, chains...), Nodes: toml.EVMNodes{{ Name: ptr("fake"), - WSURL: models.MustParseURL("wss://foo.test/ws"), - HTTPURL: models.MustParseURL("http://foo.test"), + WSURL: configurl.MustParseURL("wss://foo.test/ws"), + HTTPURL: configurl.MustParseURL("http://foo.test"), }}} }) } diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 292db56f817..e5b774edd17 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -22,7 +22,6 @@ import ( "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/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) type HasEVMConfigs interface { @@ -351,12 +350,12 @@ type Chain struct { FlagsContractAddress *ethkey.EIP55Address LinkContractAddress *ethkey.EIP55Address LogBackfillBatchSize *uint32 - LogPollInterval *models.Duration + LogPollInterval *commonconfig.Duration LogKeepBlocksDepth *uint32 MinIncomingConfirmations *uint32 MinContractPayment *commonassets.Link NonceAutoSync *bool - NoNewHeadsThreshold *models.Duration + NoNewHeadsThreshold *commonconfig.Duration OperatorFactoryAddress *ethkey.EIP55Address RPCDefaultBatchSize *uint32 RPCBlockQueryDelay *uint16 @@ -404,9 +403,9 @@ type Transactions struct { ForwardersEnabled *bool MaxInFlight *uint32 MaxQueued *uint32 - ReaperInterval *models.Duration - ReaperThreshold *models.Duration - ResendAfterThreshold *models.Duration + ReaperInterval *commonconfig.Duration + ReaperThreshold *commonconfig.Duration + ResendAfterThreshold *commonconfig.Duration } func (t *Transactions) setFrom(f *Transactions) { @@ -669,7 +668,7 @@ func (e *KeySpecificGasEstimator) setFrom(f *KeySpecificGasEstimator) { type HeadTracker struct { HistoryDepth *uint32 MaxBufferSize *uint32 - SamplingInterval *models.Duration + SamplingInterval *commonconfig.Duration } func (t *HeadTracker) setFrom(f *HeadTracker) { @@ -686,10 +685,10 @@ func (t *HeadTracker) setFrom(f *HeadTracker) { type NodePool struct { PollFailureThreshold *uint32 - PollInterval *models.Duration + PollInterval *commonconfig.Duration SelectionMode *string SyncThreshold *uint32 - LeaseDuration *models.Duration + LeaseDuration *commonconfig.Duration } func (p *NodePool) setFrom(f *NodePool) { @@ -712,11 +711,11 @@ func (p *NodePool) setFrom(f *NodePool) { type OCR struct { ContractConfirmations *uint16 - ContractTransmitterTransmitTimeout *models.Duration - DatabaseTimeout *models.Duration - DeltaCOverride *models.Duration - DeltaCJitterOverride *models.Duration - ObservationGracePeriod *models.Duration + ContractTransmitterTransmitTimeout *commonconfig.Duration + DatabaseTimeout *commonconfig.Duration + DeltaCOverride *commonconfig.Duration + DeltaCJitterOverride *commonconfig.Duration + ObservationGracePeriod *commonconfig.Duration } func (o *OCR) setFrom(f *OCR) { @@ -742,8 +741,8 @@ func (o *OCR) setFrom(f *OCR) { type Node struct { Name *string - WSURL *models.URL - HTTPURL *models.URL + WSURL *commonconfig.URL + HTTPURL *commonconfig.URL SendOnly *bool Order *int32 } diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index eb4daa6fde4..dcbb9bd0396 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" @@ -26,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func waitHeadBroadcasterToStart(t *testing.T, hb types.HeadBroadcaster) { @@ -46,7 +46,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { g := gomega.NewWithT(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].HeadTracker.SamplingInterval = &models.Duration{} + c.EVM[0].HeadTracker.SamplingInterval = &commonconfig.Duration{} }) evmCfg := evmtest.NewChainScopedConfig(t, cfg) db := pgtest.NewSqlxDB(t) diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 8bb761bdfaa..3ba9c0863da 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" @@ -21,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func Test_HeadListener_HappyPath(t *testing.T) { @@ -39,7 +39,7 @@ func Test_HeadListener_HappyPath(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { // no need to test head timeouts here - c.EVM[0].NoNewHeadsThreshold = &models.Duration{} + c.EVM[0].NoNewHeadsThreshold = &commonconfig.Duration{} }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) chStop := make(chan struct{}) @@ -100,7 +100,7 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].NoNewHeadsThreshold = models.MustNewDuration(time.Second) + c.EVM[0].NoNewHeadsThreshold = commonconfig.MustNewDuration(time.Second) }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) chStop := make(chan struct{}) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 38cbe1fbfe4..22e931d6d0f 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -20,6 +20,7 @@ import ( "github.com/jmoiron/sqlx" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" @@ -38,7 +39,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func firstHead(t *testing.T, db *sqlx.DB) (h evmtypes.Head) { @@ -426,7 +426,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) c.EVM[0].FinalityDepth = ptr[uint32](50) // Need to set the buffer to something large since we inject a lot of heads at once and otherwise they will be dropped c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) - c.EVM[0].HeadTracker.SamplingInterval = models.MustNewDuration(2500 * time.Millisecond) + c.EVM[0].HeadTracker.SamplingInterval = commonconfig.MustNewDuration(2500 * time.Millisecond) }) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -554,7 +554,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T c.EVM[0].FinalityDepth = ptr[uint32](50) // Need to set the buffer to something large since we inject a lot of heads at once and otherwise they will be dropped c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) - c.EVM[0].HeadTracker.SamplingInterval = models.MustNewDuration(0) + c.EVM[0].HeadTracker.SamplingInterval = commonconfig.MustNewDuration(0) }) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 1a1ee1bcd3a..b5da5527448 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -23,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -1379,7 +1379,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { etx := mustInsertInProgressEthTxWithAttempt(t, txStore, nonce, fromAddress) require.Len(t, etx.TxAttempts, 1) - zero := models.MustNewDuration(time.Duration(0)) + zero := commonconfig.MustNewDuration(time.Duration(0)) evmCfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].Chain.Transactions.ReaperInterval = zero c.EVM[0].Chain.Transactions.ReaperThreshold = zero diff --git a/core/chains/evm/txmgr/resender_test.go b/core/chains/evm/txmgr/resender_test.go index f6b0e3e29c7..fd3d1745010 100644 --- a/core/chains/evm/txmgr/resender_test.go +++ b/core/chains/evm/txmgr/resender_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" @@ -23,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func Test_EthResender_resendUnconfirmed(t *testing.T) { @@ -106,7 +106,7 @@ func Test_EthResender_alertUnconfirmed(t *testing.T) { ethKeyStore := cltest.NewKeyStore(t, db, logCfg).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) // Set this to the smallest non-zero value possible for the attempt to be eligible for resend - delay := models.MustNewDuration(1 * time.Nanosecond) + delay := commonconfig.MustNewDuration(1 * time.Nanosecond) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0] = &toml.EVMConfig{ Chain: toml.Defaults(ubig.New(big.NewInt(0)), &toml.Chain{ @@ -144,7 +144,7 @@ func Test_EthResender_Start(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { // This can be anything as long as it isn't zero - c.EVM[0].Transactions.ResendAfterThreshold = models.MustNewDuration(42 * time.Hour) + c.EVM[0].Transactions.ResendAfterThreshold = commonconfig.MustNewDuration(42 * time.Hour) // Set batch size low to test batching c.EVM[0].RPCDefaultBatchSize = ptr[uint32](1) }) diff --git a/core/cmd/evm_node_commands_test.go b/core/cmd/evm_node_commands_test.go index 869ef1b9b3e..dae950fce01 100644 --- a/core/cmd/evm_node_commands_test.go +++ b/core/cmd/evm_node_commands_test.go @@ -9,11 +9,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func assertTableRenders(t *testing.T, r *cltest.RendererMock) { @@ -31,15 +31,15 @@ func TestShell_IndexEVMNodes(t *testing.T) { chainID := newRandChainID() node1 := evmcfg.Node{ Name: ptr("Test node 1"), - WSURL: models.MustParseURL("ws://localhost:8546"), - HTTPURL: models.MustParseURL("http://localhost:8546"), + WSURL: commonconfig.MustParseURL("ws://localhost:8546"), + HTTPURL: commonconfig.MustParseURL("http://localhost:8546"), SendOnly: ptr(false), Order: ptr(int32(15)), } node2 := evmcfg.Node{ Name: ptr("Test node 2"), - WSURL: models.MustParseURL("ws://localhost:8547"), - HTTPURL: models.MustParseURL("http://localhost:8547"), + WSURL: commonconfig.MustParseURL("ws://localhost:8547"), + HTTPURL: commonconfig.MustParseURL("http://localhost:8547"), SendOnly: ptr(false), Order: ptr(int32(36)), } diff --git a/core/cmd/evm_transaction_commands_test.go b/core/cmd/evm_transaction_commands_test.go index 5c80a7d74a0..588e65027f2 100644 --- a/core/cmd/evm_transaction_commands_test.go +++ b/core/cmd/evm_transaction_commands_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestShell_IndexTransactions(t *testing.T) { @@ -151,7 +151,7 @@ func TestShell_SendEther_From_Txm(t *testing.T) { // NOTE: FallbackPollInterval is used in this test to quickly create TxAttempts // Testing triggers requires committing transactions and does not work with transactional tests - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(time.Second) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(time.Second) }, withKey(), withMocks(ethMock, key), @@ -217,7 +217,7 @@ func TestShell_SendEther_From_Txm_WEI(t *testing.T) { // NOTE: FallbackPollInterval is used in this test to quickly create TxAttempts // Testing triggers requires committing transactions and does not work with transactional tests - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(time.Second) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(time.Second) }, withKey(), withMocks(ethMock, key), diff --git a/core/cmd/jobs_commands_test.go b/core/cmd/jobs_commands_test.go index 5d9b3cb3f1a..75e95db84ca 100644 --- a/core/cmd/jobs_commands_test.go +++ b/core/cmd/jobs_commands_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -367,7 +368,7 @@ func TestShell_CreateJobV2(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(100 * time.Millisecond) c.OCR.Enabled = ptr(true) c.P2P.V2.Enabled = ptr(true) c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))} @@ -405,7 +406,7 @@ func TestShell_DeleteJob(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(100 * time.Millisecond) c.EVM[0].Enabled = ptr(true) c.EVM[0].NonceAutoSync = ptr(false) c.EVM[0].BalanceMonitor.Enabled = ptr(false) diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 5bdcbb180ef..72c2f2b5bbd 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/client" @@ -73,8 +74,8 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { s.Password.Keystore = models.NewSecret("dummy") c.EVM[0].Nodes[0].Name = ptr("fake") - c.EVM[0].Nodes[0].HTTPURL = models.MustParseURL("http://fake.com") - c.EVM[0].Nodes[0].WSURL = models.MustParseURL("WSS://fake.com/ws") + c.EVM[0].Nodes[0].HTTPURL = commonconfig.MustParseURL("http://fake.com") + c.EVM[0].Nodes[0].WSURL = commonconfig.MustParseURL("WSS://fake.com/ws") // seems to be needed for config validate c.Insecure.OCRDevelopmentMode = nil }) @@ -166,8 +167,8 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { s.Password.Keystore = models.NewSecret("16charlengthp4SsW0rD1!@#_") c.EVM[0].Nodes[0].Name = ptr("fake") - c.EVM[0].Nodes[0].WSURL = models.MustParseURL("WSS://fake.com/ws") - c.EVM[0].Nodes[0].HTTPURL = models.MustParseURL("http://fake.com") + c.EVM[0].Nodes[0].WSURL = commonconfig.MustParseURL("WSS://fake.com/ws") + c.EVM[0].Nodes[0].HTTPURL = commonconfig.MustParseURL("http://fake.com") // seems to be needed for config validate c.Insecure.OCRDevelopmentMode = nil }) diff --git a/core/cmd/shell_remote_test.go b/core/cmd/shell_remote_test.go index 6c8b46eda4c..ed675d8ee9e 100644 --- a/core/cmd/shell_remote_test.go +++ b/core/cmd/shell_remote_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -33,7 +34,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/static" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/web" ) @@ -60,7 +60,7 @@ func startNewApplicationV2(t *testing.T, overrideFn func(c *chainlink.Config, s } config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(30 * time.Millisecond) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(30 * time.Millisecond) f := false c.EVM[0].Enabled = &f c.P2P.V2.Enabled = &f diff --git a/core/config/audit_logger_config.go b/core/config/audit_logger_config.go index e5078897722..b83ca5b00d0 100644 --- a/core/config/audit_logger_config.go +++ b/core/config/audit_logger_config.go @@ -1,12 +1,13 @@ package config import ( + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) type AuditLogger interface { Enabled() bool - ForwardToUrl() (models.URL, error) + ForwardToUrl() (commonconfig.URL, error) Environment() string JsonWrapperKey() string Headers() (models.ServiceHeaders, error) diff --git a/core/config/auto_pprof_config.go b/core/config/auto_pprof_config.go index f87777cbbd0..c69ff2c86af 100644 --- a/core/config/auto_pprof_config.go +++ b/core/config/auto_pprof_config.go @@ -1,7 +1,7 @@ package config import ( - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -9,13 +9,13 @@ type AutoPprof interface { BlockProfileRate() int CPUProfileRate() int Enabled() bool - GatherDuration() models.Duration - GatherTraceDuration() models.Duration + GatherDuration() commonconfig.Duration + GatherTraceDuration() commonconfig.Duration GoroutineThreshold() int MaxProfileSize() utils.FileSize MemProfileRate() int MemThreshold() utils.FileSize MutexProfileFraction() int - PollInterval() models.Duration + PollInterval() commonconfig.Duration ProfileRoot() string } diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 30688c38879..508bd62e338 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -15,13 +15,13 @@ import ( stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink-common/pkg/config" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/docs" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/cfgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestDoc(t *testing.T) { @@ -42,7 +42,7 @@ func TestDoc(t *testing.T) { // and its only use is to signal to NOPs that these fields are no longer allowed emptyString := "" c.TelemetryIngress.ServerPubKey = &emptyString - c.TelemetryIngress.URL = new(models.URL) + c.TelemetryIngress.URL = new(commonconfig.URL) cfgtest.AssertFieldsNotNil(t, c) diff --git a/core/config/job_pipeline_config.go b/core/config/job_pipeline_config.go index 65010fc48c7..d4a01dbed03 100644 --- a/core/config/job_pipeline_config.go +++ b/core/config/job_pipeline_config.go @@ -3,12 +3,12 @@ package config import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) type JobPipeline interface { DefaultHTTPLimit() int64 - DefaultHTTPTimeout() models.Duration + DefaultHTTPTimeout() commonconfig.Duration MaxRunDuration() time.Duration MaxSuccessfulRuns() uint64 ReaperInterval() time.Duration diff --git a/core/config/p2p_v2_config.go b/core/config/p2p_v2_config.go index 7b4a3c05fc4..ff640786ed2 100644 --- a/core/config/p2p_v2_config.go +++ b/core/config/p2p_v2_config.go @@ -1,16 +1,16 @@ package config import ( - ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" ) type V2 interface { Enabled() bool AnnounceAddresses() []string DefaultBootstrappers() (locators []ocrcommontypes.BootstrapperLocator) - DeltaDial() models.Duration - DeltaReconcile() models.Duration + DeltaDial() commonconfig.Duration + DeltaReconcile() commonconfig.Duration ListenAddresses() []string } diff --git a/core/config/toml/types.go b/core/config/toml/types.go index 93fcef32611..eae5b2f533a 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -14,6 +14,7 @@ import ( ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/parse" @@ -34,7 +35,7 @@ type Core struct { AppID uuid.UUID `toml:"-"` // random or test InsecureFastScrypt *bool RootDir *string - ShutdownGracePeriod *models.Duration + ShutdownGracePeriod *commonconfig.Duration Feature Feature `toml:",omitempty"` Database Database `toml:",omitempty"` @@ -310,9 +311,9 @@ func (f *Feature) setFrom(f2 *Feature) { } type Database struct { - DefaultIdleInTxSessionTimeout *models.Duration - DefaultLockTimeout *models.Duration - DefaultQueryTimeout *models.Duration + DefaultIdleInTxSessionTimeout *commonconfig.Duration + DefaultLockTimeout *commonconfig.Duration + DefaultQueryTimeout *commonconfig.Duration Dialect dialects.DialectName `toml:"-"` LogQueries *bool MaxIdleConns *int64 @@ -353,9 +354,9 @@ func (d *Database) setFrom(f *Database) { } type DatabaseListener struct { - MaxReconnectDuration *models.Duration - MinReconnectInterval *models.Duration - FallbackPollInterval *models.Duration + MaxReconnectDuration *commonconfig.Duration + MinReconnectInterval *commonconfig.Duration + FallbackPollInterval *commonconfig.Duration } func (d *DatabaseListener) setFrom(f *DatabaseListener) { @@ -372,8 +373,8 @@ func (d *DatabaseListener) setFrom(f *DatabaseListener) { type DatabaseLock struct { Enabled *bool - LeaseDuration *models.Duration - LeaseRefreshInterval *models.Duration + LeaseDuration *commonconfig.Duration + LeaseRefreshInterval *commonconfig.Duration } func (l *DatabaseLock) Mode() string { @@ -408,7 +409,7 @@ func (l *DatabaseLock) setFrom(f *DatabaseLock) { // Note: url is stored in Secrets.DatabaseBackupURL type DatabaseBackup struct { Dir *string - Frequency *models.Duration + Frequency *commonconfig.Duration Mode *config.DatabaseBackupMode OnVersionUpgrade *bool } @@ -433,19 +434,19 @@ type TelemetryIngress struct { Logging *bool BufferSize *uint16 MaxBatchSize *uint16 - SendInterval *models.Duration - SendTimeout *models.Duration + SendInterval *commonconfig.Duration + SendTimeout *commonconfig.Duration UseBatchSend *bool Endpoints []TelemetryIngressEndpoint `toml:",omitempty"` - URL *models.URL `toml:",omitempty"` // Deprecated: Use TelemetryIngressEndpoint.URL instead, this field will be removed in future versions - ServerPubKey *string `toml:",omitempty"` // Deprecated: Use TelemetryIngressEndpoint.ServerPubKey instead, this field will be removed in future versions + URL *commonconfig.URL `toml:",omitempty"` // Deprecated: Use TelemetryIngressEndpoint.URL instead, this field will be removed in future versions + ServerPubKey *string `toml:",omitempty"` // Deprecated: Use TelemetryIngressEndpoint.ServerPubKey instead, this field will be removed in future versions } type TelemetryIngressEndpoint struct { Network *string ChainID *string - URL *models.URL + URL *commonconfig.URL ServerPubKey *string } @@ -498,7 +499,7 @@ func (t *TelemetryIngress) ValidateConfig() (err error) { type AuditLogger struct { Enabled *bool - ForwardToUrl *models.URL + ForwardToUrl *commonconfig.URL JsonWrapperKey *string Headers *[]models.ServiceHeader } @@ -597,15 +598,15 @@ func (l *LogFile) setFrom(f *LogFile) { type WebServer struct { AuthenticationMethod *string AllowOrigins *string - BridgeResponseURL *models.URL - BridgeCacheTTL *models.Duration - HTTPWriteTimeout *models.Duration + BridgeResponseURL *commonconfig.URL + BridgeCacheTTL *commonconfig.Duration + HTTPWriteTimeout *commonconfig.Duration HTTPPort *uint16 SecureCookies *bool - SessionTimeout *models.Duration - SessionReaperExpiration *models.Duration + SessionTimeout *commonconfig.Duration + SessionReaperExpiration *commonconfig.Duration HTTPMaxSize *utils.FileSize - StartTimeout *models.Duration + StartTimeout *commonconfig.Duration ListenIP *net.IP LDAP WebServerLDAP `toml:",omitempty"` @@ -708,9 +709,9 @@ func (w *WebServerMFA) setFrom(f *WebServerMFA) { type WebServerRateLimit struct { Authenticated *int64 - AuthenticatedPeriod *models.Duration + AuthenticatedPeriod *commonconfig.Duration Unauthenticated *int64 - UnauthenticatedPeriod *models.Duration + UnauthenticatedPeriod *commonconfig.Duration } func (w *WebServerRateLimit) setFrom(f *WebServerRateLimit) { @@ -760,8 +761,8 @@ func (w *WebServerTLS) setFrom(f *WebServerTLS) { type WebServerLDAP struct { ServerTLS *bool - SessionTimeout *models.Duration - QueryTimeout *models.Duration + SessionTimeout *commonconfig.Duration + QueryTimeout *commonconfig.Duration BaseUserAttr *string BaseDN *string UsersDN *string @@ -773,9 +774,9 @@ type WebServerLDAP struct { RunUserGroupCN *string ReadUserGroupCN *string UserApiTokenEnabled *bool - UserAPITokenDuration *models.Duration - UpstreamSyncInterval *models.Duration - UpstreamSyncRateLimit *models.Duration + UserAPITokenDuration *commonconfig.Duration + UpstreamSyncInterval *commonconfig.Duration + UpstreamSyncRateLimit *commonconfig.Duration } func (w *WebServerLDAP) setFrom(f *WebServerLDAP) { @@ -864,10 +865,10 @@ func (w *WebServerSecrets) SetFrom(f *WebServerSecrets) error { type JobPipeline struct { ExternalInitiatorsEnabled *bool - MaxRunDuration *models.Duration + MaxRunDuration *commonconfig.Duration MaxSuccessfulRuns *uint64 - ReaperInterval *models.Duration - ReaperThreshold *models.Duration + ReaperInterval *commonconfig.Duration + ReaperThreshold *commonconfig.Duration ResultWriteQueueDepth *uint32 HTTPRequest JobPipelineHTTPRequest `toml:",omitempty"` @@ -897,7 +898,7 @@ func (j *JobPipeline) setFrom(f *JobPipeline) { } type JobPipelineHTTPRequest struct { - DefaultTimeout *models.Duration + DefaultTimeout *commonconfig.Duration MaxSize *utils.FileSize } @@ -927,11 +928,11 @@ func (m *FluxMonitor) setFrom(f *FluxMonitor) { type OCR2 struct { Enabled *bool ContractConfirmations *uint32 - BlockchainTimeout *models.Duration - ContractPollInterval *models.Duration - ContractSubscribeInterval *models.Duration - ContractTransmitterTransmitTimeout *models.Duration - DatabaseTimeout *models.Duration + BlockchainTimeout *commonconfig.Duration + ContractPollInterval *commonconfig.Duration + ContractSubscribeInterval *commonconfig.Duration + ContractTransmitterTransmitTimeout *commonconfig.Duration + DatabaseTimeout *commonconfig.Duration KeyBundleID *models.Sha256Hash CaptureEATelemetry *bool CaptureAutomationCustomTelemetry *bool @@ -984,10 +985,10 @@ func (o *OCR2) setFrom(f *OCR2) { type OCR struct { Enabled *bool - ObservationTimeout *models.Duration - BlockchainTimeout *models.Duration - ContractPollInterval *models.Duration - ContractSubscribeInterval *models.Duration + ObservationTimeout *commonconfig.Duration + BlockchainTimeout *commonconfig.Duration + ContractPollInterval *commonconfig.Duration + ContractSubscribeInterval *commonconfig.Duration DefaultTransactionQueueDepth *uint32 // Optional KeyBundleID *models.Sha256Hash @@ -1063,8 +1064,8 @@ type P2PV2 struct { Enabled *bool AnnounceAddresses *[]string DefaultBootstrappers *[]ocrcommontypes.BootstrapperLocator - DeltaDial *models.Duration - DeltaReconcile *models.Duration + DeltaDial *commonconfig.Duration + DeltaReconcile *commonconfig.Duration ListenAddresses *[]string } @@ -1128,7 +1129,7 @@ type KeeperRegistry struct { CheckGasOverhead *uint32 PerformGasOverhead *uint32 MaxPerformDataSize *uint32 - SyncInterval *models.Duration + SyncInterval *commonconfig.Duration SyncUpkeepQueueSize *uint32 } @@ -1153,9 +1154,9 @@ func (k *KeeperRegistry) setFrom(f *KeeperRegistry) { type AutoPprof struct { Enabled *bool ProfileRoot *string - PollInterval *models.Duration - GatherDuration *models.Duration - GatherTraceDuration *models.Duration + PollInterval *commonconfig.Duration + GatherDuration *commonconfig.Duration + GatherTraceDuration *commonconfig.Duration MaxProfileSize *utils.FileSize CPUProfileRate *int64 // runtime.SetCPUProfileRate MemProfileRate *int64 // runtime.MemProfileRate @@ -1287,9 +1288,9 @@ func (ins *Insecure) setFrom(f *Insecure) { } type MercuryCache struct { - LatestReportTTL *models.Duration - MaxStaleAge *models.Duration - LatestReportDeadline *models.Duration + LatestReportTTL *commonconfig.Duration + MaxStaleAge *commonconfig.Duration + LatestReportDeadline *commonconfig.Duration } func (mc *MercuryCache) setFrom(f *MercuryCache) { diff --git a/core/config/toml/types_test.go b/core/config/toml/types_test.go index d29da688e33..9c3fd1d02df 100644 --- a/core/config/toml/types_test.go +++ b/core/config/toml/types_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -109,13 +110,13 @@ func Test_validateDBURL(t *testing.T) { } func TestDatabaseSecrets_ValidateConfig(t *testing.T) { - validUrl := models.URL(url.URL{Scheme: "https", Host: "localhost"}) + validUrl := commonconfig.URL(url.URL{Scheme: "https", Host: "localhost"}) validSecretURL := *models.NewSecretURL(&validUrl) - invalidEmptyUrl := models.URL(url.URL{}) + invalidEmptyUrl := commonconfig.URL(url.URL{}) invalidEmptySecretURL := *models.NewSecretURL(&invalidEmptyUrl) - invalidBackupURL := models.URL(url.URL{Scheme: "http", Host: "localhost"}) + invalidBackupURL := commonconfig.URL(url.URL{Scheme: "http", Host: "localhost"}) invalidBackupSecretURL := *models.NewSecretURL(&invalidBackupURL) tests := []struct { diff --git a/core/config/web_config.go b/core/config/web_config.go index 429a31e7e82..1f1adc47f5d 100644 --- a/core/config/web_config.go +++ b/core/config/web_config.go @@ -7,7 +7,7 @@ import ( "github.com/gin-contrib/sessions" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) type TLS interface { @@ -37,7 +37,7 @@ type LDAP interface { ReadOnlyUserLogin() string ReadOnlyUserPass() string ServerTLS() bool - SessionTimeout() models.Duration + SessionTimeout() commonconfig.Duration QueryTimeout() time.Duration BaseUserAttr() string BaseDN() string @@ -50,9 +50,9 @@ type LDAP interface { RunUserGroupCN() string ReadUserGroupCN() string UserApiTokenEnabled() bool - UserAPITokenDuration() models.Duration - UpstreamSyncInterval() models.Duration - UpstreamSyncRateLimit() models.Duration + UserAPITokenDuration() commonconfig.Duration + UpstreamSyncInterval() commonconfig.Duration + UpstreamSyncRateLimit() commonconfig.Duration } type WebServer interface { @@ -64,10 +64,10 @@ type WebServer interface { StartTimeout() time.Duration HTTPWriteTimeout() time.Duration HTTPPort() uint16 - SessionReaperExpiration() models.Duration + SessionReaperExpiration() commonconfig.Duration SecureCookies() bool SessionOptions() sessions.Options - SessionTimeout() models.Duration + SessionTimeout() commonconfig.Duration ListenIP() net.IP TLS() TLS diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index a0588f6abdc..8ae6a28a873 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -39,6 +39,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -85,7 +86,7 @@ func TestIntegration_ExternalInitiatorV2(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(10 * time.Millisecond) }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient, cltest.UseRealExternalInitiatorManager) @@ -362,7 +363,7 @@ func TestIntegration_DirectRequest(t *testing.T) { // Simulate a consumer contract calling to obtain ETH quotes in 3 different currencies // in a single callback. config := configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(100 * time.Millisecond) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) }) operatorContracts := setupOperatorContracts(t) @@ -467,7 +468,7 @@ func setupAppForEthTx(t *testing.T, operatorContracts OperatorContracts) (app *c lggr, o := logger.TestLoggerObserved(t, zapcore.DebugLevel) cfg := configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(100 * time.Millisecond) }) app = cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, cfg, b, lggr) b.Commit() @@ -689,10 +690,10 @@ func setupNode(t *testing.T, owner *bind.TransactOpts, portV2 int, c.P2P.V2.Enabled = ptr(true) c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) // GracePeriod < ObservationTimeout - c.EVM[0].OCR.ObservationGracePeriod = models.MustNewDuration(100 * time.Millisecond) + c.EVM[0].OCR.ObservationGracePeriod = commonconfig.MustNewDuration(100 * time.Millisecond) if overrides != nil { overrides(c, s) @@ -732,7 +733,7 @@ func setupForwarderEnabledNode(t *testing.T, owner *bind.TransactOpts, portV2 in c.P2P.PeerID = ptr(p2pKey.PeerID()) c.P2P.V2.Enabled = ptr(true) c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", portV2)} - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) c.EVM[0].Transactions.ForwardersEnabled = ptr(true) diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index e3b6c1a24d1..863451de733 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -34,6 +34,7 @@ import ( confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" @@ -120,14 +121,14 @@ func setupNodeOCR2( c.P2P.PeerID = ptr(p2pKey.PeerID()) c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(500 * time.Millisecond) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", port)} if len(p2pV2Bootstrappers) > 0 { c.P2P.V2.DefaultBootstrappers = &p2pV2Bootstrappers } - c.EVM[0].LogPollInterval = models.MustNewDuration(5 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(5 * time.Second) c.EVM[0].Transactions.ForwardersEnabled = &useForwarder }) diff --git a/core/internal/testutils/configtest/general_config.go b/core/internal/testutils/configtest/general_config.go index c414c973160..d2851035855 100644 --- a/core/internal/testutils/configtest/general_config.go +++ b/core/internal/testutils/configtest/general_config.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -45,21 +46,21 @@ func overrides(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) c.InsecureFastScrypt = ptr(true) - c.ShutdownGracePeriod = models.MustNewDuration(testutils.DefaultWaitTimeout) + c.ShutdownGracePeriod = commonconfig.MustNewDuration(testutils.DefaultWaitTimeout) c.Database.Dialect = dialects.TransactionWrappedPostgres c.Database.Lock.Enabled = ptr(false) c.Database.MaxIdleConns = ptr[int64](20) c.Database.MaxOpenConns = ptr[int64](20) c.Database.MigrateOnStartup = ptr(false) - c.Database.DefaultLockTimeout = models.MustNewDuration(1 * time.Minute) + c.Database.DefaultLockTimeout = commonconfig.MustNewDuration(1 * time.Minute) - c.JobPipeline.ReaperInterval = models.MustNewDuration(0) + c.JobPipeline.ReaperInterval = commonconfig.MustNewDuration(0) c.P2P.V2.Enabled = ptr(false) - c.WebServer.SessionTimeout = models.MustNewDuration(2 * time.Minute) - c.WebServer.BridgeResponseURL = models.MustParseURL("http://localhost:6688") + c.WebServer.SessionTimeout = commonconfig.MustNewDuration(2 * time.Minute) + c.WebServer.BridgeResponseURL = commonconfig.MustParseURL("http://localhost:6688") testIP := net.ParseIP("127.0.0.1") c.WebServer.ListenIP = &testIP c.WebServer.TLS.ListenIP = &testIP @@ -71,8 +72,8 @@ func overrides(c *chainlink.Config, s *chainlink.Secrets) { Nodes: evmcfg.EVMNodes{ &evmcfg.Node{ Name: ptr("test"), - WSURL: &models.URL{}, - HTTPURL: &models.URL{}, + WSURL: &commonconfig.URL{}, + HTTPURL: &commonconfig.URL{}, SendOnly: new(bool), Order: ptr[int32](100), }, @@ -112,8 +113,8 @@ func simulated(c *chainlink.Config, s *chainlink.Secrets) { var validTestNode = evmcfg.Node{ Name: ptr("simulated-node"), - WSURL: models.MustParseURL("WSS://simulated-wss.com/ws"), - HTTPURL: models.MustParseURL("http://simulated.com"), + WSURL: commonconfig.MustParseURL("WSS://simulated-wss.com/ws"), + HTTPURL: commonconfig.MustParseURL("http://simulated.com"), SendOnly: nil, Order: ptr(int32(1)), } diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index ef66a063a55..2f96c40586f 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -13,8 +13,8 @@ import ( "os" "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -38,7 +38,7 @@ type HTTPAuditLoggerInterface interface { type AuditLoggerService struct { logger logger.Logger // The standard logger configured in the node enabled bool // Whether the audit logger is enabled or not - forwardToUrl models.URL // Location we are going to send logs to + forwardToUrl commonconfig.URL // Location we are going to send logs to headers []models.ServiceHeader // Headers to be sent along with logs for identification/authentication jsonWrapperKey string // Wrap audit data as a map under this key if present environmentName string // Decorate the environment this is coming from diff --git a/core/logger/audit/audit_logger_test.go b/core/logger/audit/audit_logger_test.go index 576d11df3a4..a28c35b129d 100644 --- a/core/logger/audit/audit_logger_test.go +++ b/core/logger/audit/audit_logger_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -65,10 +66,10 @@ func (c Config) Environment() string { return "test" } -func (c Config) ForwardToUrl() (models.URL, error) { - url, err := models.ParseURL("http://localhost:9898") +func (c Config) ForwardToUrl() (commonconfig.URL, error) { + url, err := commonconfig.ParseURL("http://localhost:9898") if err != nil { - return models.URL{}, err + return commonconfig.URL{}, err } return *url, nil } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 98ab5cbfaf9..8cd50b08541 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 170e36ad2cc..eb5bbb9cdf7 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1164,8 +1164,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a h1:ViX8kP1/WYW1dLG85Frqpvus1nRGif/1QiK7qeyS+Rc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 h1:zFupIn0S1JYnKUJffFS+m4k+9BUlBG4IHsOpu6kkqKM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 192fbb311d3..6cd2732ece8 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -30,7 +30,7 @@ import ( // - TOML is limited to int64/float64, so fields requiring greater range/precision must use non-standard types // implementing encoding.TextMarshaler/TextUnmarshaler, like big.Big and decimal.Decimal // - std lib types that don't implement encoding.TextMarshaler/TextUnmarshaler (time.Duration, url.URL, big.Int) won't -// work as expected, and require wrapper types. See models.Duration, models.URL, big.Big. +// work as expected, and require wrapper types. See commonconfig.Duration, commonconfig.URL, big.Big. type Config struct { toml.Core diff --git a/core/services/chainlink/config_audit_logger.go b/core/services/chainlink/config_audit_logger.go index 1593f3887fb..5d8c00f771d 100644 --- a/core/services/chainlink/config_audit_logger.go +++ b/core/services/chainlink/config_audit_logger.go @@ -1,6 +1,7 @@ package chainlink import ( + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -14,7 +15,7 @@ func (a auditLoggerConfig) Enabled() bool { return *a.c.Enabled } -func (a auditLoggerConfig) ForwardToUrl() (models.URL, error) { +func (a auditLoggerConfig) ForwardToUrl() (commonconfig.URL, error) { return *a.c.ForwardToUrl, nil } diff --git a/core/services/chainlink/config_auto_pprof.go b/core/services/chainlink/config_auto_pprof.go index 8cc5f2dd3e8..48ec7749b79 100644 --- a/core/services/chainlink/config_auto_pprof.go +++ b/core/services/chainlink/config_auto_pprof.go @@ -3,9 +3,9 @@ package chainlink import ( "path/filepath" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -28,12 +28,12 @@ func (a *autoPprofConfig) CPUProfileRate() int { return int(*a.c.CPUProfileRate) } -func (a *autoPprofConfig) GatherDuration() models.Duration { - return models.MustMakeDuration(a.c.GatherDuration.Duration()) +func (a *autoPprofConfig) GatherDuration() commonconfig.Duration { + return *commonconfig.MustNewDuration(a.c.GatherDuration.Duration()) } -func (a *autoPprofConfig) GatherTraceDuration() models.Duration { - return models.MustMakeDuration(a.c.GatherTraceDuration.Duration()) +func (a *autoPprofConfig) GatherTraceDuration() commonconfig.Duration { + return *commonconfig.MustNewDuration(a.c.GatherTraceDuration.Duration()) } func (a *autoPprofConfig) GoroutineThreshold() int { @@ -56,7 +56,7 @@ func (a *autoPprofConfig) MutexProfileFraction() int { return int(*a.c.MutexProfileFraction) } -func (a *autoPprofConfig) PollInterval() models.Duration { +func (a *autoPprofConfig) PollInterval() commonconfig.Duration { return *a.c.PollInterval } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index cb68ed0e72d..97243926973 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -357,12 +357,12 @@ func (g *generalConfig) AutoPprofCPUProfileRate() int { return int(*g.c.AutoPprof.CPUProfileRate) } -func (g *generalConfig) AutoPprofGatherDuration() models.Duration { - return models.MustMakeDuration(g.c.AutoPprof.GatherDuration.Duration()) +func (g *generalConfig) AutoPprofGatherDuration() commonconfig.Duration { + return *commonconfig.MustNewDuration(g.c.AutoPprof.GatherDuration.Duration()) } -func (g *generalConfig) AutoPprofGatherTraceDuration() models.Duration { - return models.MustMakeDuration(g.c.AutoPprof.GatherTraceDuration.Duration()) +func (g *generalConfig) AutoPprofGatherTraceDuration() commonconfig.Duration { + return *commonconfig.MustNewDuration(g.c.AutoPprof.GatherTraceDuration.Duration()) } func (g *generalConfig) AutoPprofGoroutineThreshold() int { @@ -385,7 +385,7 @@ func (g *generalConfig) AutoPprofMutexProfileFraction() int { return int(*g.c.AutoPprof.MutexProfileFraction) } -func (g *generalConfig) AutoPprofPollInterval() models.Duration { +func (g *generalConfig) AutoPprofPollInterval() commonconfig.Duration { return *g.c.AutoPprof.PollInterval } diff --git a/core/services/chainlink/config_job_pipeline.go b/core/services/chainlink/config_job_pipeline.go index 1586cbb3574..95106b84199 100644 --- a/core/services/chainlink/config_job_pipeline.go +++ b/core/services/chainlink/config_job_pipeline.go @@ -3,9 +3,9 @@ package chainlink import ( "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) var _ config.JobPipeline = (*jobPipelineConfig)(nil) @@ -18,7 +18,7 @@ func (j *jobPipelineConfig) DefaultHTTPLimit() int64 { return int64(*j.c.HTTPRequest.MaxSize) } -func (j *jobPipelineConfig) DefaultHTTPTimeout() models.Duration { +func (j *jobPipelineConfig) DefaultHTTPTimeout() commonconfig.Duration { return *j.c.HTTPRequest.DefaultTimeout } diff --git a/core/services/chainlink/config_job_pipeline_test.go b/core/services/chainlink/config_job_pipeline_test.go index 58c94067f25..0b771a18fb7 100644 --- a/core/services/chainlink/config_job_pipeline_test.go +++ b/core/services/chainlink/config_job_pipeline_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -21,7 +21,7 @@ func TestJobPipelineConfigTest(t *testing.T) { jp := cfg.JobPipeline() assert.Equal(t, int64(100*utils.MB), jp.DefaultHTTPLimit()) - d, err := models.MakeDuration(1 * time.Minute) + d, err := commonconfig.MakeDuration(1 * time.Minute) require.NoError(t, err) assert.Equal(t, d, jp.DefaultHTTPTimeout()) assert.Equal(t, 1*time.Hour, jp.MaxRunDuration()) diff --git a/core/services/chainlink/config_p2p.go b/core/services/chainlink/config_p2p.go index 596a4fe0cae..4197358b148 100644 --- a/core/services/chainlink/config_p2p.go +++ b/core/services/chainlink/config_p2p.go @@ -1,12 +1,12 @@ package chainlink import ( - "github.com/smartcontractkit/libocr/commontypes" - + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + + "github.com/smartcontractkit/libocr/commontypes" ) type p2p struct { @@ -59,19 +59,19 @@ func (v *p2pv2) DefaultBootstrappers() (locators []commontypes.BootstrapperLocat return nil } -func (v *p2pv2) DeltaDial() models.Duration { +func (v *p2pv2) DeltaDial() commonconfig.Duration { if d := v.c.DeltaDial; d != nil { return *d } - return models.Duration{} + return commonconfig.Duration{} } -func (v *p2pv2) DeltaReconcile() models.Duration { +func (v *p2pv2) DeltaReconcile() commonconfig.Duration { if d := v.c.DeltaReconcile; d != nil { return *d } - return models.Duration{} + return commonconfig.Duration{} } func (v *p2pv2) ListenAddresses() []string { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 10ab00e8bd0..a7ac1e86d84 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -20,6 +20,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/config" commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -66,7 +67,7 @@ var ( }, Database: toml.Database{ Listener: toml.DatabaseListener{ - FallbackPollInterval: models.MustNewDuration(2 * time.Minute), + FallbackPollInterval: commonconfig.MustNewDuration(2 * time.Minute), }, }, Log: toml.Log{ @@ -75,16 +76,16 @@ var ( }, JobPipeline: toml.JobPipeline{ HTTPRequest: toml.JobPipelineHTTPRequest{ - DefaultTimeout: models.MustNewDuration(30 * time.Second), + DefaultTimeout: commonconfig.MustNewDuration(30 * time.Second), }, }, OCR2: toml.OCR2{ Enabled: ptr(true), - DatabaseTimeout: models.MustNewDuration(20 * time.Second), + DatabaseTimeout: commonconfig.MustNewDuration(20 * time.Second), }, OCR: toml.OCR{ Enabled: ptr(true), - BlockchainTimeout: models.MustNewDuration(5 * time.Second), + BlockchainTimeout: commonconfig.MustNewDuration(5 * time.Second), }, P2P: toml.P2P{ IncomingMessageBufferSize: ptr[int64](999), @@ -194,10 +195,10 @@ var ( ) func TestConfig_Marshal(t *testing.T) { - zeroSeconds := models.MustMakeDuration(time.Second * 0) - second := models.MustMakeDuration(time.Second) - minute := models.MustMakeDuration(time.Minute) - hour := models.MustMakeDuration(time.Hour) + zeroSeconds := *commonconfig.MustNewDuration(time.Second * 0) + second := *commonconfig.MustNewDuration(time.Second) + minute := *commonconfig.MustNewDuration(time.Minute) + hour := *commonconfig.MustNewDuration(time.Hour) mustPeerID := func(s string) *p2pkey.PeerID { id, err := p2pkey.MakePeerID(s) require.NoError(t, err) @@ -219,7 +220,7 @@ func TestConfig_Marshal(t *testing.T) { Core: toml.Core{ InsecureFastScrypt: ptr(true), RootDir: ptr("test/root/dir"), - ShutdownGracePeriod: models.MustNewDuration(10 * time.Second), + ShutdownGracePeriod: commonconfig.MustNewDuration(10 * time.Second), Insecure: toml.Insecure{ DevWebServer: ptr(false), OCRDevelopmentMode: ptr(false), @@ -260,17 +261,17 @@ func TestConfig_Marshal(t *testing.T) { UICSAKeys: ptr(true), } full.Database = toml.Database{ - DefaultIdleInTxSessionTimeout: models.MustNewDuration(time.Minute), - DefaultLockTimeout: models.MustNewDuration(time.Hour), - DefaultQueryTimeout: models.MustNewDuration(time.Second), + DefaultIdleInTxSessionTimeout: commonconfig.MustNewDuration(time.Minute), + DefaultLockTimeout: commonconfig.MustNewDuration(time.Hour), + DefaultQueryTimeout: commonconfig.MustNewDuration(time.Second), LogQueries: ptr(true), MigrateOnStartup: ptr(true), MaxIdleConns: ptr[int64](7), MaxOpenConns: ptr[int64](13), Listener: toml.DatabaseListener{ - MaxReconnectDuration: models.MustNewDuration(time.Minute), - MinReconnectInterval: models.MustNewDuration(5 * time.Minute), - FallbackPollInterval: models.MustNewDuration(2 * time.Minute), + MaxReconnectDuration: commonconfig.MustNewDuration(time.Minute), + MinReconnectInterval: commonconfig.MustNewDuration(5 * time.Minute), + FallbackPollInterval: commonconfig.MustNewDuration(2 * time.Minute), }, Lock: toml.DatabaseLock{ Enabled: ptr(false), @@ -289,10 +290,10 @@ func TestConfig_Marshal(t *testing.T) { Logging: ptr(true), BufferSize: ptr[uint16](1234), MaxBatchSize: ptr[uint16](4321), - SendInterval: models.MustNewDuration(time.Minute), - SendTimeout: models.MustNewDuration(5 * time.Second), + SendInterval: commonconfig.MustNewDuration(time.Minute), + SendTimeout: commonconfig.MustNewDuration(5 * time.Second), UseBatchSend: ptr(true), - URL: ptr(models.URL{}), + URL: ptr(commonconfig.URL{}), ServerPubKey: ptr(""), Endpoints: []toml.TelemetryIngressEndpoint{{ Network: ptr("EVM"), @@ -317,14 +318,14 @@ func TestConfig_Marshal(t *testing.T) { AuthenticationMethod: ptr("local"), AllowOrigins: ptr("*"), BridgeResponseURL: mustURL("https://bridge.response"), - BridgeCacheTTL: models.MustNewDuration(10 * time.Second), - HTTPWriteTimeout: models.MustNewDuration(time.Minute), + BridgeCacheTTL: commonconfig.MustNewDuration(10 * time.Second), + HTTPWriteTimeout: commonconfig.MustNewDuration(time.Minute), HTTPPort: ptr[uint16](56), SecureCookies: ptr(true), - SessionTimeout: models.MustNewDuration(time.Hour), - SessionReaperExpiration: models.MustNewDuration(7 * 24 * time.Hour), + SessionTimeout: commonconfig.MustNewDuration(time.Hour), + SessionReaperExpiration: commonconfig.MustNewDuration(7 * 24 * time.Hour), HTTPMaxSize: ptr(utils.FileSize(uint64(32770))), - StartTimeout: models.MustNewDuration(15 * time.Second), + StartTimeout: commonconfig.MustNewDuration(15 * time.Second), ListenIP: mustIP("192.158.1.37"), MFA: toml.WebServerMFA{ RPID: ptr("test-rpid"), @@ -332,8 +333,8 @@ func TestConfig_Marshal(t *testing.T) { }, LDAP: toml.WebServerLDAP{ ServerTLS: ptr(true), - SessionTimeout: models.MustNewDuration(15 * time.Minute), - QueryTimeout: models.MustNewDuration(2 * time.Minute), + SessionTimeout: commonconfig.MustNewDuration(15 * time.Minute), + QueryTimeout: commonconfig.MustNewDuration(2 * time.Minute), BaseUserAttr: ptr("uid"), BaseDN: ptr("dc=custom,dc=example,dc=com"), UsersDN: ptr("ou=users"), @@ -345,15 +346,15 @@ func TestConfig_Marshal(t *testing.T) { RunUserGroupCN: ptr("NodeRunners"), ReadUserGroupCN: ptr("NodeReadOnly"), UserApiTokenEnabled: ptr(false), - UserAPITokenDuration: models.MustNewDuration(240 * time.Hour), - UpstreamSyncInterval: models.MustNewDuration(0 * time.Second), - UpstreamSyncRateLimit: models.MustNewDuration(2 * time.Minute), + UserAPITokenDuration: commonconfig.MustNewDuration(240 * time.Hour), + UpstreamSyncInterval: commonconfig.MustNewDuration(0 * time.Second), + UpstreamSyncRateLimit: commonconfig.MustNewDuration(2 * time.Minute), }, RateLimit: toml.WebServerRateLimit{ Authenticated: ptr[int64](42), - AuthenticatedPeriod: models.MustNewDuration(time.Second), + AuthenticatedPeriod: commonconfig.MustNewDuration(time.Second), Unauthenticated: ptr[int64](7), - UnauthenticatedPeriod: models.MustNewDuration(time.Minute), + UnauthenticatedPeriod: commonconfig.MustNewDuration(time.Minute), }, TLS: toml.WebServerTLS{ CertPath: ptr("tls/cert/path"), @@ -366,14 +367,14 @@ func TestConfig_Marshal(t *testing.T) { } full.JobPipeline = toml.JobPipeline{ ExternalInitiatorsEnabled: ptr(true), - MaxRunDuration: models.MustNewDuration(time.Hour), + MaxRunDuration: commonconfig.MustNewDuration(time.Hour), MaxSuccessfulRuns: ptr[uint64](123456), - ReaperInterval: models.MustNewDuration(4 * time.Hour), - ReaperThreshold: models.MustNewDuration(7 * 24 * time.Hour), + ReaperInterval: commonconfig.MustNewDuration(4 * time.Hour), + ReaperThreshold: commonconfig.MustNewDuration(7 * 24 * time.Hour), ResultWriteQueueDepth: ptr[uint32](10), HTTPRequest: toml.JobPipelineHTTPRequest{ MaxSize: ptr[utils.FileSize](100 * utils.MB), - DefaultTimeout: models.MustNewDuration(time.Minute), + DefaultTimeout: commonconfig.MustNewDuration(time.Minute), }, } full.FluxMonitor = toml.FluxMonitor{ @@ -383,11 +384,11 @@ func TestConfig_Marshal(t *testing.T) { full.OCR2 = toml.OCR2{ Enabled: ptr(true), ContractConfirmations: ptr[uint32](11), - BlockchainTimeout: models.MustNewDuration(3 * time.Second), - ContractPollInterval: models.MustNewDuration(time.Hour), - ContractSubscribeInterval: models.MustNewDuration(time.Minute), - ContractTransmitterTransmitTimeout: models.MustNewDuration(time.Minute), - DatabaseTimeout: models.MustNewDuration(8 * time.Second), + BlockchainTimeout: commonconfig.MustNewDuration(3 * time.Second), + ContractPollInterval: commonconfig.MustNewDuration(time.Hour), + ContractSubscribeInterval: commonconfig.MustNewDuration(time.Minute), + ContractTransmitterTransmitTimeout: commonconfig.MustNewDuration(time.Minute), + DatabaseTimeout: commonconfig.MustNewDuration(8 * time.Second), KeyBundleID: ptr(models.MustSha256HashFromHex("7a5f66bbe6594259325bf2b4f5b1a9c9")), CaptureEATelemetry: ptr(false), CaptureAutomationCustomTelemetry: ptr(true), @@ -397,10 +398,10 @@ func TestConfig_Marshal(t *testing.T) { } full.OCR = toml.OCR{ Enabled: ptr(true), - ObservationTimeout: models.MustNewDuration(11 * time.Second), - BlockchainTimeout: models.MustNewDuration(3 * time.Second), - ContractPollInterval: models.MustNewDuration(time.Hour), - ContractSubscribeInterval: models.MustNewDuration(time.Minute), + ObservationTimeout: commonconfig.MustNewDuration(11 * time.Second), + BlockchainTimeout: commonconfig.MustNewDuration(3 * time.Second), + ContractPollInterval: commonconfig.MustNewDuration(time.Hour), + ContractSubscribeInterval: commonconfig.MustNewDuration(time.Minute), DefaultTransactionQueueDepth: ptr[uint32](12), KeyBundleID: ptr(models.MustSha256HashFromHex("acdd42797a8b921b2910497badc50006")), SimulateTransactions: ptr(true), @@ -420,8 +421,8 @@ func TestConfig_Marshal(t *testing.T) { {PeerID: "12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw", Addrs: []string{"foo:42", "bar:10"}}, {PeerID: "12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw", Addrs: []string{"test:99"}}, }, - DeltaDial: models.MustNewDuration(time.Minute), - DeltaReconcile: models.MustNewDuration(time.Second), + DeltaDial: commonconfig.MustNewDuration(time.Minute), + DeltaReconcile: commonconfig.MustNewDuration(time.Second), ListenAddresses: &[]string{"foo", "bar"}, }, } @@ -435,7 +436,7 @@ func TestConfig_Marshal(t *testing.T) { Registry: toml.KeeperRegistry{ CheckGasOverhead: ptr[uint32](90), PerformGasOverhead: ptr[uint32](math.MaxUint32), - SyncInterval: models.MustNewDuration(time.Hour), + SyncInterval: commonconfig.MustNewDuration(time.Hour), SyncUpkeepQueueSize: ptr[uint32](31), MaxPerformDataSize: ptr[uint32](5000), }, @@ -443,9 +444,9 @@ func TestConfig_Marshal(t *testing.T) { full.AutoPprof = toml.AutoPprof{ Enabled: ptr(true), ProfileRoot: ptr("prof/root"), - PollInterval: models.MustNewDuration(time.Minute), - GatherDuration: models.MustNewDuration(12 * time.Second), - GatherTraceDuration: models.MustNewDuration(13 * time.Second), + PollInterval: commonconfig.MustNewDuration(time.Minute), + GatherDuration: commonconfig.MustNewDuration(12 * time.Second), + GatherTraceDuration: commonconfig.MustNewDuration(13 * time.Second), MaxProfileSize: ptr[utils.FileSize](utils.GB), CPUProfileRate: ptr[int64](7), MemProfileRate: ptr[int64](9), @@ -564,8 +565,8 @@ func TestConfig_Marshal(t *testing.T) { ContractConfirmations: ptr[uint16](11), ContractTransmitterTransmitTimeout: &minute, DatabaseTimeout: &second, - DeltaCOverride: models.MustNewDuration(time.Hour), - DeltaCJitterOverride: models.MustNewDuration(time.Second), + DeltaCOverride: commonconfig.MustNewDuration(time.Hour), + DeltaCJitterOverride: commonconfig.MustNewDuration(time.Second), ObservationGracePeriod: &second, }, OCR2: evmcfg.OCR2{ @@ -662,9 +663,9 @@ func TestConfig_Marshal(t *testing.T) { } full.Mercury = toml.Mercury{ Cache: toml.MercuryCache{ - LatestReportTTL: models.MustNewDuration(100 * time.Second), - MaxStaleAge: models.MustNewDuration(101 * time.Second), - LatestReportDeadline: models.MustNewDuration(102 * time.Second), + LatestReportTTL: commonconfig.MustNewDuration(100 * time.Second), + MaxStaleAge: commonconfig.MustNewDuration(101 * time.Second), + LatestReportDeadline: commonconfig.MustNewDuration(102 * time.Second), }, TLS: toml.MercuryTLS{ CertFile: ptr("/path/to/cert.pem"), @@ -1129,7 +1130,7 @@ func TestConfig_full(t *testing.T) { for c := range got.EVM { for n := range got.EVM[c].Nodes { if got.EVM[c].Nodes[n].WSURL == nil { - got.EVM[c].Nodes[n].WSURL = new(models.URL) + got.EVM[c].Nodes[n].WSURL = new(commonconfig.URL) } if got.EVM[c].Nodes[n].SendOnly == nil { got.EVM[c].Nodes[n].SendOnly = ptr(true) @@ -1148,7 +1149,7 @@ func TestConfig_full(t *testing.T) { // Except for TelemetryIngress.URL as this will be removed in the future // and its only use is to signal to NOPs that these fields are no longer allowed if got.TelemetryIngress.URL == nil { - got.TelemetryIngress.URL = new(models.URL) + got.TelemetryIngress.URL = new(commonconfig.URL) } cfgtest.AssertFieldsNotNil(t, got) @@ -1261,8 +1262,8 @@ func TestConfig_Validate(t *testing.T) { } } -func mustURL(s string) *models.URL { - var u models.URL +func mustURL(s string) *commonconfig.URL { + var u commonconfig.URL if err := u.UnmarshalText([]byte(s)); err != nil { panic(err) } diff --git a/core/services/chainlink/config_web_server.go b/core/services/chainlink/config_web_server.go index 06db398e2ea..473f70a4c9c 100644 --- a/core/services/chainlink/config_web_server.go +++ b/core/services/chainlink/config_web_server.go @@ -9,9 +9,9 @@ import ( "github.com/gin-contrib/sessions" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) var _ config.WebServer = (*webServerConfig)(nil) @@ -153,7 +153,7 @@ func (w *webServerConfig) HTTPPort() uint16 { return *w.c.HTTPPort } -func (w *webServerConfig) SessionReaperExpiration() models.Duration { +func (w *webServerConfig) SessionReaperExpiration() commonconfig.Duration { return *w.c.SessionReaperExpiration } @@ -170,8 +170,8 @@ func (w *webServerConfig) SessionOptions() sessions.Options { } } -func (w *webServerConfig) SessionTimeout() models.Duration { - return models.MustMakeDuration(w.c.SessionTimeout.Duration()) +func (w *webServerConfig) SessionTimeout() commonconfig.Duration { + return *commonconfig.MustNewDuration(w.c.SessionTimeout.Duration()) } func (w *webServerConfig) ListenIP() net.IP { @@ -211,7 +211,7 @@ func (l *ldapConfig) ServerTLS() bool { return *l.c.ServerTLS } -func (l *ldapConfig) SessionTimeout() models.Duration { +func (l *ldapConfig) SessionTimeout() commonconfig.Duration { return *l.c.SessionTimeout } @@ -219,7 +219,7 @@ func (l *ldapConfig) QueryTimeout() time.Duration { return l.c.QueryTimeout.Duration() } -func (l *ldapConfig) UserAPITokenDuration() models.Duration { +func (l *ldapConfig) UserAPITokenDuration() commonconfig.Duration { return *l.c.UserAPITokenDuration } @@ -300,16 +300,16 @@ func (l *ldapConfig) UserApiTokenEnabled() bool { return *l.c.UserApiTokenEnabled } -func (l *ldapConfig) UpstreamSyncInterval() models.Duration { +func (l *ldapConfig) UpstreamSyncInterval() commonconfig.Duration { if l.c.UpstreamSyncInterval == nil { - return models.Duration{} + return commonconfig.Duration{} } return *l.c.UpstreamSyncInterval } -func (l *ldapConfig) UpstreamSyncRateLimit() models.Duration { +func (l *ldapConfig) UpstreamSyncRateLimit() commonconfig.Duration { if l.c.UpstreamSyncRateLimit == nil { - return models.Duration{} + return commonconfig.Duration{} } return *l.c.UpstreamSyncRateLimit } diff --git a/core/services/chainlink/config_web_server_test.go b/core/services/chainlink/config_web_server_test.go index 92e8dde460a..946e0b0c12b 100644 --- a/core/services/chainlink/config_web_server_test.go +++ b/core/services/chainlink/config_web_server_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestWebServerConfig(t *testing.T) { @@ -24,8 +24,8 @@ func TestWebServerConfig(t *testing.T) { assert.Equal(t, 1*time.Minute, ws.HTTPWriteTimeout()) assert.Equal(t, uint16(56), ws.HTTPPort()) assert.True(t, ws.SecureCookies()) - assert.Equal(t, *models.MustNewDuration(1 * time.Hour), ws.SessionTimeout()) - assert.Equal(t, *models.MustNewDuration(168 * time.Hour), ws.SessionReaperExpiration()) + assert.Equal(t, *commonconfig.MustNewDuration(1 * time.Hour), ws.SessionTimeout()) + assert.Equal(t, *commonconfig.MustNewDuration(168 * time.Hour), ws.SessionReaperExpiration()) assert.Equal(t, int64(32770), ws.HTTPMaxSize()) assert.Equal(t, 15*time.Second, ws.StartTimeout()) tls := ws.TLS() diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 6f6e3afce31..499eca9748a 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" @@ -26,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/plugins" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -45,22 +44,22 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { cfg := evmcfg.Defaults(evmChainID1) node1_1 := evmcfg.Node{ Name: ptr("Test node chain1:1"), - WSURL: models.MustParseURL("ws://localhost:8546"), - HTTPURL: models.MustParseURL("http://localhost:8546"), + WSURL: commonconfig.MustParseURL("ws://localhost:8546"), + HTTPURL: commonconfig.MustParseURL("http://localhost:8546"), SendOnly: ptr(false), Order: ptr(int32(15)), } node1_2 := evmcfg.Node{ Name: ptr("Test node chain1:2"), - WSURL: models.MustParseURL("ws://localhost:8547"), - HTTPURL: models.MustParseURL("http://localhost:8547"), + WSURL: commonconfig.MustParseURL("ws://localhost:8547"), + HTTPURL: commonconfig.MustParseURL("http://localhost:8547"), SendOnly: ptr(false), Order: ptr(int32(36)), } node2_1 := evmcfg.Node{ Name: ptr("Test node chain2:1"), - WSURL: models.MustParseURL("ws://localhost:8547"), - HTTPURL: models.MustParseURL("http://localhost:8547"), + WSURL: commonconfig.MustParseURL("ws://localhost:8547"), + HTTPURL: commonconfig.MustParseURL("http://localhost:8547"), SendOnly: ptr(false), Order: ptr(int32(11)), } @@ -85,7 +84,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: solcfg.Chain{}, Nodes: []*solcfg.Node{{ Name: ptr("solana chain 1 node 1"), - URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8547").URL())), + URL: ((*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:8547").URL())), }}, }, &solana.TOMLConfig{ @@ -94,7 +93,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: solcfg.Chain{}, Nodes: []*solcfg.Node{{ Name: ptr("solana chain 2 node 1"), - URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8527").URL())), + URL: ((*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:8527").URL())), }}, }, } @@ -107,15 +106,15 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: []*stkcfg.Node{ { Name: ptr("starknet chain 1 node 1"), - URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8547").URL())), + URL: ((*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:8547").URL())), }, { Name: ptr("starknet chain 1 node 2"), - URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8548").URL())), + URL: ((*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:8548").URL())), }, { Name: ptr("starknet chain 1 node 3"), - URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8549").URL())), + URL: ((*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:8549").URL())), }, }, }, @@ -126,7 +125,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: []*stkcfg.Node{ { Name: ptr("starknet chain 2 node 1"), - URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:3547").URL())), + URL: ((*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:3547").URL())), }, }, }, @@ -144,7 +143,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 1 node 1"), - TendermintURL: (*commoncfg.URL)(models.MustParseURL("http://localhost:9548").URL()), + TendermintURL: (*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:9548").URL()), }, }, }, @@ -159,7 +158,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 2 node 1"), - TendermintURL: (*commoncfg.URL)(models.MustParseURL("http://localhost:9598").URL()), + TendermintURL: (*commonconfig.URL)(commonconfig.MustParseURL("http://localhost:9598").URL()), }, }, }, diff --git a/core/services/feeds/config.go b/core/services/feeds/config.go index 605e70c24c9..141e4910960 100644 --- a/core/services/feeds/config.go +++ b/core/services/feeds/config.go @@ -3,11 +3,11 @@ package feeds import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) type JobConfig interface { - DefaultHTTPTimeout() models.Duration + DefaultHTTPTimeout() commonconfig.Duration } type InsecureConfig interface { diff --git a/core/services/feeds/mocks/config.go b/core/services/feeds/mocks/config.go deleted file mode 100644 index f2e4dcaa883..00000000000 --- a/core/services/feeds/mocks/config.go +++ /dev/null @@ -1,292 +0,0 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. - -package mocks - -import ( - time "time" - - models "github.com/smartcontractkit/chainlink/v2/core/store/models" - mock "github.com/stretchr/testify/mock" -) - -// Config is an autogenerated mock type for the Config type -type Config struct { - mock.Mock -} - -// DatabaseDefaultQueryTimeout provides a mock function with given fields: -func (_m *Config) DatabaseDefaultQueryTimeout() time.Duration { - ret := _m.Called() - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// DefaultHTTPTimeout provides a mock function with given fields: -func (_m *Config) DefaultHTTPTimeout() models.Duration { - ret := _m.Called() - - var r0 models.Duration - if rf, ok := ret.Get(0).(func() models.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(models.Duration) - } - - return r0 -} - -// Dev provides a mock function with given fields: -func (_m *Config) Dev() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// FeatureOffchainReporting provides a mock function with given fields: -func (_m *Config) FeatureOffchainReporting() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// FeatureOffchainReporting2 provides a mock function with given fields: -func (_m *Config) FeatureOffchainReporting2() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// JobPipelineMaxSuccessfulRuns provides a mock function with given fields: -func (_m *Config) JobPipelineMaxSuccessfulRuns() uint64 { - ret := _m.Called() - - var r0 uint64 - if rf, ok := ret.Get(0).(func() uint64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint64) - } - - return r0 -} - -// JobPipelineResultWriteQueueDepth provides a mock function with given fields: -func (_m *Config) JobPipelineResultWriteQueueDepth() uint64 { - ret := _m.Called() - - var r0 uint64 - if rf, ok := ret.Get(0).(func() uint64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint64) - } - - return r0 -} - -// LogSQL provides a mock function with given fields: -func (_m *Config) LogSQL() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// OCR2BlockchainTimeout provides a mock function with given fields: -func (_m *Config) OCR2BlockchainTimeout() time.Duration { - ret := _m.Called() - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// OCR2CaptureEATelemetry provides a mock function with given fields: -func (_m *Config) OCR2CaptureEATelemetry() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// OCR2ContractConfirmations provides a mock function with given fields: -func (_m *Config) OCR2ContractConfirmations() uint16 { - ret := _m.Called() - - var r0 uint16 - if rf, ok := ret.Get(0).(func() uint16); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint16) - } - - return r0 -} - -// OCR2ContractPollInterval provides a mock function with given fields: -func (_m *Config) OCR2ContractPollInterval() time.Duration { - ret := _m.Called() - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// OCR2ContractSubscribeInterval provides a mock function with given fields: -func (_m *Config) OCR2ContractSubscribeInterval() time.Duration { - ret := _m.Called() - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// OCR2ContractTransmitterTransmitTimeout provides a mock function with given fields: -func (_m *Config) OCR2ContractTransmitterTransmitTimeout() time.Duration { - ret := _m.Called() - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// OCR2DatabaseTimeout provides a mock function with given fields: -func (_m *Config) OCR2DatabaseTimeout() time.Duration { - ret := _m.Called() - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// OCR2KeyBundleID provides a mock function with given fields: -func (_m *Config) OCR2KeyBundleID() (string, error) { - ret := _m.Called() - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// OCR2TraceLogging provides a mock function with given fields: -func (_m *Config) OCR2TraceLogging() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// OCRDevelopmentMode provides a mock function with given fields: -func (_m *Config) OCRDevelopmentMode() bool { - ret := _m.Called() - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -type mockConstructorTestingTNewConfig interface { - mock.TestingT - Cleanup(func()) -} - -// NewConfig creates a new instance of Config. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewConfig(t mockConstructorTestingTNewConfig) *Config { - mock := &Config{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index d37393eba9e..d822cd9787d 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -41,7 +42,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/versioning" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" ) @@ -626,7 +626,7 @@ func Test_Service_ProposeJob(t *testing.T) { JobProposalID: idBootstrap, } - httpTimeout = models.MustMakeDuration(1 * time.Second) + httpTimeout = *commonconfig.MustNewDuration(1 * time.Second) ) testCases := []struct { @@ -806,7 +806,7 @@ func Test_Service_DeleteJob(t *testing.T) { Status: feeds.JobProposalStatusApproved, } - httpTimeout = models.MustMakeDuration(1 * time.Second) + httpTimeout = *commonconfig.MustNewDuration(1 * time.Second) ) testCases := []struct { @@ -946,7 +946,7 @@ answer1 [type=median index=0]; Definition: defn, } - httpTimeout = models.MustMakeDuration(1 * time.Second) + httpTimeout = *commonconfig.MustNewDuration(1 * time.Second) ) testCases := []struct { @@ -1610,7 +1610,7 @@ answer1 [type=median index=0]; testCases := []struct { name string - httpTimeout *models.Duration + httpTimeout *commonconfig.Duration before func(svc *TestService) id int64 force bool @@ -1618,7 +1618,7 @@ answer1 [type=median index=0]; }{ { name: "pending job success for new proposals", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -1656,7 +1656,7 @@ answer1 [type=median index=0]; }, { name: "cancelled spec success when it is the latest spec", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", cancelledSpec.ID, mock.Anything).Return(cancelledSpec, nil) @@ -1695,7 +1695,7 @@ answer1 [type=median index=0]; }, { name: "pending job fail due to spec missing external job id", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec2, nil) @@ -1775,7 +1775,7 @@ answer1 [type=median index=0]; }, { name: "already existing job replacement (found via external job id) error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -1790,7 +1790,7 @@ answer1 [type=median index=0]; }, { name: "already existing job replacement error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -1806,7 +1806,7 @@ answer1 [type=median index=0]; }, { name: "already existing self managed job replacement success if forced (via external job id)", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -1845,7 +1845,7 @@ answer1 [type=median index=0]; }, { name: "already existing self managed job replacement success if forced", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -1885,7 +1885,7 @@ answer1 [type=median index=0]; }, { name: "already existing FMS managed job replacement success if forced", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -1944,7 +1944,7 @@ answer1 [type=median index=0]; }, { name: "bridges do not exist", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -1967,7 +1967,7 @@ answer1 [type=median index=0]; }, { name: "Fetching the approved spec fails (via external job id)", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -1982,7 +1982,7 @@ answer1 [type=median index=0]; }, { name: "Fetching the approved spec fails", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -1998,7 +1998,7 @@ answer1 [type=median index=0]; }, { name: "spec cancellation fails (via external job id)", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -2014,7 +2014,7 @@ answer1 [type=median index=0]; }, { name: "spec cancellation fails", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.EXPECT().GetSpec(spec.ID, mock.Anything).Return(spec, nil) @@ -2031,7 +2031,7 @@ answer1 [type=median index=0]; }, { name: "create job error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -2056,7 +2056,7 @@ answer1 [type=median index=0]; }, { name: "approve spec orm error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -2087,7 +2087,7 @@ answer1 [type=median index=0]; }, { name: "fms call error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -2250,7 +2250,7 @@ answer1 [type=median index=0]; testCases := []struct { name string - httpTimeout *models.Duration + httpTimeout *commonconfig.Duration before func(svc *TestService) id int64 force bool @@ -2258,7 +2258,7 @@ answer1 [type=median index=0]; }{ { name: "pending job success", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2295,7 +2295,7 @@ answer1 [type=median index=0]; }, { name: "cancelled spec success when it is the latest spec", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", cancelledSpec.ID, mock.Anything).Return(cancelledSpec, nil) @@ -2361,7 +2361,7 @@ answer1 [type=median index=0]; }, { name: "already existing job replacement error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2376,7 +2376,7 @@ answer1 [type=median index=0]; }, { name: "already existing self managed job replacement success if forced without feedID", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2415,7 +2415,7 @@ answer1 [type=median index=0]; }, { name: "already existing self managed job replacement success if forced with feedID", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(&feeds.JobProposalSpec{ @@ -2460,7 +2460,7 @@ answer1 [type=median index=0]; }, { name: "already existing FMS managed job replacement success if forced", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2548,7 +2548,7 @@ answer1 [type=median index=0]; }, { name: "bridges do not exist", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2571,7 +2571,7 @@ answer1 [type=median index=0]; }, { name: "create job error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -2596,7 +2596,7 @@ answer1 [type=median index=0]; }, { name: "approve spec orm error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -2627,7 +2627,7 @@ answer1 [type=median index=0]; }, { name: "fms call error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -2756,7 +2756,7 @@ chainID = 0 testCases := []struct { name string - httpTimeout *models.Duration + httpTimeout *commonconfig.Duration before func(svc *TestService) id int64 force bool @@ -2764,7 +2764,7 @@ chainID = 0 }{ { name: "pending job success", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2801,7 +2801,7 @@ chainID = 0 }, { name: "cancelled spec success when it is the latest spec", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", cancelledSpec.ID, mock.Anything).Return(cancelledSpec, nil) @@ -2867,7 +2867,7 @@ chainID = 0 }, { name: "already existing job replacement error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2882,7 +2882,7 @@ chainID = 0 }, { name: "already existing self managed job replacement success if forced without feedID", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -2921,7 +2921,7 @@ chainID = 0 }, { name: "already existing self managed job replacement success if forced with feedID", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(&feeds.JobProposalSpec{ @@ -2966,7 +2966,7 @@ chainID = 0 }, { name: "already existing FMS managed job replacement success if forced", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -3054,7 +3054,7 @@ chainID = 0 }, { name: "bridges do not exist", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.connMgr.On("GetClient", jp.FeedsManagerID).Return(svc.fmsClient, nil) svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) @@ -3077,7 +3077,7 @@ chainID = 0 }, { name: "create job error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -3102,7 +3102,7 @@ chainID = 0 }, { name: "approve spec orm error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) @@ -3133,7 +3133,7 @@ chainID = 0 }, { name: "fms call error", - httpTimeout: models.MustNewDuration(1 * time.Minute), + httpTimeout: commonconfig.MustNewDuration(1 * time.Minute), before: func(svc *TestService) { svc.orm.On("GetSpec", spec.ID, mock.Anything).Return(spec, nil) svc.orm.On("GetJobProposal", jp.ID, mock.Anything).Return(jp, nil) diff --git a/core/services/fluxmonitorv2/config.go b/core/services/fluxmonitorv2/config.go index 2680f30a777..18fdf72798b 100644 --- a/core/services/fluxmonitorv2/config.go +++ b/core/services/fluxmonitorv2/config.go @@ -4,8 +4,8 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) // Config defines the Flux Monitor configuration. @@ -28,7 +28,7 @@ type FluxMonitorConfig interface { } type JobPipelineConfig interface { - DefaultHTTPTimeout() models.Duration + DefaultHTTPTimeout() commonconfig.Duration } // MinimumPollingInterval returns the minimum duration between polling ticks diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 48c9ca24ac3..580e7a1d086 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -26,6 +26,7 @@ import ( "github.com/jmoiron/sqlx" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -448,8 +449,8 @@ func TestFluxMonitor_Deviation(t *testing.T) { // Set up chainlink app app := startApplication(t, fa, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(100 * time.Millisecond) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(1 * time.Second) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(1 * time.Second) c.EVM[0].GasEstimator.EIP1559DynamicFees = &test.eip1559 }) @@ -620,8 +621,8 @@ func TestFluxMonitor_NewRound(t *testing.T) { // Set up chainlink app app := startApplication(t, fa, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(100 * time.Millisecond) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(1 * time.Second) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(1 * time.Second) flags := ethkey.EIP55AddressFromAddress(fa.flagsContractAddress) c.EVM[0].FlagsContractAddress = &flags }) @@ -731,8 +732,8 @@ func TestFluxMonitor_HibernationMode(t *testing.T) { // Start chainlink app app := startApplication(t, fa, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(100 * time.Millisecond) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(1 * time.Second) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(1 * time.Second) flags := ethkey.EIP55AddressFromAddress(fa.flagsContractAddress) c.EVM[0].FlagsContractAddress = &flags }) @@ -848,8 +849,8 @@ func TestFluxMonitor_InvalidSubmission(t *testing.T) { // Set up chainlink app app := startApplication(t, fa, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(100 * time.Millisecond) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(1 * time.Second) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) // Report a price that is above the maximum allowed value, @@ -925,8 +926,8 @@ func TestFluxMonitorAntiSpamLogic(t *testing.T) { // Set up chainlink app app := startApplication(t, fa, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(100 * time.Millisecond) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(1 * time.Second) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(100 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) answer := int64(1) // Answer the nodes give on the first round diff --git a/core/services/fluxmonitorv2/validate.go b/core/services/fluxmonitorv2/validate.go index 02dbfb01284..fc531fc7cb4 100644 --- a/core/services/fluxmonitorv2/validate.go +++ b/core/services/fluxmonitorv2/validate.go @@ -8,13 +8,13 @@ import ( "github.com/pelletier/go-toml" "github.com/pkg/errors" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ValidationConfig interface { - DefaultHTTPTimeout() models.Duration + DefaultHTTPTimeout() commonconfig.Duration } func ValidatedFluxMonitorSpec(config ValidationConfig, ts string) (job.Job, error) { diff --git a/core/services/fluxmonitorv2/validate_test.go b/core/services/fluxmonitorv2/validate_test.go index 94dc8b6b709..40efd1d724d 100644 --- a/core/services/fluxmonitorv2/validate_test.go +++ b/core/services/fluxmonitorv2/validate_test.go @@ -6,8 +6,8 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils/tomlutils" "github.com/stretchr/testify/assert" @@ -16,7 +16,9 @@ import ( type testcfg struct{} -func (testcfg) DefaultHTTPTimeout() models.Duration { return models.MustMakeDuration(2 * time.Second) } +func (testcfg) DefaultHTTPTimeout() commonconfig.Duration { + return *commonconfig.MustNewDuration(2 * time.Second) +} func TestValidate(t *testing.T) { var tt = []struct { diff --git a/core/services/job/job_pipeline_orm_integration_test.go b/core/services/job/job_pipeline_orm_integration_test.go index f1307753d29..dd3062fa14b 100644 --- a/core/services/job/job_pipeline_orm_integration_test.go +++ b/core/services/job/job_pipeline_orm_integration_test.go @@ -9,6 +9,7 @@ import ( "github.com/jmoiron/sqlx" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -46,7 +47,7 @@ func TestPipelineORM_Integration(t *testing.T) { ` config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(30 * time.Millisecond) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(30 * time.Millisecond) }) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, config.Database()) diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index 27c0e0e8515..fb671982ec5 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" @@ -68,8 +69,8 @@ func TestRunner(t *testing.T) { c.OCR.KeyBundleID = &kbid taddress := ethkey.EIP55AddressFromAddress(transmitterAddress) c.OCR.TransmitterAddress = &taddress - c.OCR2.DatabaseTimeout = models.MustNewDuration(time.Second) - c.OCR2.ContractTransmitterTransmitTimeout = models.MustNewDuration(time.Second) + c.OCR2.DatabaseTimeout = commonconfig.MustNewDuration(time.Second) + c.OCR2.ContractTransmitterTransmitTimeout = commonconfig.MustNewDuration(time.Second) c.Insecure.OCRDevelopmentMode = ptr(true) }) @@ -746,7 +747,7 @@ func TestRunner_Success_Callback_AsyncJob(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { t := true c.JobPipeline.ExternalInitiatorsEnabled = &t - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(10 * time.Millisecond) }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient, cltest.UseRealExternalInitiatorManager) @@ -925,7 +926,7 @@ func TestRunner_Error_Callback_AsyncJob(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { t := true c.JobPipeline.ExternalInitiatorsEnabled = &t - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(10 * time.Millisecond) }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient, cltest.UseRealExternalInitiatorManager) diff --git a/core/services/keeper/integration_test.go b/core/services/keeper/integration_test.go index c35ebc81b7f..af95788029f 100644 --- a/core/services/keeper/integration_test.go +++ b/core/services/keeper/integration_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/link_token_interface" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" @@ -36,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/store/models" webpresenters "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -238,8 +238,8 @@ func TestKeeperEthIntegration(t *testing.T) { // setup app config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = &test.eip1559 - c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps - c.Keeper.Registry.SyncInterval = models.MustNewDuration(24 * time.Hour) // disable full sync ticker for test + c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps + c.Keeper.Registry.SyncInterval = commonconfig.MustNewDuration(24 * time.Hour) // disable full sync ticker for test c.Keeper.TurnLookBack = ptr[int64](0) // testing doesn't need to do far look back @@ -396,8 +396,8 @@ func TestKeeperForwarderEthIntegration(t *testing.T) { config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = ptr(true) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) - c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps - c.Keeper.Registry.SyncInterval = models.MustNewDuration(24 * time.Hour) // disable full sync ticker for test + c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps + c.Keeper.Registry.SyncInterval = commonconfig.MustNewDuration(24 * time.Hour) // disable full sync ticker for test c.Keeper.TurnLookBack = ptr[int64](0) // testing doesn't need to do far look back @@ -541,9 +541,9 @@ func TestMaxPerformDataSize(t *testing.T) { // setup app config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps - c.Keeper.Registry.SyncInterval = models.MustNewDuration(24 * time.Hour) // disable full sync ticker for test - c.Keeper.Registry.MaxPerformDataSize = ptr(uint32(maxPerformDataSize)) // set the max perform data size + c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps + c.Keeper.Registry.SyncInterval = commonconfig.MustNewDuration(24 * time.Hour) // disable full sync ticker for test + c.Keeper.Registry.MaxPerformDataSize = ptr(uint32(maxPerformDataSize)) // set the max perform data size c.Keeper.TurnLookBack = ptr[int64](0) // testing doesn't need to do far look back diff --git a/core/services/nurse.go b/core/services/nurse.go index 3d896a80ff3..1b44beea21c 100644 --- a/core/services/nurse.go +++ b/core/services/nurse.go @@ -17,9 +17,9 @@ import ( "github.com/google/pprof/profile" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -40,14 +40,14 @@ type Nurse struct { type Config interface { BlockProfileRate() int CPUProfileRate() int - GatherDuration() models.Duration - GatherTraceDuration() models.Duration + GatherDuration() commonconfig.Duration + GatherTraceDuration() commonconfig.Duration GoroutineThreshold() int MaxProfileSize() utils.FileSize MemProfileRate() int MemThreshold() utils.FileSize MutexProfileFraction() int - PollInterval() models.Duration + PollInterval() commonconfig.Duration ProfileRoot() string } diff --git a/core/services/nurse_test.go b/core/services/nurse_test.go index 79f57d91235..46ebd749036 100644 --- a/core/services/nurse_test.go +++ b/core/services/nurse_test.go @@ -9,18 +9,18 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type mockConfig struct { t *testing.T root string - pollInterval *models.Duration - gatherDuration *models.Duration - traceDuration *models.Duration + pollInterval *commonconfig.Duration + gatherDuration *commonconfig.Duration + traceDuration *commonconfig.Duration profileSize utils.FileSize cpuProfileRate int memProfileRate int @@ -40,9 +40,9 @@ var ( func newMockConfig(t *testing.T) *mockConfig { return &mockConfig{ root: t.TempDir(), - pollInterval: models.MustNewDuration(testInterval), - gatherDuration: models.MustNewDuration(testDuration), - traceDuration: models.MustNewDuration(testDuration), + pollInterval: commonconfig.MustNewDuration(testInterval), + gatherDuration: commonconfig.MustNewDuration(testDuration), + traceDuration: commonconfig.MustNewDuration(testDuration), profileSize: utils.FileSize(testSize), memProfileRate: runtime.MemProfileRate, blockProfileRate: testRate, @@ -57,15 +57,15 @@ func (c mockConfig) ProfileRoot() string { return c.root } -func (c mockConfig) PollInterval() models.Duration { +func (c mockConfig) PollInterval() commonconfig.Duration { return *c.pollInterval } -func (c mockConfig) GatherDuration() models.Duration { +func (c mockConfig) GatherDuration() commonconfig.Duration { return *c.gatherDuration } -func (c mockConfig) GatherTraceDuration() models.Duration { +func (c mockConfig) GatherTraceDuration() commonconfig.Duration { return *c.traceDuration } diff --git a/core/services/ocr/validate_test.go b/core/services/ocr/validate_test.go index 4d8a9ae26c8..e55c5d1a484 100644 --- a/core/services/ocr/validate_test.go +++ b/core/services/ocr/validate_test.go @@ -11,13 +11,13 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestValidateOracleSpec(t *testing.T) { @@ -359,7 +359,7 @@ answer1 [type=median index=0]; require.Contains(t, err.Error(), "data source timeout must be between 1s and 20s, but is currently 20m0s") }, overrides: func(c *chainlink.Config, s *chainlink.Secrets) { - c.OCR.ObservationTimeout = models.MustNewDuration(20 * time.Minute) + c.OCR.ObservationTimeout = commonconfig.MustNewDuration(20 * time.Minute) }, }, } diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index 9388b93329b..5ea1d3e4030 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -28,6 +28,7 @@ import ( confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -318,14 +319,14 @@ func StartNewNode( c.P2P.PeerID = ptr(p2pKey.PeerID()) c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(500 * time.Millisecond) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", port)} if len(p2pV2Bootstrappers) > 0 { c.P2P.V2.DefaultBootstrappers = &p2pV2Bootstrappers } - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) c.EVM[0].Transactions.ForwardersEnabled = ptr(false) c.EVM[0].GasEstimator.LimitDefault = ptr(maxGas) c.EVM[0].GasEstimator.Mode = ptr("FixedPrice") diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index 68fb27fa82e..473db53bc6f 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -40,7 +41,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) var _ pb.MercuryServer = &mercuryServer{} @@ -199,8 +199,8 @@ func setupNode( c.P2P.V2.Enabled = ptr(true) c.P2P.V2.AnnounceAddresses = &p2paddresses c.P2P.V2.ListenAddresses = &p2paddresses - c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(500 * time.Millisecond) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) }) lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel) diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index d0a93f77631..56467c60abb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -32,6 +32,7 @@ import ( ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -123,8 +124,8 @@ func setupNode( c.P2P.PeerID = ptr(p2pKey.PeerID()) c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(500 * time.Millisecond) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) c.P2P.V2.AnnounceAddresses = &p2paddresses c.P2P.V2.ListenAddresses = &p2paddresses if len(p2pV2Bootstrappers) > 0 { diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index 55517d5719d..4a01ee7904f 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" @@ -55,7 +56,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) type ocr2vrfUniverse struct { @@ -234,8 +234,8 @@ func setupNodeOCR2( c.P2P.PeerID = ptr(p2pKey.PeerID()) c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.DeltaDial = models.MustNewDuration(500 * time.Millisecond) - c.P2P.V2.DeltaReconcile = models.MustNewDuration(5 * time.Second) + c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(500 * time.Millisecond) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(5 * time.Second) c.P2P.V2.ListenAddresses = &[]string{fmt.Sprintf("127.0.0.1:%d", port)} if len(p2pV2Bootstrappers) > 0 { c.P2P.V2.DefaultBootstrappers = &p2pV2Bootstrappers @@ -244,10 +244,10 @@ func setupNodeOCR2( c.OCR.Enabled = ptr(false) c.OCR2.Enabled = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(500 * time.Millisecond) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(500 * time.Millisecond) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](3_500_000) c.EVM[0].Transactions.ForwardersEnabled = &useForwarders - c.OCR2.ContractPollInterval = models.MustNewDuration(10 * time.Second) + c.OCR2.ContractPollInterval = commonconfig.MustNewDuration(10 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, b, p2pKey) diff --git a/core/services/ocr2/validate/validate_test.go b/core/services/ocr2/validate/validate_test.go index b03f08f6b08..52dbe5f0042 100644 --- a/core/services/ocr2/validate/validate_test.go +++ b/core/services/ocr2/validate/validate_test.go @@ -11,13 +11,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" medianconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestValidateOracleSpec(t *testing.T) { @@ -308,7 +308,7 @@ chainID = 1337 require.Contains(t, err.Error(), "database timeout must be between 100ms and 10s, but is currently 20m0s") }, overrides: func(c *chainlink.Config, s *chainlink.Secrets) { - c.OCR2.DatabaseTimeout = models.MustNewDuration(20 * time.Minute) + c.OCR2.DatabaseTimeout = commonconfig.MustNewDuration(20 * time.Minute) }, }, { diff --git a/core/services/ocrcommon/peer_wrapper_test.go b/core/services/ocrcommon/peer_wrapper_test.go index 278e0684fd2..f46b2af27c5 100644 --- a/core/services/ocrcommon/peer_wrapper_test.go +++ b/core/services/ocrcommon/peer_wrapper_test.go @@ -9,6 +9,7 @@ import ( ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -18,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func Test_SingletonPeerWrapper_Start(t *testing.T) { @@ -123,8 +123,8 @@ func Test_SingletonPeerWrapper_Close(t *testing.T) { cfg = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.P2P.V2.Enabled = ptr(true) c.P2P.PeerID = ptr(k.PeerID()) - c.P2P.V2.DeltaDial = models.MustNewDuration(100 * time.Millisecond) - c.P2P.V2.DeltaReconcile = models.MustNewDuration(1 * time.Second) + c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(100 * time.Millisecond) + c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(1 * time.Second) p2paddresses := []string{ "127.0.0.1:17193", diff --git a/core/services/pg/locked_db_test.go b/core/services/pg/locked_db_test.go index a2aebcd57f4..ed0935c1411 100644 --- a/core/services/pg/locked_db_test.go +++ b/core/services/pg/locked_db_test.go @@ -5,12 +5,12 @@ import ( "testing" "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/stretchr/testify/require" ) @@ -18,8 +18,8 @@ import ( func lease(c *chainlink.Config, s *chainlink.Secrets) { t := true c.Database.Lock.Enabled = &t - c.Database.Lock.LeaseDuration = models.MustNewDuration(10 * time.Second) - c.Database.Lock.LeaseRefreshInterval = models.MustNewDuration(time.Second) + c.Database.Lock.LeaseDuration = commonconfig.MustNewDuration(10 * time.Second) + c.Database.Lock.LeaseRefreshInterval = commonconfig.MustNewDuration(time.Second) } func TestLockedDB_HappyPath(t *testing.T) { diff --git a/core/services/pipeline/common.go b/core/services/pipeline/common.go index 0e72bed4226..ae82e5b3b81 100644 --- a/core/services/pipeline/common.go +++ b/core/services/pipeline/common.go @@ -20,11 +20,11 @@ import ( pkgerrors "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" cutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/logger" cnull "github.com/smartcontractkit/chainlink/v2/core/null" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -65,7 +65,7 @@ type ( Config interface { DefaultHTTPLimit() int64 - DefaultHTTPTimeout() models.Duration + DefaultHTTPTimeout() commonconfig.Duration MaxRunDuration() time.Duration ReaperInterval() time.Duration ReaperThreshold() time.Duration diff --git a/core/services/pipeline/mocks/config.go b/core/services/pipeline/mocks/config.go index 4cd6bc7a724..581a84dc049 100644 --- a/core/services/pipeline/mocks/config.go +++ b/core/services/pipeline/mocks/config.go @@ -3,7 +3,7 @@ package mocks import ( - models "github.com/smartcontractkit/chainlink/v2/core/store/models" + config "github.com/smartcontractkit/chainlink-common/pkg/config" mock "github.com/stretchr/testify/mock" time "time" @@ -33,18 +33,18 @@ func (_m *Config) DefaultHTTPLimit() int64 { } // DefaultHTTPTimeout provides a mock function with given fields: -func (_m *Config) DefaultHTTPTimeout() models.Duration { +func (_m *Config) DefaultHTTPTimeout() config.Duration { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for DefaultHTTPTimeout") } - var r0 models.Duration - if rf, ok := ret.Get(0).(func() models.Duration); ok { + var r0 config.Duration + if rf, ok := ret.Get(0).(func() config.Duration); ok { r0 = rf() } else { - r0 = ret.Get(0).(models.Duration) + r0 = ret.Get(0).(config.Duration) } return r0 diff --git a/core/services/pipeline/task.bridge_test.go b/core/services/pipeline/task.bridge_test.go index 03a804c9c12..bf1e63d6314 100644 --- a/core/services/pipeline/task.bridge_test.go +++ b/core/services/pipeline/task.bridge_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -270,7 +271,7 @@ func TestBridgeTask_DoesNotReturnStaleResults(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.WebServer.BridgeCacheTTL = models.MustNewDuration(30 * time.Second) + c.WebServer.BridgeCacheTTL = commonconfig.MustNewDuration(30 * time.Second) }) queryer := pg.NewQ(db, logger.TestLogger(t), cfg.Database()) s1 := httptest.NewServer(fakeIntermittentlyFailingPriceResponder(t, utils.MustUnmarshalToMap(btcUSDPairing), decimal.NewFromInt(9700), "", nil)) @@ -336,7 +337,7 @@ func TestBridgeTask_DoesNotReturnStaleResults(t *testing.T) { require.Equal(t, string(big.NewInt(9700).Bytes()), result2.Value) cfg2 := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.WebServer.BridgeCacheTTL = models.MustNewDuration(0 * time.Second) + c.WebServer.BridgeCacheTTL = commonconfig.MustNewDuration(0 * time.Second) }) task.HelperSetDependencies(cfg2.JobPipeline(), cfg2.WebServer(), orm, specID, uuid.UUID{}, c) diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 8564be8466f..23752151cd0 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -22,7 +23,6 @@ import ( mocks3 "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" mocks2 "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/mocks" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func setupMockConfig(t *testing.T, useBatchSend bool) *mocks.TelemetryIngress { @@ -291,7 +291,7 @@ func TestCorrectEndpointRouting(t *testing.T) { func TestLegacyMode(t *testing.T) { tic := setupMockConfig(t, true) tic.On("Endpoints").Return(nil) - url, err := models.ParseURL("test.test") + url, err := commonconfig.ParseURL("test.test") require.NoError(t, err) tic.On("URL").Return(url.URL()) tic.On("ServerPubKey").Return("some-pub-key") diff --git a/core/services/vrf/v1/integration_test.go b/core/services/vrf/v1/integration_test.go index 3e9cfbe0870..b10ca160858 100644 --- a/core/services/vrf/v1/integration_test.go +++ b/core/services/vrf/v1/integration_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" @@ -28,7 +29,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" ) @@ -133,7 +133,7 @@ func TestIntegration_VRF_WithBHS(t *testing.T) { c.EVM[0].BlockBackfillDepth = ptr[uint32](500) c.Feature.LogPoller = ptr(true) c.EVM[0].FinalityDepth = ptr[uint32](2) - c.EVM[0].LogPollInterval = models.MustNewDuration(time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(time.Second) c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) }) key := cltest.MustGenerateRandomKey(t) diff --git a/core/services/vrf/v2/bhs_feeder_test.go b/core/services/vrf/v2/bhs_feeder_test.go index 31a4ff815a9..a02eea75757 100644 --- a/core/services/vrf/v2/bhs_feeder_test.go +++ b/core/services/vrf/v2/bhs_feeder_test.go @@ -4,6 +4,9 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -11,9 +14,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - - "github.com/stretchr/testify/require" ) func TestStartHeartbeats(t *testing.T) { @@ -56,7 +56,7 @@ func TestStartHeartbeats(t *testing.T) { c.Feature.LogPoller = ptr(true) c.EVM[0].FinalityDepth = ptr[uint32](2) c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(gasLimit)) - c.EVM[0].LogPollInterval = models.MustNewDuration(time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(time.Second) }) heartbeatPeriod := 5 * time.Second diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index b5f6a095cff..47d0089ade6 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -35,7 +36,6 @@ import ( v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -74,7 +74,7 @@ func testSingleConsumerHappyPath( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1, key2) @@ -208,7 +208,7 @@ func testMultipleConsumersNeedBHS( simulatedOverrides(t, assets.GWei(10), keySpecificOverrides...)(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) c.EVM[0].FinalityDepth = ptr[uint32](2) }) keys = append(keys, ownerKey, vrfKey) @@ -356,7 +356,7 @@ func testMultipleConsumersNeedTrustedBHS( c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(5_000_000)) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) c.EVM[0].FinalityDepth = ptr[uint32](2) }) keys = append(keys, ownerKey, vrfKey) @@ -543,7 +543,7 @@ func testSingleConsumerHappyPathBatchFulfillment( c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) @@ -647,7 +647,7 @@ func testSingleConsumerNeedsTopUp( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key) @@ -753,7 +753,7 @@ func testBlockHeaderFeeder( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) c.EVM[0].FinalityDepth = ptr[uint32](2) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, vrfKey, bhfKey) @@ -912,7 +912,7 @@ func testSingleConsumerForcedFulfillment( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1, key2) @@ -1075,7 +1075,7 @@ func testSingleConsumerEIP150( c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(3.5e6)) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) consumer := uni.vrfConsumers[0] @@ -1145,7 +1145,7 @@ func testSingleConsumerEIP150Revert( c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(gasLimit)) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) consumer := uni.vrfConsumers[0] @@ -1210,7 +1210,7 @@ func testSingleConsumerBigGasCallbackSandwich( c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) consumer := uni.vrfConsumers[0] @@ -1332,7 +1332,7 @@ func testSingleConsumerMultipleGasLanes( c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, cheapKey, expensiveKey) @@ -1449,7 +1449,7 @@ func testSingleConsumerAlwaysRevertingCallbackStillFulfilled( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key) consumer := uni.reverter @@ -1522,7 +1522,7 @@ func testConsumerProxyHappyPath( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1, key2) consumerOwner := uni.neil @@ -1648,7 +1648,7 @@ func testMaliciousConsumer( c.EVM[0].GasEstimator.FeeCapDefault = assets.GWei(1) c.EVM[0].ChainID = (*ubig.Big)(testutils.SimulatedChainID) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) carol := uni.vrfConsumers[0] diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index fb47d84ed7a..e5eed6f09bc 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -50,7 +51,6 @@ import ( v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) type coordinatorV2PlusUniverse struct { @@ -1147,7 +1147,7 @@ func TestVRFV2PlusIntegration_Migration(t *testing.T) { c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index dee69c9abef..d415793ede4 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -30,6 +30,7 @@ import ( "github.com/jmoiron/sqlx" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" @@ -82,7 +83,6 @@ import ( v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -1227,7 +1227,7 @@ func TestVRFV2Integration_Wrapper_High_Gas(t *testing.T) { c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](3_500_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) }) ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2Universe(t, ownerKey, 1) @@ -1466,13 +1466,13 @@ func simulatedOverrides(t *testing.T, defaultGasPrice *assets.Wei, ks ...toml.Ke c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](3_500_000) c.Feature.LogPoller = ptr(true) - c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) + c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(1 * time.Second) c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) - c.EVM[0].HeadTracker.SamplingInterval = models.MustNewDuration(0) // Head sampling disabled + c.EVM[0].HeadTracker.SamplingInterval = commonconfig.MustNewDuration(0) // Head sampling disabled - c.EVM[0].Transactions.ResendAfterThreshold = models.MustNewDuration(0) - c.EVM[0].Transactions.ReaperThreshold = models.MustNewDuration(100 * time.Millisecond) + c.EVM[0].Transactions.ResendAfterThreshold = commonconfig.MustNewDuration(0) + c.EVM[0].Transactions.ReaperThreshold = commonconfig.MustNewDuration(100 * time.Millisecond) c.EVM[0].FinalityDepth = ptr[uint32](15) c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) diff --git a/core/sessions/ldapauth/helpers_test.go b/core/sessions/ldapauth/helpers_test.go index 3566ea84380..5c19afd17d3 100644 --- a/core/sessions/ldapauth/helpers_test.go +++ b/core/sessions/ldapauth/helpers_test.go @@ -5,11 +5,11 @@ import ( "github.com/jmoiron/sqlx" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) // Returns an instantiated ldapAuthenticator struct without validation for testing @@ -66,16 +66,16 @@ func (t *TestConfig) ServerTLS() bool { return false } -func (t *TestConfig) SessionTimeout() models.Duration { - return models.MustMakeDuration(time.Duration(0)) +func (t *TestConfig) SessionTimeout() commonconfig.Duration { + return *commonconfig.MustNewDuration(time.Duration(0)) } func (t *TestConfig) QueryTimeout() time.Duration { return time.Duration(0) } -func (t *TestConfig) UserAPITokenDuration() models.Duration { - return models.MustMakeDuration(time.Duration(0)) +func (t *TestConfig) UserAPITokenDuration() commonconfig.Duration { + return *commonconfig.MustNewDuration(time.Duration(0)) } func (t *TestConfig) BaseUserAttr() string { @@ -122,10 +122,10 @@ func (t *TestConfig) UserApiTokenEnabled() bool { return true } -func (t *TestConfig) UpstreamSyncInterval() models.Duration { - return models.MustMakeDuration(time.Duration(0)) +func (t *TestConfig) UpstreamSyncInterval() commonconfig.Duration { + return *commonconfig.MustNewDuration(time.Duration(0)) } -func (t *TestConfig) UpstreamSyncRateLimit() models.Duration { - return models.MustMakeDuration(time.Duration(0)) +func (t *TestConfig) UpstreamSyncRateLimit() commonconfig.Duration { + return *commonconfig.MustNewDuration(time.Duration(0)) } diff --git a/core/sessions/localauth/reaper.go b/core/sessions/localauth/reaper.go index 48112456418..eef884367aa 100644 --- a/core/sessions/localauth/reaper.go +++ b/core/sessions/localauth/reaper.go @@ -4,9 +4,9 @@ import ( "database/sql" "time" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) type sessionReaper struct { @@ -16,8 +16,8 @@ type sessionReaper struct { } type SessionReaperConfig interface { - SessionTimeout() models.Duration - SessionReaperExpiration() models.Duration + SessionTimeout() commonconfig.Duration + SessionReaperExpiration() commonconfig.Duration } // NewSessionReaper creates a reaper that cleans stale sessions from the store. diff --git a/core/sessions/localauth/reaper_test.go b/core/sessions/localauth/reaper_test.go index 43a263d0321..fa9d882f743 100644 --- a/core/sessions/localauth/reaper_test.go +++ b/core/sessions/localauth/reaper_test.go @@ -4,27 +4,28 @@ import ( "testing" "time" + "github.com/onsi/gomega" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - - "github.com/onsi/gomega" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) type sessionReaperConfig struct{} -func (c sessionReaperConfig) SessionTimeout() models.Duration { - return models.MustMakeDuration(42 * time.Second) +func (c sessionReaperConfig) SessionTimeout() commonconfig.Duration { + return *commonconfig.MustNewDuration(42 * time.Second) } -func (c sessionReaperConfig) SessionReaperExpiration() models.Duration { - return models.MustMakeDuration(142 * time.Second) +func (c sessionReaperConfig) SessionReaperExpiration() commonconfig.Duration { + return *commonconfig.MustNewDuration(142 * time.Second) } func TestSessionReaper_ReapSessions(t *testing.T) { diff --git a/core/store/models/common.go b/core/store/models/common.go index 93cc708fe0b..e446481ff64 100644 --- a/core/store/models/common.go +++ b/core/store/models/common.go @@ -197,119 +197,6 @@ func (c Cron) String() string { return string(c) } -// Duration is a non-negative time duration. -type Duration struct{ d time.Duration } - -func MakeDuration(d time.Duration) (Duration, error) { - if d < time.Duration(0) { - return Duration{}, fmt.Errorf("cannot make negative time duration: %s", d) - } - return Duration{d: d}, nil -} - -func ParseDuration(s string) (Duration, error) { - d, err := time.ParseDuration(s) - if err != nil { - return Duration{}, err - } - - return MakeDuration(d) -} - -func MustMakeDuration(d time.Duration) Duration { - rv, err := MakeDuration(d) - if err != nil { - panic(err) - } - return rv -} - -func MustNewDuration(d time.Duration) *Duration { - rv := MustMakeDuration(d) - return &rv -} - -// Duration returns the value as the standard time.Duration value. -func (d Duration) Duration() time.Duration { - return d.d -} - -// Before returns the time d units before time t -func (d Duration) Before(t time.Time) time.Time { - return t.Add(-d.Duration()) -} - -// Shorter returns true if and only if d is shorter than od. -func (d Duration) Shorter(od Duration) bool { return d.d < od.d } - -// IsInstant is true if and only if d is of duration 0 -func (d Duration) IsInstant() bool { return d.d == 0 } - -// String returns a string representing the duration in the form "72h3m0.5s". -// Leading zero units are omitted. As a special case, durations less than one -// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure -// that the leading digit is non-zero. The zero duration formats as 0s. -func (d Duration) String() string { - return d.Duration().String() -} - -// MarshalJSON implements the json.Marshaler interface. -func (d Duration) MarshalJSON() ([]byte, error) { - return json.Marshal(d.String()) -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (d *Duration) UnmarshalJSON(input []byte) error { - var txt string - err := json.Unmarshal(input, &txt) - if err != nil { - return err - } - v, err := time.ParseDuration(string(txt)) - if err != nil { - return err - } - *d, err = MakeDuration(v) - if err != nil { - return err - } - return nil -} - -func (d *Duration) Scan(v interface{}) (err error) { - switch tv := v.(type) { - case int64: - *d, err = MakeDuration(time.Duration(tv)) - return err - default: - return errors.Errorf(`don't know how to parse "%s" of type %T as a `+ - `models.Duration`, tv, tv) - } -} - -func (d Duration) Value() (driver.Value, error) { - return int64(d.d), nil -} - -// MarshalText implements the text.Marshaler interface. -func (d Duration) MarshalText() ([]byte, error) { - return []byte(d.d.String()), nil -} - -// UnmarshalText implements the text.Unmarshaler interface. -func (d *Duration) UnmarshalText(input []byte) error { - v, err := time.ParseDuration(string(input)) - if err != nil { - return err - } - pd, err := MakeDuration(v) - if err != nil { - return err - } - *d = pd - return nil -} - // Interval represents a time.Duration stored as a Postgres interval type type Interval time.Duration @@ -513,60 +400,6 @@ func (s Sha256Hash) Value() (driver.Value, error) { return b, nil } -// URL extends url.URL to implement encoding.TextMarshaler. -type URL url.URL - -func ParseURL(s string) (*URL, error) { - u, err := url.Parse(s) - if err != nil { - return nil, err - } - return (*URL)(u), nil -} - -func MustParseURL(s string) *URL { - u, err := ParseURL(s) - if err != nil { - panic(err) - } - return u -} - -func (u *URL) String() string { - return (*url.URL)(u).String() -} - -// URL returns a copy of u as a *url.URL -func (u *URL) URL() *url.URL { - if u == nil { - return nil - } - // defensive copy - r := url.URL(*u) - if u.User != nil { - r.User = new(url.Userinfo) - *r.User = *u.User - } - return &r -} - -func (u *URL) IsZero() bool { - return (url.URL)(*u) == url.URL{} -} - -func (u *URL) MarshalText() ([]byte, error) { - return []byte(u.String()), nil -} - -func (u *URL) UnmarshalText(input []byte) error { - v, err := url.Parse(string(input)) - if err != nil { - return err - } - *u = URL(*v) - return nil -} - // ServiceHeader is an HTTP header to include in POST to log service. type ServiceHeader struct { Header string diff --git a/core/store/models/common_test.go b/core/store/models/common_test.go index 57b7ca73c6b..eca703d5f3a 100644 --- a/core/store/models/common_test.go +++ b/core/store/models/common_test.go @@ -189,27 +189,6 @@ func TestWebURL_String_HasNilURL(t *testing.T) { assert.Equal(t, "", w.String()) } -func TestDuration_MarshalJSON(t *testing.T) { - tests := []struct { - name string - input models.Duration - want string - }{ - {"zero", models.MustMakeDuration(0), `"0s"`}, - {"one second", models.MustMakeDuration(time.Second), `"1s"`}, - {"one minute", models.MustMakeDuration(time.Minute), `"1m0s"`}, - {"one hour", models.MustMakeDuration(time.Hour), `"1h0m0s"`}, - {"one hour thirty minutes", models.MustMakeDuration(time.Hour + 30*time.Minute), `"1h30m0s"`}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - b, err := json.Marshal(test.input) - assert.NoError(t, err) - assert.Equal(t, test.want, string(b)) - }) - } -} - func TestCron_UnmarshalJSON_Success(t *testing.T) { t.Parallel() @@ -385,49 +364,6 @@ func TestInterval_MarshalText_UnmarshalText(t *testing.T) { require.Equal(t, i, iNew) } -func TestDuration_Scan_Value(t *testing.T) { - t.Parallel() - - d := models.MustMakeDuration(100) - require.NotNil(t, d) - - val, err := d.Value() - require.NoError(t, err) - - dNew := models.MustMakeDuration(0) - err = dNew.Scan(val) - require.NoError(t, err) - - require.Equal(t, d, dNew) -} - -func TestDuration_MarshalJSON_UnmarshalJSON(t *testing.T) { - t.Parallel() - - d := models.MustMakeDuration(100) - require.NotNil(t, d) - - json, err := d.MarshalJSON() - require.NoError(t, err) - - dNew := models.MustMakeDuration(0) - err = dNew.UnmarshalJSON(json) - require.NoError(t, err) - - require.Equal(t, d, dNew) -} - -func TestDuration_MakeDurationFromString(t *testing.T) { - t.Parallel() - - d, err := models.ParseDuration("1s") - require.NoError(t, err) - require.Equal(t, 1*time.Second, d.Duration()) - - _, err = models.ParseDuration("xyz") - require.Error(t, err) -} - func TestWebURL_Scan_Value(t *testing.T) { t.Parallel() diff --git a/core/store/models/secrets.go b/core/store/models/secrets.go index a7afc95b6cc..ac5cac01734 100644 --- a/core/store/models/secrets.go +++ b/core/store/models/secrets.go @@ -4,6 +4,8 @@ import ( "encoding" "fmt" "net/url" + + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) const redacted = "xxxxx" @@ -33,22 +35,22 @@ var ( ) // SecretURL is a URL that formats and encodes redacted, as "xxxxx". -type SecretURL URL +type SecretURL commonconfig.URL -func NewSecretURL(u *URL) *SecretURL { return (*SecretURL)(u) } +func NewSecretURL(u *commonconfig.URL) *SecretURL { return (*SecretURL)(u) } -func MustSecretURL(u string) *SecretURL { return NewSecretURL(MustParseURL(u)) } +func MustSecretURL(u string) *SecretURL { return NewSecretURL(commonconfig.MustParseURL(u)) } func (s *SecretURL) String() string { return redacted } func (s *SecretURL) GoString() string { return redacted } -func (s *SecretURL) URL() *url.URL { return (*URL)(s).URL() } +func (s *SecretURL) URL() *url.URL { return (*commonconfig.URL)(s).URL() } func (s *SecretURL) MarshalText() ([]byte, error) { return []byte(redacted), nil } func (s *SecretURL) UnmarshalText(text []byte) error { - if err := (*URL)(s).UnmarshalText(text); err != nil { + if err := (*commonconfig.URL)(s).UnmarshalText(text); err != nil { //opt: if errors.Is(url.Error), just redact the err.URL field? return fmt.Errorf("failed to parse url: %s", redacted) } diff --git a/core/web/evm_transfer_controller_test.go b/core/web/evm_transfer_controller_test.go index cff996dd21c..86d6b4618aa 100644 --- a/core/web/evm_transfer_controller_test.go +++ b/core/web/evm_transfer_controller_test.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -295,7 +296,7 @@ func TestTransfersController_CreateSuccess_eip1559(t *testing.T) { c.EVM[0].ChainID = (*ubig.Big)(testutils.FixtureChainID) // NOTE: FallbackPollInterval is used in this test to quickly create TxAttempts // Testing triggers requires committing transactions and does not work with transactional tests - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(time.Second) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(time.Second) }) app := cltest.NewApplicationWithConfigAndKey(t, config, ethClient, key) diff --git a/core/web/pipeline_runs_controller_test.go b/core/web/pipeline_runs_controller_test.go index 6c45c1eb319..3c57847d187 100644 --- a/core/web/pipeline_runs_controller_test.go +++ b/core/web/pipeline_runs_controller_test.go @@ -18,13 +18,13 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -35,8 +35,8 @@ func TestPipelineRunsController_CreateWithBody_HappyPath(t *testing.T) { ethClient := cltest.NewEthMocksWithStartupAssertions(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(2 * time.Second) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(2 * time.Second) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(10 * time.Millisecond) }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient) @@ -90,8 +90,8 @@ func TestPipelineRunsController_CreateNoBody_HappyPath(t *testing.T) { ethClient := cltest.NewEthMocksWithStartupAssertions(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(2 * time.Second) - c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) + c.JobPipeline.HTTPRequest.DefaultTimeout = commonconfig.MustNewDuration(2 * time.Second) + c.Database.Listener.FallbackPollInterval = commonconfig.MustNewDuration(10 * time.Millisecond) }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient) diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index e9b63c73615..a7aed0e5552 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -8,6 +8,7 @@ import ( "gopkg.in/guregu/null.v4" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" @@ -272,15 +273,15 @@ type VRFSpec struct { CoordinatorAddress ethkey.EIP55Address `json:"coordinatorAddress"` PublicKey secp256k1.PublicKey `json:"publicKey"` FromAddresses []ethkey.EIP55Address `json:"fromAddresses"` - PollPeriod models.Duration `json:"pollPeriod"` + PollPeriod commonconfig.Duration `json:"pollPeriod"` MinIncomingConfirmations uint32 `json:"confirmations"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` EVMChainID *big.Big `json:"evmChainID"` ChunkSize uint32 `json:"chunkSize"` - RequestTimeout models.Duration `json:"requestTimeout"` - BackoffInitialDelay models.Duration `json:"backoffInitialDelay"` - BackoffMaxDelay models.Duration `json:"backoffMaxDelay"` + RequestTimeout commonconfig.Duration `json:"requestTimeout"` + BackoffInitialDelay commonconfig.Duration `json:"backoffInitialDelay"` + BackoffMaxDelay commonconfig.Duration `json:"backoffMaxDelay"` GasLanePrice *assets.Wei `json:"gasLanePrice"` RequestedConfsDelay int64 `json:"requestedConfsDelay"` VRFOwnerAddress *ethkey.EIP55Address `json:"vrfOwnerAddress,omitempty"` @@ -295,15 +296,15 @@ func NewVRFSpec(spec *job.VRFSpec) *VRFSpec { CoordinatorAddress: spec.CoordinatorAddress, PublicKey: spec.PublicKey, FromAddresses: spec.FromAddresses, - PollPeriod: models.MustMakeDuration(spec.PollPeriod), + PollPeriod: *commonconfig.MustNewDuration(spec.PollPeriod), MinIncomingConfirmations: spec.MinIncomingConfirmations, CreatedAt: spec.CreatedAt, UpdatedAt: spec.UpdatedAt, EVMChainID: spec.EVMChainID, ChunkSize: spec.ChunkSize, - RequestTimeout: models.MustMakeDuration(spec.RequestTimeout), - BackoffInitialDelay: models.MustMakeDuration(spec.BackoffInitialDelay), - BackoffMaxDelay: models.MustMakeDuration(spec.BackoffMaxDelay), + RequestTimeout: *commonconfig.MustNewDuration(spec.RequestTimeout), + BackoffInitialDelay: *commonconfig.MustNewDuration(spec.BackoffInitialDelay), + BackoffMaxDelay: *commonconfig.MustNewDuration(spec.BackoffMaxDelay), GasLanePrice: spec.GasLanePrice, RequestedConfsDelay: spec.RequestedConfsDelay, VRFOwnerAddress: spec.VRFOwnerAddress, diff --git a/core/web/resolver/node_test.go b/core/web/resolver/node_test.go index a209a60fc3c..7f4e69ac4ae 100644 --- a/core/web/resolver/node_test.go +++ b/core/web/resolver/node_test.go @@ -6,11 +6,11 @@ import ( gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/pkg/errors" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestResolver_Nodes(t *testing.T) { @@ -124,8 +124,8 @@ func Test_NodeQuery(t *testing.T) { f.App.On("EVMORM").Return(f.Mocks.evmORM) f.Mocks.evmORM.PutChains(toml.EVMConfig{Nodes: []*toml.Node{{ Name: &name, - WSURL: models.MustParseURL("ws://some-url"), - HTTPURL: models.MustParseURL("http://some-url"), + WSURL: commonconfig.MustParseURL("ws://some-url"), + HTTPURL: commonconfig.MustParseURL("http://some-url"), Order: ptr(int32(11)), }}}) }, diff --git a/go.mod b/go.mod index 7fe535a425e..11659ac4645 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 38eaa89993a..4ca0b032a08 100644 --- a/go.sum +++ b/go.sum @@ -1150,8 +1150,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a h1:ViX8kP1/WYW1dLG85Frqpvus1nRGif/1QiK7qeyS+Rc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 h1:zFupIn0S1JYnKUJffFS+m4k+9BUlBG4IHsOpu6kkqKM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 673fbf4dbd5..7d5ce8a9513 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,8 +25,8 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a - github.com/smartcontractkit/chainlink-testing-framework v1.22.1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 + github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 9977ed84b8b..f6a201dc667 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1481,8 +1481,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a h1:ViX8kP1/WYW1dLG85Frqpvus1nRGif/1QiK7qeyS+Rc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231222010926-795676d23c7a/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 h1:zFupIn0S1JYnKUJffFS+m4k+9BUlBG4IHsOpu6kkqKM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= @@ -1493,8 +1493,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f31 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= -github.com/smartcontractkit/chainlink-testing-framework v1.22.1 h1:2XDxU1CTWJruUZv15/VPdaBT1W9ym4OI3I5baRbDhFg= -github.com/smartcontractkit/chainlink-testing-framework v1.22.1/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= +github.com/smartcontractkit/chainlink-testing-framework v1.22.0 h1:Lur628wkrceWgcLmxGZe7Mauwxht4YO71hX9Jj5YslE= +github.com/smartcontractkit/chainlink-testing-framework v1.22.0/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/load/functions/config.go b/integration-tests/load/functions/config.go index ad7e7446afb..d34e8a9f1eb 100644 --- a/integration-tests/load/functions/config.go +++ b/integration-tests/load/functions/config.go @@ -8,7 +8,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) const ( @@ -56,49 +56,49 @@ type Funding struct { } type Soak struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + RequestsPerCall uint32 `toml:"requests_per_call"` + Duration *commonconfig.Duration `toml:"duration"` } type SecretsSoak struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + RequestsPerCall uint32 `toml:"requests_per_call"` + Duration *commonconfig.Duration `toml:"duration"` } type RealSoak struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + RequestsPerCall uint32 `toml:"requests_per_call"` + Duration *commonconfig.Duration `toml:"duration"` } type Stress struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + RequestsPerCall uint32 `toml:"requests_per_call"` + Duration *commonconfig.Duration `toml:"duration"` } type SecretsStress struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + RequestsPerCall uint32 `toml:"requests_per_call"` + Duration *commonconfig.Duration `toml:"duration"` } type RealStress struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + RequestsPerCall uint32 `toml:"requests_per_call"` + Duration *commonconfig.Duration `toml:"duration"` } type GatewayListSoak struct { - RPS int64 `toml:"rps"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + Duration *commonconfig.Duration `toml:"duration"` } type GatewaySetSoak struct { - RPS int64 `toml:"rps"` - Duration *models.Duration `toml:"duration"` + RPS int64 `toml:"rps"` + Duration *commonconfig.Duration `toml:"duration"` } func ReadConfig() (*PerformanceConfig, error) { diff --git a/integration-tests/load/ocr/config.go b/integration-tests/load/ocr/config.go index 2991df3774a..4b324210b09 100644 --- a/integration-tests/load/ocr/config.go +++ b/integration-tests/load/ocr/config.go @@ -8,7 +8,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) const ( @@ -28,22 +28,22 @@ type Common struct { } type Load struct { - TestDuration *models.Duration `toml:"test_duration"` - Rate int64 `toml:"rate"` - RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` - VerificationInterval *models.Duration `toml:"verification_interval"` - VerificationTimeout *models.Duration `toml:"verification_timeout"` - EAChangeInterval *models.Duration `toml:"ea_change_interval"` + TestDuration *commonconfig.Duration `toml:"test_duration"` + Rate int64 `toml:"rate"` + RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` + VerificationInterval *commonconfig.Duration `toml:"verification_interval"` + VerificationTimeout *commonconfig.Duration `toml:"verification_timeout"` + EAChangeInterval *commonconfig.Duration `toml:"ea_change_interval"` } type Volume struct { - TestDuration *models.Duration `toml:"test_duration"` - Rate int64 `toml:"rate"` - VURequestsPerUnit int `toml:"vu_requests_per_unit"` - RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` - VerificationInterval *models.Duration `toml:"verification_interval"` - VerificationTimeout *models.Duration `toml:"verification_timeout"` - EAChangeInterval *models.Duration `toml:"ea_change_interval"` + TestDuration *commonconfig.Duration `toml:"test_duration"` + Rate int64 `toml:"rate"` + VURequestsPerUnit int `toml:"vu_requests_per_unit"` + RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` + VerificationInterval *commonconfig.Duration `toml:"verification_interval"` + VerificationTimeout *commonconfig.Duration `toml:"verification_timeout"` + EAChangeInterval *commonconfig.Duration `toml:"ea_change_interval"` } func ReadConfig() (*PerformanceConfig, error) { diff --git a/integration-tests/load/vrfv2/config.go b/integration-tests/load/vrfv2/config.go index d5e94bb75fb..e97ac6a964e 100644 --- a/integration-tests/load/vrfv2/config.go +++ b/integration-tests/load/vrfv2/config.go @@ -10,7 +10,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) const ( @@ -86,10 +86,10 @@ type PerformanceTestConfig struct { NumberOfSubToCreate int `toml:"number_of_sub_to_create"` RPS int64 `toml:"rps"` - //Duration *models.Duration `toml:"duration"` - RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` - RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` - RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` + //Duration *commonconfig.Duration `toml:"duration"` + RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` + RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` + RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` } func ReadConfig() (*PerformanceConfig, error) { diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index 43bcf44d044..2fc6b6a4c7f 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -8,8 +8,8 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) const ( @@ -86,10 +86,10 @@ type PerformanceTestConfig struct { NumberOfSubToCreate int `toml:"number_of_sub_to_create"` RPS int64 `toml:"rps"` - //Duration *models.Duration `toml:"duration"` - RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` - RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` - RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` + //Duration *commonconfig.Duration `toml:"duration"` + RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` + RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` + RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` } func ReadConfig() (*PerformanceConfig, error) { diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 70b6a8f6856..0bdb6cf1f4e 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -29,16 +29,15 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" ) var utilsABI = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) @@ -1073,7 +1072,7 @@ func setupAutomationTestDocker( // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) - syncInterval := models.MustMakeDuration(5 * time.Minute) + syncInterval := *commonconfig.MustNewDuration(5 * time.Minute) clNodeConfig.Feature.LogPoller = ptr.Ptr[bool](true) clNodeConfig.OCR2.Enabled = ptr.Ptr[bool](true) clNodeConfig.Keeper.TurnLookBack = ptr.Ptr[int64](int64(0)) diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 330d041c8f6..cac5aa93d36 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -11,12 +11,11 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -1100,7 +1099,7 @@ func setupKeeperTest(t *testing.T) ( ) { clNodeConfig := node.NewConfig(node.NewBaseConfig(), node.WithP2Pv2()) turnLookBack := int64(0) - syncInterval := models.MustMakeDuration(5 * time.Second) + syncInterval := *commonconfig.MustNewDuration(5 * time.Second) performGasOverhead := uint32(150000) clNodeConfig.Keeper.TurnLookBack = &turnLookBack clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index d8536c1395e..3508f77555f 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -7,25 +7,22 @@ import ( "os" "time" - "go.uber.org/zap/zapcore" - "github.com/segmentio/ksuid" + "go.uber.org/zap/zapcore" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/config" - + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func NewBaseConfig() *chainlink.Config { @@ -48,7 +45,7 @@ func NewBaseConfig() *chainlink.Config { AllowOrigins: ptr.Ptr("*"), HTTPPort: ptr.Ptr[uint16](6688), SecureCookies: ptr.Ptr(false), - SessionTimeout: models.MustNewDuration(time.Hour * 999), + SessionTimeout: commonconfig.MustNewDuration(time.Hour * 999), TLS: toml.WebServerTLS{ HTTPSPort: ptr.Ptr[uint16](0), }, @@ -186,7 +183,7 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { AutoCreateKey: ptr.Ptr(true), FinalityDepth: ptr.Ptr[uint32](50), MinContractPayment: commonassets.NewLinkFromJuels(0), - LogPollInterval: models.MustNewDuration(1 * time.Second), + LogPollInterval: commonconfig.MustNewDuration(1 * time.Second), HeadTracker: evmcfg.HeadTracker{ HistoryDepth: ptr.Ptr(uint32(100)), }, @@ -237,6 +234,6 @@ func WithVRFv2EVMEstimator(addresses []string, maxGasPriceGWei int64) NodeConfig func WithLogPollInterval(interval time.Duration) NodeConfigOpt { return func(c *chainlink.Config) { - c.EVM[0].Chain.LogPollInterval = models.MustNewDuration(interval) + c.EVM[0].Chain.LogPollInterval = commonconfig.MustNewDuration(interval) } } diff --git a/integration-tests/universal/log_poller/config.go b/integration-tests/universal/log_poller/config.go index 78a0da46bc6..d42520397e8 100644 --- a/integration-tests/universal/log_poller/config.go +++ b/integration-tests/universal/log_poller/config.go @@ -10,7 +10,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) const ( @@ -65,11 +65,11 @@ type WaspConfig struct { } type Load struct { - RPS int64 `toml:"rps"` - LPS int64 `toml:"lps"` - RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` - Duration *models.Duration `toml:"duration"` - CallTimeout *models.Duration `toml:"call_timeout"` + RPS int64 `toml:"rps"` + LPS int64 `toml:"lps"` + RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` + Duration *commonconfig.Duration `toml:"duration"` + CallTimeout *commonconfig.Duration `toml:"call_timeout"` } func ReadConfig(configName string) (*Config, error) { @@ -105,7 +105,7 @@ func (c *Config) OverrideFromEnv() error { } if duration := os.Getenv("LOAD_DURATION"); duration != "" { - d, err := models.ParseDuration(duration) + d, err := commonconfig.ParseDuration(duration) if err != nil { return err } diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 40040fd0aee..8ad115ecec8 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -23,11 +23,18 @@ import ( "github.com/smartcontractkit/wasp" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -35,14 +42,6 @@ import ( le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" ) var ( @@ -1009,7 +1008,7 @@ func setupLogPollerTestDocker( // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) - syncInterval := models.MustMakeDuration(5 * time.Minute) + syncInterval := *commonconfig.MustNewDuration(5 * time.Minute) clNodeConfig.Feature.LogPoller = ptr.Ptr[bool](true) clNodeConfig.OCR2.Enabled = ptr.Ptr[bool](true) clNodeConfig.Keeper.TurnLookBack = ptr.Ptr[int64](int64(0)) @@ -1026,7 +1025,7 @@ func setupLogPollerTestDocker( clNodesCount := 5 var logPolllerSettingsFn = func(chain *evmcfg.Chain) *evmcfg.Chain { - chain.LogPollInterval = models.MustNewDuration(lpPollingInterval) + chain.LogPollInterval = commonconfig.MustNewDuration(lpPollingInterval) chain.FinalityDepth = ptr.Ptr[uint32](uint32(finalityDepth)) chain.FinalityTagEnabled = ptr.Ptr[bool](finalityTagEnabled) return chain diff --git a/integration-tests/utils/common.go b/integration-tests/utils/common.go index 34a40e396d2..cc77e4cc3b0 100644 --- a/integration-tests/utils/common.go +++ b/integration-tests/utils/common.go @@ -4,11 +4,11 @@ import ( "math/big" "net" - "github.com/smartcontractkit/chainlink/v2/core/store/models" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" ) -func MustURL(s string) *models.URL { - var u models.URL +func MustURL(s string) *commonconfig.URL { + var u commonconfig.URL if err := u.UnmarshalText([]byte(s)); err != nil { panic(err) } From e8c68bd183a30f7742f20ac77943ab33db6cc97e Mon Sep 17 00:00:00 2001 From: Francisco de Borja Aranda Castillejo Date: Wed, 10 Jan 2024 19:45:40 +0100 Subject: [PATCH 138/234] Add onRamps support to LinkMon (#11571) * Add onRamps support to LinkMon * apply audit * review changes * add dstChainSelector to setWatchList * transform onRampAddresses into an enumerableMap * move watchList to EnumerableSet * moving vars from internal to private * fix natspec --------- Co-authored-by: Ryan Hall --- .../upkeeps/LinkAvailableBalanceMonitor.sol | 186 ++++++++++--- .../v4.8.3/contracts/access/AccessControl.sol | 247 ++++++++++++++++++ .../contracts/access/IAccessControl.sol | 88 +++++++ .../contracts/utils/introspection/ERC165.sol | 29 ++ .../LinkAvailableBalanceMonitor.test.ts | 223 +++++++++++++++- 5 files changed, 721 insertions(+), 52 deletions(-) create mode 100644 contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol create mode 100644 contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/IAccessControl.sol create mode 100644 contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165.sol diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index 9b9dc2d6b7d..b858800d73a 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -3,7 +3,9 @@ pragma solidity 0.8.19; import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; -import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {AccessControl} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol"; +import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol"; +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; @@ -33,7 +35,10 @@ interface ILinkAvailable { /// this is a "trusless" upkeep, meaning it does not trust the caller of performUpkeep; /// we could save a fair amount of gas and re-write this upkeep for use with Automation v2.0+, /// which has significantly different trust assumptions -contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationCompatibleInterface { +contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInterface, Pausable { + using EnumerableMap for EnumerableMap.UintToAddressMap; + using EnumerableSet for EnumerableSet.AddressSet; + event BalanceUpdated(address indexed addr, uint256 oldBalance, uint256 newBalance); event FundsWithdrawn(uint256 amountWithdrawn, address payee); event UpkeepIntervalSet(uint256 oldUpkeepInterval, uint256 newUpkeepInterval); @@ -54,6 +59,7 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp error InvalidUpkeepInterval(uint8 upkeepInterval); error InvalidLinkTokenAddress(address lt); error InvalidWatchList(); + error InvalidChainSelector(); error DuplicateAddress(address duplicate); struct MonitoredAddress { @@ -63,24 +69,49 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp bool isActive; } - IERC20 private immutable LINK_TOKEN; + bytes32 private constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 private constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); + uint96 private constant DEFAULT_TOP_UP_AMOUNT_JULES = 9000000000000000000; + uint96 private constant DEFAULT_MIN_BALANCE_JULES = 1000000000000000000; + IERC20 private immutable i_linkToken; + uint256 private s_minWaitPeriodSeconds; uint16 private s_maxPerform; uint16 private s_maxCheck; uint8 private s_upkeepInterval; - address[] private s_watchList; - mapping(address targetAddress => MonitoredAddress targetProperties) internal s_targets; - /// @param linkTokenAddress the LINK token address + /// @notice s_watchList contains all the addresses watched by this monitor + /// @dev It mainly provides the length() function + EnumerableSet.AddressSet private s_watchList; + + /// @notice s_targets contains all the addresses watched by this monitor + /// Each key points to a MonitoredAddress with all the needed metadata + mapping(address targetAddress => MonitoredAddress targetProperties) private s_targets; + + /// @notice s_onRampAddresses represents a list of CCIP onRamp addresses watched on this contract + /// There has to be only one onRamp per dstChainSelector. + /// dstChainSelector is needed as we have to track the live onRamp, and delete the onRamp + /// whenever a new one is deployed with the same dstChainSelector. + EnumerableMap.UintToAddressMap private s_onRampAddresses; + + /// @param admin is the administrator address of this contract + /// @param linkToken the LINK token address + /// @param minWaitPeriodSeconds represents the amount of time that has to wait a contract to be funded + /// @param maxPerform maximum amount of contracts to fund + /// @param maxCheck maximum amount of contracts to check + /// @param upkeepInterval randomizes the check for underfunded contracts constructor( - address linkTokenAddress, + address admin, + IERC20 linkToken, uint256 minWaitPeriodSeconds, uint16 maxPerform, uint16 maxCheck, uint8 upkeepInterval - ) ConfirmedOwner(msg.sender) { - if (linkTokenAddress == address(0)) revert InvalidLinkTokenAddress(linkTokenAddress); - LINK_TOKEN = IERC20(linkTokenAddress); + ) { + _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + _setRoleAdmin(EXECUTOR_ROLE, ADMIN_ROLE); + _grantRole(ADMIN_ROLE, admin); + i_linkToken = linkToken; setMinWaitPeriodSeconds(minWaitPeriodSeconds); setMaxPerform(maxPerform); setMaxCheck(maxCheck); @@ -94,18 +125,31 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp function setWatchList( address[] calldata addresses, uint96[] calldata minBalances, - uint96[] calldata topUpAmounts - ) external onlyOwner { - if (addresses.length != minBalances.length || addresses.length != topUpAmounts.length) { + uint96[] calldata topUpAmounts, + uint64[] calldata dstChainSelectors + ) external onlyAdminOrExecutor { + if ( + addresses.length != minBalances.length || + addresses.length != topUpAmounts.length || + addresses.length != dstChainSelectors.length + ) { revert InvalidWatchList(); } - for (uint256 idx = 0; idx < s_watchList.length; idx++) { - delete s_targets[s_watchList[idx]]; + for (uint256 idx = s_watchList.length(); idx > 0; idx--) { + address member = s_watchList.at(idx - 1); + s_watchList.remove(member); + delete s_targets[member]; + } + // s_onRampAddresses is not the same length as s_watchList, so it has + // to be clean in a separate loop + for (uint256 idx = 0; idx < s_onRampAddresses.length(); idx++) { + (uint256 key, ) = s_onRampAddresses.at(idx); + s_onRampAddresses.remove(key); } for (uint256 idx = 0; idx < addresses.length; idx++) { address targetAddress = addresses[idx]; - if (s_targets[targetAddress].isActive) revert DuplicateAddress(addresses[idx]); - if (addresses[idx] == address(0)) revert InvalidWatchList(); + if (s_targets[targetAddress].isActive) revert DuplicateAddress(targetAddress); + if (targetAddress == address(0)) revert InvalidWatchList(); if (topUpAmounts[idx] == 0) revert InvalidWatchList(); s_targets[targetAddress] = MonitoredAddress({ isActive: true, @@ -113,11 +157,55 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp topUpAmount: topUpAmounts[idx], lastTopUpTimestamp: 0 }); + if (dstChainSelectors[idx] > 0) { + s_onRampAddresses.set(dstChainSelectors[idx], targetAddress); + } + s_watchList.add(targetAddress); } - s_watchList = addresses; emit WatchlistUpdated(); } + /// @notice Adds a new address to the watchlist + /// @param targetAddress the address to be added to the watchlist + /// @param dstChainSelector carries a non-zero value in case the targetAddress is an onRamp, otherwise it carries a 0 + /// @dev this function has to be compatible with the event onRampSet(address, dstChainSelector) emitted by + /// the CCIP router. Important detail to know is this event is also emitted when an onRamp is decomissioned, + /// in which case it will carry the proper dstChainSelector along with the 0x0 address + function addToWatchListOrDecomission(address targetAddress, uint64 dstChainSelector) public onlyAdminOrExecutor { + if (s_targets[targetAddress].isActive) revert DuplicateAddress(targetAddress); + bool onRampExists = s_onRampAddresses.contains(dstChainSelector); + // if targetAddress is an existing onRamp, there's a need of cleaning the previous onRamp associated to this dstChainSelector + // there's no need to remove any other address that's not an onRamp + if (dstChainSelector > 0 && onRampExists) { + address oldAddress = s_onRampAddresses.get(dstChainSelector); + removeFromWatchList(oldAddress); + } + // only add the new address if it's not 0x0 + if (targetAddress != address(0)) { + s_onRampAddresses.set(dstChainSelector, targetAddress); + s_targets[targetAddress] = MonitoredAddress({ + isActive: true, + minBalance: DEFAULT_MIN_BALANCE_JULES, + topUpAmount: DEFAULT_TOP_UP_AMOUNT_JULES, + lastTopUpTimestamp: 0 + }); + s_watchList.add(targetAddress); + } else { + // if the address is 0x0, it means the onRamp has ben decomissioned and has to be cleaned + s_onRampAddresses.remove(dstChainSelector); + } + } + + /// @notice Delete an address from the watchlist and sets the target to inactive + /// @param targetAddress the address to be deleted + function removeFromWatchList(address targetAddress) public onlyAdminOrExecutor returns (bool) { + if (s_watchList.remove(targetAddress)) { + delete s_targets[targetAddress]; + return true; + } + return false; + } + /// @notice Gets a list of proxies that are underfunded, up to the s_maxPerform size /// @dev the function starts at a random index in the list to avoid biasing the first /// addresses in the list over latter ones. @@ -127,7 +215,7 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp function sampleUnderfundedAddresses() public view returns (address[] memory) { uint16 maxPerform = s_maxPerform; uint16 maxCheck = s_maxCheck; - uint256 numTargets = s_watchList.length; + uint256 numTargets = s_watchList.length(); uint256 idx = uint256(blockhash(block.number - (block.number % s_upkeepInterval) - 1)) % numTargets; uint256 numToCheck = numTargets < maxCheck ? numTargets : maxCheck; uint256 numFound = 0; @@ -138,7 +226,7 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp numChecked < numToCheck; (idx, numChecked) = ((idx + 1) % numTargets, numChecked + 1) ) { - address targetAddress = s_watchList[idx]; + address targetAddress = s_watchList.at(idx); target = s_targets[targetAddress]; if (_needsFunding(targetAddress, target.minBalance)) { targetsToFund[numFound] = targetAddress; @@ -156,17 +244,19 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp return targetsToFund; } + /// @notice tries to fund an array of target addresses, checking if they're underfunded in the process + /// @param targetAddresses is an array of contract addresses to be funded in case they're underfunded function topUp(address[] memory targetAddresses) public whenNotPaused { MonitoredAddress memory target; - uint256 localBalance = LINK_TOKEN.balanceOf(address(this)); + uint256 localBalance = i_linkToken.balanceOf(address(this)); for (uint256 idx = 0; idx < targetAddresses.length; idx++) { address targetAddress = targetAddresses[idx]; target = s_targets[targetAddress]; if (localBalance >= target.topUpAmount && _needsFunding(targetAddress, target.minBalance)) { - bool success = LINK_TOKEN.transfer(targetAddress, target.topUpAmount); + bool success = i_linkToken.transfer(targetAddress, target.topUpAmount); if (success) { localBalance -= target.topUpAmount; - target.lastTopUpTimestamp = uint56(block.timestamp); + s_targets[targetAddress].lastTopUpTimestamp = uint56(block.timestamp); emit TopUpSucceeded(targetAddress); } else { emit TopUpFailed(targetAddress); @@ -201,7 +291,9 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp } try target.linkAvailableForPayment() returns (int256 balance) { if ( - balance < int256(minBalance) && addressToCheck.lastTopUpTimestamp + s_minWaitPeriodSeconds <= block.timestamp + balance < int256(minBalance) && + addressToCheck.lastTopUpTimestamp + s_minWaitPeriodSeconds <= block.timestamp && + addressToCheck.isActive ) { return true; } @@ -231,14 +323,14 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp /// @notice Withdraws the contract balance in the LINK token. /// @param amount the amount of the LINK to withdraw /// @param payee the address to pay - function withdraw(uint256 amount, address payable payee) external onlyOwner { + function withdraw(uint256 amount, address payable payee) external onlyAdminOrExecutor { if (payee == address(0)) revert InvalidAddress(payee); - LINK_TOKEN.transfer(payee, amount); + i_linkToken.transfer(payee, amount); emit FundsWithdrawn(amount, payee); } /// @notice Sets the minimum balance for the given target address - function setMinBalance(address target, uint96 minBalance) external onlyOwner { + function setMinBalance(address target, uint96 minBalance) external onlyRole(ADMIN_ROLE) { if (target == address(0)) revert InvalidAddress(target); if (minBalance == 0) revert InvalidMinBalance(minBalance); if (!s_targets[target].isActive) revert InvalidWatchList(); @@ -248,7 +340,7 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp } /// @notice Sets the minimum balance for the given target address - function setTopUpAmount(address target, uint96 topUpAmount) external onlyOwner { + function setTopUpAmount(address target, uint96 topUpAmount) external onlyRole(ADMIN_ROLE) { if (target == address(0)) revert InvalidAddress(target); if (topUpAmount == 0) revert InvalidTopUpAmount(topUpAmount); if (!s_targets[target].isActive) revert InvalidWatchList(); @@ -258,28 +350,28 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp } /// @notice Update s_maxPerform - function setMaxPerform(uint16 maxPerform) public onlyOwner { - s_maxPerform = maxPerform; + function setMaxPerform(uint16 maxPerform) public onlyRole(ADMIN_ROLE) { emit MaxPerformSet(s_maxPerform, maxPerform); + s_maxPerform = maxPerform; } /// @notice Update s_maxCheck - function setMaxCheck(uint16 maxCheck) public onlyOwner { - s_maxCheck = maxCheck; + function setMaxCheck(uint16 maxCheck) public onlyRole(ADMIN_ROLE) { emit MaxCheckSet(s_maxCheck, maxCheck); + s_maxCheck = maxCheck; } /// @notice Sets the minimum wait period (in seconds) for addresses between funding - function setMinWaitPeriodSeconds(uint256 minWaitPeriodSeconds) public onlyOwner { - s_minWaitPeriodSeconds = minWaitPeriodSeconds; + function setMinWaitPeriodSeconds(uint256 minWaitPeriodSeconds) public onlyRole(ADMIN_ROLE) { emit MinWaitPeriodSet(s_minWaitPeriodSeconds, minWaitPeriodSeconds); + s_minWaitPeriodSeconds = minWaitPeriodSeconds; } /// @notice Update s_upkeepInterval - function setUpkeepInterval(uint8 upkeepInterval) public onlyOwner { + function setUpkeepInterval(uint8 upkeepInterval) public onlyRole(ADMIN_ROLE) { if (upkeepInterval > 255) revert InvalidUpkeepInterval(upkeepInterval); - s_upkeepInterval = upkeepInterval; emit UpkeepIntervalSet(s_upkeepInterval, upkeepInterval); + s_upkeepInterval = upkeepInterval; } /// @notice Gets maxPerform @@ -304,7 +396,13 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp /// @notice Gets the list of subscription ids being watched function getWatchList() external view returns (address[] memory) { - return s_watchList; + return s_watchList.values(); + } + + /// @notice Gets the onRamp address with the specified dstChainSelector + function getOnRampAddressAtChainSelector(uint64 dstChainSelector) external view returns (address) { + if (dstChainSelector == 0) revert InvalidChainSelector(); + return s_onRampAddresses.get(dstChainSelector); } /// @notice Gets configuration information for an address on the watchlist @@ -315,13 +413,23 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp return (target.isActive, target.minBalance, target.topUpAmount); } + /// @dev Modifier to make a function callable only by executor role or the + /// admin role. + modifier onlyAdminOrExecutor() { + address sender = _msgSender(); + if (!hasRole(ADMIN_ROLE, sender)) { + _checkRole(EXECUTOR_ROLE, sender); + } + _; + } + /// @notice Pause the contract, which prevents executing performUpkeep - function pause() external onlyOwner { + function pause() external onlyRole(ADMIN_ROLE) { _pause(); } /// @notice Unpause the contract - function unpause() external onlyOwner { + function unpause() external onlyRole(ADMIN_ROLE) { _unpause(); } } diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol new file mode 100644 index 00000000000..4e388f9d83d --- /dev/null +++ b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; +import "../utils/Context.sol"; +import "../utils/Strings.sol"; +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Contract module that allows children to implement role-based access + * control mechanisms. This is a lightweight version that doesn't allow enumerating role + * members except through off-chain means by accessing the contract event logs. Some + * applications may benefit from on-chain enumerability, for those cases see + * {AccessControlEnumerable}. + * + * Roles are referred to by their `bytes32` identifier. These should be exposed + * in the external API and be unique. The best way to achieve this is by + * using `public constant` hash digests: + * + * ``` + * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); + * ``` + * + * Roles can be used to represent a set of permissions. To restrict access to a + * function call, use {hasRole}: + * + * ``` + * function foo() public { + * require(hasRole(MY_ROLE, msg.sender)); + * ... + * } + * ``` + * + * Roles can be granted and revoked dynamically via the {grantRole} and + * {revokeRole} functions. Each role has an associated admin role, and only + * accounts that have a role's admin role can call {grantRole} and {revokeRole}. + * + * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means + * that only accounts with this role will be able to grant or revoke other + * roles. More complex role relationships can be created by using + * {_setRoleAdmin}. + * + * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to + * grant and revoke this role. Extra precautions should be taken to secure + * accounts that have been granted it. + */ +abstract contract AccessControl is Context, IAccessControl, ERC165 { + struct RoleData { + mapping(address => bool) members; + bytes32 adminRole; + } + + mapping(bytes32 => RoleData) private _roles; + + bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; + + /** + * @dev Modifier that checks that an account has a specific role. Reverts + * with a standardized message including the required role. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + * + * _Available since v4.1._ + */ + modifier onlyRole(bytes32 role) { + _checkRole(role); + _; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) public view virtual override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @dev Revert with a standard message if `_msgSender()` is missing `role`. + * Overriding this function changes the behavior of the {onlyRole} modifier. + * + * Format of the revert message is described in {_checkRole}. + * + * _Available since v4.6._ + */ + function _checkRole(bytes32 role) internal view virtual { + _checkRole(role, _msgSender()); + } + + /** + * @dev Revert with a standard message if `account` is missing `role`. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + */ + function _checkRole(bytes32 role, address account) internal view virtual { + if (!hasRole(role, account)) { + revert( + string( + abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(account), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ) + ); + } + } + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { + return _roles[role].adminRole; + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + * + * May emit a {RoleGranted} event. + */ + function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _grantRole(role, account); + } + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + * + * May emit a {RoleRevoked} event. + */ + function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _revokeRole(role, account); + } + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been revoked `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + * + * May emit a {RoleRevoked} event. + */ + function renounceRole(bytes32 role, address account) public virtual override { + require(account == _msgSender(), "AccessControl: can only renounce roles for self"); + + _revokeRole(role, account); + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. Note that unlike {grantRole}, this function doesn't perform any + * checks on the calling account. + * + * May emit a {RoleGranted} event. + * + * [WARNING] + * ==== + * This function should only be called from the constructor when setting + * up the initial roles for the system. + * + * Using this function in any other way is effectively circumventing the admin + * system imposed by {AccessControl}. + * ==== + * + * NOTE: This function is deprecated in favor of {_grantRole}. + */ + function _setupRole(bytes32 role, address account) internal virtual { + _grantRole(role, account); + } + + /** + * @dev Sets `adminRole` as ``role``'s admin role. + * + * Emits a {RoleAdminChanged} event. + */ + function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { + bytes32 previousAdminRole = getRoleAdmin(role); + _roles[role].adminRole = adminRole; + emit RoleAdminChanged(role, previousAdminRole, adminRole); + } + + /** + * @dev Grants `role` to `account`. + * + * Internal function without access restriction. + * + * May emit a {RoleGranted} event. + */ + function _grantRole(bytes32 role, address account) internal virtual { + if (!hasRole(role, account)) { + _roles[role].members[account] = true; + emit RoleGranted(role, account, _msgSender()); + } + } + + /** + * @dev Revokes `role` from `account`. + * + * Internal function without access restriction. + * + * May emit a {RoleRevoked} event. + */ + function _revokeRole(bytes32 role, address account) internal virtual { + if (hasRole(role, account)) { + _roles[role].members[account] = false; + emit RoleRevoked(role, account, _msgSender()); + } + } +} \ No newline at end of file diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/IAccessControl.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/IAccessControl.sol new file mode 100644 index 00000000000..efb82a3cb5e --- /dev/null +++ b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/IAccessControl.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) + +pragma solidity ^0.8.0; + +/** + * @dev External interface of AccessControl declared to support ERC165 detection. + */ +interface IAccessControl { + /** + * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` + * + * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + * {RoleAdminChanged} not being emitted signaling this. + * + * _Available since v3.1._ + */ + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); + + /** + * @dev Emitted when `account` is granted `role`. + * + * `sender` is the account that originated the contract call, an admin role + * bearer except when using {AccessControl-_setupRole}. + */ + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Emitted when `account` is revoked `role`. + * + * `sender` is the account that originated the contract call: + * - if using `revokeRole`, it is the admin role bearer + * - if using `renounceRole`, it is the role bearer (i.e. `account`) + */ + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {AccessControl-_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) external view returns (bytes32); + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been granted `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + */ + function renounceRole(bytes32 role, address account) external; +} \ No newline at end of file diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165.sol new file mode 100644 index 00000000000..c682b07a65b --- /dev/null +++ b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + * + * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. + */ +abstract contract ERC165 is IERC165 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} \ No newline at end of file diff --git a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts index 76a3dcfff1b..2627bee34a3 100644 --- a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts +++ b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts @@ -26,8 +26,6 @@ const TARGET_PERFORM_GAS_LIMIT = 2_000_000 const TARGET_CHECK_GAS_LIMIT = 3_500_000 // // ////////////////////////////////////////////////////////////////////////////////////////////////// - -const OWNABLE_ERR = 'Only callable by owner' const INVALID_WATCHLIST_ERR = `InvalidWatchList()` const PAUSED_ERR = 'Pausable: paused' @@ -35,7 +33,6 @@ const zeroLINK = ethers.utils.parseEther('0') const oneLINK = ethers.utils.parseEther('1') const twoLINK = ethers.utils.parseEther('2') const fourLINK = ethers.utils.parseEther('4') -const fiveLINK = ethers.utils.parseEther('5') const tenLINK = ethers.utils.parseEther('10') const oneHundredLINK = ethers.utils.parseEther('100') @@ -61,6 +58,7 @@ let directTarget2: MockContract let watchListAddresses: string[] let watchListMinBalances: BigNumber[] let watchListTopUpAmounts: BigNumber[] +let watchListDstChainSelectors: number[] async function assertContractLinkBalances( balance1: BigNumber, @@ -123,6 +121,7 @@ const setup = async () => { ] watchListMinBalances = [oneLINK, oneLINK, oneLINK, twoLINK, twoLINK] watchListTopUpAmounts = [twoLINK, twoLINK, twoLINK, twoLINK, twoLINK] + watchListDstChainSelectors = [1, 2, 3, 4, 5] await proxy1.mock.aggregator.returns(aggregator1.address) await proxy2.mock.aggregator.returns(aggregator2.address) @@ -152,6 +151,7 @@ const setup = async () => { lt = (await ltFactory.deploy()) as LinkToken labm = await labmFactory.deploy( + owner.address, lt.address, minWaitPeriodSeconds, maxPerform, @@ -171,6 +171,7 @@ const setup = async () => { watchListAddresses, watchListMinBalances, watchListTopUpAmounts, + watchListDstChainSelectors, ) await setTx.wait() } @@ -223,6 +224,42 @@ describe('LinkAvailableBalanceMonitor', () => { }) }) + describe('setMaxPerform()', () => { + it('configures the MaxPerform', async () => { + await labm.connect(owner).setMaxPerform(BigNumber.from(100)) + const report = await labm.getMaxPerform() + assert.equal(report.toString(), '100') + }) + + it('is only callable by the owner', async () => { + await expect(labm.connect(stranger).setMaxPerform(100)).to.be.reverted + }) + }) + + describe('setMaxCheck()', () => { + it('configures the MaxCheck', async () => { + await labm.connect(owner).setMaxCheck(BigNumber.from(100)) + const report = await labm.getMaxCheck() + assert.equal(report.toString(), '100') + }) + + it('is only callable by the owner', async () => { + await expect(labm.connect(stranger).setMaxCheck(100)).to.be.reverted + }) + }) + + describe('setUpkeepInterval()', () => { + it('configures the UpkeepInterval', async () => { + await labm.connect(owner).setUpkeepInterval(BigNumber.from(100)) + const report = await labm.getUpkeepInterval() + assert.equal(report.toString(), '100') + }) + + it('is only callable by the owner', async () => { + await expect(labm.connect(stranger).setUpkeepInterval(100)).to.be.reverted + }) + }) + describe('withdraw()', () => { beforeEach(async () => { const tx = await lt.connect(owner).transfer(labm.address, oneLINK) @@ -260,7 +297,7 @@ describe('LinkAvailableBalanceMonitor', () => { it('Should not allow strangers to withdraw', async () => { const tx = labm.connect(stranger).withdraw(oneLINK, owner.address) - await expect(tx).to.be.revertedWith(OWNABLE_ERR) + await expect(tx).to.be.reverted }) }) @@ -274,22 +311,22 @@ describe('LinkAvailableBalanceMonitor', () => { it('Should not allow strangers to pause / unpause', async () => { const pauseTxStranger = labm.connect(stranger).pause() - await expect(pauseTxStranger).to.be.revertedWith(OWNABLE_ERR) + await expect(pauseTxStranger).to.be.reverted const pauseTxOwner = await labm.connect(owner).pause() await pauseTxOwner.wait() const unpauseTxStranger = labm.connect(stranger).unpause() - await expect(unpauseTxStranger).to.be.revertedWith(OWNABLE_ERR) + await expect(unpauseTxStranger).to.be.reverted }) }) - describe('setWatchList() / addToWatchList() / removeFromWatchlist() / getWatchList()', () => { + describe('setWatchList() / addToWatchListOrDecomissionOrDecomission() / removeFromWatchlist() / getWatchList()', () => { const watchAddress1 = randAddr() const watchAddress2 = randAddr() const watchAddress3 = randAddr() beforeEach(async () => { // reset watchlist to empty before running these tests - await labm.connect(owner).setWatchList([], [], []) + await labm.connect(owner).setWatchList([], [], [], []) const watchList = await labm.getWatchList() assert.deepEqual(watchList, []) }) @@ -298,7 +335,7 @@ describe('LinkAvailableBalanceMonitor', () => { // add first watchlist let tx = await labm .connect(owner) - .setWatchList([watchAddress1], [oneLINK], [oneLINK]) + .setWatchList([watchAddress1], [oneLINK], [oneLINK], [0]) let watchList = await labm.getWatchList() assert.deepEqual(watchList[0], watchAddress1) // add more to watchlist @@ -308,6 +345,7 @@ describe('LinkAvailableBalanceMonitor', () => { [watchAddress1, watchAddress2, watchAddress3], [oneLINK, oneLINK, oneLINK], [oneLINK, oneLINK, oneLINK], + [1, 2, 3], ) await tx.wait() watchList = await labm.getWatchList() @@ -322,6 +360,7 @@ describe('LinkAvailableBalanceMonitor', () => { [watchAddress1, watchAddress2, watchAddress1], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) await expect(tx).to.be.revertedWith(errMsg) }) @@ -334,6 +373,7 @@ describe('LinkAvailableBalanceMonitor', () => { [watchAddress1, watchAddress2, watchAddress1], [oneLINK, oneLINK, oneLINK], [oneLINK, oneLINK, oneLINK], + [1, 2, 3], ) await expect(tx).to.be.revertedWith(errMsg) }) @@ -341,8 +381,8 @@ describe('LinkAvailableBalanceMonitor', () => { it('Should not allow strangers to set the watchlist', async () => { const setTxStranger = labm .connect(stranger) - .setWatchList([watchAddress1], [oneLINK], [oneLINK]) - await expect(setTxStranger).to.be.revertedWith(OWNABLE_ERR) + .setWatchList([watchAddress1], [oneLINK], [oneLINK], [0]) + await expect(setTxStranger).to.be.reverted }) it('Should revert if any of the addresses are empty', async () => { @@ -352,9 +392,143 @@ describe('LinkAvailableBalanceMonitor', () => { [watchAddress1, ethers.constants.AddressZero], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) await expect(tx).to.be.revertedWith(INVALID_WATCHLIST_ERR) }) + + it('Should allow owner to add multiple addresses with dstChainSelector 0 to the watchlist', async () => { + let tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress1, 0) + await tx.wait + let watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress1) + + tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress2, 0) + await tx.wait + watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress1) + assert.deepEqual(watchList[1], watchAddress2) + + tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress3, 0) + await tx.wait + watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress1) + assert.deepEqual(watchList[1], watchAddress2) + assert.deepEqual(watchList[2], watchAddress3) + }) + + it('Should allow owner to add only one address with an unique non-zero dstChainSelector 0 to the watchlist', async () => { + let tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress1, 1) + await tx.wait + let watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress1) + + // 1 is active + let report = await labm.getAccountInfo(watchAddress1) + assert.isTrue(report.isActive) + + tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress2, 1) + await tx.wait + watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress2) + + // 2 is active, 1 should be false + report = await labm.getAccountInfo(watchAddress2) + assert.isTrue(report.isActive) + report = await labm.getAccountInfo(watchAddress1) + assert.isFalse(report.isActive) + + tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress3, 1) + await tx.wait + watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress3) + + // 3 is active, 1 and 2 should be false + report = await labm.getAccountInfo(watchAddress3) + assert.isTrue(report.isActive) + report = await labm.getAccountInfo(watchAddress2) + assert.isFalse(report.isActive) + report = await labm.getAccountInfo(watchAddress1) + assert.isFalse(report.isActive) + }) + + it('Should not add address 0 to the watchlist', async () => { + await labm + .connect(owner) + .addToWatchListOrDecomission(ethers.constants.AddressZero, 1) + expect(await labm.getWatchList()).to.not.contain( + ethers.constants.AddressZero, + ) + }) + + it('Should not allow stangers to add addresses to the watchlist', async () => { + await expect( + labm.connect(stranger).addToWatchListOrDecomission(watchAddress1, 1), + ).to.be.reverted + }) + + it('Should allow owner to remove addresses from the watchlist', async () => { + let tx = await labm + .connect(owner) + .addToWatchListOrDecomission(watchAddress1, 1) + await tx.wait + let watchList = await labm.getWatchList() + assert.deepEqual(watchList[0], watchAddress1) + let report = await labm.getAccountInfo(watchAddress1) + assert.isTrue(report.isActive) + + // remove address + tx = await labm.connect(owner).removeFromWatchList(watchAddress1) + + // address should be false + report = await labm.getAccountInfo(watchAddress1) + assert.isFalse(report.isActive) + + watchList = await labm.getWatchList() + assert.deepEqual(watchList, []) + }) + + it('Should allow only one address per dstChainSelector', async () => { + // add address1 + await labm.connect(owner).addToWatchListOrDecomission(watchAddress1, 1) + expect(await labm.getWatchList()).to.contain(watchAddress1) + + // add address2 + await labm.connect(owner).addToWatchListOrDecomission(watchAddress2, 1) + + // only address2 has to be in the watchlist + const watchlist = await labm.getWatchList() + expect(watchlist).to.not.contain(watchAddress1) + expect(watchlist).to.contain(watchAddress2) + }) + + it('Should delete the onRamp address on a zero-address with same dstChainSelector', async () => { + // add address1 + await labm.connect(owner).addToWatchListOrDecomission(watchAddress1, 1) + expect(await labm.getWatchList()).to.contain(watchAddress1) + + // simulates an onRampSet(zeroAddress, same dstChainSelector) + await labm + .connect(owner) + .addToWatchListOrDecomission(ethers.constants.AddressZero, 1) + + // address1 should be cleaned + const watchlist = await labm.getWatchList() + expect(watchlist).to.not.contain(watchAddress1) + assert.deepEqual(watchlist, []) + }) }) describe('checkUpkeep() / sampleUnderfundedAddresses() [ @skip-coverage ]', () => { @@ -368,6 +542,7 @@ describe('LinkAvailableBalanceMonitor', () => { watchListAddresses, watchListMinBalances, watchListTopUpAmounts, + watchListDstChainSelectors, ) const [should, payload] = await labm.checkUpkeep('0x') @@ -393,6 +568,7 @@ describe('LinkAvailableBalanceMonitor', () => { [aggregator2.address, directTarget1.address, directTarget2.address], [oneLINK, twoLINK, twoLINK], [oneLINK, oneLINK, oneLINK], + [1, 2, 3], ) // all of them are underfunded, return 3 @@ -441,6 +617,7 @@ describe('LinkAvailableBalanceMonitor', () => { let minBalances: BigNumber[] let topUpAmount: BigNumber[] let aggregators: MockContract[] + let dstChainSelectors: number[] beforeEach(async () => { MAX_PERFORM = await labm.getMaxPerform() @@ -449,6 +626,7 @@ describe('LinkAvailableBalanceMonitor', () => { minBalances = [] topUpAmount = [] aggregators = [] + dstChainSelectors = [] const numAggregators = MAX_CHECK + 50 for (let idx = 0; idx < numAggregators; idx++) { const proxy = await deployMockContract( @@ -465,8 +643,14 @@ describe('LinkAvailableBalanceMonitor', () => { minBalances.push(oneLINK) topUpAmount.push(oneLINK) aggregators.push(aggregator) + dstChainSelectors.push(0) } - await labm.setWatchList(proxyAddresses, minBalances, topUpAmount) + await labm.setWatchList( + proxyAddresses, + minBalances, + topUpAmount, + dstChainSelectors, + ) let watchlist = await labm.getWatchList() expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses) assert.equal(watchlist.length, minBalances.length) @@ -521,6 +705,7 @@ describe('LinkAvailableBalanceMonitor', () => { watchListAddresses, watchListMinBalances, watchListTopUpAmounts, + watchListDstChainSelectors, ) }) @@ -563,6 +748,7 @@ describe('LinkAvailableBalanceMonitor', () => { const proxyAddresses = [] const minBalances = [] const topUpAmount = [] + const dstChainSelectors = [] for (let idx = 0; idx < MAX_PERFORM; idx++) { const proxy = await deployMockContract( owner, @@ -577,8 +763,14 @@ describe('LinkAvailableBalanceMonitor', () => { proxyAddresses.push(proxy.address) minBalances.push(oneLINK) topUpAmount.push(oneLINK) + dstChainSelectors.push(0) } - await labm.setWatchList(proxyAddresses, minBalances, topUpAmount) + await labm.setWatchList( + proxyAddresses, + minBalances, + topUpAmount, + dstChainSelectors, + ) let watchlist = await labm.getWatchList() expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses) assert.equal(watchlist.length, minBalances.length) @@ -691,6 +883,7 @@ describe('LinkAvailableBalanceMonitor', () => { [proxy1.address, directTarget1.address], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) const tx = await labm .connect(keeperRegistry) @@ -728,6 +921,7 @@ describe('LinkAvailableBalanceMonitor', () => { [proxy1.address, proxy4.address], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) const tx = await labm .connect(keeperRegistry) @@ -746,6 +940,7 @@ describe('LinkAvailableBalanceMonitor', () => { [proxy1.address, proxy4.address], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) const tx = await labm .connect(keeperRegistry) @@ -765,6 +960,7 @@ describe('LinkAvailableBalanceMonitor', () => { [proxy1.address, proxy4.address], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) const tx = await labm .connect(keeperRegistry) @@ -783,6 +979,7 @@ describe('LinkAvailableBalanceMonitor', () => { [proxy1.address, directTarget1.address], [oneLINK, oneLINK], [oneLINK, oneLINK], + [1, 2], ) const tx = await labm .connect(keeperRegistry) From c1eeb283e442babc077fddafcb3687808d674b96 Mon Sep 17 00:00:00 2001 From: krehermann Date: Wed, 10 Jan 2024 12:08:47 -0700 Subject: [PATCH 139/234] BCF-2880 remove unused db funcs and triggers (#11730) --- ...16_drop_terra_state_transition_trigger.sql | 27 +++++++++++ .../0217_drop_unused_job_triggers.sql | 48 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 core/store/migrate/migrations/0216_drop_terra_state_transition_trigger.sql create mode 100644 core/store/migrate/migrations/0217_drop_unused_job_triggers.sql diff --git a/core/store/migrate/migrations/0216_drop_terra_state_transition_trigger.sql b/core/store/migrate/migrations/0216_drop_terra_state_transition_trigger.sql new file mode 100644 index 00000000000..77a7c04a4f6 --- /dev/null +++ b/core/store/migrate/migrations/0216_drop_terra_state_transition_trigger.sql @@ -0,0 +1,27 @@ +-- +goose Up + +-- +goose StatementBegin +DROP FUNCTION IF EXISTS PUBLIC.check_terra_msg_state_transition; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin + +CREATE OR REPLACE FUNCTION PUBLIC.check_terra_msg_state_transition() RETURNS TRIGGER AS $$ +DECLARE +state_transition_map jsonb := json_build_object( + 'unstarted', json_build_object('errored', true, 'started', true), + 'started', json_build_object('errored', true, 'broadcasted', true), + 'broadcasted', json_build_object('errored', true, 'confirmed', true)); +BEGIN + IF NOT state_transition_map ? OLD.state THEN + RAISE EXCEPTION 'Invalid from state %. Valid from states %', OLD.state, state_transition_map; +END IF; + IF NOT state_transition_map->OLD.state ? NEW.state THEN + RAISE EXCEPTION 'Invalid state transition from % to %. Valid to states %', OLD.state, NEW.state, state_transition_map->OLD.state; +END IF; +RETURN NEW; +END +$$ LANGUAGE plpgsql; + +-- +goose StatementEnd \ No newline at end of file diff --git a/core/store/migrate/migrations/0217_drop_unused_job_triggers.sql b/core/store/migrate/migrations/0217_drop_unused_job_triggers.sql new file mode 100644 index 00000000000..a59e5d5b225 --- /dev/null +++ b/core/store/migrate/migrations/0217_drop_unused_job_triggers.sql @@ -0,0 +1,48 @@ +-- +goose Up +-- +goose StatementBegin +DROP TRIGGER IF EXISTS notify_job_created ON PUBLIC.jobs; +DROP FUNCTION IF EXISTS PUBLIC.notifyjobcreated(); + +DROP TRIGGER IF EXISTS notify_job_deleted ON PUBLIC.jobs; +DROP FUNCTION IF EXISTS PUBLIC.notifyjobdeleted(); + +DROP TRIGGER IF EXISTS notify_pipeline_run_started ON PUBLIC.pipeline_runs; +DROP FUNCTION IF EXISTS PUBLIC.notifypipelinerunstarted(); +-- +goose StatementEnd + + +-- +goose Down +-- +goose StatementBegin +CREATE FUNCTION PUBLIC.notifyjobcreated() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + PERFORM pg_notify('insert_on_jobs', NEW.id::text); + RETURN NEW; + END + $$; +CREATE TRIGGER notify_job_created AFTER INSERT ON PUBLIC.jobs FOR EACH ROW EXECUTE PROCEDURE PUBLIC.notifyjobcreated(); + +CREATE FUNCTION PUBLIC.notifyjobdeleted() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + PERFORM pg_notify('delete_from_jobs', OLD.id::text); + RETURN OLD; + END + $$; +CREATE TRIGGER notify_job_deleted AFTER DELETE ON PUBLIC.jobs FOR EACH ROW EXECUTE PROCEDURE PUBLIC.notifyjobdeleted(); + +CREATE FUNCTION PUBLIC.notifypipelinerunstarted() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + IF NEW.finished_at IS NULL THEN + PERFORM pg_notify('pipeline_run_started', NEW.id::text); + END IF; + RETURN NEW; + END + $$; +CREATE TRIGGER notify_pipeline_run_started AFTER INSERT ON PUBLIC.pipeline_runs FOR EACH ROW EXECUTE PROCEDURE PUBLIC.notifypipelinerunstarted(); + +-- +goose StatementEnd From de75c03b5aca4cafc180d727efd3521e1a2adaf2 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 10 Jan 2024 16:40:10 -0600 Subject: [PATCH 140/234] core/services/relay: remove unecessary conversions of Network & ChainID (#11736) --- .../relayer_chain_interoperators_test.go | 32 +++++++++---------- core/services/job/models.go | 8 ++--- core/services/job/models_test.go | 4 +-- core/services/relay/relay.go | 27 ++++++++-------- core/web/chains_controller.go | 2 +- core/web/loader/chain.go | 2 +- core/web/loader/node.go | 2 +- core/web/solana_transfer_controller.go | 2 +- 8 files changed, 40 insertions(+), 39 deletions(-) diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 499eca9748a..ea1a9ec3746 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -214,8 +214,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedEVMChainCnt: 2, expectedEVMNodeCnt: 3, expectedEVMRelayerIds: []relay.ID{ - {Network: relay.EVM, ChainID: relay.ChainID(evmChainID1.String())}, - {Network: relay.EVM, ChainID: relay.ChainID(evmChainID2.String())}, + {Network: relay.EVM, ChainID: evmChainID1.String()}, + {Network: relay.EVM, ChainID: evmChainID2.String()}, }, expectedRelayerNetworks: map[relay.Network]struct{}{relay.EVM: {}}, }, @@ -230,8 +230,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedSolanaChainCnt: 2, expectedSolanaNodeCnt: 2, expectedSolanaRelayerIds: []relay.ID{ - {Network: relay.Solana, ChainID: relay.ChainID(solanaChainID1)}, - {Network: relay.Solana, ChainID: relay.ChainID(solanaChainID2)}, + {Network: relay.Solana, ChainID: solanaChainID1}, + {Network: relay.Solana, ChainID: solanaChainID2}, }, expectedRelayerNetworks: map[relay.Network]struct{}{relay.Solana: {}}, }, @@ -246,8 +246,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedStarknetChainCnt: 2, expectedStarknetNodeCnt: 4, expectedStarknetRelayerIds: []relay.ID{ - {Network: relay.StarkNet, ChainID: relay.ChainID(starknetChainID1)}, - {Network: relay.StarkNet, ChainID: relay.ChainID(starknetChainID2)}, + {Network: relay.StarkNet, ChainID: starknetChainID1}, + {Network: relay.StarkNet, ChainID: starknetChainID2}, }, expectedRelayerNetworks: map[relay.Network]struct{}{relay.StarkNet: {}}, }, @@ -264,8 +264,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedCosmosChainCnt: 2, expectedCosmosNodeCnt: 2, expectedCosmosRelayerIds: []relay.ID{ - {Network: relay.Cosmos, ChainID: relay.ChainID(cosmosChainID1)}, - {Network: relay.Cosmos, ChainID: relay.ChainID(cosmosChainID2)}, + {Network: relay.Cosmos, ChainID: cosmosChainID1}, + {Network: relay.Cosmos, ChainID: cosmosChainID2}, }, expectedRelayerNetworks: map[relay.Network]struct{}{relay.Cosmos: {}}, }, @@ -297,29 +297,29 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { expectedEVMChainCnt: 2, expectedEVMNodeCnt: 3, expectedEVMRelayerIds: []relay.ID{ - {Network: relay.EVM, ChainID: relay.ChainID(evmChainID1.String())}, - {Network: relay.EVM, ChainID: relay.ChainID(evmChainID2.String())}, + {Network: relay.EVM, ChainID: evmChainID1.String()}, + {Network: relay.EVM, ChainID: evmChainID2.String()}, }, expectedSolanaChainCnt: 2, expectedSolanaNodeCnt: 2, expectedSolanaRelayerIds: []relay.ID{ - {Network: relay.Solana, ChainID: relay.ChainID(solanaChainID1)}, - {Network: relay.Solana, ChainID: relay.ChainID(solanaChainID2)}, + {Network: relay.Solana, ChainID: solanaChainID1}, + {Network: relay.Solana, ChainID: solanaChainID2}, }, expectedStarknetChainCnt: 2, expectedStarknetNodeCnt: 4, expectedStarknetRelayerIds: []relay.ID{ - {Network: relay.StarkNet, ChainID: relay.ChainID(starknetChainID1)}, - {Network: relay.StarkNet, ChainID: relay.ChainID(starknetChainID2)}, + {Network: relay.StarkNet, ChainID: starknetChainID1}, + {Network: relay.StarkNet, ChainID: starknetChainID2}, }, expectedCosmosChainCnt: 2, expectedCosmosNodeCnt: 2, expectedCosmosRelayerIds: []relay.ID{ - {Network: relay.Cosmos, ChainID: relay.ChainID(cosmosChainID1)}, - {Network: relay.Cosmos, ChainID: relay.ChainID(cosmosChainID2)}, + {Network: relay.Cosmos, ChainID: cosmosChainID1}, + {Network: relay.Cosmos, ChainID: cosmosChainID2}, }, expectedRelayerNetworks: map[relay.Network]struct{}{relay.EVM: {}, relay.Cosmos: {}, relay.Solana: {}, relay.StarkNet: {}}, diff --git a/core/services/job/models.go b/core/services/job/models.go index 50e986d8db1..ab9490eee9a 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -361,7 +361,7 @@ func (s *OCR2OracleSpec) RelayID() (relay.ID, error) { func (s *OCR2OracleSpec) getChainID() (relay.ChainID, error) { if s.ChainID != "" { - return relay.ChainID(s.ChainID), nil + return s.ChainID, nil } // backward compatible job spec return s.getChainIdFromRelayConfig() @@ -375,13 +375,13 @@ func (s *OCR2OracleSpec) getChainIdFromRelayConfig() (relay.ChainID, error) { } switch t := v.(type) { case string: - return relay.ChainID(t), nil + return t, nil case int, int64, int32: - return relay.ChainID(fmt.Sprintf("%d", v)), nil + return fmt.Sprintf("%d", v), nil case float64: // backward compatibility with JSONConfig.EVMChainID i := int64(t) - return relay.ChainID(strconv.FormatInt(i, 10)), nil + return strconv.FormatInt(i, 10), nil default: return "", fmt.Errorf("unable to parse chainID: unexpected type %T", t) diff --git a/core/services/job/models_test.go b/core/services/job/models_test.go index 277f0d7eb29..ddbb815e730 100644 --- a/core/services/job/models_test.go +++ b/core/services/job/models_test.go @@ -30,7 +30,7 @@ func TestOCR2OracleSpec_RelayIdentifier(t *testing.T) { Relay: relay.EVM, ChainID: "1", }, - want: relay.ID{Network: relay.EVM, ChainID: relay.ChainID("1")}, + want: relay.ID{Network: relay.EVM, ChainID: "1"}, }, { name: "evm implicitly configured", @@ -38,7 +38,7 @@ func TestOCR2OracleSpec_RelayIdentifier(t *testing.T) { Relay: relay.EVM, RelayConfig: map[string]any{"chainID": 1}, }, - want: relay.ID{Network: relay.EVM, ChainID: relay.ChainID("1")}, + want: relay.ID{Network: relay.EVM, ChainID: "1"}, }, { name: "evm implicitly configured with bad value", diff --git a/core/services/relay/relay.go b/core/services/relay/relay.go index 118d5935851..826c3d17a44 100644 --- a/core/services/relay/relay.go +++ b/core/services/relay/relay.go @@ -12,19 +12,20 @@ import ( type Network = string type ChainID = string -var ( - EVM Network = "evm" - Cosmos Network = "cosmos" - Solana Network = "solana" - StarkNet Network = "starknet" - SupportedRelays = map[Network]struct{}{ - EVM: {}, - Cosmos: {}, - Solana: {}, - StarkNet: {}, - } +const ( + EVM = "evm" + Cosmos = "cosmos" + Solana = "solana" + StarkNet = "starknet" ) +var SupportedRelays = map[Network]struct{}{ + EVM: {}, + Cosmos: {}, + Solana: {}, + StarkNet: {}, +} + // ID uniquely identifies a relayer by network and chain id type ID struct { Network Network @@ -54,9 +55,9 @@ func (i *ID) UnmarshalString(s string) error { // ignore the `.` in the match by dropping last rune network := s[idxs[0] : idxs[1]-1] chainID := s[idxs[1]:] - newID := &ID{ChainID: ChainID(chainID)} + newID := &ID{ChainID: chainID} for n := range SupportedRelays { - if Network(network) == n { + if network == n { newID.Network = n break } diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index e547cf0150e..4caa3460857 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -79,7 +79,7 @@ func (cc *chainsController[R]) Show(c *gin.Context) { jsonAPIError(c, http.StatusBadRequest, cc.errNotEnabled) return } - relayID := relay.ID{Network: cc.network, ChainID: relay.ChainID(c.Param("ID"))} + relayID := relay.ID{Network: cc.network, ChainID: c.Param("ID")} chain, err := cc.chainStats.ChainStatus(c, relayID) if err != nil { jsonAPIError(c, http.StatusBadRequest, err) diff --git a/core/web/loader/chain.go b/core/web/loader/chain.go index c91c2f02a3b..a12fef3d590 100644 --- a/core/web/loader/chain.go +++ b/core/web/loader/chain.go @@ -20,7 +20,7 @@ func (b *chainBatcher) loadByIDs(_ context.Context, keys dataloader.Keys) []*dat // Collect the keys to search for var chainIDs []relay.ChainID for ix, key := range keys { - chainIDs = append(chainIDs, relay.ChainID(key.String())) + chainIDs = append(chainIDs, key.String()) keyOrder[key.String()] = ix } diff --git a/core/web/loader/node.go b/core/web/loader/node.go index ef8e363d9f3..3d229813101 100644 --- a/core/web/loader/node.go +++ b/core/web/loader/node.go @@ -23,7 +23,7 @@ func (b *nodeBatcher) loadByChainIDs(ctx context.Context, keys dataloader.Keys) evmrelayIDs := make([]relay.ID, 0, len(keys)) for ix, key := range keys { - rid := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(key.String())} + rid := relay.ID{Network: relay.EVM, ChainID: key.String()} evmrelayIDs = append(evmrelayIDs, rid) keyOrder[key.String()] = ix } diff --git a/core/web/solana_transfer_controller.go b/core/web/solana_transfer_controller.go index f9a2c627932..70218080a87 100644 --- a/core/web/solana_transfer_controller.go +++ b/core/web/solana_transfer_controller.go @@ -48,7 +48,7 @@ func (tc *SolanaTransfersController) Create(c *gin.Context) { } amount := new(big.Int).SetUint64(tr.Amount) - relayerID := relay.ID{Network: relay.Solana, ChainID: relay.ChainID(tr.SolanaChainID)} + relayerID := relay.ID{Network: relay.Solana, ChainID: tr.SolanaChainID} relayer, err := relayers.Get(relayerID) if err != nil { if errors.Is(err, chainlink.ErrNoSuchRelayer) { From 9759ff9101006b5d544b6c791f8f9431f2b74f64 Mon Sep 17 00:00:00 2001 From: krehermann Date: Wed, 10 Jan 2024 16:12:51 -0700 Subject: [PATCH 141/234] BCF-2882 remove libpq notify (#11738) * delete unused libpq notify channels * include migration * BCF-2882 lock in no-triggers-allowed with a test * Update core/store/migrate/migrate_test.go Co-authored-by: Jordan Krage --------- Co-authored-by: Jordan Krage --- core/chains/evm/logpoller/log_poller_test.go | 57 ------------------- core/services/pg/channels.go | 4 -- core/store/migrate/migrate_test.go | 25 ++++++++ .../0218_drop_log_topic_trigger.sql | 27 +++++++++ 4 files changed, 52 insertions(+), 61 deletions(-) delete mode 100644 core/services/pg/channels.go create mode 100644 core/store/migrate/migrations/0218_drop_log_topic_trigger.sql diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 81d1b74e42d..4bfc403e341 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -20,7 +20,6 @@ import ( "github.com/leanovate/gopter" "github.com/leanovate/gopter/gen" "github.com/leanovate/gopter/prop" - "github.com/lib/pq" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -39,7 +38,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -1325,61 +1323,6 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { assert.Contains(t, logMsgs, "Backup log poller ran before filters loaded, skipping") } -func TestNotifyAfterInsert(t *testing.T) { - t.Parallel() - - // Use a non-transactional db for this test because notify events - // are not delivered until the transaction is committed. - var dbURL string - _, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { - dbURL = s.Database.URL.URL().String() - }) - - lggr, _ := logger.TestObserved(t, zapcore.WarnLevel) - chainID := big.NewInt(1337) - o := logpoller.NewORM(chainID, sqlxDB, lggr, pgtest.NewQConfig(true)) - - listener := pq.NewListener(dbURL, time.Second, time.Second, nil) - err := listener.Listen(pg.ChannelInsertOnEVMLogs) - require.NoError(t, err) - - log := logpoller.Log{ - EvmChainId: ubig.New(chainID), - LogIndex: 10, - BlockHash: testutils.Random32Byte(), - BlockNumber: 100, - BlockTimestamp: time.Now(), - Topics: pq.ByteaArray{ - testutils.NewAddress().Bytes(), - testutils.NewAddress().Bytes(), - }, - EventSig: testutils.Random32Byte(), - Address: testutils.NewAddress(), - TxHash: testutils.Random32Byte(), - Data: []byte("test_data"), - CreatedAt: time.Now(), - } - - err = o.InsertLogs([]logpoller.Log{log}) - require.NoError(t, err) - - testutils.AssertEventually(t, func() bool { - select { - case event := <-listener.Notify: - expectedPayload := fmt.Sprintf( - "%s:%s,%s", - hexutil.Encode(log.Address.Bytes())[2:], // strip the leading 0x - hexutil.Encode(log.Topics[0])[2:], - hexutil.Encode(log.Topics[1])[2:], - ) - require.Equal(t, event.Extra, expectedPayload) - return true - default: - return false - } - }) -} - type getLogErrData struct { From string To string diff --git a/core/services/pg/channels.go b/core/services/pg/channels.go deleted file mode 100644 index aed132a7f2c..00000000000 --- a/core/services/pg/channels.go +++ /dev/null @@ -1,4 +0,0 @@ -package pg - -// Postgres channel to listen for new evm.txes -const ChannelInsertOnEVMLogs = "evm.insert_on_logs" diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index 10c698e96fd..56d1fe41eb5 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -516,6 +516,31 @@ func TestDatabaseBackFillWithMigration202(t *testing.T) { } } +func TestNoTriggers(t *testing.T) { + _, db := heavyweight.FullTestDBEmptyV2(t, nil) + + assert_num_triggers := func(expected int) { + + row := db.DB.QueryRow("select count(*) from information_schema.triggers") + var count int + err := row.Scan(&count) + + require.NoError(t, err) + require.Equal(t, expected, count) + } + + // if you find yourself here and are tempted to add a trigger, something has gone wrong + // and you should talk to the foundations team before proceeding + assert_num_triggers(0) + + // version prior to removal of all triggers + v := 217 + err := goose.UpTo(db.DB, migrationDir, int64(v)) + require.NoError(t, err) + assert_num_triggers(1) + +} + func BenchmarkBackfillingRecordsWithMigration202(b *testing.B) { previousMigration := int64(201) backfillMigration := int64(202) diff --git a/core/store/migrate/migrations/0218_drop_log_topic_trigger.sql b/core/store/migrate/migrations/0218_drop_log_topic_trigger.sql new file mode 100644 index 00000000000..ea80cccd2b5 --- /dev/null +++ b/core/store/migrate/migrations/0218_drop_log_topic_trigger.sql @@ -0,0 +1,27 @@ +-- +goose Up +-- +goose StatementBegin +DROP TRIGGER IF EXISTS notify_insert_on_logs_topics ON EVM.logs; +DROP FUNCTION IF EXISTS evm.notifysavedlogtopics(); + +-- +goose StatementEnd + + +-- +goose Down +-- +goose StatementBegin + +CREATE FUNCTION evm.notifysavedlogtopics() RETURNS trigger + LANGUAGE plpgsql +AS $$ +BEGIN + PERFORM pg_notify( + 'evm.insert_on_logs'::text, + -- hex encoded address plus comma separated list of hex encoded topic values + -- e.g. "
:," + encode(NEW.address, 'hex') || ':' || array_to_string(array(SELECT encode(unnest(NEW.topics), 'hex')), ',') + ); + RETURN NULL; +END +$$; + +CREATE TRIGGER notify_insert_on_logs_topics AFTER INSERT ON evm.logs FOR EACH ROW EXECUTE PROCEDURE evm.notifysavedlogtopics(); +-- +goose StatementEnd From c3b156b6bd202870eaa6ae037b25aaada11c6852 Mon Sep 17 00:00:00 2001 From: krehermann Date: Wed, 10 Jan 2024 17:48:55 -0700 Subject: [PATCH 142/234] BCF-2883 remove stranded, unused db func evm.notifytxinsertion (#11739) --- .../migrations/0219_drop_notifytxinsertion.sql | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 core/store/migrate/migrations/0219_drop_notifytxinsertion.sql diff --git a/core/store/migrate/migrations/0219_drop_notifytxinsertion.sql b/core/store/migrate/migrations/0219_drop_notifytxinsertion.sql new file mode 100644 index 00000000000..9569fa48728 --- /dev/null +++ b/core/store/migrate/migrations/0219_drop_notifytxinsertion.sql @@ -0,0 +1,16 @@ +-- +goose Up +-- +goose StatementBegin +DROP FUNCTION IF EXISTS evm.notifytxinsertion(); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +CREATE OR REPLACE FUNCTION evm.notifytxinsertion() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + PERFORM pg_notify('evm.insert_on_txes'::text, encode(NEW.from_address, 'hex')); + RETURN NULL; + END + $$; +-- +goose StatementEnd From dbc0f91220057695db4d29bc4a02b8d02053e078 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Jan 2024 11:04:42 -0500 Subject: [PATCH 143/234] Implement stream specs (#11685) * Implement stream specs * Fix linter * Rename StreamRegistry => Registry * rename migration * StreamID => type alias --- core/services/chainlink/application.go | 7 + core/services/job/models.go | 66 +++---- core/services/job/orm.go | 2 + core/services/job/validate.go | 17 +- core/services/ocrcommon/run_saver.go | 9 +- core/services/pipeline/common.go | 17 +- core/services/pipeline/models.go | 11 +- core/services/pipeline/runner.go | 62 ++++--- core/services/pipeline/runner_test.go | 40 +++++ core/services/streams/delegate.go | 110 ++++++++++++ core/services/streams/delegate_test.go | 161 ++++++++++++++++++ core/services/streams/stream.go | 133 +++++++++++++++ core/services/streams/stream_registry.go | 60 +++++++ core/services/streams/stream_registry_test.go | 107 ++++++++++++ core/services/streams/stream_test.go | 133 +++++++++++++++ core/services/vrf/delegate.go | 2 +- .../migrate/migrations/0220_stream_specs.sql | 40 +++++ core/testdata/testspecs/v2_specs.go | 31 ++++ core/web/jobs_controller.go | 3 + core/web/jobs_controller_test.go | 38 ++++- core/web/presenters/job.go | 2 + docs/CHANGELOG.md | 1 + 22 files changed, 967 insertions(+), 85 deletions(-) create mode 100644 core/services/streams/delegate.go create mode 100644 core/services/streams/delegate_test.go create mode 100644 core/services/streams/stream.go create mode 100644 core/services/streams/stream_registry.go create mode 100644 core/services/streams/stream_registry_test.go create mode 100644 core/services/streams/stream_test.go create mode 100644 core/store/migrate/migrations/0220_stream_specs.sql diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index fae938c0db6..a9f9c22df52 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -53,6 +53,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" + "github.com/smartcontractkit/chainlink/v2/core/services/streams" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/core/services/vrf" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" @@ -290,6 +291,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { pipelineRunner = pipeline.NewRunner(pipelineORM, bridgeORM, cfg.JobPipeline(), cfg.WebServer(), legacyEVMChains, keyStore.Eth(), keyStore.VRF(), globalLogger, restrictedHTTPClient, unrestrictedHTTPClient) jobORM = job.NewORM(db, pipelineORM, bridgeORM, keyStore, globalLogger, cfg.Database()) txmORM = txmgr.NewTxStore(db, globalLogger, cfg.Database()) + streamRegistry = streams.NewRegistry(globalLogger, pipelineRunner) ) for _, chain := range legacyEVMChains.Slice() { @@ -344,6 +346,11 @@ func NewApplication(opts ApplicationOpts) (Application, error) { db, cfg.Database(), globalLogger), + job.Stream: streams.NewDelegate( + globalLogger, + streamRegistry, + pipelineRunner, + cfg.JobPipeline()), } webhookJobRunner = delegates[job.Webhook].(*webhook.Delegate).WebhookJobRunner() ) diff --git a/core/services/job/models.go b/core/services/job/models.go index ab9490eee9a..5dcf4928e35 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -33,20 +33,21 @@ import ( ) const ( + BlockHeaderFeeder Type = (Type)(pipeline.BlockHeaderFeederJobType) + BlockhashStore Type = (Type)(pipeline.BlockhashStoreJobType) + Bootstrap Type = (Type)(pipeline.BootstrapJobType) Cron Type = (Type)(pipeline.CronJobType) DirectRequest Type = (Type)(pipeline.DirectRequestJobType) FluxMonitor Type = (Type)(pipeline.FluxMonitorJobType) - OffchainReporting Type = (Type)(pipeline.OffchainReportingJobType) - OffchainReporting2 Type = (Type)(pipeline.OffchainReporting2JobType) + Gateway Type = (Type)(pipeline.GatewayJobType) Keeper Type = (Type)(pipeline.KeeperJobType) - VRF Type = (Type)(pipeline.VRFJobType) - BlockhashStore Type = (Type)(pipeline.BlockhashStoreJobType) - BlockHeaderFeeder Type = (Type)(pipeline.BlockHeaderFeederJobType) LegacyGasStationServer Type = (Type)(pipeline.LegacyGasStationServerJobType) LegacyGasStationSidecar Type = (Type)(pipeline.LegacyGasStationSidecarJobType) + OffchainReporting Type = (Type)(pipeline.OffchainReportingJobType) + OffchainReporting2 Type = (Type)(pipeline.OffchainReporting2JobType) + Stream Type = (Type)(pipeline.StreamJobType) + VRF Type = (Type)(pipeline.VRFJobType) Webhook Type = (Type)(pipeline.WebhookJobType) - Bootstrap Type = (Type)(pipeline.BootstrapJobType) - Gateway Type = (Type)(pipeline.GatewayJobType) ) //revive:disable:redefines-builtin-id @@ -70,52 +71,55 @@ func (t Type) SchemaVersion() uint32 { var ( requiresPipelineSpec = map[Type]bool{ + BlockHeaderFeeder: false, + BlockhashStore: false, + Bootstrap: false, Cron: true, DirectRequest: true, FluxMonitor: true, - OffchainReporting: false, // bootstrap jobs do not require it - OffchainReporting2: false, // bootstrap jobs do not require it + Gateway: false, Keeper: false, // observationSource is injected in the upkeep executor - VRF: true, - Webhook: true, - BlockhashStore: false, - BlockHeaderFeeder: false, LegacyGasStationServer: false, LegacyGasStationSidecar: false, - Bootstrap: false, - Gateway: false, + OffchainReporting2: false, // bootstrap jobs do not require it + OffchainReporting: false, // bootstrap jobs do not require it + Stream: true, + VRF: true, + Webhook: true, } supportsAsync = map[Type]bool{ + BlockHeaderFeeder: false, + BlockhashStore: false, + Bootstrap: false, Cron: true, DirectRequest: true, FluxMonitor: false, - OffchainReporting: false, - OffchainReporting2: false, + Gateway: false, Keeper: true, - VRF: true, - Webhook: true, - BlockhashStore: false, - BlockHeaderFeeder: false, LegacyGasStationServer: false, LegacyGasStationSidecar: false, - Bootstrap: false, - Gateway: false, + OffchainReporting2: false, + OffchainReporting: false, + Stream: true, + VRF: true, + Webhook: true, } schemaVersions = map[Type]uint32{ + BlockHeaderFeeder: 1, + BlockhashStore: 1, + Bootstrap: 1, Cron: 1, DirectRequest: 1, FluxMonitor: 1, - OffchainReporting: 1, - OffchainReporting2: 1, + Gateway: 1, Keeper: 1, - VRF: 1, - Webhook: 1, - BlockhashStore: 1, - BlockHeaderFeeder: 1, LegacyGasStationServer: 1, LegacyGasStationSidecar: 1, - Bootstrap: 1, - Gateway: 1, + OffchainReporting2: 1, + OffchainReporting: 1, + Stream: 1, + VRF: 1, + Webhook: 1, } ) diff --git a/core/services/job/orm.go b/core/services/job/orm.go index 6c5a879ebd0..fb52dafdf5d 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -440,6 +440,8 @@ func (o *orm) CreateJob(jb *Job, qopts ...pg.QOpt) error { return errors.Wrap(err, "failed to create GatewaySpec for jobSpec") } jb.GatewaySpecID = &specID + case Stream: + // 'stream' type has no associated spec, nothing to do here default: o.lggr.Panicf("Unsupported jb.Type: %v", jb.Type) } diff --git a/core/services/job/validate.go b/core/services/job/validate.go index b7a1dca3616..f108031f72e 100644 --- a/core/services/job/validate.go +++ b/core/services/job/validate.go @@ -12,20 +12,21 @@ var ( ErrInvalidJobType = errors.New("invalid job type") ErrInvalidSchemaVersion = errors.New("invalid schema version") jobTypes = map[Type]struct{}{ + BlockHeaderFeeder: {}, + BlockhashStore: {}, + Bootstrap: {}, Cron: {}, DirectRequest: {}, FluxMonitor: {}, - OffchainReporting: {}, - OffchainReporting2: {}, - Keeper: {}, - VRF: {}, - Webhook: {}, - BlockhashStore: {}, - Bootstrap: {}, - BlockHeaderFeeder: {}, Gateway: {}, + Keeper: {}, LegacyGasStationServer: {}, LegacyGasStationSidecar: {}, + OffchainReporting2: {}, + OffchainReporting: {}, + Stream: {}, + VRF: {}, + Webhook: {}, } ) diff --git a/core/services/ocrcommon/run_saver.go b/core/services/ocrcommon/run_saver.go index b1a0fc7b141..6d85aa857a4 100644 --- a/core/services/ocrcommon/run_saver.go +++ b/core/services/ocrcommon/run_saver.go @@ -5,15 +5,20 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" ) +type Runner interface { + InsertFinishedRun(run *pipeline.Run, saveSuccessfulTaskRuns bool, qopts ...pg.QOpt) error +} + type RunResultSaver struct { services.StateMachine maxSuccessfulRuns uint64 runResults chan *pipeline.Run - pipelineRunner pipeline.Runner + pipelineRunner Runner done chan struct{} logger logger.Logger } @@ -24,7 +29,7 @@ func (r *RunResultSaver) HealthReport() map[string]error { func (r *RunResultSaver) Name() string { return r.logger.Name() } -func NewResultRunSaver(pipelineRunner pipeline.Runner, +func NewResultRunSaver(pipelineRunner Runner, logger logger.Logger, maxSuccessfulRuns uint64, resultsWriteDepth uint64, ) *RunResultSaver { return &RunResultSaver{ diff --git a/core/services/pipeline/common.go b/core/services/pipeline/common.go index ae82e5b3b81..6efa7aa2148 100644 --- a/core/services/pipeline/common.go +++ b/core/services/pipeline/common.go @@ -29,20 +29,21 @@ import ( ) const ( + BlockHeaderFeederJobType string = "blockheaderfeeder" + BlockhashStoreJobType string = "blockhashstore" + BootstrapJobType string = "bootstrap" CronJobType string = "cron" DirectRequestJobType string = "directrequest" FluxMonitorJobType string = "fluxmonitor" - OffchainReportingJobType string = "offchainreporting" - OffchainReporting2JobType string = "offchainreporting2" - KeeperJobType string = "keeper" - VRFJobType string = "vrf" - BlockhashStoreJobType string = "blockhashstore" - BlockHeaderFeederJobType string = "blockheaderfeeder" - WebhookJobType string = "webhook" - BootstrapJobType string = "bootstrap" GatewayJobType string = "gateway" + KeeperJobType string = "keeper" LegacyGasStationServerJobType string = "legacygasstationserver" LegacyGasStationSidecarJobType string = "legacygasstationsidecar" + OffchainReporting2JobType string = "offchainreporting2" + OffchainReportingJobType string = "offchainreporting" + StreamJobType string = "stream" + VRFJobType string = "vrf" + WebhookJobType string = "webhook" ) //go:generate mockery --quiet --name Config --output ./mocks/ --case=underscore diff --git a/core/services/pipeline/models.go b/core/services/pipeline/models.go index cfabae82772..e198c1b788c 100644 --- a/core/services/pipeline/models.go +++ b/core/services/pipeline/models.go @@ -28,9 +28,18 @@ type Spec struct { JobID int32 `json:"-"` JobName string `json:"-"` JobType string `json:"-"` + + Pipeline *Pipeline `json:"-" db:"-"` // This may be nil, or may be populated manually as a cache. There is no locking on this, so be careful +} + +func (s *Spec) GetOrParsePipeline() (*Pipeline, error) { + if s.Pipeline != nil { + return s.Pipeline, nil + } + return s.ParsePipeline() } -func (s Spec) Pipeline() (*Pipeline, error) { +func (s *Spec) ParsePipeline() (*Pipeline, error) { return Parse(s.DotDagSource) } diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 3e5d77db5f2..a432d9fec11 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -243,26 +243,32 @@ func (r *runner) ExecuteRun( }) defer cancel() - run := NewRun(spec, vars) - - pipeline, err := r.initializePipeline(run) - if err != nil { - return run, nil, err + var pipeline *Pipeline + if spec.Pipeline != nil { + // assume if set that it has been pre-initialized + pipeline = spec.Pipeline + } else { + var err error + pipeline, err = r.InitializePipeline(spec) + if err != nil { + return nil, nil, err + } } + run := NewRun(spec, vars) taskRunResults := r.run(ctx, pipeline, run, vars, l) if run.Pending { - return run, nil, pkgerrors.Wrapf(err, "unexpected async run for spec ID %v, tried executing via ExecuteAndInsertFinishedRun", spec.ID) + return run, nil, fmt.Errorf("unexpected async run for spec ID %v, tried executing via ExecuteRun", spec.ID) } return run, taskRunResults, nil } -func (r *runner) initializePipeline(run *Run) (*Pipeline, error) { - pipeline, err := Parse(run.PipelineSpec.DotDagSource) +func (r *runner) InitializePipeline(spec Spec) (pipeline *Pipeline, err error) { + pipeline, err = spec.GetOrParsePipeline() if err != nil { - return nil, err + return } // initialize certain task params @@ -278,7 +284,7 @@ func (r *runner) initializePipeline(run *Run) (*Pipeline, error) { task.(*BridgeTask).config = r.config task.(*BridgeTask).bridgeConfig = r.bridgeConfig task.(*BridgeTask).orm = r.btORM - task.(*BridgeTask).specId = run.PipelineSpec.ID + task.(*BridgeTask).specId = spec.ID // URL is "safe" because it comes from the node's own database. We // must use the unrestrictedHTTPClient because some node operators // may run external adapters on their own hardware @@ -286,8 +292,8 @@ func (r *runner) initializePipeline(run *Run) (*Pipeline, error) { case TaskTypeETHCall: task.(*ETHCallTask).legacyChains = r.legacyEVMChains task.(*ETHCallTask).config = r.config - task.(*ETHCallTask).specGasLimit = run.PipelineSpec.GasLimit - task.(*ETHCallTask).jobType = run.PipelineSpec.JobType + task.(*ETHCallTask).specGasLimit = spec.GasLimit + task.(*ETHCallTask).jobType = spec.JobType case TaskTypeVRF: task.(*VRFTask).keyStore = r.vrfKeyStore case TaskTypeVRFV2: @@ -296,28 +302,18 @@ func (r *runner) initializePipeline(run *Run) (*Pipeline, error) { task.(*VRFTaskV2Plus).keyStore = r.vrfKeyStore case TaskTypeEstimateGasLimit: task.(*EstimateGasLimitTask).legacyChains = r.legacyEVMChains - task.(*EstimateGasLimitTask).specGasLimit = run.PipelineSpec.GasLimit - task.(*EstimateGasLimitTask).jobType = run.PipelineSpec.JobType + task.(*EstimateGasLimitTask).specGasLimit = spec.GasLimit + task.(*EstimateGasLimitTask).jobType = spec.JobType case TaskTypeETHTx: task.(*ETHTxTask).keyStore = r.ethKeyStore task.(*ETHTxTask).legacyChains = r.legacyEVMChains - task.(*ETHTxTask).specGasLimit = run.PipelineSpec.GasLimit - task.(*ETHTxTask).jobType = run.PipelineSpec.JobType - task.(*ETHTxTask).forwardingAllowed = run.PipelineSpec.ForwardingAllowed + task.(*ETHTxTask).specGasLimit = spec.GasLimit + task.(*ETHTxTask).jobType = spec.JobType + task.(*ETHTxTask).forwardingAllowed = spec.ForwardingAllowed default: } } - // retain old UUID values - for _, taskRun := range run.PipelineTaskRuns { - task := pipeline.ByDotID(taskRun.DotID) - if task != nil && task.Base() != nil { - task.Base().uuid = taskRun.ID - } else { - return nil, pkgerrors.Errorf("failed to match a pipeline task for dot ID: %v", taskRun.DotID) - } - } - return pipeline, nil } @@ -542,11 +538,21 @@ func (r *runner) ExecuteAndInsertFinishedRun(ctx context.Context, spec Spec, var } func (r *runner) Run(ctx context.Context, run *Run, l logger.Logger, saveSuccessfulTaskRuns bool, fn func(tx pg.Queryer) error) (incomplete bool, err error) { - pipeline, err := r.initializePipeline(run) + pipeline, err := r.InitializePipeline(run.PipelineSpec) if err != nil { return false, err } + // retain old UUID values + for _, taskRun := range run.PipelineTaskRuns { + task := pipeline.ByDotID(taskRun.DotID) + if task != nil && task.Base() != nil { + task.Base().uuid = taskRun.ID + } else { + return false, pkgerrors.Errorf("failed to match a pipeline task for dot ID: %v", taskRun.DotID) + } + } + preinsert := pipeline.RequiresPreInsert() q := r.orm.GetQ().WithOpts(pg.WithParentCtx(ctx)) diff --git a/core/services/pipeline/runner_test.go b/core/services/pipeline/runner_test.go index 695590e7bd0..5b4aaef7e88 100644 --- a/core/services/pipeline/runner_test.go +++ b/core/services/pipeline/runner_test.go @@ -942,3 +942,43 @@ en->de require.NoError(t, err) assert.Equal(t, inputBytes, result.Value) } + +func Test_PipelineRunner_ExecuteRun(t *testing.T) { + t.Run("uses cached *Pipeline if available", func(t *testing.T) { + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewTestGeneralConfig(t) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: cfg, KeyStore: ethKeyStore}) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) + lggr := logger.TestLogger(t) + r := pipeline.NewRunner(nil, nil, cfg.JobPipeline(), cfg.WebServer(), legacyChains, ethKeyStore, nil, lggr, nil, nil) + + template := ` +succeed [type=memo value=%d] +succeed; +` + + spec := pipeline.Spec{DotDagSource: fmt.Sprintf(template, 1)} + vars := pipeline.NewVarsFrom(nil) + + _, trrs, err := r.ExecuteRun(testutils.Context(t), spec, vars, lggr) + require.NoError(t, err) + require.Len(t, trrs, 1) + assert.Equal(t, "1", trrs[0].Result.Value.(pipeline.ObjectParam).DecimalValue.Decimal().String()) + + // does not automatically cache + require.Nil(t, spec.Pipeline) + + // initialize it + spec.Pipeline, err = spec.ParsePipeline() + require.NoError(t, err) + + // even though this is set to 2, it should use the cached version + spec.DotDagSource = fmt.Sprintf(template, 2) + + _, trrs, err = r.ExecuteRun(testutils.Context(t), spec, vars, lggr) + require.NoError(t, err) + require.Len(t, trrs, 1) + assert.Equal(t, "1", trrs[0].Result.Value.(pipeline.ObjectParam).DecimalValue.Decimal().String()) + }) +} diff --git a/core/services/streams/delegate.go b/core/services/streams/delegate.go new file mode 100644 index 00000000000..3b9b5c773ae --- /dev/null +++ b/core/services/streams/delegate.go @@ -0,0 +1,110 @@ +package streams + +import ( + "context" + "fmt" + "strings" + + "github.com/google/uuid" + "github.com/pelletier/go-toml/v2" + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" +) + +type DelegateConfig interface { + MaxSuccessfulRuns() uint64 + ResultWriteQueueDepth() uint64 +} + +type Delegate struct { + lggr logger.Logger + registry Registry + runner ocrcommon.Runner + cfg DelegateConfig +} + +var _ job.Delegate = (*Delegate)(nil) + +func NewDelegate(lggr logger.Logger, registry Registry, runner ocrcommon.Runner, cfg DelegateConfig) *Delegate { + return &Delegate{lggr, registry, runner, cfg} +} + +func (d *Delegate) JobType() job.Type { + return job.Stream +} + +func (d *Delegate) BeforeJobCreated(jb job.Job) {} +func (d *Delegate) AfterJobCreated(jb job.Job) {} +func (d *Delegate) BeforeJobDeleted(jb job.Job) {} +func (d *Delegate) OnDeleteJob(jb job.Job, q pg.Queryer) error { return nil } + +func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err error) { + if !jb.Name.Valid { + return nil, errors.New("job name is required to be present for stream specs") + } + id := StreamID(jb.Name.String) + lggr := d.lggr.Named(id).With("streamID", id) + + rrs := ocrcommon.NewResultRunSaver(d.runner, lggr, d.cfg.MaxSuccessfulRuns(), d.cfg.ResultWriteQueueDepth()) + services = append(services, rrs, &StreamService{ + d.registry, + id, + jb.PipelineSpec, + lggr, + rrs, + }) + return services, nil +} + +type ResultRunSaver interface { + Save(run *pipeline.Run) +} + +type StreamService struct { + registry Registry + id StreamID + spec *pipeline.Spec + lggr logger.Logger + rrs ResultRunSaver +} + +func (s *StreamService) Start(_ context.Context) error { + if s.spec == nil { + return fmt.Errorf("pipeline spec unexpectedly missing for stream %q", s.id) + } + s.lggr.Debugf("Starting stream %q", s.id) + return s.registry.Register(s.id, *s.spec, s.rrs) +} + +func (s *StreamService) Close() error { + s.lggr.Debugf("Stopping stream %q", s.id) + s.registry.Unregister(s.id) + return nil +} + +func ValidatedStreamSpec(tomlString string) (job.Job, error) { + var jb = job.Job{ExternalJobID: uuid.New()} + + r := strings.NewReader(tomlString) + d := toml.NewDecoder(r) + d.DisallowUnknownFields() + err := d.Decode(&jb) + if err != nil { + return jb, errors.Wrap(err, "toml unmarshal error on job") + } + + if jb.Type != job.Stream { + return jb, errors.Errorf("unsupported type: %q", jb.Type) + } + + if !jb.Name.Valid { + return jb, errors.New("jobs of type 'stream' require a non-blank name as stream ID") + } + + return jb, nil +} diff --git a/core/services/streams/delegate_test.go b/core/services/streams/delegate_test.go new file mode 100644 index 00000000000..77b10260375 --- /dev/null +++ b/core/services/streams/delegate_test.go @@ -0,0 +1,161 @@ +package streams + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" +) + +type mockRegistry struct{} + +func (m *mockRegistry) Get(streamID StreamID) (strm Stream, exists bool) { return } +func (m *mockRegistry) Register(streamID StreamID, spec pipeline.Spec, rrs ResultRunSaver) error { + return nil +} +func (m *mockRegistry) Unregister(streamID StreamID) {} + +type mockDelegateConfig struct{} + +func (m *mockDelegateConfig) MaxSuccessfulRuns() uint64 { return 0 } +func (m *mockDelegateConfig) ResultWriteQueueDepth() uint64 { return 0 } + +func Test_Delegate(t *testing.T) { + lggr := logger.TestLogger(t) + registry := &mockRegistry{} + runner := &mockRunner{} + cfg := &mockDelegateConfig{} + d := NewDelegate(lggr, registry, runner, cfg) + + t.Run("ServicesForSpec", func(t *testing.T) { + jb := job.Job{PipelineSpec: &pipeline.Spec{ID: 1}} + t.Run("errors if job is missing name", func(t *testing.T) { + _, err := d.ServicesForSpec(jb) + assert.EqualError(t, err, "job name is required to be present for stream specs") + }) + jb.Name = null.StringFrom("jobname") + t.Run("returns services", func(t *testing.T) { + srvs, err := d.ServicesForSpec(jb) + require.NoError(t, err) + + assert.Len(t, srvs, 2) + assert.IsType(t, &ocrcommon.RunResultSaver{}, srvs[0]) + + strmSrv := srvs[1].(*StreamService) + assert.Equal(t, registry, strmSrv.registry) + assert.Equal(t, StreamID("jobname"), strmSrv.id) + assert.Equal(t, jb.PipelineSpec, strmSrv.spec) + assert.NotNil(t, strmSrv.lggr) + assert.Equal(t, srvs[0], strmSrv.rrs) + }) + }) +} + +func Test_ValidatedStreamSpec(t *testing.T) { + var tt = []struct { + name string + toml string + assertion func(t *testing.T, os job.Job, err error) + }{ + { + name: "minimal stream spec", + toml: ` +type = "stream" +name = "voter-turnout" +schemaVersion = 1 +observationSource = """ +ds1 [type=bridge name=voter_turnout]; +ds1_parse [type=jsonparse path="one,two"]; +ds1_multiply [type=multiply times=1.23]; +ds1 -> ds1_parse -> ds1_multiply -> answer1; +answer1 [type=median index=0]; +""" +`, + assertion: func(t *testing.T, jb job.Job, err error) { + require.NoError(t, err) + assert.Equal(t, job.Type("stream"), jb.Type) + assert.Equal(t, uint32(1), jb.SchemaVersion) + assert.True(t, jb.Name.Valid) + assert.Equal(t, "voter-turnout", jb.Name.String) + }, + }, + { + name: "unparseable toml", + toml: `not toml`, + assertion: func(t *testing.T, jb job.Job, err error) { + assert.EqualError(t, err, "toml unmarshal error on job: toml: expected character =") + }, + }, + { + name: "invalid field type", + toml: ` +type = "stream" +name = "voter-turnout" +schemaVersion = "should be integer" +`, + assertion: func(t *testing.T, jb job.Job, err error) { + assert.EqualError(t, err, "toml unmarshal error on job: toml: cannot decode TOML string into struct field job.Job.SchemaVersion of type uint32") + }, + }, + { + name: "invalid fields", + toml: ` +type = "stream" +name = "voter-turnout" +notAValidField = "some value" +schemaVersion = 1 +`, + assertion: func(t *testing.T, jb job.Job, err error) { + assert.EqualError(t, err, "toml unmarshal error on job: strict mode: fields in the document are missing in the target struct") + }, + }, + { + name: "wrong type", + toml: ` +type = "not a valid type" +name = "voter-turnout" +schemaVersion = 1 +observationSource = """ +ds1 [type=bridge name=voter_turnout]; +ds1_parse [type=jsonparse path="one,two"]; +ds1_multiply [type=multiply times=1.23]; +ds1 -> ds1_parse -> ds1_multiply -> answer1; +answer1 [type=median index=0]; +""" +`, + assertion: func(t *testing.T, jb job.Job, err error) { + assert.EqualError(t, err, "unsupported type: \"not a valid type\"") + }, + }, + { + name: "error if missing name", + toml: ` +type = "stream" +schemaVersion = 1 +observationSource = """ +ds1 [type=bridge name=voter_turnout]; +ds1_parse [type=jsonparse path="one,two"]; +ds1_multiply [type=multiply times=1.23]; +ds1 -> ds1_parse -> ds1_multiply -> answer1; +answer1 [type=median index=0]; +""" +`, + assertion: func(t *testing.T, jb job.Job, err error) { + assert.EqualError(t, err, "jobs of type 'stream' require a non-blank name as stream ID") + }, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + s, err := ValidatedStreamSpec(tc.toml) + tc.assertion(t, s, err) + }) + } +} diff --git a/core/services/streams/stream.go b/core/services/streams/stream.go new file mode 100644 index 00000000000..51535a0cb86 --- /dev/null +++ b/core/services/streams/stream.go @@ -0,0 +1,133 @@ +package streams + +import ( + "context" + "fmt" + "math/big" + "sync" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type Runner interface { + ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) + InitializePipeline(spec pipeline.Spec) (*pipeline.Pipeline, error) +} + +type RunResultSaver interface { + Save(run *pipeline.Run) +} + +type Stream interface { + Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) +} + +type stream struct { + sync.RWMutex + id StreamID + lggr logger.Logger + spec *pipeline.Spec + runner Runner + rrs RunResultSaver +} + +func NewStream(lggr logger.Logger, id StreamID, spec pipeline.Spec, runner Runner, rrs RunResultSaver) Stream { + return newStream(lggr, id, spec, runner, rrs) +} + +func newStream(lggr logger.Logger, id StreamID, spec pipeline.Spec, runner Runner, rrs RunResultSaver) *stream { + return &stream{sync.RWMutex{}, id, lggr.Named("Stream").With("streamID", id), &spec, runner, rrs} +} + +func (s *stream) Run(ctx context.Context) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) { + run, trrs, err = s.executeRun(ctx) + + if err != nil { + return nil, nil, fmt.Errorf("Run failed: %w", err) + } + if s.rrs != nil { + s.rrs.Save(run) + } + + return +} + +// The context passed in here has a timeout of (ObservationTimeout + ObservationGracePeriod). +// Upon context cancellation, its expected that we return any usable values within ObservationGracePeriod. +func (s *stream) executeRun(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) { + // the hot path here is to avoid parsing and use the pre-parsed, cached, pipeline + s.RLock() + initialize := s.spec.Pipeline == nil + s.RUnlock() + if initialize { + pipeline, err := s.spec.ParsePipeline() + if err != nil { + return nil, nil, fmt.Errorf("Run failed due to unparseable pipeline: %w", err) + } + + s.Lock() + if s.spec.Pipeline == nil { + s.spec.Pipeline = pipeline + // initialize it for the given runner + if _, err := s.runner.InitializePipeline(*s.spec); err != nil { + return nil, nil, fmt.Errorf("Run failed due to error while initializing pipeline: %w", err) + } + } + s.Unlock() + } + + vars := pipeline.NewVarsFrom(map[string]interface{}{ + "pipelineSpec": map[string]interface{}{ + "id": s.spec.ID, + }, + "stream": map[string]interface{}{ + "id": s.id, + }, + }) + + run, trrs, err := s.runner.ExecuteRun(ctx, *s.spec, vars, s.lggr) + if err != nil { + return nil, nil, fmt.Errorf("error executing run for spec ID %v: %w", s.spec.ID, err) + } + + return run, trrs, err +} + +// ExtractBigInt returns a result of a pipeline run that returns one single +// decimal result, as a *big.Int. +// This acts as a reference/example method, other methods can be implemented to +// extract any desired type that matches a particular pipeline run output. +// Returns error on parse errors: if results are wrong type +func ExtractBigInt(trrs pipeline.TaskRunResults) (*big.Int, error) { + var finaltrrs []pipeline.TaskRunResult + // pipeline.TaskRunResults comes ordered asc by index, this is guaranteed + // by the pipeline executor + for _, trr := range trrs { + if trr.IsTerminal() { + finaltrrs = append(finaltrrs, trr) + } + } + + if len(finaltrrs) != 1 { + return nil, fmt.Errorf("invalid number of results, expected: 1, got: %d", len(finaltrrs)) + } + res := finaltrrs[0].Result + if res.Error != nil { + return nil, res.Error + } + val, err := toBigInt(res.Value) + if err != nil { + return nil, fmt.Errorf("failed to parse BenchmarkPrice: %w", err) + } + return val, nil +} + +func toBigInt(val interface{}) (*big.Int, error) { + dec, err := utils.ToDecimal(val) + if err != nil { + return nil, err + } + return dec.BigInt(), nil +} diff --git a/core/services/streams/stream_registry.go b/core/services/streams/stream_registry.go new file mode 100644 index 00000000000..c79c6c4e043 --- /dev/null +++ b/core/services/streams/stream_registry.go @@ -0,0 +1,60 @@ +package streams + +import ( + "fmt" + "sync" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" +) + +type StreamID = string + +type Registry interface { + Get(streamID StreamID) (strm Stream, exists bool) + Register(streamID StreamID, spec pipeline.Spec, rrs ResultRunSaver) error + Unregister(streamID StreamID) +} + +type streamRegistry struct { + sync.RWMutex + lggr logger.Logger + runner Runner + streams map[StreamID]Stream +} + +func NewRegistry(lggr logger.Logger, runner Runner) Registry { + return newRegistry(lggr, runner) +} + +func newRegistry(lggr logger.Logger, runner Runner) *streamRegistry { + return &streamRegistry{ + sync.RWMutex{}, + lggr.Named("Registry"), + runner, + make(map[StreamID]Stream), + } +} + +func (s *streamRegistry) Get(streamID StreamID) (strm Stream, exists bool) { + s.RLock() + defer s.RUnlock() + strm, exists = s.streams[streamID] + return +} + +func (s *streamRegistry) Register(streamID StreamID, spec pipeline.Spec, rrs ResultRunSaver) error { + s.Lock() + defer s.Unlock() + if _, exists := s.streams[streamID]; exists { + return fmt.Errorf("stream already registered for id: %q", streamID) + } + s.streams[streamID] = NewStream(s.lggr, streamID, spec, s.runner, rrs) + return nil +} + +func (s *streamRegistry) Unregister(streamID StreamID) { + s.Lock() + defer s.Unlock() + delete(s.streams, streamID) +} diff --git a/core/services/streams/stream_registry_test.go b/core/services/streams/stream_registry_test.go new file mode 100644 index 00000000000..2c7c2bd6ecc --- /dev/null +++ b/core/services/streams/stream_registry_test.go @@ -0,0 +1,107 @@ +package streams + +import ( + "context" + "testing" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockStream struct { + run *pipeline.Run + trrs pipeline.TaskRunResults + err error +} + +func (m *mockStream) Run(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) { + return m.run, m.trrs, m.err +} + +func Test_Registry(t *testing.T) { + lggr := logger.TestLogger(t) + runner := &mockRunner{} + + t.Run("Get", func(t *testing.T) { + sr := newRegistry(lggr, runner) + + sr.streams["foo"] = &mockStream{run: &pipeline.Run{ID: 1}} + sr.streams["bar"] = &mockStream{run: &pipeline.Run{ID: 2}} + sr.streams["baz"] = &mockStream{run: &pipeline.Run{ID: 3}} + + v, exists := sr.Get("foo") + assert.True(t, exists) + assert.Equal(t, sr.streams["foo"], v) + + v, exists = sr.Get("bar") + assert.True(t, exists) + assert.Equal(t, sr.streams["bar"], v) + + v, exists = sr.Get("baz") + assert.True(t, exists) + assert.Equal(t, sr.streams["baz"], v) + + v, exists = sr.Get("qux") + assert.Nil(t, v) + assert.False(t, exists) + }) + t.Run("Register", func(t *testing.T) { + sr := newRegistry(lggr, runner) + + t.Run("registers new stream", func(t *testing.T) { + assert.Len(t, sr.streams, 0) + err := sr.Register("foo", pipeline.Spec{ID: 32, DotDagSource: "source"}, nil) + require.NoError(t, err) + assert.Len(t, sr.streams, 1) + + v, exists := sr.Get("foo") + require.True(t, exists) + strm := v.(*stream) + assert.Equal(t, StreamID("foo"), strm.id) + assert.Equal(t, int32(32), strm.spec.ID) + }) + + t.Run("errors when attempt to re-register a stream with an existing ID", func(t *testing.T) { + assert.Len(t, sr.streams, 1) + err := sr.Register("foo", pipeline.Spec{ID: 33, DotDagSource: "source"}, nil) + require.Error(t, err) + assert.Len(t, sr.streams, 1) + assert.EqualError(t, err, "stream already registered for id: \"foo\"") + + v, exists := sr.Get("foo") + require.True(t, exists) + strm := v.(*stream) + assert.Equal(t, StreamID("foo"), strm.id) + assert.Equal(t, int32(32), strm.spec.ID) + }) + }) + t.Run("Unregister", func(t *testing.T) { + sr := newRegistry(lggr, runner) + + sr.streams["foo"] = &mockStream{run: &pipeline.Run{ID: 1}} + sr.streams["bar"] = &mockStream{run: &pipeline.Run{ID: 2}} + sr.streams["baz"] = &mockStream{run: &pipeline.Run{ID: 3}} + + t.Run("unregisters a stream", func(t *testing.T) { + assert.Len(t, sr.streams, 3) + + sr.Unregister("foo") + + assert.Len(t, sr.streams, 2) + _, exists := sr.streams["foo"] + assert.False(t, exists) + }) + t.Run("no effect when unregistering a non-existent stream", func(t *testing.T) { + assert.Len(t, sr.streams, 2) + + sr.Unregister("foo") + + assert.Len(t, sr.streams, 2) + _, exists := sr.streams["foo"] + assert.False(t, exists) + }) + }) +} diff --git a/core/services/streams/stream_test.go b/core/services/streams/stream_test.go new file mode 100644 index 00000000000..3a556411bc6 --- /dev/null +++ b/core/services/streams/stream_test.go @@ -0,0 +1,133 @@ +package streams + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/google/uuid" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" +) + +var UUID = uuid.New() + +type mockRunner struct { + p *pipeline.Pipeline + run *pipeline.Run + trrs pipeline.TaskRunResults + err error +} + +func (m *mockRunner) ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) { + return m.run, m.trrs, m.err +} +func (m *mockRunner) InitializePipeline(spec pipeline.Spec) (p *pipeline.Pipeline, err error) { + return m.p, m.err +} +func (m *mockRunner) InsertFinishedRun(run *pipeline.Run, saveSuccessfulTaskRuns bool, qopts ...pg.QOpt) error { + return m.err +} + +type MockTask struct { + result pipeline.Result +} + +func (m *MockTask) Type() pipeline.TaskType { return "MockTask" } +func (m *MockTask) ID() int { return 0 } +func (m *MockTask) DotID() string { return "" } +func (m *MockTask) Run(ctx context.Context, lggr logger.Logger, vars pipeline.Vars, inputs []pipeline.Result) (pipeline.Result, pipeline.RunInfo) { + return m.result, pipeline.RunInfo{} +} +func (m *MockTask) Base() *pipeline.BaseTask { return nil } +func (m *MockTask) Outputs() []pipeline.Task { return nil } +func (m *MockTask) Inputs() []pipeline.TaskDependency { return nil } +func (m *MockTask) OutputIndex() int32 { return 0 } +func (m *MockTask) TaskTimeout() (time.Duration, bool) { return 0, false } +func (m *MockTask) TaskRetries() uint32 { return 0 } +func (m *MockTask) TaskMinBackoff() time.Duration { return 0 } +func (m *MockTask) TaskMaxBackoff() time.Duration { return 0 } + +func Test_Stream(t *testing.T) { + lggr := logger.TestLogger(t) + runner := &mockRunner{} + spec := pipeline.Spec{} + id := StreamID("stream-id-foo") + ctx := testutils.Context(t) + + t.Run("Run", func(t *testing.T) { + strm := newStream(lggr, id, spec, runner, nil) + + t.Run("errors with empty pipeline", func(t *testing.T) { + _, _, err := strm.Run(ctx) + assert.EqualError(t, err, "Run failed: Run failed due to unparseable pipeline: empty pipeline") + }) + + spec.DotDagSource = ` +succeed [type=memo value=42] +succeed; +` + + strm = newStream(lggr, id, spec, runner, nil) + + t.Run("executes the pipeline (success)", func(t *testing.T) { + runner.run = &pipeline.Run{ID: 42} + runner.trrs = []pipeline.TaskRunResult{pipeline.TaskRunResult{ID: UUID}} + runner.err = nil + + run, trrs, err := strm.Run(ctx) + assert.NoError(t, err) + + assert.Equal(t, int64(42), run.ID) + require.Len(t, trrs, 1) + assert.Equal(t, UUID, trrs[0].ID) + }) + t.Run("executes the pipeline (failure)", func(t *testing.T) { + runner.err = errors.New("something exploded") + + _, _, err := strm.Run(ctx) + require.Error(t, err) + + assert.EqualError(t, err, "Run failed: error executing run for spec ID 0: something exploded") + }) + }) +} + +func Test_ExtractBigInt(t *testing.T) { + t.Run("wrong number of inputs", func(t *testing.T) { + trrs := []pipeline.TaskRunResult{} + + _, err := ExtractBigInt(trrs) + assert.EqualError(t, err, "invalid number of results, expected: 1, got: 0") + }) + t.Run("wrong type", func(t *testing.T) { + trrs := []pipeline.TaskRunResult{ + { + Result: pipeline.Result{Value: []byte{1, 2, 3}}, + Task: &MockTask{}, + }, + } + + _, err := ExtractBigInt(trrs) + assert.EqualError(t, err, "failed to parse BenchmarkPrice: type []uint8 cannot be converted to decimal.Decimal ([1 2 3])") + }) + t.Run("correct inputs", func(t *testing.T) { + trrs := []pipeline.TaskRunResult{ + { + Result: pipeline.Result{Value: "122.345"}, + Task: &MockTask{}, + }, + } + + val, err := ExtractBigInt(trrs) + require.NoError(t, err) + assert.Equal(t, big.NewInt(122), val) + }) +} diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index ba28e83bf3f..ecabbc09c71 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -76,7 +76,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { if jb.VRFSpec == nil || jb.PipelineSpec == nil { return nil, errors.Errorf("vrf.Delegate expects a VRFSpec and PipelineSpec to be present, got %+v", jb) } - pl, err := jb.PipelineSpec.Pipeline() + pl, err := jb.PipelineSpec.ParsePipeline() if err != nil { return nil, err } diff --git a/core/store/migrate/migrations/0220_stream_specs.sql b/core/store/migrate/migrations/0220_stream_specs.sql new file mode 100644 index 00000000000..f446928702c --- /dev/null +++ b/core/store/migrate/migrations/0220_stream_specs.sql @@ -0,0 +1,40 @@ +-- +goose Up +ALTER TABLE + jobs +DROP + CONSTRAINT chk_only_one_spec, +ADD + CONSTRAINT chk_specs CHECK ( + num_nonnulls( + ocr_oracle_spec_id, ocr2_oracle_spec_id, + direct_request_spec_id, flux_monitor_spec_id, + keeper_spec_id, cron_spec_id, webhook_spec_id, + vrf_spec_id, blockhash_store_spec_id, + block_header_feeder_spec_id, bootstrap_spec_id, + gateway_spec_id, + legacy_gas_station_server_spec_id, + legacy_gas_station_sidecar_spec_id, + eal_spec_id, + CASE "type" WHEN 'stream' THEN 1 ELSE NULL END -- 'stream' type lacks a spec but should not cause validation to fail + ) = 1 + ); + +-- +goose Down +ALTER TABLE + jobs +DROP + CONSTRAINT chk_specs, +ADD + CONSTRAINT chk_only_one_spec CHECK ( + num_nonnulls( + ocr_oracle_spec_id, ocr2_oracle_spec_id, + direct_request_spec_id, flux_monitor_spec_id, + keeper_spec_id, cron_spec_id, webhook_spec_id, + vrf_spec_id, blockhash_store_spec_id, + block_header_feeder_spec_id, bootstrap_spec_id, + gateway_spec_id, + legacy_gas_station_server_spec_id, + legacy_gas_station_sidecar_spec_id, + eal_spec_id + ) = 1 + ); diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index f2a40ff332a..e66971a7a11 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -828,3 +828,34 @@ storeBlockhashesBatchSize = %d return BlockHeaderFeederSpec{BlockHeaderFeederSpecParams: params, toml: toml} } + +type StreamSpecParams struct { + Name string +} + +type StreamSpec struct { + StreamSpecParams + toml string +} + +// Toml returns the BlockhashStoreSpec in TOML string form. +func (b StreamSpec) Toml() string { + return b.toml +} + +func GenerateStreamSpec(params StreamSpecParams) StreamSpec { + template := ` +type = "stream" +schemaVersion = 1 +name = "%s" +observationSource = """ +ds [type=http method=GET url="https://chain.link/ETH-USD"]; +ds_parse [type=jsonparse path="data,price"]; +ds_multiply [type=multiply times=100]; +ds -> ds_parse -> ds_multiply; +""" +` + + toml := fmt.Sprintf(template, params.Name) + return StreamSpec{StreamSpecParams: params, toml: toml} +} diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index 0f97e0b53d3..4e11f68097d 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/streams" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -250,6 +251,8 @@ func (jc *JobsController) validateJobSpec(tomlString string) (jb job.Job, status jb, err = ocrbootstrap.ValidatedBootstrapSpecToml(tomlString) case job.Gateway: jb, err = gateway.ValidatedGatewaySpec(tomlString) + case job.Stream: + jb, err = streams.ValidatedStreamSpec(tomlString) default: return jb, http.StatusUnprocessableEntity, errors.Errorf("unknown job type: %s", jobType) } diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 0a40c8a9c71..83c4fc30db0 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -36,6 +36,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/utils/tomlutils" @@ -139,12 +140,17 @@ func TestJobController_Create_HappyPath(t *testing.T) { app, client := setupJobsControllerTests(t) b1, b2 := setupBridges(t, app.GetSqlxDB(), app.GetConfig().Database()) require.NoError(t, app.KeyStore.OCR().Add(cltest.DefaultOCRKey)) - pks, err := app.KeyStore.VRF().GetAll() - require.NoError(t, err) - require.Len(t, pks, 1) - k, err := app.KeyStore.P2P().GetAll() - require.NoError(t, err) - require.Len(t, k, 1) + var pks []vrfkey.KeyV2 + var k []p2pkey.KeyV2 + { + var err error + pks, err = app.KeyStore.VRF().GetAll() + require.NoError(t, err) + require.Len(t, pks, 1) + k, err = app.KeyStore.P2P().GetAll() + require.NoError(t, err) + require.Len(t, k, 1) + } jorm := app.JobORM() var tt = []struct { @@ -360,6 +366,26 @@ func TestJobController_Create_HappyPath(t *testing.T) { assert.Equal(t, jb.VRFSpec.CoordinatorAddress.Hex(), resource.VRFSpec.CoordinatorAddress.Hex()) }, }, + { + name: "stream", + tomlTemplate: func(_ string) string { + return testspecs.GenerateStreamSpec(testspecs.StreamSpecParams{Name: "ETH/USD"}).Toml() + }, + assertion: func(t *testing.T, nameAndExternalJobID string, r *http.Response) { + require.Equal(t, http.StatusOK, r.StatusCode) + resp := cltest.ParseResponseBody(t, r) + resource := presenters.JobResource{} + err := web.ParseJSONAPIResponse(resp, &resource) + require.NoError(t, err) + + jb, err := jorm.FindJob(testutils.Context(t), mustInt32FromString(t, resource.ID)) + require.NoError(t, err) + require.NotNil(t, jb.PipelineSpec) + + assert.NotNil(t, resource.PipelineSpec.DotDAGSource) + assert.Equal(t, jb.Name.ValueOrZero(), resource.Name) + }, + }, } for _, tc := range tt { c := tc diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index a7aed0e5552..d0a6cfb5ca9 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -513,6 +513,8 @@ func NewJobResource(j job.Job) *JobResource { resource.BootstrapSpec = NewBootstrapSpec(j.BootstrapSpec) case job.Gateway: resource.GatewaySpec = NewGatewaySpec(j.GatewaySpec) + case job.Stream: + // no spec; nothing to do case job.LegacyGasStationServer, job.LegacyGasStationSidecar: // unsupported } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2ba80927256..6b70fd51c63 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `chainlink health` CLI command and HTML `/health` endpoint, to provide human-readable views of the underlying JSON health data. +- New job type `stream` to represent streamspecs. This job type is not yet used anywhere but will be required for Data Streams V1. ### Fixed From 3aa5e4681dc4475727f2167db253cc1e1f2e8c7c Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 11 Jan 2024 10:19:56 -0600 Subject: [PATCH 144/234] golangci-lint: add noctx, sqlclosecheck; rm redundant revive.toml (#11362) * golangci-lint: add noctx, sqlclosecheck; rm redundant revive.toml * sqlclosecheck * noctx --- .golangci.yml | 2 + core/bridges/orm.go | 2 + core/cmd/admin_commands.go | 17 +++--- core/cmd/blocks_commands.go | 2 +- core/cmd/bridge_commands.go | 6 +-- core/cmd/cosmos_transaction_commands.go | 2 +- core/cmd/csa_keys_commands.go | 8 +-- core/cmd/eth_keys_commands.go | 12 ++--- core/cmd/evm_transaction_commands.go | 4 +- core/cmd/forwarders_commands.go | 4 +- core/cmd/jobs_commands.go | 8 +-- core/cmd/keys_commands.go | 10 ++-- core/cmd/ocr2_keys_commands.go | 10 ++-- core/cmd/ocr_keys_commands.go | 10 ++-- core/cmd/p2p_keys_commands.go | 10 ++-- core/cmd/shell.go | 42 +++++++-------- core/cmd/shell_local.go | 6 ++- core/cmd/shell_remote.go | 22 ++++---- core/cmd/shell_remote_test.go | 23 ++++---- core/cmd/shell_test.go | 8 +-- core/cmd/solana_transaction_commands.go | 2 +- core/cmd/vrf_keys_commands.go | 10 ++-- core/internal/cltest/cltest.go | 17 +++--- core/internal/cltest/mocks.go | 2 +- core/scripts/chaincli/handler/bootstrap.go | 10 ++-- core/scripts/chaincli/handler/debug.go | 17 +++--- core/scripts/chaincli/handler/handler.go | 26 ++++----- core/scripts/chaincli/handler/jobs.go | 8 +-- core/scripts/chaincli/handler/keeper.go | 7 +-- .../chaincli/handler/keeper_deployer.go | 19 +++---- .../scripts/chaincli/handler/keeper_launch.go | 28 +++++----- .../chaincli/handler/scrape_node_config.go | 18 +++---- .../gateway/network/httpserver_test.go | 2 +- .../services/gateway/network/wsserver_test.go | 2 +- core/services/health_test.go | 5 +- core/services/keystore/p2p_test.go | 1 + core/services/ocr/database.go | 4 +- core/services/ocr2/database.go | 2 +- .../ocr2/plugins/dkg/persistence/db_test.go | 3 ++ .../services/ocrcommon/discoverer_database.go | 16 +++--- core/services/pg/q.go | 1 + core/services/pipeline/orm.go | 1 + core/services/pipeline/orm_test.go | 3 +- core/services/relay/evm/request_round_db.go | 1 + core/services/webhook/delegate.go | 10 +++- .../webhook/external_initiator_manager.go | 25 ++++----- .../external_initiator_manager_test.go | 12 +++-- .../mocks/external_initiator_manager.go | 23 ++++---- core/utils/http/http_test.go | 3 +- core/web/auth/auth_test.go | 22 +++++--- core/web/auth/gql_test.go | 4 +- core/web/build_info_controller_test.go | 7 ++- core/web/gui_assets_test.go | 24 ++++++--- core/web/jobs_controller_test.go | 12 +++-- core/web/log_controller_test.go | 4 +- core/web/ping_controller_test.go | 9 ++-- core/web/router_test.go | 37 ++++++++----- core/web/sessions_controller_test.go | 17 ++++-- revive.toml | 53 ------------------- tools/flakeytests/cmd/runner/main.go | 15 ++++-- tools/flakeytests/reporter.go | 9 ++-- tools/flakeytests/runner.go | 7 +-- tools/flakeytests/runner_test.go | 25 +++++---- 63 files changed, 384 insertions(+), 347 deletions(-) delete mode 100644 revive.toml diff --git a/.golangci.yml b/.golangci.yml index f9d06ad471c..00541302f08 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,6 +10,8 @@ linters: - misspell - rowserrcheck - errorlint + - sqlclosecheck + - noctx linters-settings: exhaustive: default-signifies-exhaustive: true diff --git a/core/bridges/orm.go b/core/bridges/orm.go index cfad1da836e..8ae6b855c88 100644 --- a/core/bridges/orm.go +++ b/core/bridges/orm.go @@ -150,6 +150,7 @@ func (o *orm) CreateBridgeType(bt *BridgeType) error { if err != nil { return err } + defer stmt.Close() return stmt.Get(bt, bt) }) if err == nil { @@ -222,6 +223,7 @@ func (o *orm) CreateExternalInitiator(externalInitiator *ExternalInitiator) (err if err != nil { return errors.Wrap(err, "failed to prepare named stmt") } + defer stmt.Close() return errors.Wrap(stmt.Get(externalInitiator, externalInitiator), "failed to load external_initiator") }) return errors.Wrap(err, "CreateExternalInitiator failed") diff --git a/core/cmd/admin_commands.go b/core/cmd/admin_commands.go index a0e69867e71..799709ad205 100644 --- a/core/cmd/admin_commands.go +++ b/core/cmd/admin_commands.go @@ -180,7 +180,7 @@ func (ps AdminUsersPresenters) RenderTable(rt RendererTable) error { // ListUsers renders all API users and their roles func (s *Shell) ListUsers(_ *cli.Context) (err error) { - resp, err := s.HTTP.Get("/v2/users/", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/users/", nil) if err != nil { return s.errorOut(err) } @@ -195,7 +195,7 @@ func (s *Shell) ListUsers(_ *cli.Context) (err error) { // CreateUser creates a new user by prompting for email, password, and role func (s *Shell) CreateUser(c *cli.Context) (err error) { - resp, err := s.HTTP.Get("/v2/users/", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/users/", nil) if err != nil { return s.errorOut(err) } @@ -234,7 +234,7 @@ func (s *Shell) CreateUser(c *cli.Context) (err error) { } buf := bytes.NewBuffer(requestData) - response, err := s.HTTP.Post("/v2/users", buf) + response, err := s.HTTP.Post(s.ctx(), "/v2/users", buf) if err != nil { return s.errorOut(err) } @@ -263,7 +263,7 @@ func (s *Shell) ChangeRole(c *cli.Context) (err error) { } buf := bytes.NewBuffer(requestData) - response, err := s.HTTP.Patch("/v2/users", buf) + response, err := s.HTTP.Patch(s.ctx(), "/v2/users", buf) if err != nil { return s.errorOut(err) } @@ -283,7 +283,7 @@ func (s *Shell) DeleteUser(c *cli.Context) (err error) { return s.errorOut(errors.New("email flag is empty, must specify an email")) } - response, err := s.HTTP.Delete(fmt.Sprintf("/v2/users/%s", email)) + response, err := s.HTTP.Delete(s.ctx(), fmt.Sprintf("/v2/users/%s", email)) if err != nil { return s.errorOut(err) } @@ -297,8 +297,8 @@ func (s *Shell) DeleteUser(c *cli.Context) (err error) { } // Status will display the health of various services -func (s *Shell) Status(_ *cli.Context) error { - resp, err := s.HTTP.Get("/health?full=1", nil) +func (s *Shell) Status(c *cli.Context) error { + resp, err := s.HTTP.Get(s.ctx(), "/health?full=1", nil) if err != nil { return s.errorOut(err) } @@ -313,6 +313,7 @@ func (s *Shell) Status(_ *cli.Context) error { // Profile will collect pprof metrics and store them in a folder. func (s *Shell) Profile(c *cli.Context) error { + ctx := s.ctx() seconds := c.Uint("seconds") baseDir := c.String("output_dir") @@ -342,7 +343,7 @@ func (s *Shell) Profile(c *cli.Context) error { go func(vt string) { defer wgPprof.Done() uri := fmt.Sprintf("/v2/debug/pprof/%s?seconds=%d", vt, seconds) - resp, err := s.HTTP.Get(uri) + resp, err := s.HTTP.Get(ctx, uri) if err != nil { errs <- fmt.Errorf("error collecting %s: %w", vt, err) return diff --git a/core/cmd/blocks_commands.go b/core/cmd/blocks_commands.go index 29c9e7642a5..72b0523e18d 100644 --- a/core/cmd/blocks_commands.go +++ b/core/cmd/blocks_commands.go @@ -52,7 +52,7 @@ func (s *Shell) ReplayFromBlock(c *cli.Context) (err error) { } buf := bytes.NewBufferString("{}") - resp, err := s.HTTP.Post( + resp, err := s.HTTP.Post(s.ctx(), fmt.Sprintf( "/v2/replay_from_block/%v?%s", blockNumber, diff --git a/core/cmd/bridge_commands.go b/core/cmd/bridge_commands.go index 8389d548c79..398d466c43a 100644 --- a/core/cmd/bridge_commands.go +++ b/core/cmd/bridge_commands.go @@ -91,7 +91,7 @@ func (s *Shell) ShowBridge(c *cli.Context) (err error) { return s.errorOut(errors.New("must pass the name of the bridge to be shown")) } bridgeName := c.Args().First() - resp, err := s.HTTP.Get("/v2/bridge_types/" + bridgeName) + resp, err := s.HTTP.Get(s.ctx(), "/v2/bridge_types/"+bridgeName) if err != nil { return s.errorOut(err) } @@ -115,7 +115,7 @@ func (s *Shell) CreateBridge(c *cli.Context) (err error) { return s.errorOut(err) } - resp, err := s.HTTP.Post("/v2/bridge_types", buf) + resp, err := s.HTTP.Post(s.ctx(), "/v2/bridge_types", buf) if err != nil { return s.errorOut(err) } @@ -134,7 +134,7 @@ func (s *Shell) RemoveBridge(c *cli.Context) (err error) { return s.errorOut(errors.New("must pass the name of the bridge to be removed")) } bridgeName := c.Args().First() - resp, err := s.HTTP.Delete("/v2/bridge_types/" + bridgeName) + resp, err := s.HTTP.Delete(s.ctx(), "/v2/bridge_types/"+bridgeName) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/cosmos_transaction_commands.go b/core/cmd/cosmos_transaction_commands.go index 7c53d1ff1da..576a64adfb0 100644 --- a/core/cmd/cosmos_transaction_commands.go +++ b/core/cmd/cosmos_transaction_commands.go @@ -115,7 +115,7 @@ func (s *Shell) CosmosSendNativeToken(c *cli.Context) (err error) { buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Post("/v2/transfers/cosmos", buf) + resp, err := s.HTTP.Post(s.ctx(), "/v2/transfers/cosmos", buf) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/csa_keys_commands.go b/core/cmd/csa_keys_commands.go index c2ab3697b18..1c0fe54ab09 100644 --- a/core/cmd/csa_keys_commands.go +++ b/core/cmd/csa_keys_commands.go @@ -108,7 +108,7 @@ func (ps CSAKeyPresenters) RenderTable(rt RendererTable) error { // ListCSAKeys retrieves a list of all CSA keys func (s *Shell) ListCSAKeys(_ *cli.Context) (err error) { - resp, err := s.HTTP.Get("/v2/keys/csa", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/keys/csa", nil) if err != nil { return s.errorOut(err) } @@ -123,7 +123,7 @@ func (s *Shell) ListCSAKeys(_ *cli.Context) (err error) { // CreateCSAKey creates a new CSA key func (s *Shell) CreateCSAKey(_ *cli.Context) (err error) { - resp, err := s.HTTP.Post("/v2/keys/csa", nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/csa", nil) if err != nil { return s.errorOut(err) } @@ -165,7 +165,7 @@ func (s *Shell) ImportCSAKey(c *cli.Context) (err error) { query.Set("oldpassword", normalizePassword(string(oldPassword))) exportUrl.RawQuery = query.Encode() - resp, err := s.HTTP.Post(exportUrl.String(), bytes.NewReader(keyJSON)) + resp, err := s.HTTP.Post(s.ctx(), exportUrl.String(), bytes.NewReader(keyJSON)) if err != nil { return s.errorOut(err) } @@ -208,7 +208,7 @@ func (s *Shell) ExportCSAKey(c *cli.Context) (err error) { query.Set("newpassword", normalizePassword(string(newPassword))) exportUrl.RawQuery = query.Encode() - resp, err := s.HTTP.Post(exportUrl.String(), nil) + resp, err := s.HTTP.Post(s.ctx(), exportUrl.String(), nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } diff --git a/core/cmd/eth_keys_commands.go b/core/cmd/eth_keys_commands.go index 4fb3044ecb6..5adac3b382b 100644 --- a/core/cmd/eth_keys_commands.go +++ b/core/cmd/eth_keys_commands.go @@ -176,7 +176,7 @@ func (ps EthKeyPresenters) RenderTable(rt RendererTable) error { // ListETHKeys renders the active account address with its ETH & LINK balance func (s *Shell) ListETHKeys(_ *cli.Context) (err error) { - resp, err := s.HTTP.Get("/v2/keys/evm") + resp, err := s.HTTP.Get(s.ctx(), "/v2/keys/evm") if err != nil { return s.errorOut(err) @@ -206,7 +206,7 @@ func (s *Shell) CreateETHKey(c *cli.Context) (err error) { } createUrl.RawQuery = query.Encode() - resp, err := s.HTTP.Post(createUrl.String(), nil) + resp, err := s.HTTP.Post(s.ctx(), createUrl.String(), nil) if err != nil { return s.errorOut(err) } @@ -231,7 +231,7 @@ func (s *Shell) DeleteETHKey(c *cli.Context) (err error) { return nil } - resp, err := s.HTTP.Delete("/v2/keys/evm/" + address) + resp, err := s.HTTP.Delete(s.ctx(), "/v2/keys/evm/"+address) if err != nil { return s.errorOut(err) } @@ -290,7 +290,7 @@ func (s *Shell) ImportETHKey(c *cli.Context) (err error) { } importUrl.RawQuery = query.Encode() - resp, err := s.HTTP.Post(importUrl.String(), bytes.NewReader(keyJSON)) + resp, err := s.HTTP.Post(s.ctx(), importUrl.String(), bytes.NewReader(keyJSON)) if err != nil { return s.errorOut(err) } @@ -332,7 +332,7 @@ func (s *Shell) ExportETHKey(c *cli.Context) (err error) { query.Set("newpassword", strings.TrimSpace(string(newPassword))) exportUrl.RawQuery = query.Encode() - resp, err := s.HTTP.Post(exportUrl.String(), nil) + resp, err := s.HTTP.Post(s.ctx(), exportUrl.String(), nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } @@ -385,7 +385,7 @@ func (s *Shell) UpdateChainEVMKey(c *cli.Context) (err error) { } chainURL.RawQuery = query.Encode() - resp, err := s.HTTP.Post(chainURL.String(), nil) + resp, err := s.HTTP.Post(s.ctx(), chainURL.String(), nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } diff --git a/core/cmd/evm_transaction_commands.go b/core/cmd/evm_transaction_commands.go index 76628944a6c..28a4fa23a3b 100644 --- a/core/cmd/evm_transaction_commands.go +++ b/core/cmd/evm_transaction_commands.go @@ -117,7 +117,7 @@ func (s *Shell) ShowTransaction(c *cli.Context) (err error) { return s.errorOut(errors.New("must pass the hash of the transaction")) } hash := c.Args().First() - resp, err := s.HTTP.Get("/v2/transactions/evm/" + hash) + resp, err := s.HTTP.Get(s.ctx(), "/v2/transactions/evm/"+hash) if err != nil { return s.errorOut(err) } @@ -198,7 +198,7 @@ func (s *Shell) SendEther(c *cli.Context) (err error) { buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Post("/v2/transfers/evm", buf) + resp, err := s.HTTP.Post(s.ctx(), "/v2/transfers/evm", buf) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/forwarders_commands.go b/core/cmd/forwarders_commands.go index a870d4714c7..2445be5bfec 100644 --- a/core/cmd/forwarders_commands.go +++ b/core/cmd/forwarders_commands.go @@ -102,7 +102,7 @@ func (s *Shell) DeleteForwarder(c *cli.Context) (err error) { if !c.Args().Present() { return s.errorOut(errors.New("must pass the forwarder id to be archived")) } - resp, err := s.HTTP.Delete("/v2/nodes/evm/forwarders/" + c.Args().First()) + resp, err := s.HTTP.Delete(s.ctx(), "/v2/nodes/evm/forwarders/"+c.Args().First()) if err != nil { return s.errorOut(err) } @@ -143,7 +143,7 @@ func (s *Shell) TrackForwarder(c *cli.Context) (err error) { return s.errorOut(err) } - resp, err := s.HTTP.Post("/v2/nodes/evm/forwarders/track", bytes.NewReader(request)) + resp, err := s.HTTP.Post(s.ctx(), "/v2/nodes/evm/forwarders/track", bytes.NewReader(request)) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/jobs_commands.go b/core/cmd/jobs_commands.go index d6e752e1a10..1f9ca33c78e 100644 --- a/core/cmd/jobs_commands.go +++ b/core/cmd/jobs_commands.go @@ -212,7 +212,7 @@ func (s *Shell) ShowJob(c *cli.Context) (err error) { return s.errorOut(errors.New("must provide the id of the job")) } id := c.Args().First() - resp, err := s.HTTP.Get("/v2/jobs/" + id) + resp, err := s.HTTP.Get(s.ctx(), "/v2/jobs/"+id) if err != nil { return s.errorOut(err) } @@ -244,7 +244,7 @@ func (s *Shell) CreateJob(c *cli.Context) (err error) { return s.errorOut(err) } - resp, err := s.HTTP.Post("/v2/jobs", bytes.NewReader(request)) + resp, err := s.HTTP.Post(s.ctx(), "/v2/jobs", bytes.NewReader(request)) if err != nil { return s.errorOut(err) } @@ -273,7 +273,7 @@ func (s *Shell) DeleteJob(c *cli.Context) error { if !c.Args().Present() { return s.errorOut(errors.New("must pass the job id to be archived")) } - resp, err := s.HTTP.Delete("/v2/jobs/" + c.Args().First()) + resp, err := s.HTTP.Delete(s.ctx(), "/v2/jobs/"+c.Args().First()) if err != nil { return s.errorOut(err) } @@ -291,7 +291,7 @@ func (s *Shell) TriggerPipelineRun(c *cli.Context) error { if !c.Args().Present() { return s.errorOut(errors.New("Must pass the job id to trigger a run")) } - resp, err := s.HTTP.Post("/v2/jobs/"+c.Args().First()+"/runs", nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/jobs/"+c.Args().First()+"/runs", nil) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/keys_commands.go b/core/cmd/keys_commands.go index 07340c9cf08..7408d168887 100644 --- a/core/cmd/keys_commands.go +++ b/core/cmd/keys_commands.go @@ -105,7 +105,7 @@ func newKeysClient[K keystore.Key, P TableRenderer, P2 ~[]P](typ string, s *Shel // ListKeys retrieves a list of all keys func (cli *keysClient[K, P, P2]) ListKeys(_ *cli.Context) (err error) { - resp, err := cli.HTTP.Get(cli.path, nil) + resp, err := cli.HTTP.Get(cli.ctx(), cli.path, nil) if err != nil { return cli.errorOut(err) } @@ -121,7 +121,7 @@ func (cli *keysClient[K, P, P2]) ListKeys(_ *cli.Context) (err error) { // CreateKey creates a new key func (cli *keysClient[K, P, P2]) CreateKey(_ *cli.Context) (err error) { - resp, err := cli.HTTP.Post(cli.path, nil) + resp, err := cli.HTTP.Post(cli.ctx(), cli.path, nil) if err != nil { return cli.errorOut(err) } @@ -152,7 +152,7 @@ func (cli *keysClient[K, P, P2]) DeleteKey(c *cli.Context) (err error) { queryStr = "?hard=true" } - resp, err := cli.HTTP.Delete(fmt.Sprintf(cli.path+"/%s%s", id, queryStr)) + resp, err := cli.HTTP.Delete(cli.ctx(), fmt.Sprintf(cli.path+"/%s%s", id, queryStr)) if err != nil { return cli.errorOut(err) } @@ -189,7 +189,7 @@ func (cli *keysClient[K, P, P2]) ImportKey(c *cli.Context) (err error) { } normalizedPassword := normalizePassword(string(oldPassword)) - resp, err := cli.HTTP.Post(cli.path+"/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) + resp, err := cli.HTTP.Post(cli.ctx(), cli.path+"/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) if err != nil { return cli.errorOut(err) } @@ -227,7 +227,7 @@ func (cli *keysClient[K, P, P2]) ExportKey(c *cli.Context) (err error) { ID := c.Args().Get(0) normalizedPassword := normalizePassword(string(newPassword)) - resp, err := cli.HTTP.Post(cli.path+"/export/"+ID+"?newpassword="+normalizedPassword, nil) + resp, err := cli.HTTP.Post(cli.ctx(), cli.path+"/export/"+ID+"?newpassword="+normalizedPassword, nil) if err != nil { return cli.errorOut(errors.Wrap(err, "Could not make HTTP request")) } diff --git a/core/cmd/ocr2_keys_commands.go b/core/cmd/ocr2_keys_commands.go index ad4b30df4c2..1d469024878 100644 --- a/core/cmd/ocr2_keys_commands.go +++ b/core/cmd/ocr2_keys_commands.go @@ -127,7 +127,7 @@ func (ps OCR2KeyBundlePresenters) RenderTable(rt RendererTable) error { // ListOCR2KeyBundles lists the available OCR2 Key Bundles func (s *Shell) ListOCR2KeyBundles(_ *cli.Context) error { - resp, err := s.HTTP.Get("/v2/keys/ocr2", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/keys/ocr2", nil) if err != nil { return s.errorOut(err) } @@ -149,7 +149,7 @@ func (s *Shell) CreateOCR2KeyBundle(c *cli.Context) error { ) } chainType := c.Args().Get(0) - resp, err := s.HTTP.Post(fmt.Sprintf("/v2/keys/ocr2/%s", chainType), nil) + resp, err := s.HTTP.Post(s.ctx(), fmt.Sprintf("/v2/keys/ocr2/%s", chainType), nil) if err != nil { return s.errorOut(err) } @@ -182,7 +182,7 @@ func (s *Shell) DeleteOCR2KeyBundle(c *cli.Context) error { queryStr = "?hard=true" } - resp, err := s.HTTP.Delete(fmt.Sprintf("/v2/keys/ocr2/%s%s", id, queryStr)) + resp, err := s.HTTP.Delete(s.ctx(), fmt.Sprintf("/v2/keys/ocr2/%s%s", id, queryStr)) if err != nil { return s.errorOut(err) } @@ -218,7 +218,7 @@ func (s *Shell) ImportOCR2Key(c *cli.Context) (err error) { } normalizedPassword := normalizePassword(string(oldPassword)) - resp, err := s.HTTP.Post("/v2/keys/ocr2/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/ocr2/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) if err != nil { return s.errorOut(err) } @@ -255,7 +255,7 @@ func (s *Shell) ExportOCR2Key(c *cli.Context) (err error) { ID := c.Args().Get(0) normalizedPassword := normalizePassword(string(newPassword)) - resp, err := s.HTTP.Post("/v2/keys/ocr2/export/"+ID+"?newpassword="+normalizedPassword, nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/ocr2/export/"+ID+"?newpassword="+normalizedPassword, nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } diff --git a/core/cmd/ocr_keys_commands.go b/core/cmd/ocr_keys_commands.go index 2628cd9b270..399333bba93 100644 --- a/core/cmd/ocr_keys_commands.go +++ b/core/cmd/ocr_keys_commands.go @@ -108,7 +108,7 @@ type OCRKeyBundlePresenters []OCRKeyBundlePresenter // ListOCRKeyBundles lists the available OCR Key Bundles func (s *Shell) ListOCRKeyBundles(_ *cli.Context) error { - resp, err := s.HTTP.Get("/v2/keys/ocr", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/keys/ocr", nil) if err != nil { return s.errorOut(err) } @@ -141,7 +141,7 @@ func (ps OCRKeyBundlePresenters) RenderTable(rt RendererTable) error { // CreateOCR2KeyBundle creates an OCR key bundle and saves it to the keystore func (s *Shell) CreateOCRKeyBundle(_ *cli.Context) error { - resp, err := s.HTTP.Post("/v2/keys/ocr", nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/ocr", nil) if err != nil { return s.errorOut(err) } @@ -174,7 +174,7 @@ func (s *Shell) DeleteOCRKeyBundle(c *cli.Context) error { queryStr = "?hard=true" } - resp, err := s.HTTP.Delete(fmt.Sprintf("/v2/keys/ocr/%s%s", id, queryStr)) + resp, err := s.HTTP.Delete(s.ctx(), fmt.Sprintf("/v2/keys/ocr/%s%s", id, queryStr)) if err != nil { return s.errorOut(err) } @@ -210,7 +210,7 @@ func (s *Shell) ImportOCRKey(c *cli.Context) (err error) { } normalizedPassword := normalizePassword(string(oldPassword)) - resp, err := s.HTTP.Post("/v2/keys/ocr/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/ocr/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) if err != nil { return s.errorOut(err) } @@ -247,7 +247,7 @@ func (s *Shell) ExportOCRKey(c *cli.Context) (err error) { ID := c.Args().Get(0) normalizedPassword := normalizePassword(string(newPassword)) - resp, err := s.HTTP.Post("/v2/keys/ocr/export/"+ID+"?newpassword="+normalizedPassword, nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/ocr/export/"+ID+"?newpassword="+normalizedPassword, nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } diff --git a/core/cmd/p2p_keys_commands.go b/core/cmd/p2p_keys_commands.go index 4ec03da96ca..da3bf412a04 100644 --- a/core/cmd/p2p_keys_commands.go +++ b/core/cmd/p2p_keys_commands.go @@ -125,7 +125,7 @@ func (ps P2PKeyPresenters) RenderTable(rt RendererTable) error { // ListP2PKeys retrieves a list of all P2P keys func (s *Shell) ListP2PKeys(_ *cli.Context) (err error) { - resp, err := s.HTTP.Get("/v2/keys/p2p", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/keys/p2p", nil) if err != nil { return s.errorOut(err) } @@ -140,7 +140,7 @@ func (s *Shell) ListP2PKeys(_ *cli.Context) (err error) { // CreateP2PKey creates a new P2P key func (s *Shell) CreateP2PKey(_ *cli.Context) (err error) { - resp, err := s.HTTP.Post("/v2/keys/p2p", nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/p2p", nil) if err != nil { return s.errorOut(err) } @@ -170,7 +170,7 @@ func (s *Shell) DeleteP2PKey(c *cli.Context) (err error) { queryStr = "?hard=true" } - resp, err := s.HTTP.Delete(fmt.Sprintf("/v2/keys/p2p/%s%s", id, queryStr)) + resp, err := s.HTTP.Delete(s.ctx(), fmt.Sprintf("/v2/keys/p2p/%s%s", id, queryStr)) if err != nil { return s.errorOut(err) } @@ -206,7 +206,7 @@ func (s *Shell) ImportP2PKey(c *cli.Context) (err error) { } normalizedPassword := normalizePassword(string(oldPassword)) - resp, err := s.HTTP.Post("/v2/keys/p2p/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/p2p/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) if err != nil { return s.errorOut(err) } @@ -243,7 +243,7 @@ func (s *Shell) ExportP2PKey(c *cli.Context) (err error) { ID := c.Args().Get(0) normalizedPassword := normalizePassword(string(newPassword)) - resp, err := s.HTTP.Post("/v2/keys/p2p/export/"+ID+"?newpassword="+normalizedPassword, nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/p2p/export/"+ID+"?newpassword="+normalizedPassword, nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 547de67210f..b2298ab399d 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -473,11 +473,11 @@ func createServer(handler *gin.Engine, addr string, requestTimeout time.Duration // HTTPClient encapsulates all methods used to interact with a chainlink node API. type HTTPClient interface { - Get(string, ...map[string]string) (*http.Response, error) - Post(string, io.Reader) (*http.Response, error) - Put(string, io.Reader) (*http.Response, error) - Patch(string, io.Reader, ...map[string]string) (*http.Response, error) - Delete(string) (*http.Response, error) + Get(context.Context, string, ...map[string]string) (*http.Response, error) + Post(context.Context, string, io.Reader) (*http.Response, error) + Put(context.Context, string, io.Reader) (*http.Response, error) + Patch(context.Context, string, io.Reader, ...map[string]string) (*http.Response, error) + Delete(context.Context, string) (*http.Response, error) } type authenticatedHTTPClient struct { @@ -511,31 +511,31 @@ func newHttpClient(lggr logger.Logger, insecureSkipVerify bool) *http.Client { } // Get performs an HTTP Get using the authenticated HTTP client's cookie. -func (h *authenticatedHTTPClient) Get(path string, headers ...map[string]string) (*http.Response, error) { - return h.doRequest("GET", path, nil, headers...) +func (h *authenticatedHTTPClient) Get(ctx context.Context, path string, headers ...map[string]string) (*http.Response, error) { + return h.doRequest(ctx, "GET", path, nil, headers...) } // Post performs an HTTP Post using the authenticated HTTP client's cookie. -func (h *authenticatedHTTPClient) Post(path string, body io.Reader) (*http.Response, error) { - return h.doRequest("POST", path, body) +func (h *authenticatedHTTPClient) Post(ctx context.Context, path string, body io.Reader) (*http.Response, error) { + return h.doRequest(ctx, "POST", path, body) } // Put performs an HTTP Put using the authenticated HTTP client's cookie. -func (h *authenticatedHTTPClient) Put(path string, body io.Reader) (*http.Response, error) { - return h.doRequest("PUT", path, body) +func (h *authenticatedHTTPClient) Put(ctx context.Context, path string, body io.Reader) (*http.Response, error) { + return h.doRequest(ctx, "PUT", path, body) } // Patch performs an HTTP Patch using the authenticated HTTP client's cookie. -func (h *authenticatedHTTPClient) Patch(path string, body io.Reader, headers ...map[string]string) (*http.Response, error) { - return h.doRequest("PATCH", path, body, headers...) +func (h *authenticatedHTTPClient) Patch(ctx context.Context, path string, body io.Reader, headers ...map[string]string) (*http.Response, error) { + return h.doRequest(ctx, "PATCH", path, body, headers...) } // Delete performs an HTTP Delete using the authenticated HTTP client's cookie. -func (h *authenticatedHTTPClient) Delete(path string) (*http.Response, error) { - return h.doRequest("DELETE", path, nil) +func (h *authenticatedHTTPClient) Delete(ctx context.Context, path string) (*http.Response, error) { + return h.doRequest(ctx, "DELETE", path, nil) } -func (h *authenticatedHTTPClient) doRequest(verb, path string, body io.Reader, headerArgs ...map[string]string) (*http.Response, error) { +func (h *authenticatedHTTPClient) doRequest(ctx context.Context, verb, path string, body io.Reader, headerArgs ...map[string]string) (*http.Response, error) { var headers map[string]string if len(headerArgs) > 0 { headers = headerArgs[0] @@ -543,7 +543,7 @@ func (h *authenticatedHTTPClient) doRequest(verb, path string, body io.Reader, h headers = map[string]string{} } - request, err := http.NewRequest(verb, h.remoteNodeURL.String()+path, body) + request, err := http.NewRequestWithContext(ctx, verb, h.remoteNodeURL.String()+path, body) if err != nil { return nil, err } @@ -565,7 +565,7 @@ func (h *authenticatedHTTPClient) doRequest(verb, path string, body io.Reader, h } if response.StatusCode == http.StatusUnauthorized && (h.sessionRequest.Email != "" || h.sessionRequest.Password != "") { var cookieerr error - cookie, cookieerr = h.cookieAuth.Authenticate(h.sessionRequest) + cookie, cookieerr = h.cookieAuth.Authenticate(ctx, h.sessionRequest) if cookieerr != nil { return response, err } @@ -583,7 +583,7 @@ func (h *authenticatedHTTPClient) doRequest(verb, path string, body io.Reader, h // future HTTP requests. type CookieAuthenticator interface { Cookie() (*http.Cookie, error) - Authenticate(sessions.SessionRequest) (*http.Cookie, error) + Authenticate(context.Context, sessions.SessionRequest) (*http.Cookie, error) Logout() error } @@ -612,14 +612,14 @@ func (t *SessionCookieAuthenticator) Cookie() (*http.Cookie, error) { } // Authenticate retrieves a session ID via a cookie and saves it to disk. -func (t *SessionCookieAuthenticator) Authenticate(sessionRequest sessions.SessionRequest) (*http.Cookie, error) { +func (t *SessionCookieAuthenticator) Authenticate(ctx context.Context, sessionRequest sessions.SessionRequest) (*http.Cookie, error) { b := new(bytes.Buffer) err := json.NewEncoder(b).Encode(sessionRequest) if err != nil { return nil, err } url := t.config.RemoteNodeURL.String() + "/sessions" - req, err := http.NewRequest("POST", url, b) + req, err := http.NewRequestWithContext(ctx, "POST", url, b) if err != nil { return nil, err } diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 69b7373ed70..b970b516413 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -1006,9 +1006,8 @@ func (s *Shell) CleanupChainTables(c *cli.Context) error { rows, err := db.Query(tablesToDeleteFromQuery, "evm_chain_id") if err != nil { return err - } else if rows.Err() != nil { - return rows.Err() } + defer rows.Close() var tablesToDeleteFrom []string for rows.Next() { @@ -1019,6 +1018,9 @@ func (s *Shell) CleanupChainTables(c *cli.Context) error { } tablesToDeleteFrom = append(tablesToDeleteFrom, schema+"."+name) } + if rows.Err() != nil { + return rows.Err() + } for _, tableName := range tablesToDeleteFrom { query := fmt.Sprintf(`DELETE FROM %s WHERE "evm_chain_id"=$1;`, tableName) diff --git a/core/cmd/shell_remote.go b/core/cmd/shell_remote.go index bc4620d0732..aab4a94da6f 100644 --- a/core/cmd/shell_remote.go +++ b/core/cmd/shell_remote.go @@ -110,7 +110,7 @@ func (s *Shell) CreateExternalInitiator(c *cli.Context) (err error) { } buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Post("/v2/external_initiators", buf) + resp, err := s.HTTP.Post(s.ctx(), "/v2/external_initiators", buf) if err != nil { return s.errorOut(err) } @@ -131,7 +131,7 @@ func (s *Shell) DeleteExternalInitiator(c *cli.Context) (err error) { return s.errorOut(errors.New("Must pass the name of the external initiator to delete")) } - resp, err := s.HTTP.Delete("/v2/external_initiators/" + c.Args().First()) + resp, err := s.HTTP.Delete(s.ctx(), "/v2/external_initiators/"+c.Args().First()) if err != nil { return s.errorOut(err) } @@ -155,7 +155,7 @@ func (s *Shell) getPage(requestURI string, page int, model interface{}) (err err } uri.RawQuery = q.Encode() - resp, err := s.HTTP.Get(uri.String()) + resp, err := s.HTTP.Get(s.ctx(), uri.String()) if err != nil { return s.errorOut(err) } @@ -180,7 +180,7 @@ func (s *Shell) RemoteLogin(c *cli.Context) error { if err != nil { return s.errorOut(err) } - _, err = s.CookieAuthenticator.Authenticate(sessionRequest) + _, err = s.CookieAuthenticator.Authenticate(s.ctx(), sessionRequest) if err != nil { return s.errorOut(err) } @@ -194,7 +194,7 @@ func (s *Shell) RemoteLogin(c *cli.Context) error { // Logout removes local and remote session. func (s *Shell) Logout(_ *cli.Context) (err error) { - resp, err := s.HTTP.Delete("/sessions") + resp, err := s.HTTP.Delete(s.ctx(), "/sessions") if err != nil { return s.errorOut(err) } @@ -224,7 +224,7 @@ func (s *Shell) ChangePassword(_ *cli.Context) (err error) { } buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Patch("/v2/user/password", buf) + resp, err := s.HTTP.Patch(s.ctx(), "/v2/user/password", buf) if err != nil { return s.errorOut(err) } @@ -313,7 +313,7 @@ func (s *Shell) ConfigV2(c *cli.Context) error { } func (s *Shell) configV2Str(userOnly bool) (string, error) { - resp, err := s.HTTP.Get(fmt.Sprintf("/v2/config/v2?userOnly=%t", userOnly)) + resp, err := s.HTTP.Get(s.ctx(), fmt.Sprintf("/v2/config/v2?userOnly=%t", userOnly)) if err != nil { return "", s.errorOut(err) } @@ -351,7 +351,7 @@ func (s *Shell) SetLogLevel(c *cli.Context) (err error) { } buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Patch("/v2/log", buf) + resp, err := s.HTTP.Patch(s.ctx(), "/v2/log", buf) if err != nil { return s.errorOut(err) } @@ -383,7 +383,7 @@ func (s *Shell) SetLogSQL(c *cli.Context) (err error) { } buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Patch("/v2/log", buf) + resp, err := s.HTTP.Patch(s.ctx(), "/v2/log", buf) if err != nil { return s.errorOut(err) } @@ -476,7 +476,7 @@ func parseResponse(resp *http.Response) ([]byte, error) { } func (s *Shell) checkRemoteBuildCompatibility(lggr logger.Logger, onlyWarn bool, cliVersion, cliSha string) error { - resp, err := s.HTTP.Get("/v2/build_info") + resp, err := s.HTTP.Get(s.ctx(), "/v2/build_info") if err != nil { lggr.Warnw("Got error querying for version. Remote node version is unknown and CLI may behave in unexpected ways.", "err", err) return nil @@ -517,7 +517,7 @@ func (s *Shell) Health(c *cli.Context) error { if c.Bool("json") { mime = gin.MIMEJSON } - resp, err := s.HTTP.Get("/health", map[string]string{"Accept": mime}) + resp, err := s.HTTP.Get(s.ctx(), "/health", map[string]string{"Accept": mime}) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/shell_remote_test.go b/core/cmd/shell_remote_test.go index ed675d8ee9e..dbd9968daab 100644 --- a/core/cmd/shell_remote_test.go +++ b/core/cmd/shell_remote_test.go @@ -2,6 +2,7 @@ package cmd_test import ( "bytes" + "context" "errors" "flag" "fmt" @@ -383,7 +384,7 @@ type mockHTTPClient struct { mockSha string } -func (h *mockHTTPClient) Get(path string, headers ...map[string]string) (*http.Response, error) { +func (h *mockHTTPClient) Get(ctx context.Context, path string, headers ...map[string]string) (*http.Response, error) { if path == "/v2/build_info" { // Return mocked response here json := fmt.Sprintf(`{"version":"%s","commitSHA":"%s"}`, h.mockVersion, h.mockSha) @@ -393,23 +394,23 @@ func (h *mockHTTPClient) Get(path string, headers ...map[string]string) (*http.R Body: r, }, nil } - return h.HTTP.Get(path, headers...) + return h.HTTP.Get(ctx, path, headers...) } -func (h *mockHTTPClient) Post(path string, body io.Reader) (*http.Response, error) { - return h.HTTP.Post(path, body) +func (h *mockHTTPClient) Post(ctx context.Context, path string, body io.Reader) (*http.Response, error) { + return h.HTTP.Post(ctx, path, body) } -func (h *mockHTTPClient) Put(path string, body io.Reader) (*http.Response, error) { - return h.HTTP.Put(path, body) +func (h *mockHTTPClient) Put(ctx context.Context, path string, body io.Reader) (*http.Response, error) { + return h.HTTP.Put(ctx, path, body) } -func (h *mockHTTPClient) Patch(path string, body io.Reader, headers ...map[string]string) (*http.Response, error) { - return h.HTTP.Patch(path, body, headers...) +func (h *mockHTTPClient) Patch(ctx context.Context, path string, body io.Reader, headers ...map[string]string) (*http.Response, error) { + return h.HTTP.Patch(ctx, path, body, headers...) } -func (h *mockHTTPClient) Delete(path string) (*http.Response, error) { - return h.HTTP.Delete(path) +func (h *mockHTTPClient) Delete(ctx context.Context, path string) (*http.Response, error) { + return h.HTTP.Delete(ctx, path) } func TestShell_ChangePassword(t *testing.T) { @@ -700,7 +701,7 @@ func (FailingAuthenticator) Cookie() (*http.Cookie, error) { } // Authenticate retrieves a session ID via a cookie and saves it to disk. -func (FailingAuthenticator) Authenticate(sessionRequest sessions.SessionRequest) (*http.Cookie, error) { +func (FailingAuthenticator) Authenticate(context.Context, sessions.SessionRequest) (*http.Cookie, error) { return nil, errors.New("no luck") } diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index ade14aa0d8a..ec9606e0ac6 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -38,6 +38,7 @@ import ( func TestTerminalCookieAuthenticator_AuthenticateWithoutSession(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) @@ -54,7 +55,7 @@ func TestTerminalCookieAuthenticator_AuthenticateWithoutSession(t *testing.T) { sr := sessions.SessionRequest{Email: test.email, Password: test.pwd} store := &cmd.MemoryCookieStore{} tca := cmd.NewSessionCookieAuthenticator(cmd.ClientOpts{}, store, logger.TestLogger(t)) - cookie, err := tca.Authenticate(sr) + cookie, err := tca.Authenticate(ctx, sr) assert.Error(t, err) assert.Nil(t, cookie) @@ -68,8 +69,9 @@ func TestTerminalCookieAuthenticator_AuthenticateWithoutSession(t *testing.T) { func TestTerminalCookieAuthenticator_AuthenticateWithSession(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) @@ -87,7 +89,7 @@ func TestTerminalCookieAuthenticator_AuthenticateWithSession(t *testing.T) { sr := sessions.SessionRequest{Email: test.email, Password: test.pwd} store := &cmd.MemoryCookieStore{} tca := cmd.NewSessionCookieAuthenticator(app.NewClientOpts(), store, logger.TestLogger(t)) - cookie, err := tca.Authenticate(sr) + cookie, err := tca.Authenticate(ctx, sr) if test.wantError { assert.Error(t, err) diff --git a/core/cmd/solana_transaction_commands.go b/core/cmd/solana_transaction_commands.go index c92cc3e29dd..23e94eee50b 100644 --- a/core/cmd/solana_transaction_commands.go +++ b/core/cmd/solana_transaction_commands.go @@ -105,7 +105,7 @@ func (s *Shell) SolanaSendSol(c *cli.Context) (err error) { buf := bytes.NewBuffer(requestData) - resp, err := s.HTTP.Post("/v2/transfers/solana", buf) + resp, err := s.HTTP.Post(s.ctx(), "/v2/transfers/solana", buf) if err != nil { return s.errorOut(err) } diff --git a/core/cmd/vrf_keys_commands.go b/core/cmd/vrf_keys_commands.go index 1a04ef37c3e..32d32334af5 100644 --- a/core/cmd/vrf_keys_commands.go +++ b/core/cmd/vrf_keys_commands.go @@ -118,7 +118,7 @@ func (ps VRFKeyPresenters) RenderTable(rt RendererTable) error { // CreateVRFKey creates a key in the VRF keystore, protected by the password in // the vrf password file provided when starting the chainlink node. func (s *Shell) CreateVRFKey(_ *cli.Context) error { - resp, err := s.HTTP.Post("/v2/keys/vrf", nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/vrf", nil) if err != nil { return s.errorOut(err) } @@ -154,7 +154,7 @@ func (s *Shell) ImportVRFKey(c *cli.Context) error { } normalizedPassword := normalizePassword(string(oldPassword)) - resp, err := s.HTTP.Post("/v2/keys/vrf/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/vrf/import?oldpassword="+normalizedPassword, bytes.NewReader(keyJSON)) if err != nil { return s.errorOut(err) } @@ -195,7 +195,7 @@ func (s *Shell) ExportVRFKey(c *cli.Context) error { } normalizedPassword := normalizePassword(string(newPassword)) - resp, err := s.HTTP.Post("/v2/keys/vrf/export/"+pk.String()+"?newpassword="+normalizedPassword, nil) + resp, err := s.HTTP.Post(s.ctx(), "/v2/keys/vrf/export/"+pk.String()+"?newpassword="+normalizedPassword, nil) if err != nil { return s.errorOut(errors.Wrap(err, "Could not make HTTP request")) } @@ -248,7 +248,7 @@ func (s *Shell) DeleteVRFKey(c *cli.Context) error { queryStr = "?hard=true" } - resp, err := s.HTTP.Delete(fmt.Sprintf("/v2/keys/vrf/%s%s", id, queryStr)) + resp, err := s.HTTP.Delete(s.ctx(), fmt.Sprintf("/v2/keys/vrf/%s%s", id, queryStr)) if err != nil { return s.errorOut(err) } @@ -276,7 +276,7 @@ func getPublicKey(c *cli.Context) (secp256k1.PublicKey, error) { // ListKeys Lists the keys in the db func (s *Shell) ListVRFKeys(_ *cli.Context) error { - resp, err := s.HTTP.Get("/v2/keys/vrf", nil) + resp, err := s.HTTP.Get(s.ctx(), "/v2/keys/vrf", nil) if err != nil { return s.errorOut(err) } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index dcd16b8e59c..c7abfb31a2a 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -692,27 +692,27 @@ type HTTPClientCleaner struct { } func (r *HTTPClientCleaner) Get(path string, headers ...map[string]string) (*http.Response, func()) { - resp, err := r.HTTPClient.Get(path, headers...) + resp, err := r.HTTPClient.Get(testutils.Context(r.t), path, headers...) return bodyCleaner(r.t, resp, err) } func (r *HTTPClientCleaner) Post(path string, body io.Reader) (*http.Response, func()) { - resp, err := r.HTTPClient.Post(path, body) + resp, err := r.HTTPClient.Post(testutils.Context(r.t), path, body) return bodyCleaner(r.t, resp, err) } func (r *HTTPClientCleaner) Put(path string, body io.Reader) (*http.Response, func()) { - resp, err := r.HTTPClient.Put(path, body) + resp, err := r.HTTPClient.Put(testutils.Context(r.t), path, body) return bodyCleaner(r.t, resp, err) } func (r *HTTPClientCleaner) Patch(path string, body io.Reader, headers ...map[string]string) (*http.Response, func()) { - resp, err := r.HTTPClient.Patch(path, body, headers...) + resp, err := r.HTTPClient.Patch(testutils.Context(r.t), path, body, headers...) return bodyCleaner(r.t, resp, err) } func (r *HTTPClientCleaner) Delete(path string) (*http.Response, func()) { - resp, err := r.HTTPClient.Delete(path) + resp, err := r.HTTPClient.Delete(testutils.Context(r.t), path) return bodyCleaner(r.t, resp, err) } @@ -872,10 +872,7 @@ func CreateExternalInitiatorViaWeb( t.Helper() client := app.NewHTTPClient(nil) - resp, cleanup := client.Post( - "/v2/external_initiators", - bytes.NewBufferString(payload), - ) + resp, cleanup := client.Post("/v2/external_initiators", bytes.NewBufferString(payload)) defer cleanup() AssertServerResponse(t, resp, http.StatusCreated) ei := &webpresenters.ExternalInitiatorAuthentication{} @@ -1125,7 +1122,7 @@ func unauthenticatedHTTP(t testing.TB, method string, url string, body io.Reader t.Helper() client := clhttptest.NewTestLocalOnlyHTTPClient() - request, err := http.NewRequest(method, url, body) + request, err := http.NewRequestWithContext(testutils.Context(t), method, url, body) require.NoError(t, err) request.Header.Set("Content-Type", "application/json") for key, value := range headers { diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index 073b3ba246c..fbfd820309a 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -355,7 +355,7 @@ func (m MockCookieAuthenticator) Cookie() (*http.Cookie, error) { return MustGenerateSessionCookie(m.t, m.SessionID), m.Error } -func (m MockCookieAuthenticator) Authenticate(sessions.SessionRequest) (*http.Cookie, error) { +func (m MockCookieAuthenticator) Authenticate(context.Context, sessions.SessionRequest) (*http.Cookie, error) { return MustGenerateSessionCookie(m.t, m.SessionID), m.Error } diff --git a/core/scripts/chaincli/handler/bootstrap.go b/core/scripts/chaincli/handler/bootstrap.go index 4cc19299cca..bf79f5698dc 100644 --- a/core/scripts/chaincli/handler/bootstrap.go +++ b/core/scripts/chaincli/handler/bootstrap.go @@ -47,17 +47,17 @@ func (h *baseHandler) StartBootstrapNode(ctx context.Context, addr string, uiPor lggr.Fatal("Failed to launch chainlink node, ", err) } - cl, err := authenticate(urlRaw, defaultChainlinkNodeLogin, defaultChainlinkNodePassword, lggr) + cl, err := authenticate(ctx, urlRaw, defaultChainlinkNodeLogin, defaultChainlinkNodePassword, lggr) if err != nil { lggr.Fatal("Authentication failed, ", err) } - p2pKeyID, err := getP2PKeyID(cl) + p2pKeyID, err := getP2PKeyID(ctx, cl) if err != nil { lggr.Fatal("Failed to get P2P key ID, ", err) } - if err = h.createBootstrapJob(cl, addr); err != nil { + if err = h.createBootstrapJob(ctx, cl, addr); err != nil { lggr.Fatal("Failed to create keeper job: ", err) } @@ -68,7 +68,7 @@ func (h *baseHandler) StartBootstrapNode(ctx context.Context, addr string, uiPor } // createBootstrapJob creates a bootstrap job in the chainlink node by the given address -func (h *baseHandler) createBootstrapJob(client cmd.HTTPClient, contractAddr string) error { +func (h *baseHandler) createBootstrapJob(ctx context.Context, client cmd.HTTPClient, contractAddr string) error { request, err := json.Marshal(web.CreateJobRequest{ TOML: fmt.Sprintf(bootstrapJobSpec, contractAddr, h.cfg.ChainID), }) @@ -76,7 +76,7 @@ func (h *baseHandler) createBootstrapJob(client cmd.HTTPClient, contractAddr str return fmt.Errorf("failed to marshal request: %s", err) } - resp, err := client.Post("/v2/jobs", bytes.NewReader(request)) + resp, err := client.Post(ctx, "/v2/jobs", bytes.NewReader(request)) if err != nil { return fmt.Errorf("failed to create bootstrap job: %s", err) } diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 9782f5b72fe..c317edcac37 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -150,7 +150,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to pack raw checkUpkeep call", err) } - addLink("checkUpkeep simulation", tenderlySimLink(k.cfg, chainID, 0, rawCall, registryAddress)) + addLink("checkUpkeep simulation", tenderlySimLink(ctx, k.cfg, chainID, 0, rawCall, registryAddress)) } else if triggerType == LogTrigger { // validate inputs message("upkeep identified as log trigger") @@ -243,9 +243,9 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to pack raw checkUpkeep call", err) } - addLink("checkUpkeep simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) + addLink("checkUpkeep simulation", tenderlySimLink(ctx, k.cfg, chainID, blockNum, rawCall, registryAddress)) rawCall = append(core.ILogAutomationABI.Methods["checkLog"].ID, triggerData...) - addLink("checkLog (direct) simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, upkeepInfo.Target)) + addLink("checkLog (direct) simulation", tenderlySimLink(ctx, k.cfg, chainID, blockNum, rawCall, upkeepInfo.Target)) } else { resolveIneligible(fmt.Sprintf("invalid trigger type: %d", triggerType)) } @@ -334,12 +334,12 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to pack raw checkCallback call", err) } - addLink("checkCallback simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) + addLink("checkCallback simulation", tenderlySimLink(ctx, k.cfg, chainID, blockNum, rawCall, registryAddress)) rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.ExtraData) if err != nil { failUnknown("failed to pack raw checkCallback (direct) call", err) } - addLink("checkCallback (direct) simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, upkeepInfo.Target)) + addLink("checkCallback (direct) simulation", tenderlySimLink(ctx, k.cfg, chainID, blockNum, rawCall, upkeepInfo.Target)) } else { message("did not revert with StreamsLookup error") } @@ -358,7 +358,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to pack raw simulatePerformUpkeep call", err) } - addLink("simulatePerformUpkeep simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) + addLink("simulatePerformUpkeep simulation", tenderlySimLink(ctx, k.cfg, chainID, blockNum, rawCall, registryAddress)) if simulateResult.Success { resolveEligible() @@ -536,7 +536,7 @@ type TenderlyAPIResponse struct { } } -func tenderlySimLink(cfg *config.Config, chainID int64, blockNumber uint64, input []byte, contractAddress gethcommon.Address) string { +func tenderlySimLink(ctx context.Context, cfg *config.Config, chainID int64, blockNumber uint64, input []byte, contractAddress gethcommon.Address) string { errResult := "" if cfg.TenderlyAccountName == "" || cfg.TenderlyKey == "" || cfg.TenderlyProjectName == "" { warning("tenderly credentials not properly configured - this is optional but helpful") @@ -558,7 +558,8 @@ func tenderlySimLink(cfg *config.Config, chainID int64, blockNumber uint64, inpu warning(fmt.Sprintf("unable to marshal tenderly request data: %v", err)) return errResult } - request, err := http.NewRequest( + request, err := http.NewRequestWithContext( + ctx, "POST", fmt.Sprintf("https://api.tenderly.co/api/v1/account/%s/project/%s/simulate", cfg.TenderlyAccountName, cfg.TenderlyProjectName), bytes.NewBuffer(jsonData), diff --git a/core/scripts/chaincli/handler/handler.go b/core/scripts/chaincli/handler/handler.go index 6423df3aa23..591431555a9 100644 --- a/core/scripts/chaincli/handler/handler.go +++ b/core/scripts/chaincli/handler/handler.go @@ -399,7 +399,7 @@ func (h *baseHandler) launchChainlinkNode(ctx context.Context, port int, contain addr := fmt.Sprintf("http://localhost:%s", portStr) log.Println("Node docker container successfully created and started: ", nodeContainerResp.ID, addr) - if err = waitForNodeReady(addr); err != nil { + if err = waitForNodeReady(ctx, addr); err != nil { log.Fatal(err, nodeContainerResp.ID) } log.Println("Node ready: ", nodeContainerResp.ID) @@ -477,13 +477,13 @@ func checkAndRemoveContainer(ctx context.Context, dockerClient *client.Client, c return nil } -func waitForNodeReady(addr string) error { +func waitForNodeReady(ctx context.Context, addr string) error { client := &http.Client{} defer client.CloseIdleConnections() const timeout = 120 startTime := time.Now().Unix() for { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/health", addr), nil) + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/health", addr), nil) if err != nil { return err } @@ -503,7 +503,7 @@ func waitForNodeReady(addr string) error { } // authenticate creates a http client with URL, email and password -func authenticate(urlStr, email, password string, lggr logger.Logger) (cmd.HTTPClient, error) { +func authenticate(ctx context.Context, urlStr, email, password string, lggr logger.Logger) (cmd.HTTPClient, error) { remoteNodeURL, err := url.Parse(urlStr) if err != nil { return nil, err @@ -514,7 +514,7 @@ func authenticate(urlStr, email, password string, lggr logger.Logger) (cmd.HTTPC store := &cmd.MemoryCookieStore{} tca := cmd.NewSessionCookieAuthenticator(c, store, lggr) - if _, err = tca.Authenticate(sr); err != nil { + if _, err = tca.Authenticate(ctx, sr); err != nil { log.Println("failed to authenticate: ", err) return nil, err } @@ -522,8 +522,8 @@ func authenticate(urlStr, email, password string, lggr logger.Logger) (cmd.HTTPC return cmd.NewAuthenticatedHTTPClient(lggr, c, tca, sr), nil } -func nodeRequest(client cmd.HTTPClient, path string) ([]byte, error) { - resp, err := client.Get(path) +func nodeRequest(ctx context.Context, client cmd.HTTPClient, path string) ([]byte, error) { + resp, err := client.Get(ctx, path) if err != nil { return []byte{}, fmt.Errorf("GET error from client: %w", err) } @@ -551,8 +551,8 @@ func nodeRequest(client cmd.HTTPClient, path string) ([]byte, error) { } // getNodeAddress returns chainlink node's wallet address -func getNodeAddress(client cmd.HTTPClient) (string, error) { - resp, err := nodeRequest(client, ethKeysEndpoint) +func getNodeAddress(ctx context.Context, client cmd.HTTPClient) (string, error) { + resp, err := nodeRequest(ctx, client, ethKeysEndpoint) if err != nil { return "", fmt.Errorf("failed to get ETH keys: %w", err) } @@ -566,8 +566,8 @@ func getNodeAddress(client cmd.HTTPClient) (string, error) { } // getNodeOCR2Config returns chainlink node's OCR2 bundle key ID -func getNodeOCR2Config(client cmd.HTTPClient) (*cmd.OCR2KeyBundlePresenter, error) { - resp, err := nodeRequest(client, ocr2KeysEndpoint) +func getNodeOCR2Config(ctx context.Context, client cmd.HTTPClient) (*cmd.OCR2KeyBundlePresenter, error) { + resp, err := nodeRequest(ctx, client, ocr2KeysEndpoint) if err != nil { return nil, fmt.Errorf("failed to get OCR2 keys: %w", err) } @@ -589,8 +589,8 @@ func getNodeOCR2Config(client cmd.HTTPClient) (*cmd.OCR2KeyBundlePresenter, erro } // getP2PKeyID returns chainlink node's P2P key ID -func getP2PKeyID(client cmd.HTTPClient) (string, error) { - resp, err := nodeRequest(client, p2pKeysEndpoint) +func getP2PKeyID(ctx context.Context, client cmd.HTTPClient) (string, error) { + resp, err := nodeRequest(ctx, client, p2pKeysEndpoint) if err != nil { return "", fmt.Errorf("failed to get P2P keys: %w", err) } diff --git a/core/scripts/chaincli/handler/jobs.go b/core/scripts/chaincli/handler/jobs.go index 9787d7fc175..89f1a3ea5bc 100644 --- a/core/scripts/chaincli/handler/jobs.go +++ b/core/scripts/chaincli/handler/jobs.go @@ -8,10 +8,10 @@ import ( ) func (k *Keeper) CreateJob(ctx context.Context) { - k.createJobs() + k.createJobs(ctx) } -func (k *Keeper) createJobs() { +func (k *Keeper) createJobs(ctx context.Context) { lggr, closeLggr := logger.NewLogger() logger.Sugared(lggr).ErrorIfFn(closeLggr, "Failed to close logger") @@ -27,12 +27,12 @@ func (k *Keeper) createJobs() { pwd = defaultChainlinkNodePassword } - cl, err := authenticate(url, email, pwd, lggr) + cl, err := authenticate(ctx, url, email, pwd, lggr) if err != nil { log.Fatal(err) } - if err = k.createKeeperJob(cl, k.cfg.RegistryAddress, keeperAddr); err != nil { + if err = k.createKeeperJob(ctx, cl, k.cfg.RegistryAddress, keeperAddr); err != nil { log.Fatal(err) } } diff --git a/core/scripts/chaincli/handler/keeper.go b/core/scripts/chaincli/handler/keeper.go index a5fb505adb4..1f56eb14080 100644 --- a/core/scripts/chaincli/handler/keeper.go +++ b/core/scripts/chaincli/handler/keeper.go @@ -77,13 +77,13 @@ func (k *Keeper) DeployKeepers(ctx context.Context) { pwd = defaultChainlinkNodePassword } - cl, err := authenticate(url, email, pwd, lggr) + cl, err := authenticate(ctx, url, email, pwd, lggr) if err != nil { log.Fatal(err) } cls[i] = cl - if err = k.createKeeperJob(cl, k.cfg.RegistryAddress, keeperAddr); err != nil { + if err = k.createKeeperJob(ctx, cl, k.cfg.RegistryAddress, keeperAddr); err != nil { log.Fatal(err) } } @@ -753,7 +753,8 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, func (k *Keeper) setKeepers(ctx context.Context, cls []cmd.HTTPClient, deployer keepersDeployer, keepers, owners []common.Address) { if len(keepers) > 0 { log.Println("Set keepers...") - setKeepersTx, err := deployer.SetKeepers(k.buildTxOpts(ctx), cls, keepers, owners) + opts := k.buildTxOpts(ctx) + setKeepersTx, err := deployer.SetKeepers(ctx, opts, cls, keepers, owners) if err != nil { log.Fatal("SetKeepers failed: ", err) } diff --git a/core/scripts/chaincli/handler/keeper_deployer.go b/core/scripts/chaincli/handler/keeper_deployer.go index 7c532753426..118cbbb0ff9 100644 --- a/core/scripts/chaincli/handler/keeper_deployer.go +++ b/core/scripts/chaincli/handler/keeper_deployer.go @@ -1,6 +1,7 @@ package handler import ( + "context" "crypto/ed25519" "encoding/hex" "encoding/json" @@ -48,14 +49,14 @@ type upkeepDeployer interface { type keepersDeployer interface { canceller upkeepDeployer - SetKeepers(opts *bind.TransactOpts, _ []cmd.HTTPClient, keepers []common.Address, payees []common.Address) (*types.Transaction, error) + SetKeepers(ctx context.Context, opts *bind.TransactOpts, _ []cmd.HTTPClient, keepers []common.Address, payees []common.Address) (*types.Transaction, error) } type v11KeeperDeployer struct { registry11.KeeperRegistryInterface } -func (d *v11KeeperDeployer) SetKeepers(opts *bind.TransactOpts, _ []cmd.HTTPClient, keepers []common.Address, payees []common.Address) (*types.Transaction, error) { +func (d *v11KeeperDeployer) SetKeepers(ctx context.Context, opts *bind.TransactOpts, _ []cmd.HTTPClient, keepers []common.Address, payees []common.Address) (*types.Transaction, error) { return d.KeeperRegistryInterface.SetKeepers(opts, keepers, payees) } @@ -71,7 +72,7 @@ type v12KeeperDeployer struct { registry12.KeeperRegistryInterface } -func (d *v12KeeperDeployer) SetKeepers(opts *bind.TransactOpts, _ []cmd.HTTPClient, keepers []common.Address, payees []common.Address) (*types.Transaction, error) { +func (d *v12KeeperDeployer) SetKeepers(ctx context.Context, opts *bind.TransactOpts, _ []cmd.HTTPClient, keepers []common.Address, payees []common.Address) (*types.Transaction, error) { return d.KeeperRegistryInterface.SetKeepers(opts, keepers, payees) } @@ -88,7 +89,7 @@ type v20KeeperDeployer struct { cfg *config.Config } -func (d *v20KeeperDeployer) SetKeepers(opts *bind.TransactOpts, cls []cmd.HTTPClient, keepers []common.Address, _ []common.Address) (*types.Transaction, error) { +func (d *v20KeeperDeployer) SetKeepers(ctx context.Context, opts *bind.TransactOpts, cls []cmd.HTTPClient, keepers []common.Address, _ []common.Address) (*types.Transaction, error) { S := make([]int, len(cls)) oracleIdentities := make([]ocr2config.OracleIdentityExtra, len(cls)) sharedSecretEncryptionPublicKeys := make([]ocr2types.ConfigEncryptionPublicKey, len(cls)) @@ -98,12 +99,12 @@ func (d *v20KeeperDeployer) SetKeepers(opts *bind.TransactOpts, cls []cmd.HTTPCl go func(i int, cl cmd.HTTPClient) { defer wg.Done() - ocr2Config, err := getNodeOCR2Config(cl) + ocr2Config, err := getNodeOCR2Config(ctx, cl) if err != nil { panic(err) } - p2pKeyID, err := getP2PKeyID(cl) + p2pKeyID, err := getP2PKeyID(ctx, cl) if err != nil { panic(err) } @@ -228,7 +229,7 @@ type v21KeeperDeployer struct { cfg *config.Config } -func (d *v21KeeperDeployer) SetKeepers(opts *bind.TransactOpts, cls []cmd.HTTPClient, keepers []common.Address, _ []common.Address) (*types.Transaction, error) { +func (d *v21KeeperDeployer) SetKeepers(ctx context.Context, opts *bind.TransactOpts, cls []cmd.HTTPClient, keepers []common.Address, _ []common.Address) (*types.Transaction, error) { S := make([]int, len(cls)) oracleIdentities := make([]ocr2config.OracleIdentityExtra, len(cls)) sharedSecretEncryptionPublicKeys := make([]ocr2types.ConfigEncryptionPublicKey, len(cls)) @@ -238,12 +239,12 @@ func (d *v21KeeperDeployer) SetKeepers(opts *bind.TransactOpts, cls []cmd.HTTPCl go func(i int, cl cmd.HTTPClient) { defer wg.Done() - ocr2Config, err := getNodeOCR2Config(cl) + ocr2Config, err := getNodeOCR2Config(ctx, cl) if err != nil { panic(err) } - p2pKeyID, err := getP2PKeyID(cl) + p2pKeyID, err := getP2PKeyID(ctx, cl) if err != nil { panic(err) } diff --git a/core/scripts/chaincli/handler/keeper_launch.go b/core/scripts/chaincli/handler/keeper_launch.go index e8be82a4bb7..25af77f1d5c 100644 --- a/core/scripts/chaincli/handler/keeper_launch.go +++ b/core/scripts/chaincli/handler/keeper_launch.go @@ -92,7 +92,7 @@ func (k *Keeper) LaunchAndTest(ctx context.Context, withdraw, printLogs, force, // Create authenticated client var cl cmd.HTTPClient var err error - cl, err = authenticate(startedNode.url, defaultChainlinkNodeLogin, defaultChainlinkNodePassword, lggr) + cl, err = authenticate(ctx, startedNode.url, defaultChainlinkNodeLogin, defaultChainlinkNodePassword, lggr) if err != nil { log.Fatal("Authentication failed, ", err) } @@ -101,13 +101,13 @@ func (k *Keeper) LaunchAndTest(ctx context.Context, withdraw, printLogs, force, if len(k.cfg.KeeperKeys) > 0 { // import key if exists - nodeAddrHex, err = k.addKeyToKeeper(cl, k.cfg.KeeperKeys[i]) + nodeAddrHex, err = k.addKeyToKeeper(ctx, cl, k.cfg.KeeperKeys[i]) if err != nil { log.Fatal("could not add key to keeper", err) } } else { // get node's default wallet address - nodeAddrHex, err = getNodeAddress(cl) + nodeAddrHex, err = getNodeAddress(ctx, cl) if err != nil { log.Println("Failed to get node addr: ", err) continue @@ -117,7 +117,7 @@ func (k *Keeper) LaunchAndTest(ctx context.Context, withdraw, printLogs, force, nodeAddr := common.HexToAddress(nodeAddrHex) // Create keepers - if err = k.createKeeperJob(cl, registryAddr.Hex(), nodeAddr.Hex()); err != nil { + if err = k.createKeeperJob(ctx, cl, registryAddr.Hex(), nodeAddr.Hex()); err != nil { log.Println("Failed to create keeper job: ", err) continue } @@ -291,12 +291,12 @@ func (k *Keeper) cancelAndWithdrawUpkeeps(ctx context.Context, upkeepCount *big. } // createKeeperJob creates a keeper job in the chainlink node by the given address -func (k *Keeper) createKeeperJob(client cmd.HTTPClient, registryAddr, nodeAddr string) error { +func (k *Keeper) createKeeperJob(ctx context.Context, client cmd.HTTPClient, registryAddr, nodeAddr string) error { var err error if k.cfg.OCR2Keepers { - err = k.createOCR2KeeperJob(client, registryAddr, nodeAddr) + err = k.createOCR2KeeperJob(ctx, client, registryAddr, nodeAddr) } else { - err = k.createLegacyKeeperJob(client, registryAddr, nodeAddr) + err = k.createLegacyKeeperJob(ctx, client, registryAddr, nodeAddr) } if err != nil { return err @@ -308,7 +308,7 @@ func (k *Keeper) createKeeperJob(client cmd.HTTPClient, registryAddr, nodeAddr s } // createLegacyKeeperJob creates a legacy keeper job in the chainlink node by the given address -func (k *Keeper) createLegacyKeeperJob(client cmd.HTTPClient, registryAddr, nodeAddr string) error { +func (k *Keeper) createLegacyKeeperJob(ctx context.Context, client cmd.HTTPClient, registryAddr, nodeAddr string) error { request, err := json.Marshal(web.CreateJobRequest{ TOML: testspecs.GenerateKeeperSpec(testspecs.KeeperSpecParams{ Name: fmt.Sprintf("keeper job - registry %s", registryAddr), @@ -321,7 +321,7 @@ func (k *Keeper) createLegacyKeeperJob(client cmd.HTTPClient, registryAddr, node return fmt.Errorf("failed to marshal request: %s", err) } - resp, err := client.Post("/v2/jobs", bytes.NewReader(request)) + resp, err := client.Post(ctx, "/v2/jobs", bytes.NewReader(request)) if err != nil { return fmt.Errorf("failed to create keeper job: %s", err) } @@ -363,8 +363,8 @@ contractVersion = "%s" mercuryCredentialName = "%s"` // createOCR2KeeperJob creates an ocr2keeper job in the chainlink node by the given address -func (k *Keeper) createOCR2KeeperJob(client cmd.HTTPClient, contractAddr, nodeAddr string) error { - ocr2KeyConfig, err := getNodeOCR2Config(client) +func (k *Keeper) createOCR2KeeperJob(ctx context.Context, client cmd.HTTPClient, contractAddr, nodeAddr string) error { + ocr2KeyConfig, err := getNodeOCR2Config(ctx, client) if err != nil { return fmt.Errorf("failed to get node OCR2 key bundle ID: %s", err) } @@ -390,7 +390,7 @@ func (k *Keeper) createOCR2KeeperJob(client cmd.HTTPClient, contractAddr, nodeAd return fmt.Errorf("failed to marshal request: %s", err) } - resp, err := client.Post("/v2/jobs", bytes.NewReader(request)) + resp, err := client.Post(ctx, "/v2/jobs", bytes.NewReader(request)) if err != nil { return fmt.Errorf("failed to create ocr2keeper job: %s", err) } @@ -409,7 +409,7 @@ func (k *Keeper) createOCR2KeeperJob(client cmd.HTTPClient, contractAddr, nodeAd } // addKeyToKeeper imports the provided ETH sending key to the keeper -func (k *Keeper) addKeyToKeeper(client cmd.HTTPClient, privKeyHex string) (string, error) { +func (k *Keeper) addKeyToKeeper(ctx context.Context, client cmd.HTTPClient, privKeyHex string) (string, error) { privkey, err := crypto.HexToECDSA(hex.TrimPrefix(privKeyHex)) if err != nil { log.Fatalf("Failed to decode priv key %s: %v", privKeyHex, err) @@ -429,7 +429,7 @@ func (k *Keeper) addKeyToKeeper(client cmd.HTTPClient, privKeyHex string) (strin query.Set("evmChainID", fmt.Sprint(k.cfg.ChainID)) importUrl.RawQuery = query.Encode() - resp, err := client.Post(importUrl.String(), bytes.NewReader(keyJSON)) + resp, err := client.Post(ctx, importUrl.String(), bytes.NewReader(keyJSON)) if err != nil { log.Fatalf("Failed to import priv key %s: %v", privKeyHex, err) } diff --git a/core/scripts/chaincli/handler/scrape_node_config.go b/core/scripts/chaincli/handler/scrape_node_config.go index aaaf5d26477..6525a4794d0 100644 --- a/core/scripts/chaincli/handler/scrape_node_config.go +++ b/core/scripts/chaincli/handler/scrape_node_config.go @@ -114,7 +114,7 @@ func (h *baseHandler) scrapeNodes(ctx context.Context, log logger.Logger) { pwd = defaultChainlinkNodePassword } - cl, err := authenticate(url, email, pwd, log) + cl, err := authenticate(ctx, url, email, pwd, log) if err != nil { log.Fatal(err) } @@ -125,7 +125,7 @@ func (h *baseHandler) scrapeNodes(ctx context.Context, log logger.Logger) { var wg sync.WaitGroup for i, cl := range cls { wg.Add(1) - go h.scrapeNodeInfo(&wg, i, cl, nodes, log) + go h.scrapeNodeInfo(ctx, &wg, i, cl, nodes, log) } wg.Wait() @@ -184,8 +184,8 @@ func (h *baseHandler) fetchNodeInfosFromWeiwatchers(ctx context.Context, log log return nodeInfos } -func (h *baseHandler) fetchNodeInfosFromNodes(i int, cl cmd.HTTPClient, log logger.Logger) ([]string, *cmd.OCR2KeyBundlePresenter, string, *cmd.CSAKeyPresenters) { - resp, err := nodeRequest(cl, ethKeysEndpoint) +func (h *baseHandler) fetchNodeInfosFromNodes(ctx context.Context, i int, cl cmd.HTTPClient, log logger.Logger) ([]string, *cmd.OCR2KeyBundlePresenter, string, *cmd.CSAKeyPresenters) { + resp, err := nodeRequest(ctx, cl, ethKeysEndpoint) if err != nil { log.Fatalf("failed to get ETH keys: %s", err) } @@ -204,17 +204,17 @@ func (h *baseHandler) fetchNodeInfosFromNodes(i int, cl cmd.HTTPClient, log logg log.Warnf("%d th node has more than 1 node addresses. is this a multi-chain node? or this node used to serve another chain?", i) } - ocr2Config, err := getNodeOCR2Config(cl) + ocr2Config, err := getNodeOCR2Config(ctx, cl) if err != nil { log.Fatalf("failed to get node OCR2 config: %s", err) } - peerId, err := getP2PKeyID(cl) + peerId, err := getP2PKeyID(ctx, cl) if err != nil { log.Fatalf("failed to get p2p keys: %s", err) } - resp, err = nodeRequest(cl, csaKeysEndpoint) + resp, err = nodeRequest(ctx, cl, csaKeysEndpoint) if err != nil { log.Fatalf("failed to get CSA keys: %s", err) } @@ -232,10 +232,10 @@ func (h *baseHandler) fetchNodeInfosFromNodes(i int, cl cmd.HTTPClient, log logg return nodeAddresses, ocr2Config, peerId, &csaKeys } -func (h *baseHandler) scrapeNodeInfo(wg *sync.WaitGroup, i int, cl cmd.HTTPClient, nodes map[string]*NodeInfo, log logger.Logger) { +func (h *baseHandler) scrapeNodeInfo(ctx context.Context, wg *sync.WaitGroup, i int, cl cmd.HTTPClient, nodes map[string]*NodeInfo, log logger.Logger) { defer wg.Done() - nodeAddresses, ocr2Config, peerId, csaKeys := h.fetchNodeInfosFromNodes(i, cl, log) + nodeAddresses, ocr2Config, peerId, csaKeys := h.fetchNodeInfosFromNodes(ctx, i, cl, log) // this assumes the nodes are not multichain nodes and have only 1 node address assigned. // for a multichain node, we can pass in a chain id and filter `ethKeys` array based on the chain id diff --git a/core/services/gateway/network/httpserver_test.go b/core/services/gateway/network/httpserver_test.go index 92215245e20..dac00df2a12 100644 --- a/core/services/gateway/network/httpserver_test.go +++ b/core/services/gateway/network/httpserver_test.go @@ -47,7 +47,7 @@ func startNewServer(t *testing.T, maxRequestBytes int64, readTimeoutMillis uint3 } func sendRequest(t *testing.T, url string, body []byte) *http.Response { - req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) + req, err := http.NewRequestWithContext(testutils.Context(t), "POST", url, bytes.NewBuffer(body)) require.NoError(t, err) client := &http.Client{} resp, err := client.Do(req) diff --git a/core/services/gateway/network/wsserver_test.go b/core/services/gateway/network/wsserver_test.go index 1a4c04787b4..0b24dbe6614 100644 --- a/core/services/gateway/network/wsserver_test.go +++ b/core/services/gateway/network/wsserver_test.go @@ -49,7 +49,7 @@ func startNewWSServer(t *testing.T, readTimeoutMillis uint32) (server network.We } func sendRequestWithHeader(t *testing.T, url string, headerName string, headerValue string) *http.Response { - req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte{})) + req, err := http.NewRequestWithContext(testutils.Context(t), "POST", url, bytes.NewBuffer([]byte{})) require.NoError(t, err) req.Header.Set(headerName, headerValue) diff --git a/core/services/health_test.go b/core/services/health_test.go index 3bd0e5d39b2..b95b266ca19 100644 --- a/core/services/health_test.go +++ b/core/services/health_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" ) @@ -20,7 +21,9 @@ func TestNewInBackupHealthReport(t *testing.T) { require.Eventually(t, func() bool { return observed.Len() >= 1 }, time.Second*5, time.Millisecond*100) require.Equal(t, "Starting InBackupHealthReport", observed.TakeAll()[0].Message) - res, err := http.Get("http://localhost:1234/health") + req, err := http.NewRequestWithContext(tests.Context(t), "GET", "http://localhost:1234/health", nil) + require.NoError(t, err) + res, err := http.DefaultClient.Do(req) require.NoError(t, err) require.Equal(t, http.StatusNoContent, res.StatusCode) diff --git a/core/services/keystore/p2p_test.go b/core/services/keystore/p2p_test.go index 4e9ca75c456..4dc44651473 100644 --- a/core/services/keystore/p2p_test.go +++ b/core/services/keystore/p2p_test.go @@ -177,6 +177,7 @@ func Test_P2PKeyStore_E2E(t *testing.T) { RETURNING *;`, p2pTableName) stmt, err := db.PrepareNamed(sql) require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, stmt.Close()) }) require.NoError(t, stmt.Get(&p2pPeer1, &p2pPeer1)) require.NoError(t, stmt.Get(&p2pPeer2, &p2pPeer2)) cltest.AssertCount(t, db, p2pTableName, 2) diff --git a/core/services/ocr/database.go b/core/services/ocr/database.go index cec9596bb91..977c371c15d 100644 --- a/core/services/ocr/database.go +++ b/core/services/ocr/database.go @@ -207,6 +207,7 @@ func (d *db) StorePendingTransmission(ctx context.Context, k ocrtypes.ReportTime } func (d *db) PendingTransmissionsWithConfigDigest(ctx context.Context, cd ocrtypes.ConfigDigest) (map[ocrtypes.ReportTimestamp]ocrtypes.PendingTransmission, error) { + //nolint sqlclosecheck false positive rows, err := d.q.QueryContext(ctx, ` SELECT config_digest, @@ -317,6 +318,7 @@ LIMIT 1 if err != nil { return rr, errors.Wrap(err, "LoadLatestRoundRequested failed to query rows") } + defer func() { err = multierr.Combine(err, rows.Close()) }() for rows.Next() { var configDigest []byte @@ -337,7 +339,5 @@ LIMIT 1 return } - err = multierr.Combine(err, rows.Close()) - return } diff --git a/core/services/ocr2/database.go b/core/services/ocr2/database.go index 5591f33fd40..1d449047578 100644 --- a/core/services/ocr2/database.go +++ b/core/services/ocr2/database.go @@ -279,7 +279,7 @@ func (d *db) PendingTransmissionsWithConfigDigest(ctx context.Context, cd ocrtyp FROM ocr2_pending_transmissions WHERE ocr2_oracle_spec_id = $1 AND config_digest = $2 ` - rows, err := d.q.QueryxContext(ctx, stmt, d.oracleSpecID, cd) + rows, err := d.q.QueryxContext(ctx, stmt, d.oracleSpecID, cd) //nolint sqlclosecheck false positive if err != nil { return nil, errors.Wrap(err, "PendingTransmissionsWithConfigDigest failed to query rows") } diff --git a/core/services/ocr2/plugins/dkg/persistence/db_test.go b/core/services/ocr2/plugins/dkg/persistence/db_test.go index b4fa000cb99..53a5ae26758 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db_test.go +++ b/core/services/ocr2/plugins/dkg/persistence/db_test.go @@ -55,6 +55,7 @@ func TestShareDB_WriteShareRecords(t *testing.T) { rows, err := db.Query(`SELECT COUNT(*) AS count FROM dkg_shares`) require.NoError(tt, err) + t.Cleanup(func() { assert.NoError(t, rows.Close()) }) var count int for rows.Next() { @@ -84,6 +85,7 @@ func TestShareDB_WriteShareRecords(t *testing.T) { rows, err := db.Query(`SELECT COUNT(*) AS count FROM dkg_shares`) require.NoError(tt, err) + t.Cleanup(func() { assert.NoError(t, rows.Close()) }) var count int for rows.Next() { @@ -122,6 +124,7 @@ func TestShareDB_WriteShareRecords(t *testing.T) { // no rows should have been inserted rows, err := db.Query(`SELECT COUNT(*) AS count FROM dkg_shares`) require.NoError(tt, err) + t.Cleanup(func() { assert.NoError(t, rows.Close()) }) var count int for rows.Next() { diff --git a/core/services/ocrcommon/discoverer_database.go b/core/services/ocrcommon/discoverer_database.go index 6468a910a65..9413b11ad07 100644 --- a/core/services/ocrcommon/discoverer_database.go +++ b/core/services/ocrcommon/discoverer_database.go @@ -39,27 +39,25 @@ updated_at = EXCLUDED.updated_at // ReadAnnouncements returns one serialized announcement (if available) for each of the peerIDs in the form of a map // keyed by each announcement's corresponding peer ID. -func (d *DiscovererDatabase) ReadAnnouncements(ctx context.Context, peerIDs []string) (map[string][]byte, error) { +func (d *DiscovererDatabase) ReadAnnouncements(ctx context.Context, peerIDs []string) (results map[string][]byte, err error) { rows, err := d.db.QueryContext(ctx, ` SELECT remote_peer_id, ann FROM ocr_discoverer_announcements WHERE remote_peer_id = ANY($1) AND local_peer_id = $2`, pq.Array(peerIDs), d.peerID) if err != nil { return nil, errors.Wrap(err, "DiscovererDatabase failed to ReadAnnouncements") } - results := make(map[string][]byte) + defer func() { err = multierr.Combine(err, rows.Close()) }() + results = make(map[string][]byte) for rows.Next() { var peerID string var ann []byte - err := rows.Scan(&peerID, &ann) + err = rows.Scan(&peerID, &ann) if err != nil { - return nil, multierr.Combine(err, rows.Close()) + return } results[peerID] = ann } - if err := rows.Err(); err != nil { - return nil, err - } - if err := rows.Close(); err != nil { - return nil, errors.WithStack(err) + if err = rows.Err(); err != nil { + return } return results, nil } diff --git a/core/services/pg/q.go b/core/services/pg/q.go index e69e16ec48f..ba2627fa745 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -154,6 +154,7 @@ func PrepareQueryRowx(q Queryer, sql string, dest interface{}, arg interface{}) if err != nil { return errors.Wrap(err, "error preparing named statement") } + defer stmt.Close() return errors.Wrap(stmt.QueryRowx(arg).Scan(dest), "error querying row") } diff --git a/core/services/pipeline/orm.go b/core/services/pipeline/orm.go index eb242e62765..70ff244ab3c 100644 --- a/core/services/pipeline/orm.go +++ b/core/services/pipeline/orm.go @@ -346,6 +346,7 @@ RETURNING id if errQ != nil { return errors.Wrap(errQ, "inserting finished pipeline runs") } + defer rows.Close() var runIDs []int64 for rows.Next() { diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index 638977863d6..5578bdcd4ca 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -291,7 +291,7 @@ func Test_PipelineORM_StoreRun_DetectsRestarts(t *testing.T) { ds1_id := uuid.New() // insert something for this pipeline_run to trigger an early resume while the pipeline is running - _, err = db.NamedQuery(` + rows, err := db.NamedQuery(` INSERT INTO pipeline_task_runs (pipeline_run_id, id, type, index, output, error, dot_id, created_at, finished_at) VALUES (:pipeline_run_id, :id, :type, :index, :output, :error, :dot_id, :created_at, :finished_at) `, pipeline.TaskRun{ @@ -304,6 +304,7 @@ func Test_PipelineORM_StoreRun_DetectsRestarts(t *testing.T) { FinishedAt: null.TimeFrom(now), }) require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, rows.Close()) }) run.PipelineTaskRuns = []pipeline.TaskRun{ // pending task diff --git a/core/services/relay/evm/request_round_db.go b/core/services/relay/evm/request_round_db.go index 331d663918a..b3a5b01bc2c 100644 --- a/core/services/relay/evm/request_round_db.go +++ b/core/services/relay/evm/request_round_db.go @@ -61,6 +61,7 @@ LIMIT 1 if err != nil { return rr, errors.Wrap(err, "LoadLatestRoundRequested failed to query rows") } + defer rows.Close() for rows.Next() { var configDigest []byte diff --git a/core/services/webhook/delegate.go b/core/services/webhook/delegate.go index 237245b81c9..7e6aab0bb07 100644 --- a/core/services/webhook/delegate.go +++ b/core/services/webhook/delegate.go @@ -21,6 +21,7 @@ type ( webhookJobRunner *webhookJobRunner externalInitiatorManager ExternalInitiatorManager lggr logger.Logger + stopCh services.StopChan } JobRunner interface { @@ -36,6 +37,7 @@ func NewDelegate(runner pipeline.Runner, externalInitiatorManager ExternalInitia externalInitiatorManager: externalInitiatorManager, webhookJobRunner: newWebhookJobRunner(runner, lggr), lggr: lggr, + stopCh: make(services.StopChan), } } @@ -49,7 +51,9 @@ func (d *Delegate) JobType() job.Type { func (d *Delegate) BeforeJobCreated(spec job.Job) {} func (d *Delegate) AfterJobCreated(jb job.Job) { - err := d.externalInitiatorManager.Notify(*jb.WebhookSpecID) + ctx, cancel := d.stopCh.NewCtx() + defer cancel() + err := d.externalInitiatorManager.Notify(ctx, *jb.WebhookSpecID) if err != nil { d.lggr.Errorw("Webhook delegate AfterJobCreated errored", "err", err, @@ -59,7 +63,9 @@ func (d *Delegate) AfterJobCreated(jb job.Job) { } func (d *Delegate) BeforeJobDeleted(spec job.Job) { - err := d.externalInitiatorManager.DeleteJob(*spec.WebhookSpecID) + ctx, cancel := d.stopCh.NewCtx() + defer cancel() + err := d.externalInitiatorManager.DeleteJob(ctx, *spec.WebhookSpecID) if err != nil { d.lggr.Errorw("Webhook delegate OnDeleteJob errored", "err", err, diff --git a/core/services/webhook/external_initiator_manager.go b/core/services/webhook/external_initiator_manager.go index 01edf82b114..0c035abde7a 100644 --- a/core/services/webhook/external_initiator_manager.go +++ b/core/services/webhook/external_initiator_manager.go @@ -2,6 +2,7 @@ package webhook import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -24,8 +25,8 @@ import ( // ExternalInitiatorManager manages HTTP requests to remote external initiators type ExternalInitiatorManager interface { - Notify(webhookSpecID int32) error - DeleteJob(webhookSpecID int32) error + Notify(ctx context.Context, webhookSpecID int32) error + DeleteJob(ctx context.Context, webhookSpecID int32) error FindExternalInitiatorByName(name string) (bridges.ExternalInitiator, error) } @@ -52,7 +53,7 @@ func NewExternalInitiatorManager(db *sqlx.DB, httpclient HTTPClient, lggr logger // Notify sends a POST notification to the External Initiator // responsible for initiating the Job Spec. -func (m externalInitiatorManager) Notify(webhookSpecID int32) error { +func (m externalInitiatorManager) Notify(ctx context.Context, webhookSpecID int32) error { eiWebhookSpecs, jobID, err := m.Load(webhookSpecID) if err != nil { return err @@ -71,7 +72,7 @@ func (m externalInitiatorManager) Notify(webhookSpecID int32) error { if err != nil { return errors.Wrap(err, "new Job Spec notification") } - req, err := newNotifyHTTPRequest(buf, ei) + req, err := newNotifyHTTPRequest(ctx, buf, ei) if err != nil { return errors.Wrap(err, "creating notify HTTP request") } @@ -136,7 +137,7 @@ func (m externalInitiatorManager) eagerLoadExternalInitiator(q pg.Queryer, txs [ return nil } -func (m externalInitiatorManager) DeleteJob(webhookSpecID int32) error { +func (m externalInitiatorManager) DeleteJob(ctx context.Context, webhookSpecID int32) error { eiWebhookSpecs, jobID, err := m.Load(webhookSpecID) if err != nil { return err @@ -147,7 +148,7 @@ func (m externalInitiatorManager) DeleteJob(webhookSpecID int32) error { continue } - req, err := newDeleteJobFromExternalInitiatorHTTPRequest(ei, jobID) + req, err := newDeleteJobFromExternalInitiatorHTTPRequest(ctx, ei, jobID) if err != nil { return errors.Wrap(err, "creating delete HTTP request") } @@ -178,8 +179,8 @@ type JobSpecNotice struct { Params models.JSON `json:"params,omitempty"` } -func newNotifyHTTPRequest(buf []byte, ei bridges.ExternalInitiator) (*http.Request, error) { - req, err := http.NewRequest(http.MethodPost, ei.URL.String(), bytes.NewBuffer(buf)) +func newNotifyHTTPRequest(ctx context.Context, buf []byte, ei bridges.ExternalInitiator) (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, ei.URL.String(), bytes.NewBuffer(buf)) if err != nil { return nil, err } @@ -187,10 +188,10 @@ func newNotifyHTTPRequest(buf []byte, ei bridges.ExternalInitiator) (*http.Reque return req, nil } -func newDeleteJobFromExternalInitiatorHTTPRequest(ei bridges.ExternalInitiator, jobID uuid.UUID) (*http.Request, error) { +func newDeleteJobFromExternalInitiatorHTTPRequest(ctx context.Context, ei bridges.ExternalInitiator, jobID uuid.UUID) (*http.Request, error) { url := fmt.Sprintf("%s/%s", ei.URL.String(), jobID) - req, err := http.NewRequest(http.MethodDelete, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { return nil, err } @@ -208,8 +209,8 @@ type NullExternalInitiatorManager struct{} var _ ExternalInitiatorManager = (*NullExternalInitiatorManager)(nil) -func (NullExternalInitiatorManager) Notify(int32) error { return nil } -func (NullExternalInitiatorManager) DeleteJob(int32) error { return nil } +func (NullExternalInitiatorManager) Notify(context.Context, int32) error { return nil } +func (NullExternalInitiatorManager) DeleteJob(context.Context, int32) error { return nil } func (NullExternalInitiatorManager) FindExternalInitiatorByName(name string) (bridges.ExternalInitiator, error) { return bridges.ExternalInitiator{}, nil } diff --git a/core/services/webhook/external_initiator_manager_test.go b/core/services/webhook/external_initiator_manager_test.go index 568008c2117..553455ebe63 100644 --- a/core/services/webhook/external_initiator_manager_test.go +++ b/core/services/webhook/external_initiator_manager_test.go @@ -12,7 +12,9 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "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/pg" @@ -57,6 +59,7 @@ func Test_ExternalInitiatorManager_Load(t *testing.T) { } func Test_ExternalInitiatorManager_Notify(t *testing.T) { + ctx := tests.Context(t) db := pgtest.NewSqlxDB(t) cfg := pgtest.NewQConfig(true) borm := newBridgeORM(t, db, cfg) @@ -78,7 +81,7 @@ func Test_ExternalInitiatorManager_Notify(t *testing.T) { eim := webhook.NewExternalInitiatorManager(db, client, logger.TestLogger(t), cfg) // Does nothing with no EI - require.NoError(t, eim.Notify(webhookSpecNoEIs.ID)) + require.NoError(t, eim.Notify(ctx, webhookSpecNoEIs.ID)) client.On("Do", mock.MatchedBy(func(r *http.Request) bool { body, err := r.GetBody() @@ -92,10 +95,11 @@ func Test_ExternalInitiatorManager_Notify(t *testing.T) { return r.Method == "POST" && r.URL.String() == eiWithURL.URL.String() && r.Header["Content-Type"][0] == "application/json" && r.Header["X-Chainlink-Ea-Accesskey"][0] == "token" && r.Header["X-Chainlink-Ea-Secret"][0] == "secret" })).Once().Return(&http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(""))}, nil) - require.NoError(t, eim.Notify(webhookSpecTwoEIs.ID)) + require.NoError(t, eim.Notify(ctx, webhookSpecTwoEIs.ID)) } func Test_ExternalInitiatorManager_DeleteJob(t *testing.T) { + ctx := testutils.Context(t) db := pgtest.NewSqlxDB(t) cfg := pgtest.NewQConfig(true) borm := newBridgeORM(t, db, cfg) @@ -117,11 +121,11 @@ func Test_ExternalInitiatorManager_DeleteJob(t *testing.T) { eim := webhook.NewExternalInitiatorManager(db, client, logger.TestLogger(t), cfg) // Does nothing with no EI - require.NoError(t, eim.DeleteJob(webhookSpecNoEIs.ID)) + require.NoError(t, eim.DeleteJob(ctx, webhookSpecNoEIs.ID)) client.On("Do", mock.MatchedBy(func(r *http.Request) bool { expectedURL := fmt.Sprintf("%s/%s", eiWithURL.URL.String(), jb.ExternalJobID.String()) return r.Method == "DELETE" && r.URL.String() == expectedURL && r.Header["Content-Type"][0] == "application/json" && r.Header["X-Chainlink-Ea-Accesskey"][0] == "token" && r.Header["X-Chainlink-Ea-Secret"][0] == "secret" })).Once().Return(&http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(""))}, nil) - require.NoError(t, eim.DeleteJob(webhookSpecTwoEIs.ID)) + require.NoError(t, eim.DeleteJob(ctx, webhookSpecTwoEIs.ID)) } diff --git a/core/services/webhook/mocks/external_initiator_manager.go b/core/services/webhook/mocks/external_initiator_manager.go index 6c061f5412d..010b6f8db0a 100644 --- a/core/services/webhook/mocks/external_initiator_manager.go +++ b/core/services/webhook/mocks/external_initiator_manager.go @@ -3,7 +3,10 @@ package mocks import ( + context "context" + bridges "github.com/smartcontractkit/chainlink/v2/core/bridges" + mock "github.com/stretchr/testify/mock" ) @@ -12,17 +15,17 @@ type ExternalInitiatorManager struct { mock.Mock } -// DeleteJob provides a mock function with given fields: webhookSpecID -func (_m *ExternalInitiatorManager) DeleteJob(webhookSpecID int32) error { - ret := _m.Called(webhookSpecID) +// DeleteJob provides a mock function with given fields: ctx, webhookSpecID +func (_m *ExternalInitiatorManager) DeleteJob(ctx context.Context, webhookSpecID int32) error { + ret := _m.Called(ctx, webhookSpecID) if len(ret) == 0 { panic("no return value specified for DeleteJob") } var r0 error - if rf, ok := ret.Get(0).(func(int32) error); ok { - r0 = rf(webhookSpecID) + if rf, ok := ret.Get(0).(func(context.Context, int32) error); ok { + r0 = rf(ctx, webhookSpecID) } else { r0 = ret.Error(0) } @@ -58,17 +61,17 @@ func (_m *ExternalInitiatorManager) FindExternalInitiatorByName(name string) (br return r0, r1 } -// Notify provides a mock function with given fields: webhookSpecID -func (_m *ExternalInitiatorManager) Notify(webhookSpecID int32) error { - ret := _m.Called(webhookSpecID) +// Notify provides a mock function with given fields: ctx, webhookSpecID +func (_m *ExternalInitiatorManager) Notify(ctx context.Context, webhookSpecID int32) error { + ret := _m.Called(ctx, webhookSpecID) if len(ret) == 0 { panic("no return value specified for Notify") } var r0 error - if rf, ok := ret.Get(0).(func(int32) error); ok { - r0 = rf(webhookSpecID) + if rf, ok := ret.Get(0).(func(context.Context, int32) error); ok { + r0 = rf(ctx, webhookSpecID) } else { r0 = ret.Error(0) } diff --git a/core/utils/http/http_test.go b/core/utils/http/http_test.go index db7a593a8cd..7ef2f1523ab 100644 --- a/core/utils/http/http_test.go +++ b/core/utils/http/http_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils/http" ) @@ -20,7 +21,7 @@ func TestUnrestrictedHTTPClient(t *testing.T) { assert.True(t, client.Transport.(*netHttp.Transport).DisableCompression) client.Transport = newMockTransport() - netReq, err := netHttp.NewRequest("GET", "http://localhost", bytes.NewReader([]byte{})) + netReq, err := netHttp.NewRequestWithContext(testutils.Context(t), "GET", "http://localhost", bytes.NewReader([]byte{})) assert.NoError(t, err) req := &http.HTTPRequest{ diff --git a/core/web/auth/auth_test.go b/core/web/auth/auth_test.go index f0b4e5068fb..2ac6473ed2e 100644 --- a/core/web/auth/auth_test.go +++ b/core/web/auth/auth_test.go @@ -2,6 +2,7 @@ package auth_test import ( "fmt" + "io" "net/http" "net/http/httptest" "testing" @@ -75,7 +76,7 @@ func TestAuthenticateByToken_Success(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) req.Header.Set(webauth.APIKey, key) req.Header.Set(webauth.APISecret, secret) router.ServeHTTP(w, req) @@ -96,7 +97,7 @@ func TestAuthenticateByToken_AuthFailed(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) req.Header.Set(webauth.APIKey, "bad-key") req.Header.Set(webauth.APISecret, "bad-secret") router.ServeHTTP(w, req) @@ -122,7 +123,7 @@ func TestAuthenticateByToken_RejectsBlankAccessKey(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) req.Header.Set(webauth.APIKey, key) req.Header.Set(webauth.APISecret, secret) router.ServeHTTP(w, req) @@ -143,7 +144,7 @@ func TestRequireAuth_NoneRequired(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) router.ServeHTTP(w, req) assert.True(t, called) @@ -161,7 +162,7 @@ func TestRequireAuth_AuthFailed(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) router.ServeHTTP(w, req) assert.False(t, called) @@ -179,7 +180,7 @@ func TestRequireAuth_LastAuthSuccess(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) router.ServeHTTP(w, req) assert.True(t, called) @@ -197,7 +198,7 @@ func TestRequireAuth_Error(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) router.ServeHTTP(w, req) assert.False(t, called) @@ -504,3 +505,10 @@ func TestRBAC_Routemap_ViewOnly(t *testing.T) { }) } } + +func mustRequest(t *testing.T, method, url string, body io.Reader) *http.Request { + ctx := testutils.Context(t) + req, err := http.NewRequestWithContext(ctx, method, url, body) + require.NoError(t, err) + return req +} diff --git a/core/web/auth/gql_test.go b/core/web/auth/gql_test.go index 4f3f8e27baf..f3d085c71f8 100644 --- a/core/web/auth/gql_test.go +++ b/core/web/auth/gql_test.go @@ -37,7 +37,7 @@ func Test_AuthenticateGQL_Unauthenticated(t *testing.T) { }) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) r.ServeHTTP(w, req) } @@ -63,7 +63,7 @@ func Test_AuthenticateGQL_Authenticated(t *testing.T) { sessionORM.On("AuthorizedUserWithSession", sessionID).Return(clsessions.User{Email: cltest.APIEmailAdmin, Role: clsessions.UserRoleAdmin}, nil) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) + req := mustRequest(t, "GET", "/", nil) cookie := cltest.MustGenerateSessionCookie(t, sessionID) req.AddCookie(cookie) diff --git a/core/web/build_info_controller_test.go b/core/web/build_info_controller_test.go index e2d2cb0e631..5a2b88fa0dc 100644 --- a/core/web/build_info_controller_test.go +++ b/core/web/build_info_controller_test.go @@ -32,12 +32,15 @@ func TestBuildInfoController_Show_APICredentials(t *testing.T) { func TestBuildInfoController_Show_NoCredentials(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) client := clhttptest.NewTestLocalOnlyHTTPClient() url := app.Server.URL + "/v2/build_info" - resp, err := client.Get(url) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + require.NoError(t, err) + resp, err := client.Do(req) require.NoError(t, err) require.Equal(t, http.StatusUnauthorized, resp.StatusCode) } diff --git a/core/web/gui_assets_test.go b/core/web/gui_assets_test.go index 137b1231984..810aa40ca05 100644 --- a/core/web/gui_assets_test.go +++ b/core/web/gui_assets_test.go @@ -44,7 +44,9 @@ func TestGuiAssets_DefaultIndexHtml_OK(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - resp, err := client.Get(app.Server.URL + tc.path) + req, err := http.NewRequestWithContext(testutils.Context(t), "GET", app.Server.URL+tc.path, nil) + require.NoError(t, err) + resp, err := client.Do(req) require.NoError(t, err) cltest.AssertServerResponse(t, resp, http.StatusOK) }) @@ -75,7 +77,9 @@ func TestGuiAssets_DefaultIndexHtml_NotFound(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - resp, err := client.Get(app.Server.URL + tc.path) + req, err := http.NewRequestWithContext(testutils.Context(t), "GET", app.Server.URL+tc.path, nil) + require.NoError(t, err) + resp, err := client.Do(req) require.NoError(t, err) cltest.AssertServerResponse(t, resp, http.StatusNotFound) }) @@ -94,13 +98,17 @@ func TestGuiAssets_DefaultIndexHtml_RateLimited(t *testing.T) { // Make calls equal to the rate limit rateLimit := 20 for i := 0; i < rateLimit; i++ { - resp, err := client.Get(app.Server.URL + "/") + req, err := http.NewRequestWithContext(testutils.Context(t), "GET", app.Server.URL+"/", nil) + require.NoError(t, err) + resp, err := client.Do(req) require.NoError(t, err) cltest.AssertServerResponse(t, resp, http.StatusOK) } // Last request fails - resp, err := client.Get(app.Server.URL + "/") + req, err := http.NewRequestWithContext(testutils.Context(t), "GET", app.Server.URL+"/", nil) + require.NoError(t, err) + resp, err := client.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusTooManyRequests, resp.StatusCode) } @@ -115,7 +123,7 @@ func TestGuiAssets_AssetsFS(t *testing.T) { recorder := httptest.NewRecorder() c, _ := gin.CreateTestContext(recorder) var err error - c.Request, err = http.NewRequest("GET", "http://localhost:6688/fixtures/operator_ui/assets/main.js", nil) + c.Request, err = http.NewRequestWithContext(c, "GET", "http://localhost:6688/fixtures/operator_ui/assets/main.js", nil) require.NoError(t, err) handler(c) @@ -123,7 +131,7 @@ func TestGuiAssets_AssetsFS(t *testing.T) { recorder = httptest.NewRecorder() c, _ = gin.CreateTestContext(recorder) - c.Request, err = http.NewRequest("GET", "http://localhost:6688/fixtures/operator_ui/assets/kinda_main.js", nil) + c.Request, err = http.NewRequestWithContext(c, "GET", "http://localhost:6688/fixtures/operator_ui/assets/kinda_main.js", nil) require.NoError(t, err) handler(c) @@ -134,7 +142,7 @@ func TestGuiAssets_AssetsFS(t *testing.T) { recorder := httptest.NewRecorder() c, _ := gin.CreateTestContext(recorder) var err error - c.Request, err = http.NewRequest("GET", "http://localhost:6688/fixtures/operator_ui/assets/main.js", nil) + c.Request, err = http.NewRequestWithContext(c, "GET", "http://localhost:6688/fixtures/operator_ui/assets/main.js", nil) require.NoError(t, err) c.Request.Header.Set("Accept-Encoding", "gzip") handler(c) @@ -144,7 +152,7 @@ func TestGuiAssets_AssetsFS(t *testing.T) { recorder = httptest.NewRecorder() c, _ = gin.CreateTestContext(recorder) - c.Request, err = http.NewRequest("GET", "http://localhost:6688/fixtures/operator_ui/assets/kinda_main.js", nil) + c.Request, err = http.NewRequestWithContext(c, "GET", "http://localhost:6688/fixtures/operator_ui/assets/kinda_main.js", nil) require.NoError(t, err) c.Request.Header.Set("Accept-Encoding", "gzip") handler(c) diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 83c4fc30db0..eec58c30571 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -600,6 +600,7 @@ func TestJobsController_Update_HappyPath(t *testing.T) { } func TestJobsController_Update_NonExistentID(t *testing.T) { + ctx := testutils.Context(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.OCR.Enabled = ptr(true) c.P2P.V2.Enabled = ptr(true) @@ -609,7 +610,7 @@ func TestJobsController_Update_NonExistentID(t *testing.T) { app := cltest.NewApplicationWithConfigAndKey(t, cfg, cltest.DefaultP2PKey) require.NoError(t, app.KeyStore.OCR().Add(cltest.DefaultOCRKey)) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) _, bridge := cltest.MustCreateBridge(t, app.GetSqlxDB(), cltest.BridgeOpts{}, app.GetConfig().Database()) _, bridge2 := cltest.MustCreateBridge(t, app.GetSqlxDB(), cltest.BridgeOpts{}, app.GetConfig().Database()) @@ -629,7 +630,7 @@ func TestJobsController_Update_NonExistentID(t *testing.T) { require.NoError(t, err) jb.OCROracleSpec = &ocrSpec jb.OCROracleSpec.TransmitterAddress = &app.Keys[0].EIP55Address - err = app.AddJobV2(testutils.Context(t), &jb) + err = app.AddJobV2(ctx, &jb) require.NoError(t, err) // test Calling update on the job id with changed values should succeed. @@ -712,6 +713,7 @@ func setupEthClientForControllerTests(t *testing.T) *evmclimocks.Client { } func setupJobSpecsControllerTestsWithJobs(t *testing.T) (*cltest.TestApplication, cltest.HTTPClientCleaner, job.Job, int32, job.Job, int32) { + ctx := testutils.Context(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.OCR.Enabled = ptr(true) c.P2P.V2.Enabled = ptr(true) @@ -721,7 +723,7 @@ func setupJobSpecsControllerTestsWithJobs(t *testing.T) (*cltest.TestApplication app := cltest.NewApplicationWithConfigAndKey(t, cfg, cltest.DefaultP2PKey) require.NoError(t, app.KeyStore.OCR().Add(cltest.DefaultOCRKey)) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) _, bridge := cltest.MustCreateBridge(t, app.GetSqlxDB(), cltest.BridgeOpts{}, app.GetConfig().Database()) _, bridge2 := cltest.MustCreateBridge(t, app.GetSqlxDB(), cltest.BridgeOpts{}, app.GetConfig().Database()) @@ -737,7 +739,7 @@ func setupJobSpecsControllerTestsWithJobs(t *testing.T) (*cltest.TestApplication require.NoError(t, err) jb.OCROracleSpec = &ocrSpec jb.OCROracleSpec.TransmitterAddress = &app.Keys[0].EIP55Address - err = app.AddJobV2(testutils.Context(t), &jb) + err = app.AddJobV2(ctx, &jb) require.NoError(t, err) drSpec := fmt.Sprintf(` @@ -758,7 +760,7 @@ func setupJobSpecsControllerTestsWithJobs(t *testing.T) (*cltest.TestApplication erejb, err := directrequest.ValidatedDirectRequestSpec(drSpec) require.NoError(t, err) - err = app.AddJobV2(testutils.Context(t), &erejb) + err = app.AddJobV2(ctx, &erejb) require.NoError(t, err) return app, client, jb, jb.ID, erejb, erejb.ID diff --git a/core/web/log_controller_test.go b/core/web/log_controller_test.go index dbb95361b98..28c54b72450 100644 --- a/core/web/log_controller_test.go +++ b/core/web/log_controller_test.go @@ -43,8 +43,8 @@ func TestLogController_GetLogConfig(t *testing.T) { client := app.NewHTTPClient(nil) - resp, err := client.HTTPClient.Get("/v2/log") - require.NoError(t, err) + resp, clean := client.Get("/v2/log") + t.Cleanup(clean) svcLogConfig := presenters.ServiceLogConfigResource{} cltest.AssertServerResponse(t, resp, http.StatusOK) diff --git a/core/web/ping_controller_test.go b/core/web/ping_controller_test.go index 8e1862cd8c7..aa528ced534 100644 --- a/core/web/ping_controller_test.go +++ b/core/web/ping_controller_test.go @@ -55,7 +55,7 @@ func TestPingController_Show_ExternalInitiatorCredentials(t *testing.T) { require.NoError(t, err) url := app.Server.URL + "/v2/ping" - request, err := http.NewRequest("GET", url, nil) + request, err := http.NewRequestWithContext(testutils.Context(t), "GET", url, nil) require.NoError(t, err) request.Header.Set("Content-Type", web.MediaType) request.Header.Set("X-Chainlink-EA-AccessKey", eia.AccessKey) @@ -74,12 +74,15 @@ func TestPingController_Show_ExternalInitiatorCredentials(t *testing.T) { func TestPingController_Show_NoCredentials(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) client := clhttptest.NewTestLocalOnlyHTTPClient() url := app.Server.URL + "/v2/ping" - resp, err := client.Get(url) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + require.NoError(t, err) + resp, err := client.Do(req) require.NoError(t, err) require.Equal(t, http.StatusUnauthorized, resp.StatusCode) } diff --git a/core/web/router_test.go b/core/web/router_test.go index 18177a1ac28..bb371318468 100644 --- a/core/web/router_test.go +++ b/core/web/router_test.go @@ -20,14 +20,18 @@ import ( ) func TestTokenAuthRequired_NoCredentials(t *testing.T) { + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) router := web.Router(t, app, nil) ts := httptest.NewServer(router) defer ts.Close() - resp, err := http.Post(ts.URL+"/v2/jobs/", web.MediaType, bytes.NewBufferString("{}")) + req, err := http.NewRequestWithContext(ctx, "POST", ts.URL+"/v2/jobs/", bytes.NewBufferString("{}")) + require.NoError(t, err) + req.Header.Set("Content-Type", web.MediaType) + resp, err := http.DefaultClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) @@ -49,8 +53,9 @@ func TestTokenAuthRequired_SessionCredentials(t *testing.T) { } func TestTokenAuthRequired_TokenCredentials(t *testing.T) { + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) router := web.Router(t, app, nil) ts := httptest.NewServer(router) @@ -67,7 +72,7 @@ func TestTokenAuthRequired_TokenCredentials(t *testing.T) { err = app.BridgeORM().CreateExternalInitiator(ea) require.NoError(t, err) - request, err := http.NewRequest("GET", ts.URL+"/v2/ping/", bytes.NewBufferString("{}")) + request, err := http.NewRequestWithContext(ctx, "GET", ts.URL+"/v2/ping/", bytes.NewBufferString("{}")) require.NoError(t, err) request.Header.Set("Content-Type", web.MediaType) request.Header.Set("X-Chainlink-EA-AccessKey", eia.AccessKey) @@ -81,8 +86,9 @@ func TestTokenAuthRequired_TokenCredentials(t *testing.T) { } func TestTokenAuthRequired_BadTokenCredentials(t *testing.T) { + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) router := web.Router(t, app, nil) ts := httptest.NewServer(router) @@ -99,7 +105,7 @@ func TestTokenAuthRequired_BadTokenCredentials(t *testing.T) { err = app.BridgeORM().CreateExternalInitiator(ea) require.NoError(t, err) - request, err := http.NewRequest("GET", ts.URL+"/v2/ping/", bytes.NewBufferString("{}")) + request, err := http.NewRequestWithContext(ctx, "GET", ts.URL+"/v2/ping/", bytes.NewBufferString("{}")) require.NoError(t, err) request.Header.Set("Content-Type", web.MediaType) request.Header.Set("X-Chainlink-EA-AccessKey", eia.AccessKey) @@ -113,8 +119,9 @@ func TestTokenAuthRequired_BadTokenCredentials(t *testing.T) { } func TestSessions_RateLimited(t *testing.T) { + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) router := web.Router(t, app, nil) ts := httptest.NewServer(router) @@ -124,7 +131,7 @@ func TestSessions_RateLimited(t *testing.T) { input := `{"email":"brute@force.com", "password": "wrongpassword"}` for i := 0; i < 5; i++ { - request, err := http.NewRequest("POST", ts.URL+"/sessions", bytes.NewBufferString(input)) + request, err := http.NewRequestWithContext(ctx, "POST", ts.URL+"/sessions", bytes.NewBufferString(input)) require.NoError(t, err) resp, err := client.Do(request) @@ -132,7 +139,7 @@ func TestSessions_RateLimited(t *testing.T) { assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) } - request, err := http.NewRequest("POST", ts.URL+"/sessions", bytes.NewBufferString(input)) + request, err := http.NewRequestWithContext(ctx, "POST", ts.URL+"/sessions", bytes.NewBufferString(input)) require.NoError(t, err) resp, err := client.Do(request) @@ -141,8 +148,9 @@ func TestSessions_RateLimited(t *testing.T) { } func TestRouter_LargePOSTBody(t *testing.T) { + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) router := web.Router(t, app, nil) ts := httptest.NewServer(router) @@ -151,7 +159,7 @@ func TestRouter_LargePOSTBody(t *testing.T) { client := clhttptest.NewTestLocalOnlyHTTPClient() body := string(make([]byte, 70000)) - request, err := http.NewRequest("POST", ts.URL+"/sessions", bytes.NewBufferString(body)) + request, err := http.NewRequestWithContext(ctx, "POST", ts.URL+"/sessions", bytes.NewBufferString(body)) require.NoError(t, err) resp, err := client.Do(request) @@ -160,13 +168,16 @@ func TestRouter_LargePOSTBody(t *testing.T) { } func TestRouter_GinHelmetHeaders(t *testing.T) { + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) router := web.Router(t, app, nil) ts := httptest.NewServer(router) defer ts.Close() - res, err := http.Get(ts.URL) + req, err := http.NewRequestWithContext(ctx, "GET", ts.URL, nil) + require.NoError(t, err) + res, err := http.DefaultClient.Do(req) require.NoError(t, err) for _, tt := range []struct { HelmetName string diff --git a/core/web/sessions_controller_test.go b/core/web/sessions_controller_test.go index c2950caf3d1..9f883ef54b8 100644 --- a/core/web/sessions_controller_test.go +++ b/core/web/sessions_controller_test.go @@ -22,6 +22,7 @@ import ( func TestSessionsController_Create(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) require.NoError(t, app.Start(testutils.Context(t))) @@ -44,7 +45,7 @@ func TestSessionsController_Create(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { body := fmt.Sprintf(`{"email":"%s","password":"%s"}`, test.email, test.password) - request, err := http.NewRequest("POST", app.Server.URL+"/sessions", bytes.NewBufferString(body)) + request, err := http.NewRequestWithContext(ctx, "POST", app.Server.URL+"/sessions", bytes.NewBufferString(body)) assert.NoError(t, err) resp, err := client.Do(request) assert.NoError(t, err) @@ -86,8 +87,9 @@ func mustInsertSession(t *testing.T, q pg.Q, session *sessions.Session) { func TestSessionsController_Create_ReapSessions(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) - require.NoError(t, app.Start(testutils.Context(t))) + require.NoError(t, app.Start(ctx)) user := cltest.MustRandomUser(t) require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) @@ -99,7 +101,10 @@ func TestSessionsController_Create_ReapSessions(t *testing.T) { mustInsertSession(t, q, &staleSession) body := fmt.Sprintf(`{"email":"%s","password":"%s"}`, user.Email, cltest.Password) - resp, err := http.Post(app.Server.URL+"/sessions", "application/json", bytes.NewBufferString(body)) + req, err := http.NewRequestWithContext(ctx, "POST", app.Server.URL+"/sessions", bytes.NewBufferString(body)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer func() { assert.NoError(t, resp.Body.Close()) }() @@ -119,6 +124,7 @@ func TestSessionsController_Create_ReapSessions(t *testing.T) { func TestSessionsController_Destroy(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) app := cltest.NewApplicationEVMDisabled(t) require.NoError(t, app.Start(testutils.Context(t))) @@ -143,7 +149,7 @@ func TestSessionsController_Destroy(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { cookie := cltest.MustGenerateSessionCookie(t, test.sessionID) - request, err := http.NewRequest("DELETE", app.Server.URL+"/sessions", nil) + request, err := http.NewRequestWithContext(ctx, "DELETE", app.Server.URL+"/sessions", nil) assert.NoError(t, err) request.AddCookie(cookie) @@ -163,6 +169,7 @@ func TestSessionsController_Destroy(t *testing.T) { func TestSessionsController_Destroy_ReapSessions(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) client := clhttptest.NewTestLocalOnlyHTTPClient() app := cltest.NewApplicationEVMDisabled(t) @@ -183,7 +190,7 @@ func TestSessionsController_Destroy_ReapSessions(t *testing.T) { staleSession.LastUsed = time.Now().Add(-cltest.MustParseDuration(t, "241h")) mustInsertSession(t, q, &staleSession) - request, err := http.NewRequest("DELETE", app.Server.URL+"/sessions", nil) + request, err := http.NewRequestWithContext(ctx, "DELETE", app.Server.URL+"/sessions", nil) assert.NoError(t, err) request.AddCookie(cookie) diff --git a/revive.toml b/revive.toml deleted file mode 100644 index daa62cd5487..00000000000 --- a/revive.toml +++ /dev/null @@ -1,53 +0,0 @@ -ignoreGeneratedHeader = false -severity = "warning" -confidence = 0.8 -errorCode = 0 -warningCode = 0 - -[rule.blank-imports] -[rule.context-as-argument] -[rule.context-keys-type] -[rule.dot-imports] -[rule.error-return] -[rule.error-strings] -[rule.error-naming] -[rule.exported] -Disabled = true -[rule.if-return] -[rule.increment-decrement] -[rule.var-naming] -Disabled = true -[rule.var-declaration] -[rule.package-comments] -[rule.range] -[rule.receiver-naming] -[rule.time-naming] -[rule.unexported-return] -Disabled = true -[rule.indent-error-flow] -[rule.errorf] -[rule.empty-block] -[rule.superfluous-else] -[rule.unused-parameter] -Disabled = true -[rule.unreachable-code] -[rule.redefines-builtin-id] -[rule.waitgroup-by-value] -[rule.unconditional-recursion] -[rule.struct-tag] -# [rule.string-format] -[rule.string-of-int] -# [rule.range-val-address] -[rule.range-val-in-closure] -[rule.modifies-value-receiver] -[rule.modifies-parameter] -[rule.identical-branches] -[rule.get-return] -# [rule.flag-parameter] -# [rule.early-return] -[rule.defer] -[rule.constant-logical-expr] -# [rule.confusing-naming] -# [rule.confusing-results] -[rule.bool-literal-in-expr] -[rule.atomic] diff --git a/tools/flakeytests/cmd/runner/main.go b/tools/flakeytests/cmd/runner/main.go index f38179f502b..0f36ab25ef9 100644 --- a/tools/flakeytests/cmd/runner/main.go +++ b/tools/flakeytests/cmd/runner/main.go @@ -1,10 +1,12 @@ package main import ( + "context" "flag" "io" "log" "os" + "os/signal" "strings" "github.com/smartcontractkit/chainlink/v2/tools/flakeytests" @@ -13,6 +15,13 @@ import ( const numReruns = 2 func main() { + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + go func() { + <-ctx.Done() + stop() // restore default exit behavior + log.Println("Cancelling... interrupt again to exit") + }() + grafanaHost := flag.String("grafana_host", "", "grafana host URL") grafanaAuth := flag.String("grafana_auth", "", "grafana basic auth for Loki API") command := flag.String("command", "", "test command being rerun; used to tag metrics") @@ -48,10 +57,10 @@ func main() { readers = append(readers, r) } - ctx := flakeytests.GetGithubMetadata(*ghRepo, *ghEventName, *ghSHA, *ghEventPath, *ghRunID) - rep := flakeytests.NewLokiReporter(*grafanaHost, *grafanaAuth, *command, ctx) + meta := flakeytests.GetGithubMetadata(*ghRepo, *ghEventName, *ghSHA, *ghEventPath, *ghRunID) + rep := flakeytests.NewLokiReporter(*grafanaHost, *grafanaAuth, *command, meta) r := flakeytests.NewRunner(readers, rep, numReruns) - err := r.Run() + err := r.Run(ctx) if err != nil { log.Fatalf("Error re-running flakey tests: %s", err) } diff --git a/tools/flakeytests/reporter.go b/tools/flakeytests/reporter.go index f17a44ef9f1..b7c7f66698f 100644 --- a/tools/flakeytests/reporter.go +++ b/tools/flakeytests/reporter.go @@ -2,6 +2,7 @@ package flakeytests import ( "bytes" + "context" "encoding/base64" "encoding/json" "fmt" @@ -138,14 +139,14 @@ func (l *LokiReporter) createRequest(report *Report) (pushRequest, error) { return pr, nil } -func (l *LokiReporter) makeRequest(pushReq pushRequest) error { +func (l *LokiReporter) makeRequest(ctx context.Context, pushReq pushRequest) error { body, err := json.Marshal(pushReq) if err != nil { return err } u := url.URL{Scheme: "https", Host: l.host, Path: "loki/api/v1/push"} - req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body)) + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), bytes.NewReader(body)) if err != nil { return err } @@ -167,13 +168,13 @@ func (l *LokiReporter) makeRequest(pushReq pushRequest) error { return err } -func (l *LokiReporter) Report(report *Report) error { +func (l *LokiReporter) Report(ctx context.Context, report *Report) error { pushReq, err := l.createRequest(report) if err != nil { return err } - return l.makeRequest(pushReq) + return l.makeRequest(ctx, pushReq) } func NewLokiReporter(host, auth, command string, ctx Context) *LokiReporter { diff --git a/tools/flakeytests/runner.go b/tools/flakeytests/runner.go index 97402633f38..88ab647e7c1 100644 --- a/tools/flakeytests/runner.go +++ b/tools/flakeytests/runner.go @@ -3,6 +3,7 @@ package flakeytests import ( "bufio" "bytes" + "context" "encoding/json" "errors" "fmt" @@ -33,7 +34,7 @@ type tester interface { } type reporter interface { - Report(r *Report) error + Report(ctx context.Context, r *Report) error } type parseFn func(readers ...io.Reader) (*Report, error) @@ -297,7 +298,7 @@ func dedupeEntries(report *Report) (*Report, error) { return out, nil } -func (r *Runner) Run() error { +func (r *Runner) Run(ctx context.Context) error { parseReport, err := r.parse(r.readers...) if err != nil { return err @@ -323,5 +324,5 @@ func (r *Runner) Run() error { return err } - return r.reporter.Report(report) + return r.reporter.Report(ctx, report) } diff --git a/tools/flakeytests/runner_test.go b/tools/flakeytests/runner_test.go index 64e2a6c968a..ee069a1655d 100644 --- a/tools/flakeytests/runner_test.go +++ b/tools/flakeytests/runner_test.go @@ -1,6 +1,7 @@ package flakeytests import ( + "context" "io" "os" "os/exec" @@ -9,13 +10,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" ) type mockReporter struct { report *Report } -func (m *mockReporter) Report(report *Report) error { +func (m *mockReporter) Report(_ context.Context, report *Report) error { m.report = report return nil } @@ -120,7 +123,7 @@ func TestRunner_WithFlake(t *testing.T) { // This will report a flake since we've mocked the rerun // to only report one failure (not two as expected). - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) assert.Len(t, m.report.tests, 1) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] @@ -155,7 +158,7 @@ func TestRunner_WithFailedPackage(t *testing.T) { // This will report a flake since we've mocked the rerun // to only report one failure (not two as expected). - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) assert.Len(t, m.report.tests, 1) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] @@ -181,7 +184,7 @@ func TestRunner_AllFailures(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) assert.Len(t, m.report.tests, 0) } @@ -207,7 +210,7 @@ func TestRunner_RerunSuccessful(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) @@ -229,7 +232,7 @@ func TestRunner_RootLevelTest(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/"]["TestConfigDocs"] assert.True(t, ok) @@ -256,7 +259,7 @@ func TestRunner_RerunFailsWithNonzeroExitCode(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) @@ -293,7 +296,7 @@ func TestRunner_RerunWithNonZeroExitCodeDoesntStopCommand(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) calls := index assert.Equal(t, 4, calls) @@ -366,7 +369,7 @@ func TestIntegration_DealsWithSubtests(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) expectedTests := map[string]map[string]int{ "github.com/smartcontractkit/chainlink/v2/tools/flakeytests/": { @@ -402,7 +405,7 @@ func TestIntegration_ParsesPanics(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/tools/flakeytests"]["TestSkippedForTests"] assert.False(t, ok) @@ -433,7 +436,7 @@ func TestIntegration(t *testing.T) { reporter: m, } - err := r.Run() + err := r.Run(tests.Context(t)) require.NoError(t, err) _, ok := m.report.tests["github.com/smartcontractkit/chainlink/v2/tools/flakeytests"]["TestSkippedForTests_Success"] assert.False(t, ok) From 686553a6ac81121df81da275bc57667647208745 Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 11 Jan 2024 09:43:42 -0700 Subject: [PATCH 145/234] Move metrics collection to fix summary errors on no runs (#11743) --- .github/workflows/integration-tests.yml | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 75184a47965..d852520d8e1 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -249,6 +249,16 @@ jobs: runs-on: ${{ matrix.product.os }} name: ETH Smoke Tests ${{ matrix.product.name }} steps: + - name: Collect Metrics + if: needs.changes.outputs.src == 'true' + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: ETH Smoke Tests ${{ matrix.product.name }} + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: @@ -285,16 +295,6 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" - - name: Collect Metrics - if: always() - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - with: - basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ETH Smoke Tests ${{ matrix.product.name }} - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - continue-on-error: true eth-smoke-tests-matrix: if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} @@ -364,6 +364,16 @@ jobs: runs-on: ${{ matrix.product.os }} name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} steps: + - name: Collect Metrics + if: needs.changes.outputs.src == 'true' + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: @@ -476,16 +486,6 @@ jobs: if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | docker logs otel-collector - - name: Collect Metrics - if: always() - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - with: - basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - continue-on-error: true - name: Permissions on traces if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | From 3332401a0a415e7ec18b23e5c9038b1efc05d451 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Thu, 11 Jan 2024 18:51:59 +0200 Subject: [PATCH 146/234] Fix TestIntegration_BlockHistoryEstimator leak (#11741) * Fix TestIntegration_BlockHistoryEstimator leak * Use servicetest.Run --- core/internal/features/features_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 8ae6a28a873..da899cef362 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -40,6 +40,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -1319,7 +1320,7 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(cc) for _, re := range cc.Slice() { - require.NoError(t, re.Start(testutils.Context(t))) + servicetest.Run(t, re) } var newHeads evmtest.RawSub[*evmtypes.Head] select { @@ -1343,6 +1344,7 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { elems := args.Get(1).([]rpc.BatchElem) elems[0].Result = &b43 }) + ethClient.On("Close").Return().Once() // Simulate one new head and check the gas price got updated h43 := cltest.Head(43) From 4551522d9a456523e17f6dd2fd2fd6543e5d83f5 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Jan 2024 12:45:32 -0500 Subject: [PATCH 147/234] Improve on-chain config docs (#11700) * Improve on-chain config docs * Update Mercury OCR3 configuration example --------- Co-authored-by: Michael Fletcher --- docs/Mercury.md | 191 ++++++++++++------------------------------------ 1 file changed, 46 insertions(+), 145 deletions(-) diff --git a/docs/Mercury.md b/docs/Mercury.md index 610605ddbc6..82835c9519a 100644 --- a/docs/Mercury.md +++ b/docs/Mercury.md @@ -1,162 +1,63 @@ # Mercury Documentation -## Contracts +## Useful Links -Use this tool to configure contracts: +[Configuration Builder](https://github.com/smartcontractkit/the-most-amazing-mercury-contract-configuration-tool) -https://github.com/smartcontractkit/the-most-amazing-mercury-contract-configuration-tool +[Contracts](https://github.com/smartcontractkit/chainlink/contracts/src/v0.8/llo-feeds) -TODO: updated process here @Austin Born +[OCR3 Config Documentation](https://github.com/smartcontractkit/libocr/blob/master/offchainreporting2plus/internal/config/ocr3config/public_config.go) -[Reference contract](https://github.com/smartcontractkit/reference-data-directory/blob/master/ethereum-testnet-goerli-arbitrum-1/contracts/0x535051166466D159da8742167c9CA1eFe9e82613.json) -[OCR2 config documentation](https://github.com/smartcontractkit/libocr/blob/master/offchainreporting2/internal/config/public_config.go) -**🚨 Important config** -`s` - transmission schedule. This should be set to the number of oracles on the feed - meaning that every oracle will attempt to transmit to the mercury server in the first stage of transmission. eg `[4]` if there are 4 node in the DON, excluding the bootstrap node. - -`f` - set this to `n//3` (where `//` denotes integer division), e.g. if you have 16 oracles, set `f` to 5. - -`deltaRound` - report generation frequency. This determines how frequently a new round should be started at most (if rounds take longer than this due to network latency, there will be fewer rounds per second than this parameter would suggest). `100ms` is a good starting point (10 rounds/s). - -`reportingPluginConfig.alphaAccept` - set this to `0`, because our mercury ContractTransmitter doesn't know the latest report that's been sent to the mercury server and we therefore always have a "pending" report which we compare against before accepting a report for transmission. - -`reportingPluginConfig.deltaC` - set this to `0` so every round will result in a report. - -
Example `verifier/<0xaddress>.json` +### Example Feed Configuration ```json { - "contractVersion": 1001, - "digests": { - "0x0006c67c0374ab0dcfa45c63b37df2ea8d16fb903c043caa98065033e9c15666": { - "feedId": "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c", - "proxyEnabled": true, - "status": "active" - } - }, - "feeds": { - "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c": { - "digests": [ - "0x0006c67c0374ab0dcfa45c63b37df2ea8d16fb903c043caa98065033e9c15666" - ], - "docs": { - "assetName": "Chainlink", - "feedCategory": "verified", - "feedType": "Crypto", - "hidden": true - }, - "externalAdapterRequestParams": { - "endpoint": "cryptolwba", - "from": "LINK", - "to": "USD" - }, - "feedId": "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c", - "latestConfig": { - "offchainConfig": { - "deltaGrace": "0", - "deltaProgress": "2s", - "deltaResend": "20s", - "deltaRound": "250ms", - "deltaStage": "60s", - "f": 1, - "maxDurationObservation": "250ms", - "maxDurationQuery": "0s", - "maxDurationReport": "250ms", - "maxDurationShouldAcceptFinalizedReport": "250ms", - "maxDurationShouldTransmitAcceptedReport": "250ms", - "rMax": 100, - "reportingPluginConfig": { - "alphaAcceptInfinite": false, - "alphaAcceptPpb": "0", - "alphaReportInfinite": false, - "alphaReportPpb": "0", - "deltaC": "0s" - }, - "s": [ - 4 - ] - }, - "offchainConfigVersion": 30, - "onchainConfig": { - "max": "99999999999999999999999999999", - "min": "1" - }, - "onchainConfigVersion": 1, - "oracles": [ - { - "api": [ - "coinmetrics", - "ncfx", - "tiingo-test" - ], - "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-0" - }, - { - "api": [ - "coinmetrics", - "ncfx", - "tiingo-test" - ], - "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-1" - }, - { - "api": [ - "coinmetrics", - "ncfx", - "tiingo-test" - ], - "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-2" - }, - { - "api": [ - "coinmetrics", - "ncfx", - "tiingo-test" - ], - "operator": "clc-ocr-mercury-arbitrum-goerli-nodes-3" - } - ] - }, - "marketing": { - "category": "crypto", - "history": true, - "pair": [ - "LINK", - "USD" - ], - "path": "link-usd-verifier" - }, - "name": "LINK/USD-RefPricePlus-ArbitrumGoerli-002", - "reportFields": { - "ask": { - "decimals": 8, - "maxSubmissionValue": "99999999999999999999999999999", - "minSubmissionValue": "1", - "resultPath": "data,ask" - }, - "bid": { - "decimals": 8, - "maxSubmissionValue": "99999999999999999999999999999", - "minSubmissionValue": "1", - "resultPath": "data,bid" - }, - "median": { - "decimals": 8, - "maxSubmissionValue": "99999999999999999999999999999", - "minSubmissionValue": "1", - "resultPath": "data,mid" - } - }, - "status": "testing" - } - }, - "name": "Mercury v0.2 - Production Testnet Verifier (v1.0.0)", - "status": "testing" + "feedId": "0x14e044f932bb959cc2aa8dc1ba110c09224e639aae00264c1ffc2a0830904a3c", + "chainId": 42161, // source chain id + "contractAddress": "0x14e044f932bb959cc2aa8dc1ba110c09224e639a", // verifier contract address + "configCount": 1, // the index of this config + "signers": [ + "0x000....01", + "0x000....02", + "0x000....03", + "0x000....04" + ], // NOP signing addresses, + "transmitters": [ + "0x000....11", + "0x000....12", + "0x000....13", + "0x000....14" + ], // NOP transmitter addresses + "offchainConfig": { + "baseUSDFee": "0.1", // 10c base fee to verify the report + "deltaCertifiedCommitRequest": "1s", + "deltaGrace": "0s", + "deltaInitial": "600ms", + "deltaProgress": "2s", + "deltaResend": "10s", + "deltaRound": "250ms", + "deltaStage": "0s", + "expirationWindow": "86400", //window in in which a report can be verified in seconds + "f": 3, + "maxDurationObservation": "250ms", + "maxDurationQuery": "50ms", + "maxDurationShouldAcceptAttestedReport": "50ms", + "maxDurationShouldTransmitAcceptedReport": "50ms", + "rMax": "25", + "s": [ + 4 + ] + }, + "offchainConfigVersion": 30, + "onchainConfig": { + "max": "99999999999999999999999999999", + "min": "1" + } } ``` -
## Jobs From a3aa6a962fadb054578977a8da75083c5346c35b Mon Sep 17 00:00:00 2001 From: Lei Date: Thu, 11 Jan 2024 11:18:09 -0800 Subject: [PATCH 148/234] support arbitrum sepolia (#11723) --- core/scripts/common/helpers.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go index 9d2d06c89d0..0ed9929956a 100644 --- a/core/scripts/common/helpers.go +++ b/core/scripts/common/helpers.go @@ -171,6 +171,8 @@ func explorerLinkPrefix(chainID int64) (prefix string) { prefix = "https://goerli.arbiscan.io" case ArbitrumOneChainID: // Arbitrum mainnet prefix = "https://arbiscan.io" + case ArbitrumSepoliaChainID: // Arbitrum Sepolia + prefix = "https://sepolia.arbiscan.io" case 56: // BSC mainnet prefix = "https://bscscan.com" @@ -233,6 +235,8 @@ func automationExplorerNetworkName(chainID int64) (prefix string) { prefix = "arbitrum-goerli" case ArbitrumOneChainID: // Arbitrum mainnet prefix = "arbitrum" + case ArbitrumSepoliaChainID: // Arbitrum Sepolia + prefix = "arbitrum-sepolia" case 56: // BSC mainnet prefix = "bsc" From 841b455b593aae8b7fd90490ce702d65ff58e868 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:38:18 -0500 Subject: [PATCH 149/234] Change chainlink helm chart node deployment to use 'recreate' rollouts (#11753) --- .../chainlink-cluster/templates/chainlink-node-deployment.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml index a08c31c2c42..16156ef4ffc 100644 --- a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml @@ -4,6 +4,9 @@ kind: Deployment metadata: name: {{ $.Release.Name }}-{{ $cfg.name }} spec: + strategy: + # Need to recreate the pod to deal with lease lock held by old pod. + type: Recreate selector: matchLabels: app: {{ $.Release.Name }} From 64b5ba7fb2daee313dfdd2d80ce3410bc8ab8741 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Thu, 11 Jan 2024 19:39:47 -0500 Subject: [PATCH 150/234] Update chainlink-common to latest commit (#11755) * Update chainlink-common * Fix duration --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/chainlink/config_job_pipeline_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 8cd50b08541..62ae3f4a3ec 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index eb5bbb9cdf7..0da8f55aa27 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1164,8 +1164,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 h1:zFupIn0S1JYnKUJffFS+m4k+9BUlBG4IHsOpu6kkqKM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b h1:PRuvHgPka1gIGPKASuJHvqUvYWhZO7z5B/kKapjkZaM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/chainlink/config_job_pipeline_test.go b/core/services/chainlink/config_job_pipeline_test.go index 0b771a18fb7..9a865569eb3 100644 --- a/core/services/chainlink/config_job_pipeline_test.go +++ b/core/services/chainlink/config_job_pipeline_test.go @@ -21,7 +21,7 @@ func TestJobPipelineConfigTest(t *testing.T) { jp := cfg.JobPipeline() assert.Equal(t, int64(100*utils.MB), jp.DefaultHTTPLimit()) - d, err := commonconfig.MakeDuration(1 * time.Minute) + d, err := commonconfig.NewDuration(1 * time.Minute) require.NoError(t, err) assert.Equal(t, d, jp.DefaultHTTPTimeout()) assert.Equal(t, 1*time.Hour, jp.MaxRunDuration()) diff --git a/go.mod b/go.mod index 11659ac4645..3f834e2d848 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 4ca0b032a08..014ed5ba627 100644 --- a/go.sum +++ b/go.sum @@ -1150,8 +1150,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 h1:zFupIn0S1JYnKUJffFS+m4k+9BUlBG4IHsOpu6kkqKM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b h1:PRuvHgPka1gIGPKASuJHvqUvYWhZO7z5B/kKapjkZaM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 7d5ce8a9513..17a98e450a8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b github.com/smartcontractkit/chainlink-testing-framework v1.22.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f6a201dc667..916145aca49 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1481,8 +1481,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6 h1:zFupIn0S1JYnKUJffFS+m4k+9BUlBG4IHsOpu6kkqKM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240104174432-88b0f2a535a6/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b h1:PRuvHgPka1gIGPKASuJHvqUvYWhZO7z5B/kKapjkZaM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From c0f9838cd51688a5f432bcc46c608ef86f41ebe6 Mon Sep 17 00:00:00 2001 From: dkneisly Date: Fri, 12 Jan 2024 10:14:05 +0900 Subject: [PATCH 151/234] chore/VRF-325 - Added smoke test for direct funding on VRFv2 (#11690) * Added VRF v2 smoke test for direct funding * prettier * Increased max gas in vrfv2 smoke test setup and fixed sub ID in direct funding request * Addressing PR comment about error messages --- contracts/scripts/native_solc_compile_all_vrf | 1 + .../VRFV2WrapperLoadTestConsumer.sol | 137 ++ .../vrfv2_wrapper_load_test_consumer.go | 1142 +++++++++++++++++ ...rapper-dependency-versions-do-not-edit.txt | 1 + core/gethwrappers/go_generate.go | 1 + .../actions/vrfv2_actions/vrfv2_models.go | 5 + .../actions/vrfv2_actions/vrfv2_steps.go | 152 ++- .../contracts/contract_deployer.go | 2 + .../contracts/contract_vrf_models.go | 18 + .../contracts/ethereum_vrfv2_contracts.go | 195 +++ integration-tests/smoke/vrfv2_test.go | 76 ++ 11 files changed, 1729 insertions(+), 1 deletion(-) create mode 100644 contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol create mode 100644 core/gethwrappers/generated/vrfv2_wrapper_load_test_consumer/vrfv2_wrapper_load_test_consumer.go diff --git a/contracts/scripts/native_solc_compile_all_vrf b/contracts/scripts/native_solc_compile_all_vrf index 4eed35cf5bc..828ba790510 100755 --- a/contracts/scripts/native_solc_compile_all_vrf +++ b/contracts/scripts/native_solc_compile_all_vrf @@ -90,6 +90,7 @@ compileContract vrf/VRFV2Wrapper.sol compileContract vrf/interfaces/VRFV2WrapperInterface.sol compileContract vrf/VRFV2WrapperConsumerBase.sol compileContract vrf/testhelpers/VRFV2WrapperConsumerExample.sol +compileContract vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol compileContract vrf/testhelpers/VRFv2Consumer.sol # VRF Consumers and Mocks diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol b/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol new file mode 100644 index 00000000000..5a82d4b0b9d --- /dev/null +++ b/contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import {VRFV2WrapperConsumerBase} from "../VRFV2WrapperConsumerBase.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol"; +import {VRFV2WrapperInterface} from "../interfaces/VRFV2WrapperInterface.sol"; + +contract VRFV2WrapperLoadTestConsumer is VRFV2WrapperConsumerBase, ConfirmedOwner { + VRFV2WrapperInterface public immutable i_vrfV2Wrapper; + uint256 public s_responseCount; + uint256 public s_requestCount; + uint256 public s_averageFulfillmentInMillions = 0; // in millions for better precision + uint256 public s_slowestFulfillment = 0; + uint256 public s_fastestFulfillment = 999; + uint256 public s_lastRequestId; + // solhint-disable-next-line chainlink-solidity/prefix-storage-variables-with-s-underscore + mapping(uint256 => uint256) internal requestHeights; // requestIds to block number when rand request was made + mapping(uint256 => RequestStatus) /* requestId */ /* requestStatus */ public s_requests; + + event WrappedRequestFulfilled(uint256 requestId, uint256[] randomWords, uint256 payment); + event WrapperRequestMade(uint256 indexed requestId, uint256 paid); + + struct RequestStatus { + uint256 paid; + bool fulfilled; + uint256[] randomWords; + uint256 requestTimestamp; + uint256 fulfilmentTimestamp; + uint256 requestBlockNumber; + uint256 fulfilmentBlockNumber; + } + + constructor( + address _link, + address _vrfV2Wrapper + ) ConfirmedOwner(msg.sender) VRFV2WrapperConsumerBase(_link, _vrfV2Wrapper) { + i_vrfV2Wrapper = VRFV2WrapperInterface(_vrfV2Wrapper); + } + + function makeRequests( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords, + uint16 _requestCount + ) external onlyOwner { + for (uint16 i = 0; i < _requestCount; i++) { + uint256 requestId = requestRandomness(_callbackGasLimit, _requestConfirmations, _numWords); + s_lastRequestId = requestId; + uint256 requestBlockNumber = ChainSpecificUtil._getBlockNumber(); + uint256 paid = VRF_V2_WRAPPER.calculateRequestPrice(_callbackGasLimit); + s_requests[requestId] = RequestStatus({ + paid: paid, + fulfilled: false, + randomWords: new uint256[](0), + requestTimestamp: block.timestamp, + fulfilmentTimestamp: 0, + requestBlockNumber: requestBlockNumber, + fulfilmentBlockNumber: 0 + }); + s_requestCount++; + requestHeights[requestId] = requestBlockNumber; + emit WrapperRequestMade(requestId, paid); + } + } + + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override { + // solhint-disable-next-line custom-errors + require(s_requests[_requestId].paid > 0, "request not found"); + uint256 fulfilmentBlockNumber = ChainSpecificUtil._getBlockNumber(); + uint256 requestDelay = fulfilmentBlockNumber - requestHeights[_requestId]; + uint256 requestDelayInMillions = requestDelay * 1_000_000; + + if (requestDelay > s_slowestFulfillment) { + s_slowestFulfillment = requestDelay; + } + if (requestDelay < s_fastestFulfillment) { + s_fastestFulfillment = requestDelay; + } + s_averageFulfillmentInMillions = s_responseCount > 0 + ? (s_averageFulfillmentInMillions * s_responseCount + requestDelayInMillions) / (s_responseCount + 1) + : requestDelayInMillions; + + s_responseCount++; + s_requests[_requestId].fulfilled = true; + s_requests[_requestId].randomWords = _randomWords; + s_requests[_requestId].fulfilmentTimestamp = block.timestamp; + s_requests[_requestId].fulfilmentBlockNumber = fulfilmentBlockNumber; + + emit WrappedRequestFulfilled(_requestId, _randomWords, s_requests[_requestId].paid); + } + + function getRequestStatus( + uint256 _requestId + ) + external + view + returns ( + uint256 paid, + bool fulfilled, + uint256[] memory randomWords, + uint256 requestTimestamp, + uint256 fulfilmentTimestamp, + uint256 requestBlockNumber, + uint256 fulfilmentBlockNumber + ) + { + // solhint-disable-next-line custom-errors + require(s_requests[_requestId].paid > 0, "request not found"); + RequestStatus memory request = s_requests[_requestId]; + return ( + request.paid, + request.fulfilled, + request.randomWords, + request.requestTimestamp, + request.fulfilmentTimestamp, + request.requestBlockNumber, + request.fulfilmentBlockNumber + ); + } + + /// @notice withdrawLink withdraws the amount specified in amount to the owner + /// @param amount the amount to withdraw, in juels + function withdrawLink(uint256 amount) external onlyOwner { + LINK.transfer(owner(), amount); + } + + function reset() external { + s_averageFulfillmentInMillions = 0; + s_slowestFulfillment = 0; + s_fastestFulfillment = 999; + s_requestCount = 0; + s_responseCount = 0; + } + + receive() external payable {} +} diff --git a/core/gethwrappers/generated/vrfv2_wrapper_load_test_consumer/vrfv2_wrapper_load_test_consumer.go b/core/gethwrappers/generated/vrfv2_wrapper_load_test_consumer/vrfv2_wrapper_load_test_consumer.go new file mode 100644 index 00000000000..9f59d32f0a5 --- /dev/null +++ b/core/gethwrappers/generated/vrfv2_wrapper_load_test_consumer/vrfv2_wrapper_load_test_consumer.go @@ -0,0 +1,1142 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package vrfv2_wrapper_load_test_consumer + +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 +) + +var VRFV2WrapperLoadTestConsumerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_vrfV2Wrapper\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"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\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"}],\"name\":\"WrappedRequestFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"paid\",\"type\":\"uint256\"}],\"name\":\"WrapperRequestMade\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"}],\"name\":\"getRequestStatus\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"paid\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_vrfV2Wrapper\",\"outputs\":[{\"internalType\":\"contractVRFV2WrapperInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"_callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"_numWords\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestCount\",\"type\":\"uint16\"}],\"name\":\"makeRequests\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"_randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_averageFulfillmentInMillions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fastestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_lastRequestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requests\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"paid\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_responseCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_slowestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawLink\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + Bin: "0x60e0604052600060045560006005556103e76006553480156200002157600080fd5b50604051620017d6380380620017d68339810160408190526200004491620001cb565b6001600160601b0319606083811b821660805282901b1660a0523380600081620000b55760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000e857620000e88162000102565b50505060601b6001600160601b03191660c0525062000203565b6001600160a01b0381163314156200015d5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620000ac565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001c657600080fd5b919050565b60008060408385031215620001df57600080fd5b620001ea83620001ae565b9150620001fa60208401620001ae565b90509250929050565b60805160601c60a05160601c60c05160601c61157e6200025860003960006101600152600081816103ad015281816106d401528181610cea0152610e1101526000818161054e0152610cc0015261157e6000f3fe6080604052600436106100f75760003560e01c80638da5cb5b1161008a578063d826f88f11610059578063d826f88f14610300578063d8a4676f1461032c578063dc1670db1461035f578063f2fde38b1461037557600080fd5b80638da5cb5b1461021e578063a168fa8914610249578063afacbf9c146102ca578063b1e21749146102ea57600080fd5b8063737144bc116100c6578063737144bc146101bd57806374dba124146101d357806379ba5097146101e95780637a8042bd146101fe57600080fd5b80631757f11c146101035780631fe543e31461012c5780632353f2381461014e578063557d2e92146101a757600080fd5b366100fe57005b600080fd5b34801561010f57600080fd5b5061011960055481565b6040519081526020015b60405180910390f35b34801561013857600080fd5b5061014c61014736600461118b565b610395565b005b34801561015a57600080fd5b506101827f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610123565b3480156101b357600080fd5b5061011960035481565b3480156101c957600080fd5b5061011960045481565b3480156101df57600080fd5b5061011960065481565b3480156101f557600080fd5b5061014c610447565b34801561020a57600080fd5b5061014c610219366004611159565b610544565b34801561022a57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610182565b34801561025557600080fd5b5061029d610264366004611159565b600960205260009081526040902080546001820154600383015460048401546005850154600690950154939460ff909316939192909186565b604080519687529415156020870152938501929092526060840152608083015260a082015260c001610123565b3480156102d657600080fd5b5061014c6102e536600461127a565b61064c565b3480156102f657600080fd5b5061011960075481565b34801561030c57600080fd5b5061014c6000600481905560058190556103e76006556003819055600255565b34801561033857600080fd5b5061034c610347366004611159565b61089a565b60405161012397969594939291906113ca565b34801561036b57600080fd5b5061011960025481565b34801561038157600080fd5b5061014c6103903660046110fa565b610a01565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610439576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f6f6e6c792056524620563220777261707065722063616e2066756c66696c6c0060448201526064015b60405180910390fd5b6104438282610a15565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610430565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b61054c610bf1565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb6105a760005473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260248101849052604401602060405180830381600087803b15801561061457600080fd5b505af1158015610628573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104439190611137565b610654610bf1565b60005b8161ffff168161ffff161015610893576000610674868686610c74565b600781905590506000610685610eb5565b6040517f4306d35400000000000000000000000000000000000000000000000000000000815263ffffffff8916600482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634306d3549060240160206040518083038186803b15801561071657600080fd5b505afa15801561072a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074e9190611172565b6040805160e08101825282815260006020808301828152845183815280830186528486019081524260608601526080850184905260a0850189905260c0850184905289845260098352949092208351815591516001830180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905592518051949550919390926107ee92600285019291019061106f565b5060608201516003808301919091556080830151600483015560a0830151600583015560c090920151600690910155805490600061082b836114da565b9091555050600083815260086020526040908190208390555183907f5f56b4c20db9f5b294cbf6f681368de4a992a27e2de2ee702dcf2cbbfa791ec4906108759084815260200190565b60405180910390a2505050808061088b906114b8565b915050610657565b5050505050565b6000818152600960205260408120548190606090829081908190819061091c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f72657175657374206e6f7420666f756e640000000000000000000000000000006044820152606401610430565b6000888152600960209081526040808320815160e08101835281548152600182015460ff1615158185015260028201805484518187028101870186528181529295939486019383018282801561099157602002820191906000526020600020905b81548152602001906001019080831161097d575b505050505081526020016003820154815260200160048201548152602001600582015481526020016006820154815250509050806000015181602001518260400151836060015184608001518560a001518660c00151975097509750975097509750975050919395979092949650565b610a09610bf1565b610a1281610f52565b50565b600082815260096020526040902054610a8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f72657175657374206e6f7420666f756e640000000000000000000000000000006044820152606401610430565b6000610a94610eb5565b60008481526008602052604081205491925090610ab190836114a1565b90506000610ac282620f4240611464565b9050600554821115610ad45760058290555b600654821015610ae45760068290555b600060025411610af45780610b27565b600254610b02906001611411565b81600254600454610b139190611464565b610b1d9190611411565b610b279190611429565b60045560028054906000610b3a836114da565b90915550506000858152600960209081526040909120600181810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690911790558551610b929260029092019187019061106f565b5060008581526009602052604090819020426004820155600681018590555490517f6c84e12b4c188e61f1b4727024a5cf05c025fa58467e5eedf763c0744c89da7b91610be291889188916113a1565b60405180910390a15050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610c72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610430565b565b6040517f4306d35400000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811691634000aea0917f00000000000000000000000000000000000000000000000000000000000000009190821690634306d3549060240160206040518083038186803b158015610d2f57600080fd5b505afa158015610d43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d679190611172565b6040805163ffffffff808b16602083015261ffff8a169282019290925290871660608201526080016040516020818303038152906040526040518463ffffffff1660e01b8152600401610dbc93929190611309565b602060405180830381600087803b158015610dd657600080fd5b505af1158015610dea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0e9190611137565b507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663fc2a88c36040518163ffffffff1660e01b815260040160206040518083038186803b158015610e7557600080fd5b505afa158015610e89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ead9190611172565b949350505050565b600046610ec181611048565b15610f4b57606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0d57600080fd5b505afa158015610f21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f459190611172565b91505090565b4391505090565b73ffffffffffffffffffffffffffffffffffffffff8116331415610fd2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610430565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600061a4b182148061105c575062066eed82145b80611069575062066eee82145b92915050565b8280548282559060005260206000209081019282156110aa579160200282015b828111156110aa57825182559160200191906001019061108f565b506110b69291506110ba565b5090565b5b808211156110b657600081556001016110bb565b803561ffff811681146110e157600080fd5b919050565b803563ffffffff811681146110e157600080fd5b60006020828403121561110c57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461113057600080fd5b9392505050565b60006020828403121561114957600080fd5b8151801515811461113057600080fd5b60006020828403121561116b57600080fd5b5035919050565b60006020828403121561118457600080fd5b5051919050565b6000806040838503121561119e57600080fd5b8235915060208084013567ffffffffffffffff808211156111be57600080fd5b818601915086601f8301126111d257600080fd5b8135818111156111e4576111e4611542565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561122757611227611542565b604052828152858101935084860182860187018b101561124657600080fd5b600095505b8386101561126957803585526001959095019493860193860161124b565b508096505050505050509250929050565b6000806000806080858703121561129057600080fd5b611299856110e6565b93506112a7602086016110cf565b92506112b5604086016110e6565b91506112c3606086016110cf565b905092959194509250565b600081518084526020808501945080840160005b838110156112fe578151875295820195908201906001016112e2565b509495945050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260006020848184015260606040840152835180606085015260005b818110156113595785810183015185820160800152820161133d565b8181111561136b576000608083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160800195945050505050565b8381526060602082015260006113ba60608301856112ce565b9050826040830152949350505050565b878152861515602082015260e0604082015260006113eb60e08301886112ce565b90508560608301528460808301528360a08301528260c083015298975050505050505050565b6000821982111561142457611424611513565b500190565b60008261145f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561149c5761149c611513565b500290565b6000828210156114b3576114b3611513565b500390565b600061ffff808316818114156114d0576114d0611513565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561150c5761150c611513565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", +} + +var VRFV2WrapperLoadTestConsumerABI = VRFV2WrapperLoadTestConsumerMetaData.ABI + +var VRFV2WrapperLoadTestConsumerBin = VRFV2WrapperLoadTestConsumerMetaData.Bin + +func DeployVRFV2WrapperLoadTestConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, _link common.Address, _vrfV2Wrapper common.Address) (common.Address, *types.Transaction, *VRFV2WrapperLoadTestConsumer, error) { + parsed, err := VRFV2WrapperLoadTestConsumerMetaData.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(VRFV2WrapperLoadTestConsumerBin), backend, _link, _vrfV2Wrapper) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &VRFV2WrapperLoadTestConsumer{address: address, abi: *parsed, VRFV2WrapperLoadTestConsumerCaller: VRFV2WrapperLoadTestConsumerCaller{contract: contract}, VRFV2WrapperLoadTestConsumerTransactor: VRFV2WrapperLoadTestConsumerTransactor{contract: contract}, VRFV2WrapperLoadTestConsumerFilterer: VRFV2WrapperLoadTestConsumerFilterer{contract: contract}}, nil +} + +type VRFV2WrapperLoadTestConsumer struct { + address common.Address + abi abi.ABI + VRFV2WrapperLoadTestConsumerCaller + VRFV2WrapperLoadTestConsumerTransactor + VRFV2WrapperLoadTestConsumerFilterer +} + +type VRFV2WrapperLoadTestConsumerCaller struct { + contract *bind.BoundContract +} + +type VRFV2WrapperLoadTestConsumerTransactor struct { + contract *bind.BoundContract +} + +type VRFV2WrapperLoadTestConsumerFilterer struct { + contract *bind.BoundContract +} + +type VRFV2WrapperLoadTestConsumerSession struct { + Contract *VRFV2WrapperLoadTestConsumer + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type VRFV2WrapperLoadTestConsumerCallerSession struct { + Contract *VRFV2WrapperLoadTestConsumerCaller + CallOpts bind.CallOpts +} + +type VRFV2WrapperLoadTestConsumerTransactorSession struct { + Contract *VRFV2WrapperLoadTestConsumerTransactor + TransactOpts bind.TransactOpts +} + +type VRFV2WrapperLoadTestConsumerRaw struct { + Contract *VRFV2WrapperLoadTestConsumer +} + +type VRFV2WrapperLoadTestConsumerCallerRaw struct { + Contract *VRFV2WrapperLoadTestConsumerCaller +} + +type VRFV2WrapperLoadTestConsumerTransactorRaw struct { + Contract *VRFV2WrapperLoadTestConsumerTransactor +} + +func NewVRFV2WrapperLoadTestConsumer(address common.Address, backend bind.ContractBackend) (*VRFV2WrapperLoadTestConsumer, error) { + abi, err := abi.JSON(strings.NewReader(VRFV2WrapperLoadTestConsumerABI)) + if err != nil { + return nil, err + } + contract, err := bindVRFV2WrapperLoadTestConsumer(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumer{address: address, abi: abi, VRFV2WrapperLoadTestConsumerCaller: VRFV2WrapperLoadTestConsumerCaller{contract: contract}, VRFV2WrapperLoadTestConsumerTransactor: VRFV2WrapperLoadTestConsumerTransactor{contract: contract}, VRFV2WrapperLoadTestConsumerFilterer: VRFV2WrapperLoadTestConsumerFilterer{contract: contract}}, nil +} + +func NewVRFV2WrapperLoadTestConsumerCaller(address common.Address, caller bind.ContractCaller) (*VRFV2WrapperLoadTestConsumerCaller, error) { + contract, err := bindVRFV2WrapperLoadTestConsumer(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerCaller{contract: contract}, nil +} + +func NewVRFV2WrapperLoadTestConsumerTransactor(address common.Address, transactor bind.ContractTransactor) (*VRFV2WrapperLoadTestConsumerTransactor, error) { + contract, err := bindVRFV2WrapperLoadTestConsumer(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerTransactor{contract: contract}, nil +} + +func NewVRFV2WrapperLoadTestConsumerFilterer(address common.Address, filterer bind.ContractFilterer) (*VRFV2WrapperLoadTestConsumerFilterer, error) { + contract, err := bindVRFV2WrapperLoadTestConsumer(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerFilterer{contract: contract}, nil +} + +func bindVRFV2WrapperLoadTestConsumer(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := VRFV2WrapperLoadTestConsumerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFV2WrapperLoadTestConsumer.Contract.VRFV2WrapperLoadTestConsumerCaller.contract.Call(opts, result, method, params...) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.VRFV2WrapperLoadTestConsumerTransactor.contract.Transfer(opts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.VRFV2WrapperLoadTestConsumerTransactor.contract.Transact(opts, method, params...) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFV2WrapperLoadTestConsumer.Contract.contract.Call(opts, result, method, params...) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.contract.Transfer(opts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.contract.Transact(opts, method, params...) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) GetRequestStatus(opts *bind.CallOpts, _requestId *big.Int) (GetRequestStatus, + + error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "getRequestStatus", _requestId) + + outstruct := new(GetRequestStatus) + if err != nil { + return *outstruct, err + } + + outstruct.Paid = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.Fulfilled = *abi.ConvertType(out[1], new(bool)).(*bool) + outstruct.RandomWords = *abi.ConvertType(out[2], new([]*big.Int)).(*[]*big.Int) + outstruct.RequestTimestamp = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.FulfilmentTimestamp = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + outstruct.RequestBlockNumber = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) + outstruct.FulfilmentBlockNumber = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) GetRequestStatus(_requestId *big.Int) (GetRequestStatus, + + error) { + return _VRFV2WrapperLoadTestConsumer.Contract.GetRequestStatus(&_VRFV2WrapperLoadTestConsumer.CallOpts, _requestId) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) GetRequestStatus(_requestId *big.Int) (GetRequestStatus, + + error) { + return _VRFV2WrapperLoadTestConsumer.Contract.GetRequestStatus(&_VRFV2WrapperLoadTestConsumer.CallOpts, _requestId) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) IVrfV2Wrapper(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "i_vrfV2Wrapper") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) IVrfV2Wrapper() (common.Address, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.IVrfV2Wrapper(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) IVrfV2Wrapper() (common.Address, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.IVrfV2Wrapper(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) Owner() (common.Address, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.Owner(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) Owner() (common.Address, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.Owner(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SAverageFulfillmentInMillions(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_averageFulfillmentInMillions") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SAverageFulfillmentInMillions() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SAverageFulfillmentInMillions(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SAverageFulfillmentInMillions() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SAverageFulfillmentInMillions(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SFastestFulfillment(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_fastestFulfillment") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SFastestFulfillment() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SFastestFulfillment(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SFastestFulfillment() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SFastestFulfillment(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SLastRequestId(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_lastRequestId") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SLastRequestId() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SLastRequestId(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SLastRequestId() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SLastRequestId(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SRequestCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_requestCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SRequestCount() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SRequestCount(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SRequestCount() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SRequestCount(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SRequests(opts *bind.CallOpts, arg0 *big.Int) (SRequests, + + error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_requests", arg0) + + outstruct := new(SRequests) + if err != nil { + return *outstruct, err + } + + outstruct.Paid = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.Fulfilled = *abi.ConvertType(out[1], new(bool)).(*bool) + outstruct.RequestTimestamp = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.FulfilmentTimestamp = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.RequestBlockNumber = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + outstruct.FulfilmentBlockNumber = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SRequests(arg0 *big.Int) (SRequests, + + error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SRequests(&_VRFV2WrapperLoadTestConsumer.CallOpts, arg0) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SRequests(arg0 *big.Int) (SRequests, + + error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SRequests(&_VRFV2WrapperLoadTestConsumer.CallOpts, arg0) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SResponseCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_responseCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SResponseCount() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SResponseCount(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SResponseCount() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SResponseCount(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCaller) SSlowestFulfillment(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFV2WrapperLoadTestConsumer.contract.Call(opts, &out, "s_slowestFulfillment") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) SSlowestFulfillment() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SSlowestFulfillment(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerCallerSession) SSlowestFulfillment() (*big.Int, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.SSlowestFulfillment(&_VRFV2WrapperLoadTestConsumer.CallOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.Transact(opts, "acceptOwnership") +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) AcceptOwnership() (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.AcceptOwnership(&_VRFV2WrapperLoadTestConsumer.TransactOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.AcceptOwnership(&_VRFV2WrapperLoadTestConsumer.TransactOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) MakeRequests(opts *bind.TransactOpts, _callbackGasLimit uint32, _requestConfirmations uint16, _numWords uint32, _requestCount uint16) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.Transact(opts, "makeRequests", _callbackGasLimit, _requestConfirmations, _numWords, _requestCount) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) MakeRequests(_callbackGasLimit uint32, _requestConfirmations uint16, _numWords uint32, _requestCount uint16) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.MakeRequests(&_VRFV2WrapperLoadTestConsumer.TransactOpts, _callbackGasLimit, _requestConfirmations, _numWords, _requestCount) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) MakeRequests(_callbackGasLimit uint32, _requestConfirmations uint16, _numWords uint32, _requestCount uint16) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.MakeRequests(&_VRFV2WrapperLoadTestConsumer.TransactOpts, _callbackGasLimit, _requestConfirmations, _numWords, _requestCount) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) RawFulfillRandomWords(opts *bind.TransactOpts, _requestId *big.Int, _randomWords []*big.Int) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.Transact(opts, "rawFulfillRandomWords", _requestId, _randomWords) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) RawFulfillRandomWords(_requestId *big.Int, _randomWords []*big.Int) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.RawFulfillRandomWords(&_VRFV2WrapperLoadTestConsumer.TransactOpts, _requestId, _randomWords) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) RawFulfillRandomWords(_requestId *big.Int, _randomWords []*big.Int) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.RawFulfillRandomWords(&_VRFV2WrapperLoadTestConsumer.TransactOpts, _requestId, _randomWords) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) Reset(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.Transact(opts, "reset") +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) Reset() (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.Reset(&_VRFV2WrapperLoadTestConsumer.TransactOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) Reset() (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.Reset(&_VRFV2WrapperLoadTestConsumer.TransactOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.Transact(opts, "transferOwnership", to) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.TransferOwnership(&_VRFV2WrapperLoadTestConsumer.TransactOpts, to) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.TransferOwnership(&_VRFV2WrapperLoadTestConsumer.TransactOpts, to) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) WithdrawLink(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.Transact(opts, "withdrawLink", amount) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) WithdrawLink(amount *big.Int) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.WithdrawLink(&_VRFV2WrapperLoadTestConsumer.TransactOpts, amount) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) WithdrawLink(amount *big.Int) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.WithdrawLink(&_VRFV2WrapperLoadTestConsumer.TransactOpts, amount) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.contract.RawTransact(opts, nil) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerSession) Receive() (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.Receive(&_VRFV2WrapperLoadTestConsumer.TransactOpts) +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerTransactorSession) Receive() (*types.Transaction, error) { + return _VRFV2WrapperLoadTestConsumer.Contract.Receive(&_VRFV2WrapperLoadTestConsumer.TransactOpts) +} + +type VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator struct { + Event *VRFV2WrapperLoadTestConsumerOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFV2WrapperLoadTestConsumerOwnershipTransferRequested) + 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(VRFV2WrapperLoadTestConsumerOwnershipTransferRequested) + 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 *VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFV2WrapperLoadTestConsumerOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator{contract: _VRFV2WrapperLoadTestConsumer.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFV2WrapperLoadTestConsumerOwnershipTransferRequested) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) ParseOwnershipTransferRequested(log types.Log) (*VRFV2WrapperLoadTestConsumerOwnershipTransferRequested, error) { + event := new(VRFV2WrapperLoadTestConsumerOwnershipTransferRequested) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator struct { + Event *VRFV2WrapperLoadTestConsumerOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFV2WrapperLoadTestConsumerOwnershipTransferred) + 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(VRFV2WrapperLoadTestConsumerOwnershipTransferred) + 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 *VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFV2WrapperLoadTestConsumerOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator{contract: _VRFV2WrapperLoadTestConsumer.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFV2WrapperLoadTestConsumerOwnershipTransferred) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "OwnershipTransferred", 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 (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) ParseOwnershipTransferred(log types.Log) (*VRFV2WrapperLoadTestConsumerOwnershipTransferred, error) { + event := new(VRFV2WrapperLoadTestConsumerOwnershipTransferred) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator struct { + Event *VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) + 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(VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) + 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 *VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator) Error() error { + return it.fail +} + +func (it *VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled struct { + RequestId *big.Int + RandomWords []*big.Int + Payment *big.Int + Raw types.Log +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) FilterWrappedRequestFulfilled(opts *bind.FilterOpts) (*VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator, error) { + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.FilterLogs(opts, "WrappedRequestFulfilled") + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator{contract: _VRFV2WrapperLoadTestConsumer.contract, event: "WrappedRequestFulfilled", logs: logs, sub: sub}, nil +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) WatchWrappedRequestFulfilled(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) (event.Subscription, error) { + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.WatchLogs(opts, "WrappedRequestFulfilled") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "WrappedRequestFulfilled", 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 (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) ParseWrappedRequestFulfilled(log types.Log) (*VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled, error) { + event := new(VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "WrappedRequestFulfilled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator struct { + Event *VRFV2WrapperLoadTestConsumerWrapperRequestMade + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFV2WrapperLoadTestConsumerWrapperRequestMade) + 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(VRFV2WrapperLoadTestConsumerWrapperRequestMade) + 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 *VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator) Error() error { + return it.fail +} + +func (it *VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFV2WrapperLoadTestConsumerWrapperRequestMade struct { + RequestId *big.Int + Paid *big.Int + Raw types.Log +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) FilterWrapperRequestMade(opts *bind.FilterOpts, requestId []*big.Int) (*VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.FilterLogs(opts, "WrapperRequestMade", requestIdRule) + if err != nil { + return nil, err + } + return &VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator{contract: _VRFV2WrapperLoadTestConsumer.contract, event: "WrapperRequestMade", logs: logs, sub: sub}, nil +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) WatchWrapperRequestMade(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerWrapperRequestMade, requestId []*big.Int) (event.Subscription, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _VRFV2WrapperLoadTestConsumer.contract.WatchLogs(opts, "WrapperRequestMade", requestIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFV2WrapperLoadTestConsumerWrapperRequestMade) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "WrapperRequestMade", 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 (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumerFilterer) ParseWrapperRequestMade(log types.Log) (*VRFV2WrapperLoadTestConsumerWrapperRequestMade, error) { + event := new(VRFV2WrapperLoadTestConsumerWrapperRequestMade) + if err := _VRFV2WrapperLoadTestConsumer.contract.UnpackLog(event, "WrapperRequestMade", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetRequestStatus struct { + Paid *big.Int + Fulfilled bool + RandomWords []*big.Int + RequestTimestamp *big.Int + FulfilmentTimestamp *big.Int + RequestBlockNumber *big.Int + FulfilmentBlockNumber *big.Int +} +type SRequests struct { + Paid *big.Int + Fulfilled bool + RequestTimestamp *big.Int + FulfilmentTimestamp *big.Int + RequestBlockNumber *big.Int + FulfilmentBlockNumber *big.Int +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumer) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _VRFV2WrapperLoadTestConsumer.abi.Events["OwnershipTransferRequested"].ID: + return _VRFV2WrapperLoadTestConsumer.ParseOwnershipTransferRequested(log) + case _VRFV2WrapperLoadTestConsumer.abi.Events["OwnershipTransferred"].ID: + return _VRFV2WrapperLoadTestConsumer.ParseOwnershipTransferred(log) + case _VRFV2WrapperLoadTestConsumer.abi.Events["WrappedRequestFulfilled"].ID: + return _VRFV2WrapperLoadTestConsumer.ParseWrappedRequestFulfilled(log) + case _VRFV2WrapperLoadTestConsumer.abi.Events["WrapperRequestMade"].ID: + return _VRFV2WrapperLoadTestConsumer.ParseWrapperRequestMade(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (VRFV2WrapperLoadTestConsumerOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (VRFV2WrapperLoadTestConsumerOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) Topic() common.Hash { + return common.HexToHash("0x6c84e12b4c188e61f1b4727024a5cf05c025fa58467e5eedf763c0744c89da7b") +} + +func (VRFV2WrapperLoadTestConsumerWrapperRequestMade) Topic() common.Hash { + return common.HexToHash("0x5f56b4c20db9f5b294cbf6f681368de4a992a27e2de2ee702dcf2cbbfa791ec4") +} + +func (_VRFV2WrapperLoadTestConsumer *VRFV2WrapperLoadTestConsumer) Address() common.Address { + return _VRFV2WrapperLoadTestConsumer.address +} + +type VRFV2WrapperLoadTestConsumerInterface interface { + GetRequestStatus(opts *bind.CallOpts, _requestId *big.Int) (GetRequestStatus, + + error) + + IVrfV2Wrapper(opts *bind.CallOpts) (common.Address, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + SAverageFulfillmentInMillions(opts *bind.CallOpts) (*big.Int, error) + + SFastestFulfillment(opts *bind.CallOpts) (*big.Int, error) + + SLastRequestId(opts *bind.CallOpts) (*big.Int, error) + + SRequestCount(opts *bind.CallOpts) (*big.Int, error) + + SRequests(opts *bind.CallOpts, arg0 *big.Int) (SRequests, + + error) + + SResponseCount(opts *bind.CallOpts) (*big.Int, error) + + SSlowestFulfillment(opts *bind.CallOpts) (*big.Int, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + MakeRequests(opts *bind.TransactOpts, _callbackGasLimit uint32, _requestConfirmations uint16, _numWords uint32, _requestCount uint16) (*types.Transaction, error) + + RawFulfillRandomWords(opts *bind.TransactOpts, _requestId *big.Int, _randomWords []*big.Int) (*types.Transaction, error) + + Reset(opts *bind.TransactOpts) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + WithdrawLink(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) + + Receive(opts *bind.TransactOpts) (*types.Transaction, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2WrapperLoadTestConsumerOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*VRFV2WrapperLoadTestConsumerOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2WrapperLoadTestConsumerOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*VRFV2WrapperLoadTestConsumerOwnershipTransferred, error) + + FilterWrappedRequestFulfilled(opts *bind.FilterOpts) (*VRFV2WrapperLoadTestConsumerWrappedRequestFulfilledIterator, error) + + WatchWrappedRequestFulfilled(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled) (event.Subscription, error) + + ParseWrappedRequestFulfilled(log types.Log) (*VRFV2WrapperLoadTestConsumerWrappedRequestFulfilled, error) + + FilterWrapperRequestMade(opts *bind.FilterOpts, requestId []*big.Int) (*VRFV2WrapperLoadTestConsumerWrapperRequestMadeIterator, error) + + WatchWrapperRequestMade(opts *bind.WatchOpts, sink chan<- *VRFV2WrapperLoadTestConsumerWrapperRequestMade, requestId []*big.Int) (event.Subscription, error) + + ParseWrapperRequestMade(log types.Log) (*VRFV2WrapperLoadTestConsumerWrapperRequestMade, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index dcc0a4dea37..0dd8e4cbb92 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -100,6 +100,7 @@ vrfv2_transparent_upgradeable_proxy: ../../contracts/solc/v0.8.6/VRFV2Transparen vrfv2_wrapper: ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin d5e9a982325d2d4f517c4f2bc818795f61555408ef4b38fb59b923d144970e38 vrfv2_wrapper_consumer_example: ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.bin 3c5c9f1c501e697a7e77e959b48767e2a0bb1372393fd7686f7aaef3eb794231 vrfv2_wrapper_interface: ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.bin ff8560169de171a68b360b7438d13863682d07040d984fd0fb096b2379421003 +vrfv2_wrapper_load_test_consumer: ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.bin 664ca7fdf4dd65cc183bc25f20708c4b369c3401bba3ee12797a93bcd70138b6 vrfv2plus_client: ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.abi ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.bin 3ffbfa4971a7e5f46051a26b1722613f265d89ea1867547ecec58500953a9501 vrfv2plus_consumer_example: ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.bin 2c480a6d7955d33a00690fdd943486d95802e48a03f3cc243df314448e4ddb2c vrfv2plus_malicious_migrator: ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.abi ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.bin 80dbc98be5e42246960c889d29488f978d3db0127e95e9b295352c481d8c9b07 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 07e4fa9b8f3..f5aa4451bb4 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -108,6 +108,7 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin VRFV2Wrapper vrfv2_wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.bin VRFV2WrapperInterface vrfv2_wrapper_interface //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.bin VRFV2WrapperConsumerExample vrfv2_wrapper_consumer_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.bin VRFV2WrapperLoadTestConsumer vrfv2_wrapper_load_test_consumer // Keepers X VRF v2 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin KeepersVRFConsumer keepers_vrf_consumer diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_models.go b/integration-tests/actions/vrfv2_actions/vrfv2_models.go index 0576d6f7d6e..aa594a753dd 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_models.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_models.go @@ -23,6 +23,11 @@ type VRFV2Contracts struct { LoadTestConsumers []contracts.VRFv2LoadTestConsumer } +type VRFV2WrapperContracts struct { + VRFV2Wrapper contracts.VRFV2Wrapper + LoadTestConsumers []contracts.VRFv2WrapperLoadTestConsumer +} + // VRFV2PlusKeyData defines a jobs into and proving key info type VRFV2KeyData struct { VRFKey *client.VRFKey diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index 17c3948b340..d6288e58bf6 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -55,6 +55,7 @@ var ( ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event" ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event" + ErrDeployWrapper = "error deploying VRFV2PlusWrapper" ) func DeployVRFV2Contracts( @@ -103,6 +104,46 @@ func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinat return consumers, nil } +func DeployVRFV2WrapperConsumers(contractDeployer contracts.ContractDeployer, linkTokenAddress string, vrfV2Wrapper contracts.VRFV2Wrapper, consumerContractsAmount int) ([]contracts.VRFv2WrapperLoadTestConsumer, error) { + var consumers []contracts.VRFv2WrapperLoadTestConsumer + for i := 1; i <= consumerContractsAmount; i++ { + loadTestConsumer, err := contractDeployer.DeployVRFV2WrapperLoadTestConsumer(linkTokenAddress, vrfV2Wrapper.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) + } + consumers = append(consumers, loadTestConsumer) + } + return consumers, nil +} + +func DeployVRFV2DirectFundingContracts( + contractDeployer contracts.ContractDeployer, + chainClient blockchain.EVMClient, + linkTokenAddress string, + linkEthFeedAddress string, + coordinator contracts.VRFCoordinatorV2, + consumerContractsAmount int, +) (*VRFV2WrapperContracts, error) { + vrfv2Wrapper, err := contractDeployer.DeployVRFV2Wrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + consumers, err := DeployVRFV2WrapperConsumers(contractDeployer, linkTokenAddress, vrfv2Wrapper, consumerContractsAmount) + if err != nil { + return nil, err + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return &VRFV2WrapperContracts{vrfv2Wrapper, consumers}, nil +} + func CreateVRFV2Job( chainlinkNode *client.ChainlinkClient, coordinatorAddress string, @@ -213,7 +254,7 @@ func SetupVRFV2Environment( l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") err = vrfv2Contracts.Coordinator.SetConfig( vrfv2Config.MinimumConfirmations, - vrfv2Config.CallbackGasLimit, + vrfv2Config.MaxGasLimitCoordinatorConfig, vrfv2Config.StalenessSeconds, vrfv2Config.GasAfterPaymentCalculation, big.NewInt(vrfv2Config.LinkNativeFeedResponse), @@ -308,6 +349,80 @@ func SetupVRFV2Environment( return vrfv2Contracts, subIDs, &data, nil } +func SetupVRFV2WrapperEnvironment( + env *test_env.CLClusterTestEnv, + vrfv2Config vrfv2_config.VRFV2Config, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + coordinator contracts.VRFCoordinatorV2, + keyHash [32]byte, + wrapperConsumerContractsAmount int, +) (*VRFV2WrapperContracts, *uint64, error) { + // Deploy VRF v2 direct funding contracts + wrapperContracts, err := DeployVRFV2DirectFundingContracts( + env.ContractDeployer, + env.EVMClient, + linkToken.Address(), + mockNativeLINKFeed.Address(), + coordinator, + wrapperConsumerContractsAmount, + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + // Configure VRF v2 wrapper contract + err = wrapperContracts.VRFV2Wrapper.SetConfig( + vrfv2Config.WrapperGasOverhead, + vrfv2Config.CoordinatorGasOverhead, + vrfv2Config.WrapperPremiumPercentage, + keyHash, + vrfv2Config.WrapperMaxNumberOfWords, + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + // Fetch wrapper subscription ID + wrapperSubID, err := wrapperContracts.VRFV2Wrapper.GetSubID(context.Background()) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + // Fund wrapper subscription + err = FundSubscriptions(env, vrfv2Config, linkToken, coordinator, []uint64{wrapperSubID}) + if err != nil { + return nil, nil, err + } + + // Fund consumer with LINK + err = linkToken.Transfer( + wrapperContracts.LoadTestConsumers[0].Address(), + big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(vrfv2Config.WrapperConsumerFundingAmountLink)), + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + return wrapperContracts, &wrapperSubID, nil +} + func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { var newNativeTokenKeyAddresses []string for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { @@ -458,6 +573,41 @@ func FundSubscriptions( return nil } +func DirectFundingRequestRandomnessAndWaitForFulfillment( + consumer contracts.VRFv2WrapperLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2, + vrfv2Data *VRFV2Data, + subID uint64, + randomnessRequestCountPerRequest uint16, + vrfv2Config vrfv2_config.VRFV2Config, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { + logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l) + _, err := consumer.RequestRandomness( + vrfv2Config.MinimumConfirmations, + vrfv2Config.CallbackGasLimit, + vrfv2Config.NumberOfWords, + randomnessRequestCountPerRequest, + ) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + } + wrapperAddress, err := consumer.GetWrapper(context.Background()) + if err != nil { + return nil, fmt.Errorf("error getting wrapper address, err: %w", err) + } + fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( + wrapperAddress.String(), + coordinator, + vrfv2Data, + subID, + randomWordsFulfilledEventTimeout, + l, + ) + return fulfillmentEvents, err +} + func RequestRandomnessAndWaitForFulfillment( consumer contracts.VRFv2LoadTestConsumer, coordinator contracts.VRFCoordinatorV2, diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index ef1eb34e4ea..40694aa5e4e 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -116,12 +116,14 @@ type ContractDeployer interface { DeployVRFConsumerV2(linkAddr string, coordinatorAddr string) (VRFConsumerV2, error) DeployVRFv2Consumer(coordinatorAddr string) (VRFv2Consumer, error) DeployVRFv2LoadTestConsumer(coordinatorAddr string) (VRFv2LoadTestConsumer, error) + DeployVRFV2WrapperLoadTestConsumer(linkAddr string, vrfV2WrapperAddr string) (VRFv2WrapperLoadTestConsumer, error) DeployVRFv2PlusLoadTestConsumer(coordinatorAddr string) (VRFv2PlusLoadTestConsumer, error) DeployVRFV2PlusWrapperLoadTestConsumer(linkAddr string, vrfV2PlusWrapperAddr string) (VRFv2PlusWrapperLoadTestConsumer, error) DeployVRFCoordinator(linkAddr string, bhsAddr string) (VRFCoordinator, error) DeployVRFCoordinatorV2(linkAddr string, bhsAddr string, linkEthFeedAddr string) (VRFCoordinatorV2, error) DeployVRFCoordinatorV2_5(bhsAddr string) (VRFCoordinatorV2_5, error) DeployVRFCoordinatorV2PlusUpgradedVersion(bhsAddr string) (VRFCoordinatorV2PlusUpgradedVersion, error) + DeployVRFV2Wrapper(linkAddr string, linkEthFeedAddr string, coordinatorAddr string) (VRFV2Wrapper, error) DeployVRFV2PlusWrapper(linkAddr string, linkEthFeedAddr string, coordinatorAddr string) (VRFV2PlusWrapper, error) DeployDKG() (DKG, error) DeployOCR2VRFCoordinator(beaconPeriodBlocksCount *big.Int, linkAddr string) (VRFCoordinatorV3, error) diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 898abb521b3..44bc3ec2a2c 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2_consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper_load_test_consumer" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/dkg" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/vrf_beacon" @@ -36,6 +37,7 @@ type VRFCoordinator interface { } type VRFCoordinatorV2 interface { + GetRequestConfig(ctx context.Context) (GetRequestConfig, error) SetConfig( minimumRequestConfirmations uint16, maxGasLimit uint32, @@ -136,6 +138,12 @@ type VRFCoordinatorV2PlusUpgradedVersion interface { WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []*big.Int, sender []common.Address, timeout time.Duration) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsRequested, error) } +type VRFV2Wrapper interface { + Address() string + SetConfig(wrapperGasOverhead uint32, coordinatorGasOverhead uint32, wrapperPremiumPercentage uint8, keyHash [32]byte, maxNumWords uint8) error + GetSubID(ctx context.Context) (uint64, error) +} + type VRFV2PlusWrapper interface { Address() string SetConfig(wrapperGasOverhead uint32, coordinatorGasOverhead uint32, wrapperPremiumPercentage uint8, keyHash [32]byte, maxNumWords uint8, stalenessSeconds uint32, fallbackWeiPerUnitLink *big.Int, fulfillmentFlatFeeLinkPPM uint32, fulfillmentFlatFeeNativePPM uint32) error @@ -178,6 +186,16 @@ type VRFv2LoadTestConsumer interface { ResetMetrics() error } +type VRFv2WrapperLoadTestConsumer interface { + Address() string + Fund(ethAmount *big.Float) error + RequestRandomness(requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, requestCount uint16) (*types.Transaction, error) + GetRequestStatus(ctx context.Context, requestID *big.Int) (vrfv2_wrapper_load_test_consumer.GetRequestStatus, error) + GetLastRequestId(ctx context.Context) (*big.Int, error) + GetWrapper(ctx context.Context) (common.Address, error) + GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) +} + type VRFv2PlusLoadTestConsumer interface { Address() string RequestRandomness(keyHash [32]byte, subID *big.Int, requestConfirmations uint16, callbackGasLimit uint32, nativePayment bool, numWords uint32, requestCount uint16) (*types.Transaction, error) diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index 5d22167158a..579174e07c6 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -18,6 +18,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_consumer_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_load_test_with_metrics" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper_load_test_consumer" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2_consumer_wrapper" ) @@ -50,6 +52,24 @@ type EthereumVRFv2LoadTestConsumer struct { consumer *vrf_load_test_with_metrics.VRFV2LoadTestWithMetrics } +type EthereumVRFV2Wrapper struct { + address *common.Address + client blockchain.EVMClient + wrapper *vrfv2_wrapper.VRFV2Wrapper +} + +type EthereumVRFV2WrapperLoadTestConsumer struct { + address *common.Address + client blockchain.EVMClient + consumer *vrfv2_wrapper_load_test_consumer.VRFV2WrapperLoadTestConsumer +} + +type GetRequestConfig struct { + MinimumRequestConfirmations uint16 + MaxGasLimit uint32 + ProvingKeyHashes [32]byte +} + // DeployVRFCoordinatorV2 deploys VRFV2 coordinator contract func (e *EthereumContractDeployer) DeployVRFCoordinatorV2(linkAddr string, bhsAddr string, linkEthFeedAddr string) (VRFCoordinatorV2, error) { address, _, instance, err := e.client.DeployContract("VRFCoordinatorV2", func( @@ -121,6 +141,40 @@ func (e *EthereumContractDeployer) DeployVRFv2LoadTestConsumer(coordinatorAddr s }, err } +func (e *EthereumContractDeployer) DeployVRFV2Wrapper(linkAddr string, linkEthFeedAddr string, coordinatorAddr string) (VRFV2Wrapper, error) { + address, _, instance, err := e.client.DeployContract("VRFV2Wrapper", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return vrfv2_wrapper.DeployVRFV2Wrapper(auth, backend, common.HexToAddress(linkAddr), common.HexToAddress(linkEthFeedAddr), common.HexToAddress(coordinatorAddr)) + }) + if err != nil { + return nil, err + } + return &EthereumVRFV2Wrapper{ + address: address, + client: e.client, + wrapper: instance.(*vrfv2_wrapper.VRFV2Wrapper), + }, err +} + +func (e *EthereumContractDeployer) DeployVRFV2WrapperLoadTestConsumer(linkAddr string, vrfV2WrapperAddr string) (VRFv2WrapperLoadTestConsumer, error) { + address, _, instance, err := e.client.DeployContract("VRFV2WrapperLoadTestConsumer", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return vrfv2_wrapper_load_test_consumer.DeployVRFV2WrapperLoadTestConsumer(auth, backend, common.HexToAddress(linkAddr), common.HexToAddress(vrfV2WrapperAddr)) + }) + if err != nil { + return nil, err + } + return &EthereumVRFV2WrapperLoadTestConsumer{ + address: address, + client: e.client, + consumer: instance.(*vrfv2_wrapper_load_test_consumer.VRFV2WrapperLoadTestConsumer), + }, err +} + func (v *EthereumVRFCoordinatorV2) Address() string { return v.address.Hex() } @@ -149,6 +203,24 @@ func (v *EthereumVRFCoordinatorV2) GetSubscription(ctx context.Context, subID ui return subscription, nil } +func (v *EthereumVRFCoordinatorV2) GetRequestConfig(ctx context.Context) (GetRequestConfig, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + minConfirmations, maxGas, keyHashes, err := v.coordinator.GetRequestConfig(opts) + if err != nil { + return GetRequestConfig{}, err + } + requestConfig := GetRequestConfig{ + MinimumRequestConfirmations: minConfirmations, + MaxGasLimit: maxGas, + ProvingKeyHashes: keyHashes[0], + } + + return requestConfig, nil +} + func (v *EthereumVRFCoordinatorV2) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig vrf_coordinator_v2.VRFCoordinatorV2FeeConfig) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { @@ -599,3 +671,126 @@ func (v *EthereumVRFv2LoadTestConsumer) GetLoadTestMetrics(ctx context.Context) fastestFulfillment, }, nil } + +func (v *EthereumVRFV2Wrapper) Address() string { + return v.address.Hex() +} + +func (v *EthereumVRFV2Wrapper) SetConfig(wrapperGasOverhead uint32, coordinatorGasOverhead uint32, wrapperPremiumPercentage uint8, keyHash [32]byte, maxNumWords uint8) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.wrapper.SetConfig( + opts, + wrapperGasOverhead, + coordinatorGasOverhead, + wrapperPremiumPercentage, + keyHash, + maxNumWords, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFV2Wrapper) GetSubID(ctx context.Context) (uint64, error) { + return v.wrapper.SUBSCRIPTIONID(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) Address() string { + return v.address.Hex() +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) Fund(ethAmount *big.Float) error { + gasEstimates, err := v.client.EstimateGas(ethereum.CallMsg{}) + if err != nil { + return err + } + return v.client.Fund(v.address.Hex(), ethAmount, gasEstimates) +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) RequestRandomness(requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, requestCount uint16) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.consumer.MakeRequests(opts, callbackGasLimit, requestConfirmations, numWords, requestCount) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrfv2_wrapper_load_test_consumer.GetRequestStatus, error) { + return v.consumer.GetRequestStatus(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }, requestID) +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) GetLastRequestId(ctx context.Context) (*big.Int, error) { + return v.consumer.SLastRequestId(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) GetWrapper(ctx context.Context) (common.Address, error) { + return v.consumer.IVrfV2Wrapper(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) +} + +func (v *EthereumVRFV2WrapperLoadTestConsumer) GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) { + requestCount, err := v.consumer.SRequestCount(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + if err != nil { + return nil, err + } + fulfilmentCount, err := v.consumer.SResponseCount(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + + if err != nil { + return nil, err + } + averageFulfillmentInMillions, err := v.consumer.SAverageFulfillmentInMillions(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + if err != nil { + return nil, err + } + slowestFulfillment, err := v.consumer.SSlowestFulfillment(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + + if err != nil { + return nil, err + } + fastestFulfillment, err := v.consumer.SFastestFulfillment(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + if err != nil { + return nil, err + } + + return &VRFLoadTestMetrics{ + requestCount, + fulfilmentCount, + averageFulfillmentInMillions, + slowestFulfillment, + fastestFulfillment, + }, nil +} diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 6f69b579a4a..8958480d51e 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -12,6 +12,8 @@ import ( "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" @@ -115,6 +117,79 @@ func TestVRFv2Basic(t *testing.T) { } }) + t.Run("Direct Funding (VRFV2Wrapper)", func(t *testing.T) { + testConfig := vrfv2Config + wrapperContracts, wrapperSubID, err := vrfv2_actions.SetupVRFV2WrapperEnvironment( + env, + testConfig, + linkToken, + mockETHLinkFeed, + vrfv2Contracts.Coordinator, + vrfv2Data.KeyHash, + 1, + ) + require.NoError(t, err) + wrapperConsumer := wrapperContracts.LoadTestConsumers[0] + + wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) + require.NoError(t, err, "Error getting wrapper consumer balance") + + wrapperSubscription, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), *wrapperSubID) + require.NoError(t, err, "Error getting subscription information") + subBalanceBeforeRequest := wrapperSubscription.Balance + + // Request Randomness and wait for fulfillment event + randomWordsFulfilledEvent, err := vrfv2_actions.DirectFundingRequestRandomnessAndWaitForFulfillment( + wrapperConsumer, + vrfv2Contracts.Coordinator, + vrfv2Data, + *wrapperSubID, + vrfv2Config.RandomnessRequestCountPerRequest, + vrfv2Config, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err, "Error requesting randomness and waiting for fulfilment") + + // Check wrapper subscription balance + expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) + wrapperSubscription, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), *wrapperSubID) + require.NoError(t, err, "Error getting subscription information") + subBalanceAfterRequest := wrapperSubscription.Balance + require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) + + // Check status of randomness request within the wrapper consumer contract + consumerStatus, err := wrapperConsumer.GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "Error getting randomness request status") + require.True(t, consumerStatus.Fulfilled) + + // Check wrapper consumer LINK balance + expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) + wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) + require.NoError(t, err, "Error getting wrapper consumer balance") + require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) + + // Check random word count + require.Equal(t, testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) + for _, w := range consumerStatus.RandomWords { + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") + } + + l.Info(). + Str("Consumer Balance Before Request (Link)", (*commonassets.Link)(wrapperConsumerJuelsBalanceBeforeRequest).Link()). + Str("Consumer Balance After Request (Link)", (*commonassets.Link)(wrapperConsumerJuelsBalanceAfterRequest).Link()). + Bool("Fulfilment Status", consumerStatus.Fulfilled). + Str("Paid by Consumer Contract (Link)", (*commonassets.Link)(consumerStatus.Paid).Link()). + Str("Paid by Coordinator Sub (Link)", (*commonassets.Link)(randomWordsFulfilledEvent.Payment).Link()). + Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). + Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). + Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). + Str("FulfilmentBlockNumber", consumerStatus.FulfilmentBlockNumber.String()). + Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). + Msg("Random Words Fulfilment Details For Link Billing") + }) + t.Run("Oracle Withdraw", func(t *testing.T) { testConfig := vrfv2Config subIDsForOracleWithDraw, err := vrfv2_actions.CreateFundSubsAndAddConsumers( @@ -167,6 +242,7 @@ func TestVRFv2Basic(t *testing.T) { "LINK funds were not returned after oracle withdraw", ) }) + t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { testConfig := vrfv2Config subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( From e595f5b01275cf21fdedf205e4328d976ee00470 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 12 Jan 2024 07:27:59 -0600 Subject: [PATCH 152/234] golangci-lint: add unconvert (#11757) * golangci-lint: add unconvert * unconvert --- .golangci.yml | 1 + common/txmgr/txmgr.go | 2 +- core/chains/evm/client/node_lifecycle_test.go | 2 +- core/chains/evm/config/config_test.go | 2 +- core/chains/evm/config/toml/config.go | 2 +- .../evm/forwarders/forwarder_manager.go | 2 +- core/chains/evm/log/broadcaster.go | 2 +- core/chains/evm/log/eth_subscriber.go | 2 +- core/chains/evm/logpoller/log_poller.go | 2 +- core/chains/evm/txmgr/attempts.go | 2 +- core/chains/evm/txmgr/attempts_test.go | 6 +-- core/chains/evm/txmgr/broadcaster_test.go | 18 +++---- core/chains/evm/txmgr/confirmer_test.go | 4 +- core/chains/evm/txmgr/evm_tx_store.go | 2 +- core/chains/evm/types/models.go | 2 +- core/chains/evm/types/nonce.go | 2 +- core/internal/features/features_test.go | 12 ++--- core/null/int64.go | 10 ++-- core/null/uint32.go | 2 +- core/scripts/ocr2vrf/main.go | 6 +-- core/scripts/vrfv2/testnet/main.go | 2 +- core/services/blockhashstore/common.go | 4 +- .../blockheaderfeeder/block_header_feeder.go | 2 +- core/services/feeds/orm_test.go | 54 +++++++++---------- .../fluxmonitorv2/integrations_test.go | 10 ++-- core/services/functions/listener_test.go | 16 +++--- core/services/gateway/connector/connector.go | 2 +- core/services/gateway/gateway_test.go | 2 +- core/services/job/models.go | 2 +- core/services/job/orm.go | 2 +- .../keystore/keys/ocr2key/offchain_keyring.go | 2 +- core/services/nurse_test.go | 4 +- .../ocr2/plugins/dkg/persistence/db.go | 2 +- .../ocr2/plugins/functions/aggregation.go | 2 +- .../ocr2/plugins/mercury/integration_test.go | 8 +-- .../evmregistry/v21/logprovider/buffer.go | 4 +- .../evmregistry/v21/mercury/v02/request.go | 2 +- .../evmregistry/v21/transmit/cache.go | 4 +- .../ocr2vrf/coordinator/coordinator.go | 4 +- .../ocr2vrf/coordinator/coordinator_test.go | 26 ++++----- .../ocr2vrf/coordinator/ocr_cache_test.go | 8 +-- .../ocr2/plugins/promwrapper/plugin.go | 12 ++--- core/services/ocr2/plugins/s4/plugin.go | 6 +-- core/services/ocr2/plugins/s4/plugin_test.go | 2 +- core/services/pipeline/models.go | 2 +- core/services/pipeline/scheduler.go | 2 +- core/services/pipeline/task.bridge.go | 4 +- core/services/pipeline/task_params.go | 4 +- .../relay/evm/mercury/config_digest_test.go | 4 +- core/services/s4/storage_test.go | 2 +- core/services/signatures/secp256k1/field.go | 2 +- core/services/streams/delegate.go | 2 +- core/services/vrf/delegate_test.go | 2 +- .../vrf_coordinator_interface.go | 2 +- core/web/chains_controller.go | 2 +- core/web/presenters/user.go | 2 +- core/web/resolver/spec.go | 4 +- core/web/resolver/spec_test.go | 10 ++-- 58 files changed, 155 insertions(+), 154 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 00541302f08..3672692f599 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,6 +10,7 @@ linters: - misspell - rowserrcheck - errorlint + - unconvert - sqlclosecheck - noctx linters-settings: diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 36a6a1304ae..25c3b9beb35 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -240,7 +240,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Reset(addr // - marks all pending and inflight transactions fatally errored (note: at this point all transactions are either confirmed or fatally errored) // this must not be run while Broadcaster or Confirmer are running func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) abandon(addr ADDR) (err error) { - ctx, cancel := services.StopChan(b.chStop).NewCtx() + ctx, cancel := b.chStop.NewCtx() defer cancel() if err = b.txStore.Abandon(ctx, b.chainID, addr); err != nil { return fmt.Errorf("abandon failed to update txes for key %s: %w", addr.String(), err) diff --git a/core/chains/evm/client/node_lifecycle_test.go b/core/chains/evm/client/node_lifecycle_test.go index f097c2bc078..0fcaf54ae3d 100644 --- a/core/chains/evm/client/node_lifecycle_test.go +++ b/core/chains/evm/client/node_lifecycle_test.go @@ -556,7 +556,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { return }) - iN := NewNode(cfg, time.Duration(time.Second), logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) dial(t, n) diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index a8cbf6667f3..cd1967b9582 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -70,7 +70,7 @@ func TestChainScopedConfig(t *testing.T) { ChainID: id, Chain: toml.Defaults(id, &toml.Chain{ GasEstimator: toml.GasEstimator{ - BumpTxDepth: ptr(uint32(override)), + BumpTxDepth: ptr(override), }, }), } diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index e5b774edd17..2348e648696 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -380,7 +380,7 @@ func (c *Chain) ValidateConfig() (err error) { Msg: config.ErrInvalidChainType.Error()}) } - if c.GasEstimator.BumpTxDepth != nil && uint32(*c.GasEstimator.BumpTxDepth) > *c.Transactions.MaxInFlight { + if c.GasEstimator.BumpTxDepth != nil && *c.GasEstimator.BumpTxDepth > *c.Transactions.MaxInFlight { err = multierr.Append(err, commonconfig.ErrInvalid{Name: "GasEstimator.BumpTxDepth", Value: *c.GasEstimator.BumpTxDepth, Msg: "must be less than or equal to Transactions.MaxInFlight"}) } diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index 03792966fef..cabedf79aee 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -236,7 +236,7 @@ func (f *FwdMgr) runLoop() { defer f.wg.Done() tick := time.After(0) - for ; ; tick = time.After(utils.WithJitter(time.Duration(time.Minute))) { + for ; ; tick = time.After(utils.WithJitter(time.Minute)) { select { case <-tick: if err := f.logpoller.Ready(); err != nil { diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index 55a53211fc9..8321dd30bfe 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -564,7 +564,7 @@ func (b *broadcaster) onNewHeads() { b.lastSeenHeadNumber.Store(latestHead.Number) - keptLogsDepth := uint32(b.config.FinalityDepth()) + keptLogsDepth := b.config.FinalityDepth() if b.registrations.highestNumConfirmations > keptLogsDepth { keptLogsDepth = b.registrations.highestNumConfirmations } diff --git a/core/chains/evm/log/eth_subscriber.go b/core/chains/evm/log/eth_subscriber.go index 2f9cbb07cb3..ff20a6e74e8 100644 --- a/core/chains/evm/log/eth_subscriber.go +++ b/core/chains/evm/log/eth_subscriber.go @@ -131,7 +131,7 @@ func (sub *ethSubscriber) backfillLogs(fromBlockOverride null.Int64, addresses [ return true } - sub.logger.Infow(fmt.Sprintf("LogBroadcaster: Fetched a batch of %v logs from %v to %v%s", len(batchLogs), from, to, elapsedMessage), "len", len(batchLogs), "fromBlock", from, "toBlock", to, "remaining", int64(latestHeight)-to) + sub.logger.Infow(fmt.Sprintf("LogBroadcaster: Fetched a batch of %v logs from %v to %v%s", len(batchLogs), from, to, elapsedMessage), "len", len(batchLogs), "fromBlock", from, "toBlock", to, "remaining", latestHeight-to) select { case <-sub.chStop: diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index f82cec62b74..7006c1762ef 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -1060,7 +1060,7 @@ func (lp *logPoller) GetBlocksRange(ctx context.Context, numbers []uint64, qopts qopts = append(qopts, pg.WithParentCtx(ctx)) minRequestedBlock := int64(mathutil.Min(numbers[0], numbers[1:]...)) maxRequestedBlock := int64(mathutil.Max(numbers[0], numbers[1:]...)) - lpBlocks, err := lp.orm.GetBlocksRange(int64(minRequestedBlock), int64(maxRequestedBlock), qopts...) + lpBlocks, err := lp.orm.GetBlocksRange(minRequestedBlock, maxRequestedBlock, qopts...) if err != nil { lp.lggr.Warnw("Error while retrieving blocks from log pollers blocks table. Falling back to RPC...", "requestedBlocks", numbers, "err", err) } else { diff --git a/core/chains/evm/txmgr/attempts.go b/core/chains/evm/txmgr/attempts.go index ab143a0c963..91645bae6f6 100644 --- a/core/chains/evm/txmgr/attempts.go +++ b/core/chains/evm/txmgr/attempts.go @@ -124,7 +124,7 @@ func (c *evmTxAttemptBuilder) NewEmptyTxAttempt(nonce evmtypes.Nonce, feeLimit u uint64(nonce), fromAddress, value, - uint32(feeLimit), + feeLimit, fee.Legacy, payload, ) diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index 5635972d7ae..c7373e2c4f6 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -164,13 +164,13 @@ func TestTxm_NewDynamicFeeTx(t *testing.T) { {"gas tip < fee cap", assets.GWei(4), assets.GWei(5), nil, ""}, {"gas tip > fee cap", assets.GWei(6), assets.GWei(5), nil, "gas fee cap must be greater than or equal to gas tip cap (fee cap: 5 gwei, tip cap: 6 gwei)"}, {"fee cap exceeds max allowed", assets.GWei(5), assets.GWei(5), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].GasEstimator.PriceMax = (*assets.Wei)(assets.GWei(4)) + c.EVM[0].GasEstimator.PriceMax = assets.GWei(4) }, "specified gas fee cap of 5 gwei would exceed max configured gas price of 4 gwei"}, {"ignores global min gas price", assets.GWei(5), assets.GWei(5), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].GasEstimator.PriceMin = (*assets.Wei)(assets.GWei(6)) + c.EVM[0].GasEstimator.PriceMin = assets.GWei(6) }, ""}, {"tip cap below min allowed", assets.GWei(5), assets.GWei(5), func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].GasEstimator.TipCapMin = (*assets.Wei)(assets.GWei(6)) + c.EVM[0].GasEstimator.TipCapMin = assets.GWei(6) }, "specified gas tip cap of 5 gwei is below min configured gas tip of 6 gwei"}, } diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index a0711a55a56..67e9b0d8f04 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -1142,7 +1142,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }, evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), evmcfg.EVM().GasEstimator(), ethKeyStore, estimator) localNextNonce = getLocalNextNonce(t, eb, fromAddress) - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce, nil).Once() eb2 := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), evmcfg.EVM().Transactions(), evmcfg.Database().Listener(), ethKeyStore, txBuilder, nil, lggr, &testCheckerFactory{}, false) retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) @@ -1193,7 +1193,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { require.Equal(t, int64(localNextNonce), int64(nonce)) // On the second try, the tx has been accepted into the mempool - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce+1), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce+1, nil).Once() retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) @@ -1217,10 +1217,10 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce := getLocalNextNonce(t, eb, fromAddress) etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { - return tx.Nonce() == uint64(localNextNonce) + return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() // Nonce is the same as localNextNonce, implying that this sent transaction has not been accepted - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce, nil).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1269,7 +1269,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce := getLocalNextNonce(t, eb, fromAddress) etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { - return tx.Nonce() == uint64(localNextNonce) + return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("pending nonce fetch failed")).Once() @@ -1324,7 +1324,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() // Nonce is one higher than localNextNonce, implying that despite the error, this sent transaction has been accepted into the mempool - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce+1), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce+1, nil).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1466,7 +1466,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { c.EVM[0].GasEstimator.BumpMin = assets.NewWeiI(0) c.EVM[0].GasEstimator.BumpPercent = ptr[uint16](0) })) - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce, nil).Once() eb2 := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg2, &testCheckerFactory{}, false) mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) @@ -1558,7 +1558,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { c.EVM[0].GasEstimator.BumpPercent = ptr[uint16](0) })) localNextNonce := getLocalNextNonce(t, eb, fromAddress) - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce, nil).Once() eb2 := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg2, &testCheckerFactory{}, false) mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) underpricedError := "transaction underpriced" @@ -1799,7 +1799,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, txBuilder, txNonceSyncer, lggr, checkerFactory, true) - ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(ethNodeNonce), nil).Once() + ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(ethNodeNonce, nil).Once() servicetest.Run(t, eb) testutils.WaitForLogMessage(t, observed, "Fast-forward sequence") diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 8fa791a141e..9f267a8ea67 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -1724,7 +1724,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].GasEstimator.PriceMax = (*assets.Wei)(assets.GWei(500)) + c.EVM[0].GasEstimator.PriceMax = assets.GWei(500) }) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) @@ -2373,7 +2373,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].GasEstimator.PriceMax = (*assets.Wei)(assets.GWei(500)) + c.EVM[0].GasEstimator.PriceMax = assets.GWei(500) }) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index da206c46c7d..add4d915809 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -150,7 +150,7 @@ func fromDBReceiptsPlus(rs []dbReceiptPlus) []ReceiptPlus { func toOnchainReceipt(rs []*evmtypes.Receipt) []rawOnchainReceipt { receipts := make([]rawOnchainReceipt, len(rs)) for i := 0; i < len(rs); i++ { - receipts[i] = rawOnchainReceipt(*rs[i]) + receipts[i] = *rs[i] } return receipts } diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 93b300aa532..44e150b6541 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -316,7 +316,7 @@ func (h *Head) MarshalJSON() ([]byte, error) { if h.StateRoot != (common.Hash{}) { jsonHead.StateRoot = &h.StateRoot } - jsonHead.Number = (*hexutil.Big)(big.NewInt(int64(h.Number))) + jsonHead.Number = (*hexutil.Big)(big.NewInt(h.Number)) if h.ParentHash != (common.Hash{}) { jsonHead.ParentHash = &h.ParentHash } diff --git a/core/chains/evm/types/nonce.go b/core/chains/evm/types/nonce.go index e9caf98c763..be295bdd2a9 100644 --- a/core/chains/evm/types/nonce.go +++ b/core/chains/evm/types/nonce.go @@ -19,5 +19,5 @@ func (n Nonce) String() string { } func GenerateNextNonce(prev Nonce) Nonce { - return Nonce(prev + 1) + return prev + 1 } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index da899cef362..1c4d097d633 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -524,8 +524,8 @@ observationSource = """ cltest.AwaitJobActive(t, app.JobSpawner(), j.ID, testutils.WaitTimeout(t)) run := cltest.CreateJobRunViaUser(t, app, j.ExternalJobID, "") - assert.Equal(t, []*string([]*string(nil)), run.Outputs) - assert.Equal(t, []*string([]*string(nil)), run.Errors) + assert.Equal(t, []*string(nil), run.Outputs) + assert.Equal(t, []*string(nil), run.Errors) testutils.WaitForLogMessage(t, o, "Sending transaction") b.Commit() // Needs at least two confirmations @@ -570,8 +570,8 @@ observationSource = """ cltest.AwaitJobActive(t, app.JobSpawner(), j.ID, testutils.WaitTimeout(t)) run := cltest.CreateJobRunViaUser(t, app, j.ExternalJobID, "") - assert.Equal(t, []*string([]*string(nil)), run.Outputs) - assert.Equal(t, []*string([]*string(nil)), run.Errors) + assert.Equal(t, []*string(nil), run.Outputs) + assert.Equal(t, []*string(nil), run.Errors) testutils.WaitForLogMessage(t, o, "Sending transaction") b.Commit() // Needs at least two confirmations @@ -608,8 +608,8 @@ observationSource = """ cltest.AwaitJobActive(t, app.JobSpawner(), j.ID, testutils.WaitTimeout(t)) run := cltest.CreateJobRunViaUser(t, app, j.ExternalJobID, "") - assert.Equal(t, []*string([]*string(nil)), run.Outputs) - assert.Equal(t, []*string([]*string(nil)), run.Errors) + assert.Equal(t, []*string(nil), run.Outputs) + assert.Equal(t, []*string(nil), run.Errors) testutils.WaitForLogMessage(t, o, "Sending transaction") b.Commit() // Needs at least two confirmations diff --git a/core/null/int64.go b/core/null/int64.go index 2a31aea7594..ca236784486 100644 --- a/core/null/int64.go +++ b/core/null/int64.go @@ -43,7 +43,7 @@ func (i *Int64) UnmarshalJSON(data []byte) error { // Unmarshal again, directly to value, to avoid intermediate float64 err = json.Unmarshal(data, &i.Int64) case string: - str := string(x) + str := x if len(str) == 0 { i.Valid = false return nil @@ -85,7 +85,7 @@ func (i Int64) MarshalJSON() ([]byte, error) { if !i.Valid { return []byte("null"), nil } - return []byte(strconv.FormatInt(int64(i.Int64), 10)), nil + return []byte(strconv.FormatInt(i.Int64, 10)), nil } // MarshalText implements encoding.TextMarshaler. @@ -94,7 +94,7 @@ func (i Int64) MarshalText() ([]byte, error) { if !i.Valid { return []byte{}, nil } - return []byte(strconv.FormatInt(int64(i.Int64), 10)), nil + return []byte(strconv.FormatInt(i.Int64, 10)), nil } // SetValid changes this Int64's value and also sets it to be non-null. @@ -112,7 +112,7 @@ func (i Int64) Value() (driver.Value, error) { // golang's sql driver types as determined by IsValue only supports: // []byte, bool, float64, int64, string, time.Time // https://golang.org/src/database/sql/driver/types.go - return int64(i.Int64), nil + return i.Int64, nil } // Scan reads the database value and returns an instance. @@ -130,7 +130,7 @@ func (i *Int64) Scan(value interface{}) error { safe := int64(typed) *i = Int64From(safe) case int64: - safe := int64(typed) + safe := typed *i = Int64From(safe) case uint: if typed > uint(math.MaxInt64) { diff --git a/core/null/uint32.go b/core/null/uint32.go index 0bafef0c24e..1d4fbc15b79 100644 --- a/core/null/uint32.go +++ b/core/null/uint32.go @@ -42,7 +42,7 @@ func (i *Uint32) UnmarshalJSON(data []byte) error { // Unmarshal again, directly to value, to avoid intermediate float64 err = json.Unmarshal(data, &i.Uint32) case string: - str := string(x) + str := x if len(str) == 0 { i.Valid = false return nil diff --git a/core/scripts/ocr2vrf/main.go b/core/scripts/ocr2vrf/main.go index 532fbd3f22c..e7da4589951 100644 --- a/core/scripts/ocr2vrf/main.go +++ b/core/scripts/ocr2vrf/main.go @@ -364,7 +364,7 @@ func main() { *consumerAddress, uint16(*numWords), decimal.RequireFromString(*subID).BigInt(), - big.NewInt(int64(*confDelay)), + big.NewInt(*confDelay), uint32(*callbackGasLimit), nil, // test consumer doesn't use any args ) @@ -389,7 +389,7 @@ func main() { *consumerAddress, uint16(*numWords), decimal.RequireFromString(*subID).BigInt(), - big.NewInt(int64(*confDelay)), + big.NewInt(*confDelay), uint32(*callbackGasLimit), nil, // test consumer doesn't use any args, big.NewInt(*batchSize), @@ -411,7 +411,7 @@ func main() { *consumerAddress, uint16(*numWords), decimal.RequireFromString(*subID).BigInt(), - big.NewInt(int64(*confDelay)), + big.NewInt(*confDelay), uint32(*callbackGasLimit), nil, // test consumer doesn't use any args, big.NewInt(*batchSize), diff --git a/core/scripts/vrfv2/testnet/main.go b/core/scripts/vrfv2/testnet/main.go index 151549cc587..100440d6fb0 100644 --- a/core/scripts/vrfv2/testnet/main.go +++ b/core/scripts/vrfv2/testnet/main.go @@ -744,7 +744,7 @@ func main() { helpers.ParseArgs(addSubConsCmd, os.Args[2:], "coordinator-address", "sub-id", "consumer-address") coordinator, err := vrf_coordinator_v2.NewVRFCoordinatorV2(common.HexToAddress(*coordinatorAddress), e.Ec) helpers.PanicErr(err) - v2scripts.EoaAddConsumerToSub(e, *coordinator, uint64(*subID), *consumerAddress) + v2scripts.EoaAddConsumerToSub(e, *coordinator, *subID, *consumerAddress) case "eoa-create-fund-authorize-sub": // Lets just treat the owner key as the EOA controlling the sub cfaSubCmd := flag.NewFlagSet("eoa-create-fund-authorize-sub", flag.ExitOnError) diff --git a/core/services/blockhashstore/common.go b/core/services/blockhashstore/common.go index 677016253fb..a19a3b868f7 100644 --- a/core/services/blockhashstore/common.go +++ b/core/services/blockhashstore/common.go @@ -59,7 +59,7 @@ func GetUnfulfilledBlocksAndRequests( blockToRequests := make(map[uint64]map[string]struct{}) requestIDToBlock := make(map[string]uint64) - reqs, err := coordinator.Requests(ctx, uint64(fromBlock), uint64(toBlock)) + reqs, err := coordinator.Requests(ctx, fromBlock, toBlock) if err != nil { lggr.Errorw("Failed to fetch VRF requests", "err", err) @@ -73,7 +73,7 @@ func GetUnfulfilledBlocksAndRequests( requestIDToBlock[req.ID] = req.Block } - fuls, err := coordinator.Fulfillments(ctx, uint64(fromBlock)) + fuls, err := coordinator.Fulfillments(ctx, fromBlock) if err != nil { lggr.Errorw("Failed to fetch VRF fulfillments", "err", err) diff --git a/core/services/blockheaderfeeder/block_header_feeder.go b/core/services/blockheaderfeeder/block_header_feeder.go index 93799b8b419..a5bcb003613 100644 --- a/core/services/blockheaderfeeder/block_header_feeder.go +++ b/core/services/blockheaderfeeder/block_header_feeder.go @@ -120,7 +120,7 @@ func (f *BlockHeaderFeeder) Run(ctx context.Context) error { lggr.Debugw("found lowest block number without blockhash", "minBlockNumber", minBlockNumber) - earliestStoredBlockNumber, err := f.findEarliestBlockNumberWithBlockhash(ctx, lggr, minBlockNumber.Uint64()+1, uint64(toBlock)) + earliestStoredBlockNumber, err := f.findEarliestBlockNumberWithBlockhash(ctx, lggr, minBlockNumber.Uint64()+1, toBlock) if err != nil { return errors.Wrap(err, "finding earliest blocknumber with blockhash") } diff --git a/core/services/feeds/orm_test.go b/core/services/feeds/orm_test.go index 88d3b132beb..78af9bf6912 100644 --- a/core/services/feeds/orm_test.go +++ b/core/services/feeds/orm_test.go @@ -645,7 +645,7 @@ func Test_ORM_CountJobProposalsByStatus(t *testing.T) { jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) // Create a spec for the pending job proposal - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) // Defer the FK requirement of an existing job for a job proposal to be approved require.NoError(t, utils.JustError(orm.db.Exec( @@ -870,7 +870,7 @@ func Test_ORM_UpsertJobProposal(t *testing.T) { assert.True(t, actual.PendingUpdate) // Approve - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) // Defer the FK requirement of an existing job for a job proposal. require.NoError(t, utils.JustError(orm.db.Exec( @@ -943,7 +943,7 @@ func Test_ORM_ApproveSpec(t *testing.T) { PendingUpdate: true, }) require.NoError(t, err) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) // Defer the FK requirement of an existing job for a job proposal. require.NoError(t, utils.JustError(orm.db.Exec( @@ -982,7 +982,7 @@ func Test_ORM_CancelSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -994,7 +994,7 @@ func Test_ORM_CancelSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusDeleted, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1056,7 +1056,7 @@ func Test_ORM_DeleteProposal(t *testing.T) { before: func(orm *TestORM) int64 { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - createJobSpec(t, orm, int64(jpID)) + createJobSpec(t, orm, jpID) return jpID }, @@ -1068,7 +1068,7 @@ func Test_ORM_DeleteProposal(t *testing.T) { before: func(orm *TestORM) int64 { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) externalJobID := uuid.NullUUID{UUID: uuid.New(), Valid: true} @@ -1090,7 +1090,7 @@ func Test_ORM_DeleteProposal(t *testing.T) { before: func(orm *TestORM) int64 { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) externalJobID := uuid.NullUUID{UUID: uuid.New(), Valid: true} @@ -1131,7 +1131,7 @@ func Test_ORM_DeleteProposal(t *testing.T) { before: func(orm *TestORM) int64 { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusCancelled, fmID) - createJobSpec(t, orm, int64(jpID)) + createJobSpec(t, orm, jpID) return jpID }, @@ -1143,7 +1143,7 @@ func Test_ORM_DeleteProposal(t *testing.T) { before: func(orm *TestORM) int64 { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusRejected, fmID) - createJobSpec(t, orm, int64(jpID)) + createJobSpec(t, orm, jpID) return jpID }, @@ -1211,7 +1211,7 @@ func Test_ORM_RevokeSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1224,7 +1224,7 @@ func Test_ORM_RevokeSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) externalJobID := uuid.NullUUID{UUID: uuid.New(), Valid: true} @@ -1246,7 +1246,7 @@ func Test_ORM_RevokeSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusCancelled, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1258,7 +1258,7 @@ func Test_ORM_RevokeSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusRejected, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1270,7 +1270,7 @@ func Test_ORM_RevokeSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusDeleted, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1324,7 +1324,7 @@ func Test_ORM_ExistsSpecByJobProposalIDAndVersion(t *testing.T) { jpID = createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) ) - createJobSpec(t, orm, int64(jpID)) + createJobSpec(t, orm, jpID) exists, err := orm.ExistsSpecByJobProposalIDAndVersion(jpID, 1) require.NoError(t, err) @@ -1342,7 +1342,7 @@ func Test_ORM_GetSpec(t *testing.T) { orm = setupORM(t) fmID = createFeedsManager(t, orm) jpID = createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID = createJobSpec(t, orm, int64(jpID)) + specID = createJobSpec(t, orm, jpID) ) actual, err := orm.GetSpec(specID) @@ -1361,7 +1361,7 @@ func Test_ORM_GetApprovedSpec(t *testing.T) { orm = setupORM(t) fmID = createFeedsManager(t, orm) jpID = createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID = createJobSpec(t, orm, int64(jpID)) + specID = createJobSpec(t, orm, jpID) externalJobID = uuid.NullUUID{UUID: uuid.New(), Valid: true} ) @@ -1398,7 +1398,7 @@ func Test_ORM_GetLatestSpec(t *testing.T) { jpID = createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) ) - _ = createJobSpec(t, orm, int64(jpID)) + _ = createJobSpec(t, orm, jpID) spec2ID, err := orm.CreateSpec(feeds.JobProposalSpec{ Definition: "spec data", Version: 2, @@ -1429,8 +1429,8 @@ func Test_ORM_ListSpecsByJobProposalIDs(t *testing.T) { ) // Create the specs for the proposals - createJobSpec(t, orm, int64(jp1ID)) - createJobSpec(t, orm, int64(jp2ID)) + createJobSpec(t, orm, jp1ID) + createJobSpec(t, orm, jp2ID) specs, err := orm.ListSpecsByJobProposalIDs([]int64{jp1ID, jp2ID}) require.NoError(t, err) @@ -1466,7 +1466,7 @@ func Test_ORM_RejectSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1478,7 +1478,7 @@ func Test_ORM_RejectSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) externalJobID := uuid.NullUUID{UUID: uuid.New(), Valid: true} @@ -1500,7 +1500,7 @@ func Test_ORM_RejectSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusCancelled, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1512,7 +1512,7 @@ func Test_ORM_RejectSpec(t *testing.T) { before: func(orm *TestORM) (int64, int64) { fmID := createFeedsManager(t, orm) jpID := createJobProposal(t, orm, feeds.JobProposalStatusDeleted, fmID) - specID := createJobSpec(t, orm, int64(jpID)) + specID := createJobSpec(t, orm, jpID) return jpID, specID }, @@ -1566,7 +1566,7 @@ func Test_ORM_UpdateSpecDefinition(t *testing.T) { orm = setupORM(t) fmID = createFeedsManager(t, orm) jpID = createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID = createJobSpec(t, orm, int64(jpID)) + specID = createJobSpec(t, orm, jpID) ) prev, err := orm.GetSpec(specID) @@ -1596,7 +1596,7 @@ func Test_ORM_IsJobManaged(t *testing.T) { orm = setupORM(t) fmID = createFeedsManager(t, orm) jpID = createJobProposal(t, orm, feeds.JobProposalStatusPending, fmID) - specID = createJobSpec(t, orm, int64(jpID)) + specID = createJobSpec(t, orm, jpID) externalJobID = uuid.NullUUID{UUID: uuid.New(), Valid: true} ) diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 580e7a1d086..7f45e6eb19c 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -519,7 +519,7 @@ func TestFluxMonitor_Deviation(t *testing.T) { s = fmt.Sprintf(s, fa.aggregatorContractAddress, 2*time.Second) requestBody, err := json.Marshal(web.CreateJobRequest{ - TOML: string(s), + TOML: s, }) assert.NoError(t, err) @@ -676,7 +676,7 @@ ds1 -> ds1_parse fa.backend.Commit() requestBody, err := json.Marshal(web.CreateJobRequest{ - TOML: string(s), + TOML: s, }) assert.NoError(t, err) @@ -786,7 +786,7 @@ ds1 -> ds1_parse fa.backend.Commit() requestBody, err := json.Marshal(web.CreateJobRequest{ - TOML: string(s), + TOML: s, }) assert.NoError(t, err) @@ -896,7 +896,7 @@ ds1 -> ds1_parse fa.backend.Commit() requestBody, err := json.Marshal(web.CreateJobRequest{ - TOML: string(s), + TOML: s, }) assert.NoError(t, err) @@ -991,7 +991,7 @@ ds1 -> ds1_parse -> ds1_multiply s = fmt.Sprintf(s, fa.aggregatorContractAddress, testutils.SimulatedChainID.String(), "200ms", mockServer.URL) requestBody, err := json.Marshal(web.CreateJobRequest{ - TOML: string(s), + TOML: s, }) assert.NoError(t, err) diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 5d26f9a4f57..75161d3410b 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -161,7 +161,7 @@ func TestFunctionsListener_HandleOracleRequestV1_Success(t *testing.T) { request := types.OracleRequest{ RequestId: RequestID, - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner, Flags: packFlags(1, 0), // tier no 1 of request size, allows up to 100 bytes Data: make([]byte, 12), @@ -194,7 +194,7 @@ func TestFunctionsListener_HandleOffchainRequest_Success(t *testing.T) { request := &functions_service.OffchainRequest{ RequestId: RequestID[:], RequestInitiator: SubscriptionOwner.Bytes(), - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner.Bytes(), Timestamp: uint64(time.Now().Unix()), Data: functions_service.RequestData{}, @@ -210,7 +210,7 @@ func TestFunctionsListener_HandleOffchainRequest_Invalid(t *testing.T) { request := &functions_service.OffchainRequest{ RequestId: RequestID[:], RequestInitiator: []byte("invalid_address"), - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner.Bytes(), Timestamp: uint64(time.Now().Unix()), Data: functions_service.RequestData{}, @@ -238,7 +238,7 @@ func TestFunctionsListener_HandleOffchainRequest_InternalError(t *testing.T) { request := &functions_service.OffchainRequest{ RequestId: RequestID[:], RequestInitiator: SubscriptionOwner.Bytes(), - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner.Bytes(), Timestamp: uint64(time.Now().Unix()), Data: functions_service.RequestData{}, @@ -255,7 +255,7 @@ func TestFunctionsListener_HandleOracleRequestV1_ComputationError(t *testing.T) request := types.OracleRequest{ RequestId: RequestID, - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner, Flags: packFlags(1, 0), // tier no 1 of request size, allows up to 100 bytes Data: make([]byte, 12), @@ -291,7 +291,7 @@ func TestFunctionsListener_HandleOracleRequestV1_ThresholdDecryptedSecrets(t *te cborBytes = cborBytes[1:] request := types.OracleRequest{ RequestId: RequestID, - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner, Flags: packFlags(1, 1), // tiers no 1 of request size and secrets size, allow up to 100 bytes Data: cborBytes, @@ -324,7 +324,7 @@ func TestFunctionsListener_HandleOracleRequestV1_CBORTooBig(t *testing.T) { request := types.OracleRequest{ RequestId: RequestID, - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner, Flags: packFlags(0, 0), // tier no 0 of request size, allows only for max 10 bytes Data: make([]byte, 20), @@ -350,7 +350,7 @@ func TestFunctionsListener_ReportSourceCodeDomains(t *testing.T) { request := types.OracleRequest{ RequestId: RequestID, - SubscriptionId: uint64(SubscriptionID), + SubscriptionId: SubscriptionID, SubscriptionOwner: SubscriptionOwner, Flags: packFlags(1, 0), // tier no 1 of request size, allows up to 100 bytes Data: make([]byte, 12), diff --git a/core/services/gateway/connector/connector.go b/core/services/gateway/connector/connector.go index 6b399733a58..9f809a326c8 100644 --- a/core/services/gateway/connector/connector.go +++ b/core/services/gateway/connector/connector.go @@ -83,7 +83,7 @@ func NewGatewayConnector(config *ConnectorConfig, signer Signer, handler Gateway if config == nil || signer == nil || handler == nil || clock == nil || lggr == nil { return nil, errors.New("nil dependency") } - if len(config.DonId) == 0 || len(config.DonId) > int(network.HandshakeDonIdLen) { + if len(config.DonId) == 0 || len(config.DonId) > network.HandshakeDonIdLen { return nil, errors.New("invalid DON ID") } addressBytes, err := hex.DecodeString(config.NodeAddress) diff --git a/core/services/gateway/gateway_test.go b/core/services/gateway/gateway_test.go index 1c31a643c80..7a5457c788c 100644 --- a/core/services/gateway/gateway_test.go +++ b/core/services/gateway/gateway_test.go @@ -225,7 +225,7 @@ func TestGateway_ProcessRequest_HandlerTimeout(t *testing.T) { gw, handler := newGatewayWithMockHandler(t) handler.On("HandleUserMessage", mock.Anything, mock.Anything, mock.Anything).Return(nil) - timeoutCtx, cancel := context.WithTimeout(testutils.Context(t), time.Duration(time.Millisecond*10)) + timeoutCtx, cancel := context.WithTimeout(testutils.Context(t), time.Millisecond*10) defer cancel() req := newSignedRequest(t, "abcd", "request", "testDON", []byte{}) diff --git a/core/services/job/models.go b/core/services/job/models.go index 5dcf4928e35..31032d58e0e 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -236,7 +236,7 @@ func (pr *PipelineRun) SetID(value string) error { if err != nil { return err } - pr.ID = int64(ID) + pr.ID = ID return nil } diff --git a/core/services/job/orm.go b/core/services/job/orm.go index fb52dafdf5d..82c6be2963c 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -697,7 +697,7 @@ func (o *orm) FindJobs(offset, limit int) (jobs []Job, count int, err error) { return nil }) - return jobs, int(count), err + return jobs, count, err } func LoadDefaultVRFPollPeriod(vrfs VRFSpec) *VRFSpec { diff --git a/core/services/keystore/keys/ocr2key/offchain_keyring.go b/core/services/keystore/keys/ocr2key/offchain_keyring.go index 9e6d8f64e03..36fc1cfd02a 100644 --- a/core/services/keystore/keys/ocr2key/offchain_keyring.go +++ b/core/services/keystore/keys/ocr2key/offchain_keyring.go @@ -68,7 +68,7 @@ func (ok *OffchainKeyring) NaclBoxOpenAnonymous(ciphertext []byte) (plaintext [] // OffchainSign signs message using private key func (ok *OffchainKeyring) OffchainSign(msg []byte) (signature []byte, err error) { - return ed25519.Sign(ed25519.PrivateKey(ok.signingKey), msg), nil + return ed25519.Sign(ok.signingKey, msg), nil } // ConfigDiffieHellman returns the shared point obtained by multiplying someone's diff --git a/core/services/nurse_test.go b/core/services/nurse_test.go index 46ebd749036..7521168aa3f 100644 --- a/core/services/nurse_test.go +++ b/core/services/nurse_test.go @@ -31,8 +31,8 @@ type mockConfig struct { } var ( - testInterval = time.Duration(50 * time.Millisecond) - testDuration = time.Duration(20 * time.Millisecond) + testInterval = 50 * time.Millisecond + testDuration = 20 * time.Millisecond testRate = 100 testSize = 16 * 1024 * 1024 ) diff --git a/core/services/ocr2/plugins/dkg/persistence/db.go b/core/services/ocr2/plugins/dkg/persistence/db.go index 304422ace2d..72997e28699 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db.go +++ b/core/services/ocr2/plugins/dkg/persistence/db.go @@ -65,7 +65,7 @@ func NewShareDB(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, chainID *big.In q: pg.NewQ(db, lggr, cfg), lggr: lggr, chainID: chainID, - chainType: string(chainType), + chainType: chainType, } } diff --git a/core/services/ocr2/plugins/functions/aggregation.go b/core/services/ocr2/plugins/functions/aggregation.go index 32a2509e70e..7ae00532f3f 100644 --- a/core/services/ocr2/plugins/functions/aggregation.go +++ b/core/services/ocr2/plugins/functions/aggregation.go @@ -104,7 +104,7 @@ func aggregateMode(items [][]byte) []byte { mostFrequent = item } } - return []byte(mostFrequent) + return mostFrequent } func aggregateMedian(items [][]byte) []byte { diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index 51f9eaa6683..6d847098f94 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -357,7 +357,7 @@ func TestIntegration_MercuryV1(t *testing.T) { err = reportcodecv1.ReportTypes.UnpackIntoMap(reportElems, report.([]byte)) require.NoError(t, err) - feedID := ([32]byte)(reportElems["feedId"].([32]uint8)) + feedID := reportElems["feedId"].([32]uint8) feed, exists := feedM[feedID] require.True(t, exists) @@ -421,7 +421,7 @@ func TestIntegration_MercuryV1(t *testing.T) { err = reportcodecv1.ReportTypes.UnpackIntoMap(reportElems, report.([]byte)) require.NoError(t, err) - feedID := ([32]byte)(reportElems["feedId"].([32]uint8)) + feedID := reportElems["feedId"].([32]uint8) feed, exists := feedM[feedID] require.True(t, exists) @@ -691,7 +691,7 @@ func TestIntegration_MercuryV2(t *testing.T) { err = reportcodecv2.ReportTypes.UnpackIntoMap(reportElems, report.([]byte)) require.NoError(t, err) - feedID := ([32]byte)(reportElems["feedId"].([32]uint8)) + feedID := reportElems["feedId"].([32]uint8) feed, exists := feedM[feedID] require.True(t, exists) @@ -971,7 +971,7 @@ func TestIntegration_MercuryV3(t *testing.T) { err = reportcodecv3.ReportTypes.UnpackIntoMap(reportElems, report.([]byte)) require.NoError(t, err) - feedID := ([32]byte)(reportElems["feedId"].([32]uint8)) + feedID := reportElems["feedId"].([32]uint8) feed, exists := feedM[feedID] require.True(t, exists) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go index 81f51311d44..f2c58fd30c1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go @@ -176,8 +176,8 @@ func (b *logEventBuffer) enqueue(id *big.Int, logs ...logpoller.Log) int { lggr := b.lggr.With("id", id.String()) - maxBlockLogs := int(b.maxBlockLogs) - maxUpkeepLogs := int(b.maxUpkeepLogsPerBlock) + maxBlockLogs := b.maxBlockLogs + maxUpkeepLogs := b.maxUpkeepLogsPerBlock latestBlock := b.latestBlockSeen() added, dropped := 0, 0 diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go index f69fbb35e35..202661145bf 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go @@ -81,7 +81,7 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo retryable = retryable && m.Retryable allSuccess = false if m.State != encoding.NoPipelineError { - state = encoding.PipelineExecutionState(m.State) + state = m.State } continue } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go index 99e1f1dc164..2c0055482ed 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go @@ -28,7 +28,7 @@ func (c *transmitEventCache) get(block ocr2keepers.BlockNumber, logID string) (o c.lock.RLock() defer c.lock.RUnlock() - i := int64(block) % int64(c.cap) + i := int64(block) % c.cap b := c.buffer[i] if b.block != block { return ocr2keepers.TransmitEvent{}, false @@ -45,7 +45,7 @@ func (c *transmitEventCache) add(logID string, e ocr2keepers.TransmitEvent) { c.lock.Lock() defer c.lock.Unlock() - i := int64(e.TransmitBlock) % int64(c.cap) + i := int64(e.TransmitBlock) % c.cap b := c.buffer[i] isBlockEmpty := len(b.records) == 0 isNewBlock := b.block < e.TransmitBlock diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go index a25fcca9fec..88d6544d8c4 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go @@ -398,7 +398,7 @@ func (c *coordinator) ReportBlocks( // TODO BELOW: Write tests for the new blockhash retrieval. // Obtain recent blockhashes, ordered by ascending block height. - for i := recentBlockHashesStartHeight; i <= uint64(currentHeight); i++ { + for i := recentBlockHashesStartHeight; i <= currentHeight; i++ { recentBlockHashes = append(recentBlockHashes, blockhashesMapping[i]) } @@ -518,7 +518,7 @@ func (c *coordinator) getBlockhashesMappingFromRequests( } // Get a mapping of block numbers to block hashes. - blockhashesMapping, err = c.getBlockhashesMapping(ctx, append(requestedBlockNumbers, uint64(currentHeight), recentBlockHashesStartHeight)) + blockhashesMapping, err = c.getBlockhashesMapping(ctx, append(requestedBlockNumbers, currentHeight, recentBlockHashesStartHeight)) if err != nil { err = errors.Wrap(err, "get blockhashes for ReportBlocks") } diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go index 9c3eb087985..096589b2053 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go @@ -271,7 +271,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 1) assert.Len(t, callbacks, 0) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -333,7 +333,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.False(t, b.ShouldStore) } assert.Len(t, callbacks, 3) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -400,7 +400,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 0) assert.Len(t, callbacks, 0) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -471,7 +471,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 0) assert.Len(t, callbacks, 0) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -533,7 +533,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 0) assert.Len(t, callbacks, 0) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -635,7 +635,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 0) assert.Len(t, callbacks, 0) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -705,7 +705,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.True(t, b.ShouldStore) } assert.Len(t, callbacks, 0) - assert.Equal(t, uint64(latestHeadNumber-blockhashLookback+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-blockhashLookback+1, recentHeightStart) assert.Len(t, recentBlocks, int(blockhashLookback)) }) @@ -772,7 +772,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.True(t, b.ShouldStore) } assert.Len(t, callbacks, 2) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -835,7 +835,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 1) assert.Len(t, callbacks, 1) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -901,7 +901,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { assert.NoError(t, err) assert.Len(t, blocks, 2) assert.Len(t, callbacks, 2) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Len(t, recentBlocks, int(lookbackBlocks)) }) @@ -956,7 +956,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { ) assert.NoError(t, err) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Equal(t, common.HexToHash(fmt.Sprintf("0x00%d", 1)), recentBlocks[0]) assert.Equal(t, common.HexToHash(fmt.Sprintf("0x00%d", lookbackBlocks)), recentBlocks[len(recentBlocks)-1]) assert.Len(t, recentBlocks, int(lookbackBlocks)) @@ -1013,7 +1013,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { ) assert.NoError(t, err) - assert.Equal(t, uint64(latestHeadNumber-lookbackBlocks+1), recentHeightStart) + assert.Equal(t, latestHeadNumber-lookbackBlocks+1, recentHeightStart) assert.Equal(t, common.HexToHash(fmt.Sprintf("0x00%d", 1)), recentBlocks[0]) assert.Equal(t, common.HexToHash(fmt.Sprintf("0x00%d", lookbackBlocks)), recentBlocks[len(recentBlocks)-1]) assert.Len(t, recentBlocks, int(lookbackBlocks)) @@ -1035,7 +1035,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { lp.On("LatestBlock", mock.Anything). Return(logpoller.LogPollerBlock{BlockNumber: int64(latestHeadNumber)}, nil) - lp.On("GetBlocksRange", mock.Anything, append(requestedBlocks, uint64(latestHeadNumber-lookbackBlocks+1), uint64(latestHeadNumber)), mock.Anything). + lp.On("GetBlocksRange", mock.Anything, append(requestedBlocks, latestHeadNumber-lookbackBlocks+1, latestHeadNumber), mock.Anything). Return(nil, errors.New("GetBlocks error")) lp.On( "LogsWithSigs", diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/ocr_cache_test.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/ocr_cache_test.go index 6a4b97b9f0e..57aaf1c5e03 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/ocr_cache_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/ocr_cache_test.go @@ -11,7 +11,7 @@ import ( func TestNewCache(t *testing.T) { b := NewBlockCache[int](time.Second) - assert.Equal(t, time.Second, time.Duration(b.evictionWindow), "must set correct blockEvictionWindow") + assert.Equal(t, time.Second, b.evictionWindow, "must set correct blockEvictionWindow") } func TestCache(t *testing.T) { @@ -30,7 +30,7 @@ func TestCache(t *testing.T) { {Key: common.HexToHash("0x4"), Value: 5}, } - c := NewBlockCache[int](time.Second * 100) + c := NewBlockCache[int](100 * time.Second) // Populate cache with ordered items. for i, test := range tests { @@ -79,7 +79,7 @@ func TestCache(t *testing.T) { {Key: common.HexToHash("0x1"), Value: 5}, } - c := NewBlockCache[int](time.Duration(time.Second * 100)) + c := NewBlockCache[int](100 * time.Second) // Populate cache with items. for i, test := range tests { @@ -119,7 +119,7 @@ func TestCache(t *testing.T) { {Key: common.HexToHash("0x0"), Value: 5}, } - c := NewBlockCache[int](time.Duration(time.Second * 100)) + c := NewBlockCache[int](100 * time.Second) // Populate cache with items. for i, test := range tests { diff --git a/core/services/ocr2/plugins/promwrapper/plugin.go b/core/services/ocr2/plugins/promwrapper/plugin.go index a409b5ba86c..58d8e171f39 100644 --- a/core/services/ocr2/plugins/promwrapper/plugin.go +++ b/core/services/ocr2/plugins/promwrapper/plugin.go @@ -39,7 +39,7 @@ var ( labels = []string{"chainType", "chainID", "plugin", "oracleID", "configDigest"} getLabelsValues = func(p *promPlugin, t types.ReportTimestamp) []string { return []string{ - string(p.chainType), // chainType + p.chainType, // chainType p.chainID.String(), // chainID p.name, // plugin p.oracleID, // oracleID @@ -333,11 +333,11 @@ func (p *promPlugin) Close() error { defer func() { duration := float64(time.Now().UTC().Sub(start)) labelValues := []string{ - string(p.chainType), // chainType - p.chainID.String(), // chainID - p.name, // plugin - p.oracleID, // oracleID - p.configDigest, // configDigest + p.chainType, // chainType + p.chainID.String(), // chainID + p.name, // plugin + p.oracleID, // oracleID + p.configDigest, // configDigest } p.prometheusBackend.SetCloseDuration(labelValues, duration) }() diff --git a/core/services/ocr2/plugins/s4/plugin.go b/core/services/ocr2/plugins/s4/plugin.go index 677743c091d..2b55ebf3cc5 100644 --- a/core/services/ocr2/plugins/s4/plugin.go +++ b/core/services/ocr2/plugins/s4/plugin.go @@ -167,9 +167,9 @@ func (c *plugin) Observation(ctx context.Context, ts types.ReportTimestamp, quer if !sr.Confirmed { continue } - k := key{address: sr.Address.String(), slotID: uint(sr.SlotId)} + k := key{address: sr.Address.String(), slotID: sr.SlotId} if _, ok := snapshotVersionsMap[k]; ok { - toBeAdded = append(toBeAdded, rkey{address: sr.Address, slotID: uint(sr.SlotId)}) + toBeAdded = append(toBeAdded, rkey{address: sr.Address, slotID: sr.SlotId}) if len(toBeAdded) == maxRemainingRows { break } @@ -332,7 +332,7 @@ func snapshotToVersionMap(rows []*s4.SnapshotRow) map[key]uint64 { m := make(map[key]uint64) for _, row := range rows { if row.Confirmed { - m[key{address: row.Address.String(), slotID: uint(row.SlotId)}] = row.Version + m[key{address: row.Address.String(), slotID: row.SlotId}] = row.Version } } return m diff --git a/core/services/ocr2/plugins/s4/plugin_test.go b/core/services/ocr2/plugins/s4/plugin_test.go index e0aa84183e1..b53ab40bfcb 100644 --- a/core/services/ocr2/plugins/s4/plugin_test.go +++ b/core/services/ocr2/plugins/s4/plugin_test.go @@ -322,7 +322,7 @@ func TestPlugin_Query(t *testing.T) { assert.Len(t, qq.Rows, 16) for _, r := range qq.Rows { thisAddress := s4.UnmarshalAddress(r.Address) - assert.True(t, ar.Contains((*big.Big)(thisAddress))) + assert.True(t, ar.Contains(thisAddress)) } ar.Advance() diff --git a/core/services/pipeline/models.go b/core/services/pipeline/models.go index e198c1b788c..d2c722f98b8 100644 --- a/core/services/pipeline/models.go +++ b/core/services/pipeline/models.go @@ -75,7 +75,7 @@ func (r *Run) SetID(value string) error { if err != nil { return err } - r.ID = int64(ID) + r.ID = ID return nil } diff --git a/core/services/pipeline/scheduler.go b/core/services/pipeline/scheduler.go index 7c6fd78d0c2..7663ed948ff 100644 --- a/core/services/pipeline/scheduler.go +++ b/core/services/pipeline/scheduler.go @@ -33,7 +33,7 @@ func (s *scheduler) newMemoryTaskRun(task Task, vars Vars) *memoryTaskRun { // if we're confident that indices are within range for _, i := range task.Inputs() { if i.PropagateResult { - inputs = append(inputs, input{index: int32(i.InputTask.OutputIndex()), result: s.results[i.InputTask.ID()].Result}) + inputs = append(inputs, input{index: i.InputTask.OutputIndex(), result: s.results[i.InputTask.ID()].Result}) } } sort.Slice(inputs, func(i, j int) bool { diff --git a/core/services/pipeline/task.bridge.go b/core/services/pipeline/task.bridge.go index fbf861857f5..f9490ea791d 100644 --- a/core/services/pipeline/task.bridge.go +++ b/core/services/pipeline/task.bridge.go @@ -74,7 +74,7 @@ var _ Task = (*BridgeTask)(nil) var zeroURL = new(url.URL) -const stalenessCap = time.Duration(30 * time.Minute) +const stalenessCap = 30 * time.Minute func (t *BridgeTask) Type() TaskType { return TaskTypeBridge @@ -166,7 +166,7 @@ func (t *BridgeTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, inp } var cachedResponse bool - responseBytes, statusCode, headers, elapsed, err := makeHTTPRequest(requestCtx, lggr, "POST", URLParam(url), reqHeaders, requestData, t.httpClient, t.config.DefaultHTTPLimit()) + responseBytes, statusCode, headers, elapsed, err := makeHTTPRequest(requestCtx, lggr, "POST", url, reqHeaders, requestData, t.httpClient, t.config.DefaultHTTPLimit()) if err != nil { promBridgeErrors.WithLabelValues(t.Name).Inc() if cacheTTL == 0 { diff --git a/core/services/pipeline/task_params.go b/core/services/pipeline/task_params.go index 261728fbe9e..72d0d619429 100644 --- a/core/services/pipeline/task_params.go +++ b/core/services/pipeline/task_params.go @@ -334,7 +334,7 @@ func (p *MaybeInt32Param) UnmarshalPipelineParam(val interface{}) error { case int16: n = int32(v) case int32: - n = int32(v) + n = v case int64: if v > math.MaxInt32 || v < math.MinInt32 { return errors.Wrap(ErrBadInput, "overflows int32") @@ -764,7 +764,7 @@ func (p *MaybeBigIntParam) UnmarshalPipelineParam(val interface{}) error { case int32: n = big.NewInt(int64(v)) case int64: - n = big.NewInt(int64(v)) + n = big.NewInt(v) case float64: // when decoding from db: JSON numbers are floats if v < math.MinInt64 || v > math.MaxUint64 { return errors.Wrapf(ErrBadInput, "cannot cast %v to u/int64", v) diff --git a/core/services/relay/evm/mercury/config_digest_test.go b/core/services/relay/evm/mercury/config_digest_test.go index 3e94d075dce..fe718e92fe5 100644 --- a/core/services/relay/evm/mercury/config_digest_test.go +++ b/core/services/relay/evm/mercury/config_digest_test.go @@ -107,7 +107,7 @@ func GenHash(t *testing.T) gopter.Gen { array, ok := byteArray.(*gopter.GenResult).Retrieve() require.True(t, ok, "failed to retrieve gen result") for i, byteVal := range array.([]interface{}) { - rv[i] = byte(byteVal.(uint8)) + rv[i] = byteVal.(uint8) } return rv }, @@ -199,7 +199,7 @@ func GenBytes(t *testing.T) gopter.Gen { iArray := array.([]interface{}) rv := make([]byte, len(iArray)) for i, byteVal := range iArray { - rv[i] = byte(byteVal.(uint8)) + rv[i] = byteVal.(uint8) } return rv }, diff --git a/core/services/s4/storage_test.go b/core/services/s4/storage_test.go index 86161f298e4..199e3e6924b 100644 --- a/core/services/s4/storage_test.go +++ b/core/services/s4/storage_test.go @@ -52,7 +52,7 @@ func TestStorage_Errors(t *testing.T) { SlotId: 1, Version: 0, } - ormMock.On("Get", big.New(key.Address.Big()), uint(key.SlotId), mock.Anything).Return(nil, s4.ErrNotFound) + ormMock.On("Get", big.New(key.Address.Big()), key.SlotId, mock.Anything).Return(nil, s4.ErrNotFound) _, _, err := storage.Get(testutils.Context(t), key) assert.ErrorIs(t, err, s4.ErrNotFound) }) diff --git a/core/services/signatures/secp256k1/field.go b/core/services/signatures/secp256k1/field.go index 1ff0d062f3c..bf9652e5dbd 100644 --- a/core/services/signatures/secp256k1/field.go +++ b/core/services/signatures/secp256k1/field.go @@ -49,7 +49,7 @@ func (f *fieldElt) modQ() *fieldElt { func fieldEltFromBigInt(v *big.Int) *fieldElt { return (*fieldElt)(v).modQ() } func fieldEltFromInt(v int64) *fieldElt { - return fieldEltFromBigInt(big.NewInt(int64(v))).modQ() + return fieldEltFromBigInt(big.NewInt(v)).modQ() } var fieldZero = fieldEltFromInt(0) diff --git a/core/services/streams/delegate.go b/core/services/streams/delegate.go index 3b9b5c773ae..b62ceb9857b 100644 --- a/core/services/streams/delegate.go +++ b/core/services/streams/delegate.go @@ -47,7 +47,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e if !jb.Name.Valid { return nil, errors.New("job name is required to be present for stream specs") } - id := StreamID(jb.Name.String) + id := jb.Name.String lggr := d.lggr.Named(id).With("streamID", id) rrs := ocrcommon.NewResultRunSaver(d.runner, lggr, d.cfg.MaxSuccessfulRuns(), d.cfg.ResultWriteQueueDepth()) diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 591ca3e9508..8ad88d7b73b 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -693,7 +693,7 @@ func Test_VRFV2PlusServiceFailsWhenVRFOwnerProvided(t *testing.T) { vs := testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ VRFVersion: vrfcommon.V2Plus, PublicKey: vuni.vrfkey.PublicKey.String(), - FromAddresses: []string{string(vuni.submitter.Hex())}, + FromAddresses: []string{vuni.submitter.Hex()}, GasLanePrice: chain.Config().EVM().GasEstimator().PriceMax(), }) toml := "vrfOwnerAddress=\"0xF62fEFb54a0af9D32CDF0Db21C52710844c7eddb\"\n" + vs.Toml() diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go index 3603230fea0..24639af2f0d 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go @@ -35,7 +35,7 @@ func toGethLog(log types.Log) types.Log { return types.Log{ Address: log.Address, Topics: log.Topics, - Data: []byte(log.Data), + Data: log.Data, BlockNumber: log.BlockNumber, TxHash: log.TxHash, TxIndex: log.TxIndex, diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index 4caa3460857..61c8d1dc84d 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -45,7 +45,7 @@ func newChainsController[R jsonapi.EntityNamer](network relay.Network, chainStat newResource func(types.ChainStatus) R, lggr logger.Logger, auditLogger audit.AuditLogger) *chainsController[R] { return &chainsController[R]{ network: network, - resourceName: string(network) + "_chain", + resourceName: network + "_chain", chainStats: chainStats, errNotEnabled: errNotEnabled, newResource: newResource, diff --git a/core/web/presenters/user.go b/core/web/presenters/user.go index 19ccff960ac..7598c8864be 100644 --- a/core/web/presenters/user.go +++ b/core/web/presenters/user.go @@ -32,7 +32,7 @@ func NewUserResource(u sessions.User) *UserResource { return &UserResource{ JAID: NewJAID(u.Email), Email: u.Email, - Role: sessions.UserRole(u.Role), + Role: u.Role, HasActiveApiToken: hasToken, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, diff --git a/core/web/resolver/spec.go b/core/web/resolver/spec.go index 5e8937dbb96..b1cb32783f9 100644 --- a/core/web/resolver/spec.go +++ b/core/web/resolver/spec.go @@ -521,7 +521,7 @@ func (r *OCR2SpecResolver) P2PV2Bootstrappers() *[]string { // Relay resolves the spec's relay func (r *OCR2SpecResolver) Relay() string { - return string(r.spec.Relay) + return r.spec.Relay } // RelayConfig resolves the spec's relay config @@ -899,7 +899,7 @@ func (r *BootstrapSpecResolver) ContractID() string { // Relay resolves the spec's relay func (r *BootstrapSpecResolver) Relay() string { - return string(r.spec.Relay) + return r.spec.Relay } // RelayConfig resolves the spec's relay config diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index a4fb9cdb338..828e8538071 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -163,10 +163,10 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { EVMChainID: ubig.NewI(42), DrumbeatEnabled: false, IdleTimerDisabled: false, - IdleTimerPeriod: time.Duration(1 * time.Hour), + IdleTimerPeriod: 1 * time.Hour, MinPayment: commonassets.NewLinkFromJuels(1000), PollTimerDisabled: false, - PollTimerPeriod: time.Duration(1 * time.Minute), + PollTimerPeriod: 1 * time.Minute, }, }, nil) }, @@ -229,13 +229,13 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { CreatedAt: f.Timestamp(), EVMChainID: ubig.NewI(42), DrumbeatEnabled: true, - DrumbeatRandomDelay: time.Duration(1 * time.Second), + DrumbeatRandomDelay: 1 * time.Second, DrumbeatSchedule: "CRON_TZ=UTC 0 0 1 1 *", IdleTimerDisabled: true, - IdleTimerPeriod: time.Duration(1 * time.Hour), + IdleTimerPeriod: 1 * time.Hour, MinPayment: commonassets.NewLinkFromJuels(1000), PollTimerDisabled: true, - PollTimerPeriod: time.Duration(1 * time.Minute), + PollTimerPeriod: 1 * time.Minute, }, }, nil) }, From a69a78db465bfe0de65726b07b07779ec9d0362a Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:36:25 -0500 Subject: [PATCH 153/234] Run Argo CD app sync after image is published from PR (#11750) * Run Argo CD app sync after image is published from PR * Add test file * Set name to capitlized to avoid masking * Use gh cli instead of curl to get PR labels * Fix comment * Fix jq to work with gh cli * Set GH token as an env var for the cli * Combine labels to one line, comma delimited * Make comment valid --- .github/workflows/build-publish-pr.yml | 50 ++++++++++++++++++++++++-- test.txt | 0 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test.txt diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml index cdc9cf3f11c..fd5533c1b5b 100644 --- a/.github/workflows/build-publish-pr.yml +++ b/.github/workflows/build-publish-pr.yml @@ -2,8 +2,8 @@ name: "Build and Publish from PR" ## # This workflow builds and publishes a Docker image for Chainlink from a PR. -# It doesn't use an environment, has its own special IAM role, does not sign -# the image, and publishes to a special ECR repo. +# It has its own special IAM role, does not sign the image, and publishes to +# a special ECR repo. ## on: @@ -13,6 +13,7 @@ jobs: build-publish-untrusted: if: ${{ ! startsWith(github.ref_name, 'release/') }} runs-on: ubuntu-20.04 + environment: sdlc permissions: id-token: write contents: read @@ -53,6 +54,51 @@ jobs: dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + - name: Get PR labels + id: pr-labels + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.number }} + run: | + RESPONSE=$(gh pr view ${PR_NUMBER} --json labels) + # Check if the labels command was successful + if [[ $? -ne 0 ]]; then + echo "Error fetching labels" + exit 1 + fi + echo "RESPONSE=${RESPONSE}" + LABELS=$(echo "$RESPONSE" | jq -r '.labels | map(.name) | join(", ")') + # Check if any labels were found + if [[ -z "${LABELS:-}" ]]; then + echo "No labels found" + else + echo "labels=${LABELS}" | tee -a "${GITHUB_OUTPUT}" + fi + + - name: Setup GAP + if: contains(steps.pr-labels.outputs.labels, 'crib') + uses: smartcontractkit/.github/actions/setup-gap@main + with: + aws-region: ${{ secrets.AWS_REGION }} + aws-role-arn: ${{ secrets.AWS_OIDC_IAM_ROLE_PUBLISH_PR_ARN }} + api-gateway-host: ${{ secrets.AWS_API_GW_HOST_ARGO_SAND }} + use-argocd: "true" + argocd-user: ${{ secrets.ARGOCD_USER_SAND }} + argocd-pass: ${{ secrets.ARGOCD_PASS_SAND }} + + # Run an Argo CD sync after the image is built. + - name: Argo CD App Sync + if: contains(steps.pr-labels.outputs.labels, 'crib') + shell: bash + env: + PR_NUMBER: ${{ github.event.number }} + run: | + argocd app sync \ + --plaintext \ + --grpc-web \ + --async \ + "crib-chainlink-${PR_NUMBER}" + - name: Collect Metrics if: always() id: collect-gha-metrics diff --git a/test.txt b/test.txt new file mode 100644 index 00000000000..e69de29bb2d From 45bf1fd887c5e7dd84227c27998e55d1c1c70397 Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:53:08 +0100 Subject: [PATCH 154/234] Allow custom hooks for ClNode in e2e tests (#11745) * Allow custom hooks for ClNode in e2e tests * Fix for json.Marshall --- integration-tests/docker/test_env/cl_node.go | 44 +++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index a00dea3a35a..1466d03c6f1 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -51,7 +51,10 @@ type ClNode struct { PostgresDb *test_env.PostgresDb `json:"postgresDb"` UserEmail string `json:"userEmail"` UserPassword string `json:"userPassword"` - AlwaysPullImage bool + AlwaysPullImage bool `json:"-"` + PostStartsHooks []tc.ContainerHook `json:"-"` + PostStopsHooks []tc.ContainerHook `json:"-"` + PreTerminatesHooks []tc.ContainerHook `json:"-"` t *testing.T l zerolog.Logger ls *logstream.LogStream @@ -140,12 +143,32 @@ func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *ch PostgresDb: pgDb, l: log.Logger, } + n.SetDefaultHooks() for _, opt := range opts { opt(n) } return n, nil } +func (n *ClNode) SetDefaultHooks() { + n.PostStartsHooks = []tc.ContainerHook{ + func(ctx context.Context, c tc.Container) error { + if n.ls != nil { + return n.ls.ConnectContainer(ctx, c, "cl-node") + } + return nil + }, + } + n.PostStopsHooks = []tc.ContainerHook{ + func(ctx context.Context, c tc.Container) error { + if n.ls != nil { + return n.ls.DisconnectContainer(c) + } + return nil + }, + } +} + func (n *ClNode) SetTestLogger(t *testing.T) { n.l = logging.GetTestLogger(t) n.t = t @@ -475,22 +498,11 @@ func (n *ClNode) getContainerRequest(secrets string) ( }, }, LifecycleHooks: []tc.ContainerLifecycleHooks{ - {PostStarts: []tc.ContainerHook{ - func(ctx context.Context, c tc.Container) error { - if n.ls != nil { - return n.ls.ConnectContainer(ctx, c, "cl-node") - } - return nil - }, + { + PostStarts: n.PostStartsHooks, + PostStops: n.PostStopsHooks, + PreTerminates: n.PreTerminatesHooks, }, - PostStops: []tc.ContainerHook{ - func(ctx context.Context, c tc.Container) error { - if n.ls != nil { - return n.ls.DisconnectContainer(c) - } - return nil - }, - }}, }, }, nil } From 31565b630fa89ffe5f1ba8505e3890bf00554d18 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:53:06 -0500 Subject: [PATCH 155/234] Add bash scripts CI workflow (#11762) * Fix shellcheck error * Add bash scripts CI workflow * Use latest checkout action --- .github/workflows/bash-scripts.yml | 35 ++++++++++++++++++++++++++++++ tools/bin/cldev | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/bash-scripts.yml diff --git a/.github/workflows/bash-scripts.yml b/.github/workflows/bash-scripts.yml new file mode 100644 index 00000000000..52ce17f1f28 --- /dev/null +++ b/.github/workflows/bash-scripts.yml @@ -0,0 +1,35 @@ +name: Bash Scripts + +on: + pull_request: + +jobs: + changes: + name: detect changes + runs-on: ubuntu-latest + outputs: + bash-scripts-src: ${{ steps.bash-scripts.outputs.src }} + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 + id: bash-scripts + with: + filters: | + src: + - 'tools/bin/**' + - '.github/workflows/bash-scripts.yml' + shellcheck: + name: ShellCheck Lint + runs-on: ubuntu-latest + needs: [changes] + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run ShellCheck + if: needs.changes.outputs.bash-scripts-src == 'true' + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 + with: + scandir: "./tools/bin" + # Consider changing this to check for warnings once all warnings are fixed. + severity: error diff --git a/tools/bin/cldev b/tools/bin/cldev index af8d8c06135..77d0a7b8bb8 100755 --- a/tools/bin/cldev +++ b/tools/bin/cldev @@ -12,6 +12,6 @@ case "$1" in go run -ldflags "$LDFLAGS" . -- node start -d -p tools/secrets/password.txt -a tools/secrets/apicredentials ;; *) - go run . -- $@ + go run . -- "$@" ;; esac From c32efca111a53c3c1129260a4501b4cd404c888b Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 Jan 2024 15:03:46 -0500 Subject: [PATCH 156/234] Fix cold path cache hit panic (#11768) This is very difficult/impossible to test since it's reliant on specific timing. --- core/services/relay/evm/mercury/wsrpc/cache/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache.go b/core/services/relay/evm/mercury/wsrpc/cache/cache.go index 181900bc15c..712e62e5c0e 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache.go @@ -237,7 +237,7 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest // CACHE HIT promCacheHitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() m.lggr.Tracew("LatestReport CACHE HIT (cold path)", "feedID", feedIDHex) - defer v.RUnlock() + defer v.Unlock() return v.val, nil } else if v.fetching { // CACHE WAIT From 75fa7152e7a9e6b6ebaee3692a7fa027f20def39 Mon Sep 17 00:00:00 2001 From: Alex Kuznicki Date: Tue, 16 Jan 2024 07:04:42 -0700 Subject: [PATCH 157/234] increase latency bucket granularity on mercury_wsrpc_request_latency prom metric (#11772) --- core/services/relay/evm/mercury/wsrpc/metrics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/relay/evm/mercury/wsrpc/metrics.go b/core/services/relay/evm/mercury/wsrpc/metrics.go index f2000674f7f..8c12184cd85 100644 --- a/core/services/relay/evm/mercury/wsrpc/metrics.go +++ b/core/services/relay/evm/mercury/wsrpc/metrics.go @@ -28,7 +28,7 @@ var ( Namespace: "mercury", Name: "wsrpc_request_latency", Help: "Latency of requests made to the Mercury WSRPC server", - Buckets: []float64{10, 30, 100, 300, 1000, 3000, 10000}, + Buckets: []float64{10, 30, 100, 200, 250, 300, 350, 400, 500, 750, 1000, 3000, 10000}, }) ) From c2115d32801930fe9448b8b0ed036c2356c94776 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 16 Jan 2024 11:19:15 -0500 Subject: [PATCH 158/234] Deprecates Base Goerli (#11787) --- .github/workflows/live-testnet-tests.yml | 55 ++---------------------- 1 file changed, 3 insertions(+), 52 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 3298c45813a..da5bd59f76a 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -39,9 +39,6 @@ env: ARBITRUM_SEPOLIA_URLS: ${{ secrets.QA_ARBITRUM_SEPOLIA_URLS }} ARBITRUM_SEPOLIA_HTTP_URLS: ${{ secrets.QA_ARBITRUM_SEPOLIA_HTTP_URLS }} - BASE_GOERLI_URLS: ${{ secrets.QA_BASE_GOERLI_URLS }} - BASE_GOERLI_HTTP_URLS: ${{ secrets.QA_BASE_GOERLI_HTTP_URLS }} - BASE_SEPOLIA_URLS: ${{ secrets.QA_BASE_SEPOLIA_URLS }} BASE_SEPOLIA_HTTP_URLS: ${{ secrets.QA_BASE_SEPOLIA_HTTP_URLS }} @@ -66,7 +63,7 @@ env: jobs: # Build Test Dependencies - + build-chainlink: environment: integration permissions: @@ -144,7 +141,7 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: [sepolia-smoke-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-goerli-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, linea-goerli-smoke-tests] + needs: [sepolia-smoke-tests, optimism-sepolia-smoke-tests, arbitrum-sepolia-smoke-tests, base-sepolia-smoke-tests, polygon-mumbai-smoke-tests, avalanche-fuji-smoke-tests, fantom-testnet-smoke-tests, celo-alfajores-smoke-tests, linea-goerli-smoke-tests] steps: - name: Debug Result run: echo ${{ join(needs.*.result, ',') }} @@ -205,7 +202,7 @@ jobs: strategy: fail-fast: false matrix: - network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Goerli, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Linea Goerli] + network: [Sepolia, Optimism Sepolia, Arbitrum Sepolia, Base Sepolia, Polygon Mumbai, Avalanche Fuji, Fantom Testnet, Celo Alfajores, Linea Goerli] steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -427,52 +424,6 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - base-goerli-smoke-tests: - environment: integration - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: BASE_GOERLI - strategy: - max-parallel: 1 - fail-fast: false - matrix: - include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations - - product: OCR - test: TestOCRBasic - name: Base Goerli ${{ matrix.product }} Tests - runs-on: ubuntu-latest - steps: - - name: Download Tests Binary - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - with: - name: tests - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-base-goerli - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - with: - test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} - binary_name: tests - cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./logs - token: ${{ secrets.GITHUB_TOKEN }} - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: "true" - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - base-sepolia-smoke-tests: environment: integration permissions: From acebe22558d7e12ee5da035a3d49867dccd09f72 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 17 Jan 2024 09:57:49 -0800 Subject: [PATCH 159/234] Extract debugging readme to a standalone file (#11766) --- core/scripts/chaincli/DEBUGGING.md | 95 ++++++++++++++++++++++++++++++ core/scripts/chaincli/README.md | 89 ---------------------------- 2 files changed, 95 insertions(+), 89 deletions(-) create mode 100644 core/scripts/chaincli/DEBUGGING.md diff --git a/core/scripts/chaincli/DEBUGGING.md b/core/scripts/chaincli/DEBUGGING.md new file mode 100644 index 00000000000..7c644b1699a --- /dev/null +++ b/core/scripts/chaincli/DEBUGGING.md @@ -0,0 +1,95 @@ +## Automation Debugging Script + +### Context + +The debugging script is a tool within ChainCLI designed to facilitate the debugging of upkeeps in Automation v21, covering both conditional and log-based scenarios. + +### Setup + +Before starting, you will need: +1. Git clone this chainlink [repo](https://github.com/smartcontractkit/chainlink) +2. A working [Go](https://go.dev/doc/install) installation +2. Change directory to `core/scripts/chaincli` and create a `.env` file based on the example `.env.debugging.example` + +### Configuration in `.env` File + +#### Mandatory Fields + +Ensure the following fields are provided in your `.env` file: + +- `NODE_URL`: Archival node URL +- `KEEPER_REGISTRY_ADDRESS`: Address of the Keeper Registry contract. Refer to the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks#configurations) doc for addresses. + +#### Optional Fields (Streams Lookup) + +If your targeted upkeep involves streams lookup, include the following information: + +- `DATA_STREAMS_ID` +- `DATA_STREAMS_KEY` +- `DATA_STREAMS_LEGACY_URL` +- `DATA_STREAMS_URL` + +#### Optional Fields (Tenderly Integration) + +For detailed transaction simulation logs, set up Tenderly credentials. Refer to the [Tenderly Documentation](https://docs.tenderly.co/other/platform-access/how-to-generate-api-access-tokens) for creating an API key, account name, and project name. + +- `TENDERLY_KEY` +- `TENDERLY_ACCOUNT_NAME` +- `TENDERLY_PROJECT_NAME` + +### Usage + +Execute the following command based on your upkeep type: + +- For conditional upkeep: + + ```bash + go run main.go keeper debug UPKEEP_ID + ``` + +- For log trigger upkeep: + + ```bash + go run main.go keeper debug UPKEEP_ID TX_HASH LOG_INDEX + ``` + +### Checks Performed by the Debugging Script + +1. **Fetch and Sanity Check Upkeep:** + - Verify upkeep status: active, paused, or canceled + - Check upkeep balance + +2. **For Conditional Upkeep:** + - Check conditional upkeep + - Simulate `performUpkeep` + +3. **For Log Trigger Upkeep:** + - Check if the upkeep has already run for log-trigger-based upkeep + - Verify if log matches trigger configuration + - Check upkeep + - If check result indicates a streams lookup is required (TargetCheckReverted): + - Verify if the upkeep is allowed to use Mercury + - Execute Mercury request + - Execute check callback + + - Simulate `performUpkeep` + +### Examples +- Eligible and log trigger based and using mercury lookup v0.3: + + ```bash + go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xdc6d0e547a5aa85fefa5b0f3a37e3493eafb5aeba8b5f3071ce53c9e9a539e9c 0 + ``` + +- Ineligible and conditional upkeep: + + ```bash + go run main.go keeper debug 52635131310730056105456985154251306793887717546629785340977553840883117540096 + ``` + +- Ineligible and Log does not match trigger config: + + ```bash + go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xc0686ae85d2a7a976ef46df6c613517b9fd46f23340ac583be4e44f5c8b7a186 1 + ``` +--- \ No newline at end of file diff --git a/core/scripts/chaincli/README.md b/core/scripts/chaincli/README.md index bcf407b684b..992250ae77c 100644 --- a/core/scripts/chaincli/README.md +++ b/core/scripts/chaincli/README.md @@ -123,93 +123,4 @@ You can use the `grep` and `grepv` flags to filter log lines, e.g. to only show ./chaincli keeper logs --grep keepers-plugin ``` ---- - -## ChainCLI Automation Debugging Script - -### Context - -The debugging script is a tool within ChainCLI designed to facilitate the debugging of upkeeps in Automation v21, covering both conditional and log-based scenarios. - -### Configuration - -#### Mandatory Fields - -Ensure the following fields are provided in your `.env` file: - -- `NODE_URL`: Archival node URL -- `KEEPER_REGISTRY_ADDRESS`: Address of the Keeper Registry contract. Refer to the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks#configurations) doc for addresses. - -#### Additional Fields (Streams Lookup) - -If your targeted upkeep involves streams lookup, include the following information: - -- `MERCURY_ID` -- `MERCURY_KEY` -- `MERCURY_LEGACY_URL` -- `MERCURY_URL` - -#### Tenderly Integration - -For detailed transaction simulation logs, set up Tenderly credentials. Refer to the [Tenderly Documentation](https://docs.tenderly.co/other/platform-access/how-to-generate-api-access-tokens) for creating an API key, account name, and project name. - -- `TENDERLY_KEY` -- `TENDERLY_ACCOUNT_NAME` -- `TENDERLY_PROJECT_NAME` - -### Usage - -Execute the following command based on your upkeep type: - -- For conditional upkeep: - - ```bash - go run main.go keeper debug UPKEEP_ID - ``` - -- For log trigger upkeep: - - ```bash - go run main.go keeper debug UPKEEP_ID TX_HASH LOG_INDEX - ``` - -### Checks Performed by the Debugging Script - -1. **Fetch and Sanity Check Upkeep:** - - Verify upkeep status: active, paused, or canceled - - Check upkeep balance - -2. **For Conditional Upkeep:** - - Check conditional upkeep - - Simulate `performUpkeep` - -3. **For Log Trigger Upkeep:** - - Check if the upkeep has already run for log-trigger-based upkeep - - Verify if log matches trigger configuration - - Check upkeep - - If check result indicates a streams lookup is required (TargetCheckReverted): - - Verify if the upkeep is allowed to use Mercury - - Execute Mercury request - - Execute check callback - - - Simulate `performUpkeep` - -### Examples -- Eligible and log trigger based and using mercury lookup v0.3: - - ```bash - go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xdc6d0e547a5aa85fefa5b0f3a37e3493eafb5aeba8b5f3071ce53c9e9a539e9c 0 - ``` - -- Ineligible and conditional upkeep: - - ```bash - go run main.go keeper debug 52635131310730056105456985154251306793887717546629785340977553840883117540096 - ``` - -- Ineligible and Log does not match trigger config: - - ```bash - go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xc0686ae85d2a7a976ef46df6c613517b9fd46f23340ac583be4e44f5c8b7a186 1 - ``` --- \ No newline at end of file From ac372a4cbf88abc7f26d0b3a8750ab8da71bd025 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 17 Jan 2024 10:04:11 -0800 Subject: [PATCH 160/234] Use a more readable string for upkeep failure reasons (#11770) --- core/scripts/chaincli/handler/debug.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index c317edcac37..628aef0b434 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -252,7 +252,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { upkeepNeeded, performData = checkResult.UpkeepNeeded, checkResult.PerformData // handle streams lookup if checkResult.UpkeepFailureReason != 0 { - message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) + message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %s", getCheckUpkeepFailureReason(checkResult.UpkeepFailureReason))) } if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { @@ -377,6 +377,28 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } +func getCheckUpkeepFailureReason(reasonIndex uint8) string { + // Copied from KeeperRegistryBase2_1.sol + reasonStrings := []string{ + "NONE", + "UPKEEP_CANCELLED", + "UPKEEP_PAUSED", + "TARGET_CHECK_REVERTED", + "UPKEEP_NOT_NEEDED", + "PERFORM_DATA_EXCEEDS_LIMIT", + "INSUFFICIENT_BALANCE", + "CALLBACK_REVERTED", + "REVERT_DATA_EXCEEDS_LIMIT", + "REGISTRY_PAUSED", + } + + if int(reasonIndex) < len(reasonStrings) { + return reasonStrings[reasonIndex] + } + + return fmt.Sprintf("Unknown : %d", reasonIndex) +} + func mustAutomationCheckResult(upkeepID *big.Int, checkResult iregistry21.CheckUpkeep, trigger ocr2keepers.Trigger) ocr2keepers.CheckResult { upkeepIdentifier := mustUpkeepIdentifier(upkeepID) checkResult2 := ocr2keepers.CheckResult{ From 39bc71305029848ecbad46b402e1022849467966 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 17 Jan 2024 15:07:17 -0300 Subject: [PATCH 161/234] remove .run.id during test cleanup (#11786) --- integration-tests/Makefile | 5 +++ integration-tests/docker/test_env/test_env.go | 8 ++++ .../docker/test_env/test_env_builder.go | 2 +- integration-tests/go.mod | 5 ++- integration-tests/go.sum | 11 +++-- .../scripts/search_and_delete.sh | 41 +++++++++++++++++++ 6 files changed, 65 insertions(+), 7 deletions(-) create mode 100755 integration-tests/scripts/search_and_delete.sh diff --git a/integration-tests/Makefile b/integration-tests/Makefile index d1e9bdd89ef..62d9b281d66 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -212,3 +212,8 @@ run_test_with_local_image: build_docker_image ARGS="$(args)" \ PRODUCT=$(product) \ ./scripts/run_product_tests + +# removes all occurrences of .run.id file in current folder and it's subdirectories +# before making any changes lists all file locations and awaits user confirmation +remove_test_execution_artefacts: + ./scripts/search_and_delete.sh .run.id \ No newline at end of file diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index b928bc9e664..c27078dc16f 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logstream" + "github.com/smartcontractkit/chainlink-testing-framework/utils/runid" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -206,9 +207,16 @@ func (te *CLClusterTestEnv) Terminate() error { // Cleanup cleans the environment up after it's done being used, mainly for returning funds when on live networks and logs. func (te *CLClusterTestEnv) Cleanup() error { te.l.Info().Msg("Cleaning up test environment") + + runIdErr := runid.RemoveLocalRunId() + if runIdErr != nil { + te.l.Warn().Msgf("Failed to remove .run.id file due to: %s (not a big deal, you can still remove it manually)", runIdErr.Error()) + } + if te.t == nil { return fmt.Errorf("cannot cleanup test environment without a testing.T") } + if te.ClCluster == nil || len(te.ClCluster.Nodes) == 0 { return fmt.Errorf("chainlink nodes are nil, unable cleanup chainlink nodes") } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index bd73d33d9bf..dd5e2643bcd 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -267,7 +267,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { if b.te.LogStream != nil { b.t.Cleanup(func() { - b.l.Warn().Msg("Shutting down LogStream") + b.l.Info().Msg("Shutting down LogStream") logPath, err := osutil.GetAbsoluteFolderPath("logs") if err != nil { b.l.Info().Str("Absolute path", logPath).Msg("LogStream logs folder location") diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 17a98e450a8..d6f1fa94c6d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -26,7 +26,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b - github.com/smartcontractkit/chainlink-testing-framework v1.22.0 + github.com/smartcontractkit/chainlink-testing-framework v1.22.4 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 @@ -215,6 +215,7 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-github/v41 v41.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -426,7 +427,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.6.0 // indirect - golang.org/x/crypto v0.16.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 916145aca49..4a089e54997 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -761,6 +761,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg= +github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -1493,8 +1495,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f31 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= -github.com/smartcontractkit/chainlink-testing-framework v1.22.0 h1:Lur628wkrceWgcLmxGZe7Mauwxht4YO71hX9Jj5YslE= -github.com/smartcontractkit/chainlink-testing-framework v1.22.0/go.mod h1:yu6qqrppNJfutQV37fiSs4eS0uQP5QT0ebi3tlIgWN0= +github.com/smartcontractkit/chainlink-testing-framework v1.22.4 h1:617FyrN/wQNFLsGDofqKAQ5/P+swmIPo8sIJZqBPvsQ= +github.com/smartcontractkit/chainlink-testing-framework v1.22.4/go.mod h1:FBRC6elqaqO8jiMZMfa3UKrvwzZhMGUtYqVTGYmfFrA= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= @@ -1793,14 +1795,15 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= diff --git a/integration-tests/scripts/search_and_delete.sh b/integration-tests/scripts/search_and_delete.sh new file mode 100755 index 00000000000..ae1f8a6e70a --- /dev/null +++ b/integration-tests/scripts/search_and_delete.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + read -p "Enter the comma-separated list of filenames to search for: " filenames +else + filenames=$@ +fi + +IFS=',' read -ra filenames_arr <<< "$filenames" + +# Start search from the current working directory +current_dir=$(pwd) + +echo "Searching for files in $current_dir..." +found_files=() +for filename in "${filenames_arr[@]}"; do + while IFS= read -r file; do + found_files+=("$file") + done < <(find "$current_dir" -type f -name "$filename" 2>/dev/null) +done + +if [[ ${#found_files[@]} -eq 0 ]]; then + echo "No files found." + exit 0 +fi + +echo "Found files:" +for file in "${found_files[@]}"; do + echo "$file" +done + +read -p "Do you want to remove all these files? (y/n): " confirm + +if [[ $confirm == "yes" ]] || [[ $confirm == "y" ]]; then + for file in "${found_files[@]}"; do + rm "$file" + done + echo "Files removed." +else + echo "Files not removed." +fi \ No newline at end of file From 8f96cf25df5bdda4bd63b8bdb762b15727c3b0eb Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 17 Jan 2024 13:55:18 -0500 Subject: [PATCH 162/234] adding gin tracing to core (#11796) --- core/scripts/go.mod | 1 + core/scripts/go.sum | 4 ++++ core/web/router.go | 2 ++ go.mod | 1 + go.sum | 4 ++++ integration-tests/go.sum | 3 +++ 6 files changed, 15 insertions(+) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 62ae3f4a3ec..2a60e0d4a14 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -281,6 +281,7 @@ require ( go.dedis.ch/fixbuf v1.0.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect go.opentelemetry.io/otel v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 0da8f55aa27..c2fc995d0df 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1364,8 +1364,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 h1:mMv2jG58h6ZI5t5S9QCVGdzCmAsTakMa3oxVgpSD44g= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1/go.mod h1:oqRuNKG0upTaDPbLVCG8AD0G2ETrfDtmh7jViy7ox6M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1 h1:WPYiUgmw3+b7b3sQ1bFBFAf0q+Di9dvNc3AtYfnT4RQ= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1/go.mod h1:EmzokPoSqsYMBVK4nRnhsfm5mbn8J1eDuz/U1UaQaWg= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= diff --git a/core/web/router.go b/core/web/router.go index 5c5e86a12b6..6401622e192 100644 --- a/core/web/router.go +++ b/core/web/router.go @@ -32,6 +32,7 @@ import ( mgin "github.com/ulule/limiter/v3/drivers/middleware/gin" "github.com/ulule/limiter/v3/drivers/store/memory" "github.com/unrolled/secure" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -60,6 +61,7 @@ func NewRouter(app chainlink.Application, prometheus *ginprom.Prometheus) (*gin. tls := config.WebServer().TLS() engine.Use( + otelgin.Middleware("chainlink-web-routes"), limits.RequestSizeLimiter(config.WebServer().HTTPMaxSize()), loggerFunc(app.GetLogger()), gin.Recovery(), diff --git a/go.mod b/go.mod index 3f834e2d848..fd1ffe4760a 100644 --- a/go.mod +++ b/go.mod @@ -87,6 +87,7 @@ require ( github.com/urfave/cli v1.22.14 go.dedis.ch/fixbuf v1.0.3 go.dedis.ch/kyber/v3 v3.1.0 + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.16.0 diff --git a/go.sum b/go.sum index 014ed5ba627..0e2b917f251 100644 --- a/go.sum +++ b/go.sum @@ -1350,8 +1350,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 h1:mMv2jG58h6ZI5t5S9QCVGdzCmAsTakMa3oxVgpSD44g= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1/go.mod h1:oqRuNKG0upTaDPbLVCG8AD0G2ETrfDtmh7jViy7ox6M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1 h1:WPYiUgmw3+b7b3sQ1bFBFAf0q+Di9dvNc3AtYfnT4RQ= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1/go.mod h1:EmzokPoSqsYMBVK4nRnhsfm5mbn8J1eDuz/U1UaQaWg= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 4a089e54997..f4129568536 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1717,6 +1717,9 @@ go.opentelemetry.io/collector/pdata v1.0.0-rcv0016 h1:qCPXSQCoD3qeWFb1RuIks8fw9A go.opentelemetry.io/collector/pdata v1.0.0-rcv0016/go.mod h1:OdN0alYOlYhHXu6BDlGehrZWgtBuiDsz/rlNeJeXiNg= go.opentelemetry.io/collector/semconv v0.87.0 h1:BsG1jdLLRCBRlvUujk4QA86af7r/ZXnizczQpEs/gg8= go.opentelemetry.io/collector/semconv v0.87.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw= +go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 h1:mMv2jG58h6ZI5t5S9QCVGdzCmAsTakMa3oxVgpSD44g= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1/go.mod h1:oqRuNKG0upTaDPbLVCG8AD0G2ETrfDtmh7jViy7ox6M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= From a075447ef94726b2510b9e47bb835cc0b75cc493 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 17 Jan 2024 15:25:30 -0500 Subject: [PATCH 163/234] Removes 4 Core GitHub Runners (#11803) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/solidity-hardhat.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5b846d8708d..fea4260fdc9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,7 +13,7 @@ on: jobs: analyze: name: Analyze ${{ matrix.language }} - runs-on: ubuntu20.04-4cores-16GB + runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/solidity-hardhat.yml b/.github/workflows/solidity-hardhat.yml index 9301c6f3967..9e29d034225 100644 --- a/.github/workflows/solidity-hardhat.yml +++ b/.github/workflows/solidity-hardhat.yml @@ -63,7 +63,7 @@ jobs: fail-fast: false matrix: split: ${{ fromJson(needs.split-tests.outputs.splits) }} - runs-on: ubuntu20.04-4cores-16GB + runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -128,7 +128,7 @@ jobs: fail-fast: false matrix: split: ${{ fromJson(needs.split-tests.outputs.splits) }} - runs-on: ubuntu20.04-4cores-16GB + runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 From 39847bb007ebb92bd3153d4ea5962ab061ed148d Mon Sep 17 00:00:00 2001 From: Sishir Giri Date: Wed, 17 Jan 2024 17:30:22 -0800 Subject: [PATCH 164/234] [Fix] Add Scroll chainType and L1GasOracle (#11790) * added scroll l1 oracle gas * nit * updated docs * nit * linter * amits comment --- common/config/chaintype.go | 5 +++-- .../config/toml/defaults/Scroll_Mainnet.toml | 1 + .../config/toml/defaults/Scroll_Sepolia.toml | 1 + .../evm/gas/rollups/l1_gas_price_oracle.go | 12 +++++++++- .../gas/rollups/l1_gas_price_oracle_test.go | 22 +++++++++++++++++++ core/config/docs/chains-evm.toml | 2 +- core/services/chainlink/config_test.go | 4 ++-- core/services/ocr/contract_tracker.go | 2 +- docs/CONFIG.md | 4 +++- 9 files changed, 45 insertions(+), 8 deletions(-) diff --git a/common/config/chaintype.go b/common/config/chaintype.go index 21fb8cd297d..9ef4864b86e 100644 --- a/common/config/chaintype.go +++ b/common/config/chaintype.go @@ -18,16 +18,17 @@ const ( ChainWeMix ChainType = "wemix" ChainKroma ChainType = "kroma" ChainZkSync ChainType = "zksync" + ChainScroll ChainType = "scroll" ) var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Join([]string{ string(ChainArbitrum), string(ChainMetis), string(ChainXDai), string(ChainOptimismBedrock), string(ChainCelo), - string(ChainKroma), string(ChainWeMix), string(ChainZkSync)}, ", ")) + string(ChainKroma), string(ChainWeMix), string(ChainZkSync), string(ChainScroll)}, ", ")) // IsValid returns true if the ChainType value is known or empty. func (c ChainType) IsValid() bool { switch c { - case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix, ChainZkSync: + case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix, ChainZkSync, ChainScroll: return true } return false diff --git a/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml b/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml index 56ed84c7f38..e087b86fe3e 100644 --- a/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml @@ -1,5 +1,6 @@ ChainID = '534352' FinalityDepth = 1 +ChainType = 'scroll' LogPollInterval = '3s' MinIncomingConfirmations = 1 # Scroll only emits blocks when a new tx is received, so this method of liveness detection is not useful diff --git a/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml b/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml index af17c4d485e..9db7a8ebed5 100644 --- a/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml +++ b/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml @@ -1,5 +1,6 @@ ChainID = '534351' FinalityDepth = 1 +ChainType = 'scroll' LogPollInterval = '3s' MinIncomingConfirmations = 1 # Scroll only emits blocks when a new tx is received, so this method of liveness detection is not useful diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index 8df817f7864..a006ea89923 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -65,11 +65,18 @@ const ( // `function l1BaseFee() external view returns (uint256);` KromaGasOracle_l1BaseFee = "519b4bd3" + // GasOracleAddress is the address of the precompiled contract that exists on scroll chain. + // This is the case for Scroll. + ScrollGasOracleAddress = "0x5300000000000000000000000000000000000002" + // GasOracle_l1BaseFee is the a hex encoded call to: + // `function l1BaseFee() external view returns (uint256);` + ScrollGasOracle_l1BaseFee = "519b4bd3" + // Interval at which to poll for L1BaseFee. A good starting point is the L1 block time. PollPeriod = 12 * time.Second ) -var supportedChainTypes = []config.ChainType{config.ChainArbitrum, config.ChainOptimismBedrock, config.ChainKroma} +var supportedChainTypes = []config.ChainType{config.ChainArbitrum, config.ChainOptimismBedrock, config.ChainKroma, config.ChainScroll} func IsRollupWithL1Support(chainType config.ChainType) bool { return slices.Contains(supportedChainTypes, chainType) @@ -87,6 +94,9 @@ func NewL1GasPriceOracle(lggr logger.Logger, ethClient ethClient, chainType conf case config.ChainKroma: address = KromaGasOracleAddress callArgs = KromaGasOracle_l1BaseFee + case config.ChainScroll: + address = ScrollGasOracleAddress + callArgs = ScrollGasOracle_l1BaseFee default: panic(fmt.Sprintf("Received unspported chaintype %s", chainType)) } diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go index 1a1d1ffdeee..188376bf832 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go @@ -100,4 +100,26 @@ func TestL1GasPriceOracle(t *testing.T) { assert.Equal(t, assets.NewWei(l1BaseFee), gasPrice) }) + + t.Run("Calling GasPrice on started Scroll L1Oracle returns Scroll l1GasPrice", func(t *testing.T) { + l1BaseFee := big.NewInt(200) + + ethClient := mocks.NewETHClient(t) + ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { + callMsg := args.Get(1).(ethereum.CallMsg) + blockNumber := args.Get(2).(*big.Int) + assert.Equal(t, ScrollGasOracleAddress, callMsg.To.String()) + assert.Equal(t, ScrollGasOracle_l1BaseFee, fmt.Sprintf("%x", callMsg.Data)) + assert.Nil(t, blockNumber) + }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) + + oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainScroll) + require.NoError(t, oracle.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + + gasPrice, err := oracle.GasPrice(testutils.Context(t)) + require.NoError(t, err) + + assert.Equal(t, assets.NewWei(l1BaseFee), gasPrice) + }) } diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 711889b3fa5..53f5cb68529 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -14,7 +14,7 @@ BlockBackfillDepth = 10 # Default # BlockBackfillSkip enables skipping of very long backfills. BlockBackfillSkip = false # Default # ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync +# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync, scroll ChainType = 'arbitrum' # Example # FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID so it should not be necessary to change this under normal operation. # BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index a7ac1e86d84..7e4be202702 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -1196,7 +1196,7 @@ func TestConfig_Validate(t *testing.T) { - 1: 6 errors: - ChainType: invalid value (Foo): must not be set with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync or omitted + - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync, scroll or omitted - HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth - GasEstimator: 2 errors: - FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault @@ -1205,7 +1205,7 @@ func TestConfig_Validate(t *testing.T) { - 2: 5 errors: - ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync or omitted + - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync, scroll or omitted - FinalityDepth: invalid value (0): must be greater than or equal to 1 - MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1 - 3.Nodes: 5 errors: diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 1287e52e9b5..9eabf93a834 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -402,7 +402,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight // care about the block height; we have no way of getting the L1 block // height anyway return 0, nil - case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix, config.ChainZkSync: + case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix, config.ChainZkSync, config.ChainScroll: // continue } latestBlockHeight := t.getLatestBlockHeight() diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 421b0ea9dad..3ae4afa3bcf 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -5014,6 +5014,7 @@ GasLimit = 14500000 AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false +ChainType = 'scroll' FinalityDepth = 1 FinalityTagEnabled = false LogBackfillBatchSize = 1000 @@ -5094,6 +5095,7 @@ GasLimit = 5300000 AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false +ChainType = 'scroll' FinalityDepth = 1 FinalityTagEnabled = false LogBackfillBatchSize = 1000 @@ -5448,7 +5450,7 @@ BlockBackfillSkip enables skipping of very long backfills. ChainType = 'arbitrum' # Example ``` ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync +Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync, scroll ### FinalityDepth ```toml From fcc300610beb2a7526ab5a3f5a9ae3eb034733b8 Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Thu, 18 Jan 2024 03:04:10 +0000 Subject: [PATCH 165/234] Automation LOOPP services (#11631) * Import from chainlink-common instead of chainlink-automation Update chainlink-automation Update the relayer to expose services Reduce the services created outside LOOPP Bump dependencies This reverts commit aaed53e0be87c94918471d57b8144f0fca7ed5dd. Pass provider services into the delegate conf Use more provider services, bump common Move MercuryCredentials into common Clean up the old automation services for 2.1 Bump common and automation Make modgraph Pass mercury credentials directly * Bump automation and common * Bump testing framework * Bump common and automation to latest main --- core/config/mercury_config.go | 4 +- core/scripts/chaincli/handler/debug.go | 7 +- core/scripts/go.mod | 4 +- core/scripts/go.sum | 8 +- core/services/chainlink/config_mercury.go | 7 +- .../services/chainlink/config_mercury_test.go | 8 +- core/services/ocr2/delegate.go | 57 ++++---- core/services/ocr2/models/models.go | 8 -- .../ocr2keeper/evmregistry/v21/active_list.go | 8 +- .../evmregistry/v21/active_list_test.go | 30 +++-- .../v21/autotelemetry21/custom_telemetry.go | 7 +- .../evmregistry/v21/block_subscriber.go | 2 +- .../evmregistry/v21/block_subscriber_test.go | 2 +- .../evmregistry/v21/core/interfaces.go | 2 +- .../v21/core/mocks/upkeep_state_reader.go | 14 +- .../evmregistry/v21/core/payload.go | 2 +- .../evmregistry/v21/core/payload_test.go | 12 +- .../evmregistry/v21/core/testutil.go | 6 +- .../evmregistry/v21/core/trigger.go | 12 +- .../evmregistry/v21/core/trigger_test.go | 2 +- .../ocr2keeper/evmregistry/v21/core/type.go | 12 +- .../evmregistry/v21/core/type_test.go | 20 +-- .../evmregistry/v21/encoding/encoder.go | 8 +- .../evmregistry/v21/encoding/encoder_test.go | 24 ++-- .../evmregistry/v21/encoding/interface.go | 2 +- .../evmregistry/v21/encoding/packer.go | 2 +- .../evmregistry/v21/encoding/packer_test.go | 2 +- .../evmregistry/v21/logprovider/buffer.go | 2 +- .../v21/logprovider/buffer_test.go | 58 ++++---- .../v21/logprovider/integration_test.go | 8 +- .../evmregistry/v21/logprovider/provider.go | 2 +- .../logprovider/provider_life_cycle_test.go | 16 +-- .../v21/logprovider/provider_test.go | 2 +- .../evmregistry/v21/logprovider/recoverer.go | 6 +- .../v21/logprovider/recoverer_test.go | 104 +++++++------- .../evmregistry/v21/mercury/mercury.go | 5 +- .../v21/mercury/streams/streams.go | 2 +- .../v21/mercury/streams/streams_test.go | 9 +- .../v21/mercury/v02/v02_request_test.go | 7 +- .../v21/mercury/v03/v03_request_test.go | 7 +- .../evmregistry/v21/payload_builder.go | 8 +- .../evmregistry/v21/payload_builder_test.go | 30 +++-- .../ocr2keeper/evmregistry/v21/registry.go | 21 +-- .../v21/registry_check_pipeline.go | 6 +- .../v21/registry_check_pipeline_test.go | 21 +-- .../evmregistry/v21/registry_test.go | 44 +++--- .../ocr2keeper/evmregistry/v21/services.go | 109 +-------------- .../evmregistry/v21/transmit/cache.go | 2 +- .../evmregistry/v21/transmit/cache_test.go | 2 +- .../evmregistry/v21/transmit/encoding.go | 2 +- .../evmregistry/v21/transmit/encoding_test.go | 6 +- .../v21/transmit/event_provider.go | 8 +- .../v21/transmit/event_provider_test.go | 6 +- .../evmregistry/v21/upkeep_provider.go | 5 +- .../evmregistry/v21/upkeep_provider_test.go | 14 +- .../evmregistry/v21/upkeepstate/store.go | 2 +- .../evmregistry/v21/upkeepstate/store_test.go | 2 +- core/services/ocr2/plugins/ocr2keeper/util.go | 28 +--- core/services/relay/evm/evm.go | 2 +- core/services/relay/evm/ocr2keeper.go | 127 ++++++++++++++++-- go.md | 1 + go.mod | 4 +- go.sum | 8 +- integration-tests/go.mod | 4 +- integration-tests/go.sum | 8 +- 65 files changed, 499 insertions(+), 471 deletions(-) delete mode 100644 core/services/ocr2/models/models.go diff --git a/core/config/mercury_config.go b/core/config/mercury_config.go index 6a483c593e3..1210fd282ef 100644 --- a/core/config/mercury_config.go +++ b/core/config/mercury_config.go @@ -3,7 +3,7 @@ package config import ( "time" - ocr2models "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) type MercuryCache interface { @@ -17,7 +17,7 @@ type MercuryTLS interface { } type Mercury interface { - Credentials(credName string) *ocr2models.MercuryCredentials + Credentials(credName string) *types.MercuryCredentials Cache() MercuryCache TLS() MercuryTLS } diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 628aef0b434..c7b2ad7acbc 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -14,13 +14,15 @@ import ( "os" "strconv" + types2 "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" @@ -31,7 +33,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" @@ -256,7 +257,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { - mc := &models.MercuryCredentials{LegacyURL: k.cfg.MercuryLegacyURL, URL: k.cfg.MercuryURL, Username: k.cfg.MercuryID, Password: k.cfg.MercuryKey} + mc := &types2.MercuryCredentials{LegacyURL: k.cfg.MercuryLegacyURL, URL: k.cfg.MercuryURL, Username: k.cfg.MercuryID, Password: k.cfg.MercuryKey} mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) lggr, _ := logger.NewLogger() blockSub := &blockSubscriber{k.client} diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 2a60e0d4a14..1efcd5275d2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -18,8 +18,8 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 - github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b + github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index c2fc995d0df..cf504cf6047 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1162,10 +1162,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= -github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b h1:PRuvHgPka1gIGPKASuJHvqUvYWhZO7z5B/kKapjkZaM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 h1:3cWO2/lFVDul5SVTgl4/RX/GXcT8Zq5NGMPeNEz09tY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/chainlink/config_mercury.go b/core/services/chainlink/config_mercury.go index 49f1cf0a5f4..27303a68899 100644 --- a/core/services/chainlink/config_mercury.go +++ b/core/services/chainlink/config_mercury.go @@ -3,9 +3,10 @@ package chainlink import ( "time" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" ) var _ config.MercuryCache = (*mercuryCacheConfig)(nil) @@ -37,9 +38,9 @@ type mercuryConfig struct { s toml.MercurySecrets } -func (m *mercuryConfig) Credentials(credName string) *models.MercuryCredentials { +func (m *mercuryConfig) Credentials(credName string) *types.MercuryCredentials { if mc, ok := m.s.Credentials[credName]; ok { - c := &models.MercuryCredentials{ + c := &types.MercuryCredentials{ URL: mc.URL.URL().String(), Password: string(*mc.Password), Username: string(*mc.Username), diff --git a/core/services/chainlink/config_mercury_test.go b/core/services/chainlink/config_mercury_test.go index 71dfb2a01ce..1ae8dc0ba2e 100644 --- a/core/services/chainlink/config_mercury_test.go +++ b/core/services/chainlink/config_mercury_test.go @@ -3,11 +3,11 @@ package chainlink import ( "testing" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" - "github.com/smartcontractkit/chainlink/v2/core/config/toml" ) @@ -33,8 +33,8 @@ func TestMercuryConfig(t *testing.T) { require.NoError(t, err) m := cfg.Mercury() - assert.Equal(t, &models.MercuryCredentials{URL: "https://chain1.link", Username: "username1", Password: "password1"}, m.Credentials("cred1")) - assert.Equal(t, &models.MercuryCredentials{URL: "https://chain2.link", Username: "username2", Password: "password2"}, m.Credentials("cred2")) + assert.Equal(t, &types.MercuryCredentials{URL: "https://chain1.link", Username: "username1", Password: "password1"}, m.Credentials("cred1")) + assert.Equal(t, &types.MercuryCredentials{URL: "https://chain2.link", Username: "username2", Password: "password2"}, m.Credentials("cred2")) } func TestMercuryTLS(t *testing.T) { diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 37235437de1..2ea99d65cfe 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -43,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/persistence" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions" @@ -188,7 +187,7 @@ type jobPipelineConfig interface { } type mercuryConfig interface { - Credentials(credName string) *models.MercuryCredentials + Credentials(credName string) *types.MercuryCredentials Cache() coreconfig.MercuryCache TLS() coreconfig.MercuryTLS } @@ -1089,12 +1088,13 @@ func (d *Delegate) newServicesOCR2Keepers21( provider, err := relayer.NewPluginProvider(ctx, types.RelayArgs{ - ExternalJobID: jb.ExternalJobID, - JobID: jb.ID, - ContractID: spec.ContractID, - New: d.isNewlyCreatedJob, - RelayConfig: spec.RelayConfig.Bytes(), - ProviderType: string(spec.PluginType), + ExternalJobID: jb.ExternalJobID, + JobID: jb.ID, + ContractID: spec.ContractID, + New: d.isNewlyCreatedJob, + RelayConfig: spec.RelayConfig.Bytes(), + ProviderType: string(spec.PluginType), + MercuryCredentials: mc, }, types.PluginArgs{ TransmitterID: transmitterID, PluginConfig: spec.PluginConfig.Bytes(), @@ -1108,12 +1108,7 @@ func (d *Delegate) newServicesOCR2Keepers21( return nil, errors.New("could not coerce PluginProvider to AutomationProvider") } - chain, err := d.legacyChains.Get(rid.ChainID) - if err != nil { - return nil, fmt.Errorf("keeper2 services: failed to get chain %s: %w", rid.ChainID, err) - } - - services, err := ocr2keeper.EVMDependencies21(jb, d.db, lggr, chain, mc, kb, d.cfg.Database()) + services, err := ocr2keeper.EVMDependencies21(kb) if err != nil { return nil, errors.Wrap(err, "could not build dependencies for ocr2 keepers") } @@ -1154,15 +1149,15 @@ func (d *Delegate) newServicesOCR2Keepers21( OffchainKeyring: kb, OnchainKeyring: services.Keyring(), LocalConfig: lc, - LogProvider: services.LogEventProvider(), - EventProvider: services.TransmitEventProvider(), - Runnable: services.Registry(), - Encoder: services.Encoder(), - BlockSubscriber: services.BlockSubscriber(), - RecoverableProvider: services.LogRecoverer(), - PayloadBuilder: services.PayloadBuilder(), - UpkeepProvider: services.UpkeepProvider(), - UpkeepStateUpdater: services.UpkeepStateStore(), + LogProvider: keeperProvider.LogEventProvider(), + EventProvider: keeperProvider.TransmitEventProvider(), + Runnable: keeperProvider.Registry(), + Encoder: keeperProvider.Encoder(), + BlockSubscriber: keeperProvider.BlockSubscriber(), + RecoverableProvider: keeperProvider.LogRecoverer(), + PayloadBuilder: keeperProvider.PayloadBuilder(), + UpkeepProvider: keeperProvider.UpkeepProvider(), + UpkeepStateUpdater: keeperProvider.UpkeepStateStore(), UpkeepTypeGetter: ocr2keeper21core.GetUpkeepType, WorkIDGenerator: ocr2keeper21core.UpkeepWorkID, // TODO: Clean up the config @@ -1179,12 +1174,12 @@ func (d *Delegate) newServicesOCR2Keepers21( automationServices := []job.ServiceCtx{ keeperProvider, - services.Registry(), - services.BlockSubscriber(), - services.LogEventProvider(), - services.LogRecoverer(), - services.UpkeepStateStore(), - services.TransmitEventProvider(), + keeperProvider.Registry(), + keeperProvider.BlockSubscriber(), + keeperProvider.LogEventProvider(), + keeperProvider.LogRecoverer(), + keeperProvider.UpkeepStateStore(), + keeperProvider.TransmitEventProvider(), pluginService, } @@ -1194,7 +1189,7 @@ func (d *Delegate) newServicesOCR2Keepers21( customTelemService, custErr := autotelemetry21.NewAutomationCustomTelemetryService( endpoint, lggr, - services.BlockSubscriber(), + keeperProvider.BlockSubscriber(), keeperProvider.ContractConfigTracker(), ) if custErr != nil { @@ -1230,7 +1225,7 @@ func (d *Delegate) newServicesOCR2Keepers20( return nil, fmt.Errorf("keepers2.0 services: failed to get chain (%s): %w", rid.ChainID, err2) } - keeperProvider, rgstry, encoder, logProvider, err2 := ocr2keeper.EVMDependencies20(jb, d.db, lggr, chain, d.ethKs) + keeperProvider, rgstry, encoder, logProvider, err2 := ocr2keeper.EVMDependencies20(jb, d.db, lggr, chain, d.ethKs, d.cfg.Database()) if err2 != nil { return nil, errors.Wrap(err2, "could not build dependencies for ocr2 keepers") } diff --git a/core/services/ocr2/models/models.go b/core/services/ocr2/models/models.go deleted file mode 100644 index 4d3b8bf532c..00000000000 --- a/core/services/ocr2/models/models.go +++ /dev/null @@ -1,8 +0,0 @@ -package models - -type MercuryCredentials struct { - LegacyURL string - URL string - Username string - Password string -} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go index 7ea476ee773..55c01939cb8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go @@ -4,7 +4,9 @@ import ( "math/big" "sync" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) @@ -18,7 +20,7 @@ type ActiveUpkeepList interface { // Remove removes entries from the list Remove(id ...*big.Int) int // View returns the list of IDs of the given type - View(...ocr2keepers.UpkeepType) []*big.Int + View(...types.UpkeepType) []*big.Int // IsActive returns true if the given ID is of an active upkeep IsActive(id *big.Int) bool Size() int @@ -81,7 +83,7 @@ func (al *activeList) Remove(ids ...*big.Int) int { } // View returns the list of IDs of the given type -func (al *activeList) View(upkeepTypes ...ocr2keepers.UpkeepType) []*big.Int { +func (al *activeList) View(upkeepTypes ...types.UpkeepType) []*big.Int { al.lock.RLock() defer al.lock.RUnlock() diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go index 378589d1779..d9f9c45d749 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go @@ -5,27 +5,29 @@ import ( "sort" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestActiveUpkeepList(t *testing.T) { logIDs := []ocr2keepers.UpkeepIdentifier{ - core.GenUpkeepID(ocr2keepers.LogTrigger, "0"), - core.GenUpkeepID(ocr2keepers.LogTrigger, "1"), - core.GenUpkeepID(ocr2keepers.LogTrigger, "2"), - core.GenUpkeepID(ocr2keepers.LogTrigger, "3"), - core.GenUpkeepID(ocr2keepers.LogTrigger, "4"), + core.GenUpkeepID(types.LogTrigger, "0"), + core.GenUpkeepID(types.LogTrigger, "1"), + core.GenUpkeepID(types.LogTrigger, "2"), + core.GenUpkeepID(types.LogTrigger, "3"), + core.GenUpkeepID(types.LogTrigger, "4"), } conditionalIDs := []ocr2keepers.UpkeepIdentifier{ - core.GenUpkeepID(ocr2keepers.ConditionTrigger, "0"), - core.GenUpkeepID(ocr2keepers.ConditionTrigger, "1"), - core.GenUpkeepID(ocr2keepers.ConditionTrigger, "2"), - core.GenUpkeepID(ocr2keepers.ConditionTrigger, "3"), - core.GenUpkeepID(ocr2keepers.ConditionTrigger, "4"), + core.GenUpkeepID(types.ConditionTrigger, "0"), + core.GenUpkeepID(types.ConditionTrigger, "1"), + core.GenUpkeepID(types.ConditionTrigger, "2"), + core.GenUpkeepID(types.ConditionTrigger, "3"), + core.GenUpkeepID(types.ConditionTrigger, "4"), } tests := []struct { @@ -70,7 +72,7 @@ func TestActiveUpkeepList(t *testing.T) { for _, id := range tc.remove { require.False(t, al.IsActive(id)) } - logIds := al.View(ocr2keepers.LogTrigger) + logIds := al.View(types.LogTrigger) require.Equal(t, len(tc.expectedLogIds), len(logIds)) sort.Slice(logIds, func(i, j int) bool { return logIds[i].Cmp(logIds[j]) < 0 @@ -78,7 +80,7 @@ func TestActiveUpkeepList(t *testing.T) { for i := range logIds { require.Equal(t, tc.expectedLogIds[i], logIds[i]) } - conditionalIds := al.View(ocr2keepers.ConditionTrigger) + conditionalIds := al.View(types.ConditionTrigger) require.Equal(t, len(tc.expectedConditionalIds), len(conditionalIds)) sort.Slice(conditionalIds, func(i, j int) bool { return conditionalIds[i].Cmp(conditionalIds[j]) < 0 @@ -98,7 +100,7 @@ func TestActiveUpkeepList_error(t *testing.T) { al.items["-1"] = true al.items["100"] = true - keys := al.View(ocr2keepers.ConditionTrigger) + keys := al.View(types.ConditionTrigger) require.Equal(t, []*big.Int{big.NewInt(100)}, keys) }) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go index e7e9728c3a0..53303553db7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go @@ -10,12 +10,11 @@ import ( "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -24,7 +23,7 @@ import ( type AutomationCustomTelemetryService struct { services.StateMachine monitoringEndpoint commontypes.MonitoringEndpoint - blockSubscriber *evm21.BlockSubscriber + blockSubscriber ocr2keepers.BlockSubscriber blockSubChanID int threadCtrl utils.ThreadControl lggr logger.Logger @@ -34,7 +33,7 @@ type AutomationCustomTelemetryService struct { // NewAutomationCustomTelemetryService creates a telemetry service for new blocks and node version func NewAutomationCustomTelemetryService(me commontypes.MonitoringEndpoint, - lggr logger.Logger, blocksub *evm21.BlockSubscriber, configTracker types.ContractConfigTracker) (*AutomationCustomTelemetryService, error) { + lggr logger.Logger, blocksub ocr2keepers.BlockSubscriber, configTracker types.ContractConfigTracker) (*AutomationCustomTelemetryService, error) { return &AutomationCustomTelemetryService{ monitoringEndpoint: me, threadCtrl: utils.NewThreadControl(), diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go index 134a75fc2c7..9ae17c08ee3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber_test.go index 41a43b951e7..2be6a6a874c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/interfaces.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/interfaces.go index 49975855b54..4dd4d387a80 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/interfaces.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/interfaces.go @@ -3,7 +3,7 @@ package core import ( "context" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) // UpkeepStateReader is the interface for reading the current state of upkeeps. diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go index 3167cf97aad..5a815987a83 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go @@ -5,9 +5,9 @@ package mocks import ( context "context" - mock "github.com/stretchr/testify/mock" + automation "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - types "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + mock "github.com/stretchr/testify/mock" ) // UpkeepStateReader is an autogenerated mock type for the UpkeepStateReader type @@ -16,7 +16,7 @@ type UpkeepStateReader struct { } // SelectByWorkIDs provides a mock function with given fields: ctx, workIDs -func (_m *UpkeepStateReader) SelectByWorkIDs(ctx context.Context, workIDs ...string) ([]types.UpkeepState, error) { +func (_m *UpkeepStateReader) SelectByWorkIDs(ctx context.Context, workIDs ...string) ([]automation.UpkeepState, error) { _va := make([]interface{}, len(workIDs)) for _i := range workIDs { _va[_i] = workIDs[_i] @@ -30,16 +30,16 @@ func (_m *UpkeepStateReader) SelectByWorkIDs(ctx context.Context, workIDs ...str panic("no return value specified for SelectByWorkIDs") } - var r0 []types.UpkeepState + var r0 []automation.UpkeepState var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ...string) ([]types.UpkeepState, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, ...string) ([]automation.UpkeepState, error)); ok { return rf(ctx, workIDs...) } - if rf, ok := ret.Get(0).(func(context.Context, ...string) []types.UpkeepState); ok { + if rf, ok := ret.Get(0).(func(context.Context, ...string) []automation.UpkeepState); ok { r0 = rf(ctx, workIDs...) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.UpkeepState) + r0 = ret.Get(0).([]automation.UpkeepState) } } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload.go index ed5530ae7b5..5077e7b74d5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) var ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload_test.go index cb3a67dde0c..63de1f25ec4 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload_test.go @@ -4,10 +4,12 @@ import ( "math/big" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) func TestWorkID(t *testing.T) { @@ -35,7 +37,7 @@ func TestWorkID(t *testing.T) { }, { name: "happy flow with extension", - upkeepID: GenUpkeepID(ocr2keepers.LogTrigger, "12345").String(), + upkeepID: GenUpkeepID(types.LogTrigger, "12345").String(), trigger: ocr2keepers.Trigger{ BlockNumber: 123, BlockHash: common.HexToHash("0xabcdef"), @@ -96,7 +98,7 @@ func TestNewUpkeepPayload(t *testing.T) { tests := []struct { name string upkeepID *big.Int - upkeepType ocr2keepers.UpkeepType + upkeepType types.UpkeepType trigger ocr2keepers.Trigger check []byte errored bool @@ -105,7 +107,7 @@ func TestNewUpkeepPayload(t *testing.T) { { name: "happy flow no extension", upkeepID: big.NewInt(111), - upkeepType: ocr2keepers.ConditionTrigger, + upkeepType: types.ConditionTrigger, trigger: ocr2keepers.Trigger{ BlockNumber: 11, BlockHash: common.HexToHash("0x11111"), @@ -116,7 +118,7 @@ func TestNewUpkeepPayload(t *testing.T) { { name: "happy flow with extension", upkeepID: big.NewInt(111), - upkeepType: ocr2keepers.LogTrigger, + upkeepType: types.LogTrigger, trigger: ocr2keepers.Trigger{ BlockNumber: 11, BlockHash: common.HexToHash("0x11111"), diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/testutil.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/testutil.go index 3c0eeb1c44e..18f4a73de05 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/testutil.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/testutil.go @@ -3,13 +3,15 @@ package core import ( "math/big" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) // GenUpkeepID generates an ocr2keepers.UpkeepIdentifier with a specific UpkeepType and some random string -func GenUpkeepID(uType ocr2keepers.UpkeepType, rand string) ocr2keepers.UpkeepIdentifier { +func GenUpkeepID(uType types.UpkeepType, rand string) ocr2keepers.UpkeepIdentifier { b := append([]byte{1}, common.LeftPadBytes([]byte{uint8(uType)}, 15)...) b = append(b, []byte(rand)...) b = common.RightPadBytes(b, 32-len(b)) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger.go index 02e6946c8fb..b6ea7399736 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger.go @@ -5,9 +5,9 @@ import ( "math/big" "strings" - "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" ) @@ -33,13 +33,13 @@ func PackTrigger(id *big.Int, trig triggerWrapper) ([]byte, error) { return nil, ErrInvalidUpkeepID } switch upkeepType { - case ocr2keepers.ConditionTrigger: + case types.ConditionTrigger: trig := automation_utils_2_1.KeeperRegistryBase21ConditionalTrigger{ BlockNum: trig.BlockNum, BlockHash: trig.BlockHash, } trigger, err = utilsABI.Pack("_conditionalTrigger", &trig) - case ocr2keepers.LogTrigger: + case types.LogTrigger: logTrig := automation_utils_2_1.KeeperRegistryBase21LogTrigger{ BlockNum: trig.BlockNum, BlockHash: trig.BlockHash, @@ -70,7 +70,7 @@ func UnpackTrigger(id *big.Int, raw []byte) (triggerWrapper, error) { return triggerWrapper{}, ErrInvalidUpkeepID } switch upkeepType { - case ocr2keepers.ConditionTrigger: + case types.ConditionTrigger: unpacked, err := utilsABI.Methods["_conditionalTrigger"].Inputs.Unpack(raw) if err != nil { return triggerWrapper{}, fmt.Errorf("%w: failed to unpack conditional trigger", err) @@ -84,7 +84,7 @@ func UnpackTrigger(id *big.Int, raw []byte) (triggerWrapper, error) { } copy(triggerW.BlockHash[:], converted.BlockHash[:]) return triggerW, nil - case ocr2keepers.LogTrigger: + case types.LogTrigger: unpacked, err := utilsABI.Methods["_logTrigger"].Inputs.Unpack(raw) if err != nil { return triggerWrapper{}, fmt.Errorf("%w: failed to unpack log trigger", err) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger_test.go index 366d59b6397..0a5d3fba4a0 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger_test.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) func TestPackUnpackTrigger(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type.go index 7820b47e6eb..b285db4d8aa 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type.go @@ -3,7 +3,9 @@ package core import ( "math/big" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) const ( @@ -17,17 +19,17 @@ const ( // GetUpkeepType returns the upkeep type from the given ID. // it follows the same logic as the contract, but performs it locally. -func GetUpkeepType(id ocr2keepers.UpkeepIdentifier) ocr2keepers.UpkeepType { +func GetUpkeepType(id ocr2keepers.UpkeepIdentifier) types.UpkeepType { for i := upkeepTypeStartIndex; i < upkeepTypeByteIndex; i++ { if id[i] != 0 { // old id - return ocr2keepers.ConditionTrigger + return types.ConditionTrigger } } typeByte := id[upkeepTypeByteIndex] - return ocr2keepers.UpkeepType(typeByte) + return types.UpkeepType(typeByte) } -func getUpkeepTypeFromBigInt(id *big.Int) (ocr2keepers.UpkeepType, bool) { +func getUpkeepTypeFromBigInt(id *big.Int) (types.UpkeepType, bool) { uid := &ocr2keepers.UpkeepIdentifier{} ok := uid.FromBigInt(id) return GetUpkeepType(*uid), ok diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type_test.go index 6f81ca7690e..1e4ec201b47 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type_test.go @@ -4,36 +4,38 @@ import ( "math/big" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/stretchr/testify/assert" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) func TestGetUpkeepType(t *testing.T) { tests := []struct { name string upkeepID []byte - upkeepType ocr2keepers.UpkeepType + upkeepType types.UpkeepType }{ { "zeroed id", big.NewInt(0).Bytes(), - ocr2keepers.ConditionTrigger, + types.ConditionTrigger, }, { "old id", []byte("5820911532554020907796191562093071158274499580927271776163559390280294438608"), - ocr2keepers.ConditionTrigger, + types.ConditionTrigger, }, { "condition trigger", - GenUpkeepID(ocr2keepers.ConditionTrigger, "").BigInt().Bytes(), - ocr2keepers.ConditionTrigger, + GenUpkeepID(types.ConditionTrigger, "").BigInt().Bytes(), + types.ConditionTrigger, }, { "log trigger", - GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt().Bytes(), - ocr2keepers.LogTrigger, + GenUpkeepID(types.LogTrigger, "111").BigInt().Bytes(), + types.LogTrigger, }, { "log trigger id", @@ -41,7 +43,7 @@ func TestGetUpkeepType(t *testing.T) { id, _ := big.NewInt(0).SetString("32329108151019397958065800113404894502874153543356521479058624064899121404671", 10) return id.Bytes() }(), - ocr2keepers.LogTrigger, + types.LogTrigger, }, } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go index 89fcbb4a0ef..cdf2b0ea521 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go @@ -4,7 +4,9 @@ import ( "fmt" "math/big" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" @@ -65,7 +67,7 @@ func (e reportEncoder) Encode(results ...ocr2keepers.CheckResult) ([]byte, error BlockHash: result.Trigger.BlockHash, } switch core.GetUpkeepType(result.UpkeepID) { - case ocr2keepers.LogTrigger: + case types.LogTrigger: triggerW.TxHash = result.Trigger.LogTriggerExtension.TxHash triggerW.LogIndex = result.Trigger.LogTriggerExtension.Index triggerW.LogBlockHash = result.Trigger.LogTriggerExtension.BlockHash @@ -107,7 +109,7 @@ func (e reportEncoder) Extract(raw []byte) ([]ocr2keepers.ReportedUpkeep, error) triggerW.BlockHash, ) switch core.GetUpkeepType(*id) { - case ocr2keepers.LogTrigger: + case types.LogTrigger: trigger.LogTriggerExtension = &ocr2keepers.LogTriggerExtension{} trigger.LogTriggerExtension.TxHash = triggerW.TxHash trigger.LogTriggerExtension.Index = triggerW.LogIndex diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go index aa549ab3ec8..600dc851728 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go @@ -7,10 +7,12 @@ import ( "os" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) @@ -44,7 +46,7 @@ func TestReportEncoder_EncodeExtract(t *testing.T) { { "happy flow single", []ocr2keepers.CheckResult{ - newResult(1, 1, core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.LogTrigger, "123"), 1, 1), }, 736, 1, @@ -54,9 +56,9 @@ func TestReportEncoder_EncodeExtract(t *testing.T) { { "happy flow multiple", []ocr2keepers.CheckResult{ - newResult(1, 1, core.GenUpkeepID(ocr2keepers.LogTrigger, "10"), 1, 1), - newResult(1, 1, core.GenUpkeepID(ocr2keepers.ConditionTrigger, "20"), 1, 1), - newResult(1, 1, core.GenUpkeepID(ocr2keepers.ConditionTrigger, "30"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.LogTrigger, "10"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.ConditionTrigger, "20"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.ConditionTrigger, "30"), 1, 1), }, 1312, 3, @@ -66,9 +68,9 @@ func TestReportEncoder_EncodeExtract(t *testing.T) { { "happy flow highest block number first", []ocr2keepers.CheckResult{ - newResult(1, 1, core.GenUpkeepID(ocr2keepers.ConditionTrigger, "30"), 1, 1), - newResult(1, 1, core.GenUpkeepID(ocr2keepers.ConditionTrigger, "20"), 1, 1), - newResult(1, 1, core.GenUpkeepID(ocr2keepers.LogTrigger, "10"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.ConditionTrigger, "30"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.ConditionTrigger, "20"), 1, 1), + newResult(1, 1, core.GenUpkeepID(types.LogTrigger, "10"), 1, 1), }, 1312, 1000, @@ -114,8 +116,8 @@ func TestReportEncoder_BackwardsCompatibility(t *testing.T) { packer: NewAbiPacker(), } results := []ocr2keepers.CheckResult{ - newResult(1, 2, core.GenUpkeepID(ocr2keepers.LogTrigger, "10"), 5, 6), - newResult(3, 4, core.GenUpkeepID(ocr2keepers.ConditionTrigger, "20"), 7, 8), + newResult(1, 2, core.GenUpkeepID(types.LogTrigger, "10"), 5, 6), + newResult(3, 4, core.GenUpkeepID(types.ConditionTrigger, "20"), 7, 8), } encoded, err := encoder.Encode(results...) assert.NoError(t, err) @@ -136,7 +138,7 @@ func newResult(block int64, checkBlock ocr2keepers.BlockNumber, id ocr2keepers.U BlockHash: [32]byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}, } - if tp == ocr2keepers.LogTrigger { + if tp == types.LogTrigger { trig.LogTriggerExtension = &ocr2keepers.LogTriggerExtension{ Index: 1, TxHash: common.HexToHash("0x1234567890123456789012345678901234567890123456789012345678901234"), diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go index 4555a5b0b9b..1f93fd3ee22 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -1,7 +1,7 @@ package encoding import ( - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go index 7db8b220a4d..81f4716a51b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go index 3f08fcf0a2d..79221a620e1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" automation21Utils "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go index f2c58fd30c1..afdb882cee5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go @@ -8,7 +8,7 @@ import ( "sync/atomic" "github.com/smartcontractkit/chainlink-automation/pkg/v3/random" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go index 046c93a428a..c90a0f34a1b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go @@ -6,10 +6,12 @@ import ( "math/big" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -378,7 +380,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 10, maxUpkeepLogs: 2, @@ -392,7 +394,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, }, @@ -406,7 +408,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, visited: []fetchedLog{}, @@ -418,7 +420,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 10, maxUpkeepLogs: 2, @@ -432,7 +434,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, }, @@ -447,7 +449,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, toAdd: []appendArgs{ @@ -458,7 +460,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 10, maxUpkeepLogs: 2, @@ -480,7 +482,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 10, maxUpkeepLogs: 2, @@ -493,7 +495,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 1, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 10, maxUpkeepLogs: 2, @@ -506,7 +508,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 2, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 10, maxUpkeepLogs: 2, @@ -521,7 +523,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 1, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -529,7 +531,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 2, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, }, @@ -546,7 +548,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 2, maxUpkeepLogs: 4, @@ -559,7 +561,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 1, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 2, maxUpkeepLogs: 4, @@ -572,7 +574,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 2, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, maxBlockLogs: 2, maxUpkeepLogs: 4, @@ -587,7 +589,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 1, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -595,7 +597,7 @@ func TestLogEventBuffer_FetchedBlock_Append(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 2, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, }, @@ -676,7 +678,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xb711bd1103927611ee41152aa8ae27f3330"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -685,7 +687,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "222").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "222").BigInt(), }, { log: logpoller.Log{ @@ -694,7 +696,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 4, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -703,7 +705,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 3, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "222").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "222").BigInt(), }, { log: logpoller.Log{ @@ -712,7 +714,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 2, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -721,7 +723,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 5, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -730,7 +732,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 3, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -739,7 +741,7 @@ func TestLogEventBuffer_FetchedBlock_Sort(t *testing.T) { TxHash: common.HexToHash("0xa651bd1109922111ee411525ebae27f3fb6"), LogIndex: 1, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, beforeSort: []string{ @@ -801,7 +803,7 @@ func TestLogEventBuffer_FetchedBlock_Clone(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 0, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, { log: logpoller.Log{ @@ -809,7 +811,7 @@ func TestLogEventBuffer_FetchedBlock_Clone(t *testing.T) { TxHash: common.HexToHash("0x1"), LogIndex: 2, }, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "111").BigInt(), + upkeepID: core.GenUpkeepID(types.LogTrigger, "111").BigInt(), }, }, } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go index caad1adc9ad..5ef06f1bd08 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" @@ -18,7 +20,7 @@ import ( "go.uber.org/zap/zapcore" "golang.org/x/time/rate" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -150,7 +152,7 @@ func TestIntegration_LogEventProvider_UpdateConfig(t *testing.T) { require.Equal(t, 1, len(addrs)) t.Run("update filter config", func(t *testing.T) { - upkeepID := evmregistry21.GenUpkeepID(ocr2keepers.LogTrigger, "111") + upkeepID := evmregistry21.GenUpkeepID(types.LogTrigger, "111") id := upkeepID.BigInt() cfg := newPlainLogTriggerConfig(addrs[0]) b, err := ethClient.BlockByHash(ctx, backend.Commit()) @@ -182,7 +184,7 @@ func TestIntegration_LogEventProvider_UpdateConfig(t *testing.T) { }) t.Run("register same log filter", func(t *testing.T) { - upkeepID := evmregistry21.GenUpkeepID(ocr2keepers.LogTrigger, "222") + upkeepID := evmregistry21.GenUpkeepID(types.LogTrigger, "222") id := upkeepID.BigInt() cfg := newPlainLogTriggerConfig(addrs[0]) b, err := ethClient.BlockByHash(ctx, backend.Commit()) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go index 2dabcc82671..4840fa10fa1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go @@ -16,7 +16,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go index 278c727a06a..d978940d297 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go @@ -5,13 +5,13 @@ import ( "math/big" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -155,7 +155,7 @@ func TestEventLogProvider_RefreshActiveUpkeeps(t *testing.T) { p := NewLogProvider(logger.TestLogger(t), mp, &mockedPacker{}, NewUpkeepFilterStore(), NewOptions(200)) require.NoError(t, p.RegisterFilter(ctx, FilterOptions{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1111").BigInt(), + UpkeepID: core.GenUpkeepID(types.LogTrigger, "1111").BigInt(), TriggerConfig: LogTriggerConfig{ ContractAddress: common.BytesToAddress(common.LeftPadBytes([]byte{1, 2, 3, 4}, 20)), Topic0: common.BytesToHash(common.LeftPadBytes([]byte{1, 2, 3, 4}, 32)), @@ -163,7 +163,7 @@ func TestEventLogProvider_RefreshActiveUpkeeps(t *testing.T) { UpdateBlock: uint64(0), })) require.NoError(t, p.RegisterFilter(ctx, FilterOptions{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2222").BigInt(), + UpkeepID: core.GenUpkeepID(types.LogTrigger, "2222").BigInt(), TriggerConfig: LogTriggerConfig{ ContractAddress: common.BytesToAddress(common.LeftPadBytes([]byte{1, 2, 3, 4}, 20)), Topic0: common.BytesToHash(common.LeftPadBytes([]byte{1, 2, 3, 4}, 32)), @@ -175,11 +175,11 @@ func TestEventLogProvider_RefreshActiveUpkeeps(t *testing.T) { newIds, err := p.RefreshActiveUpkeeps() require.NoError(t, err) require.Len(t, newIds, 0) - mp.On("HasFilter", p.filterName(core.GenUpkeepID(ocr2keepers.LogTrigger, "2222").BigInt())).Return(true) + mp.On("HasFilter", p.filterName(core.GenUpkeepID(types.LogTrigger, "2222").BigInt())).Return(true) newIds, err = p.RefreshActiveUpkeeps( - core.GenUpkeepID(ocr2keepers.LogTrigger, "2222").BigInt(), - core.GenUpkeepID(ocr2keepers.LogTrigger, "1234").BigInt(), - core.GenUpkeepID(ocr2keepers.LogTrigger, "123").BigInt()) + core.GenUpkeepID(types.LogTrigger, "2222").BigInt(), + core.GenUpkeepID(types.LogTrigger, "1234").BigInt(), + core.GenUpkeepID(types.LogTrigger, "123").BigInt()) require.NoError(t, err) require.Len(t, newIds, 2) require.Equal(t, 1, p.filterStore.Size()) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_test.go index 81774e26387..464b9aa3ba6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/time/rate" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go index c7f6884426f..b28ece9843f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go @@ -14,10 +14,12 @@ import ( "sync/atomic" "time" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-automation/pkg/v3/random" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -193,7 +195,7 @@ func (r *logRecoverer) HealthReport() map[string]error { func (r *logRecoverer) GetProposalData(ctx context.Context, proposal ocr2keepers.CoordinatedBlockProposal) ([]byte, error) { switch core.GetUpkeepType(proposal.UpkeepID) { - case ocr2keepers.LogTrigger: + case types.LogTrigger: return r.getLogTriggerCheckData(ctx, proposal) default: return []byte{}, errors.New("not a log trigger upkeep ID") diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go index 65fe3c85fb8..eadd0446da8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go @@ -9,13 +9,15 @@ import ( "testing" "time" + types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -50,33 +52,33 @@ func TestLogRecoverer_GetRecoverables(t *testing.T) { { "happy flow", []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, }, []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, }, false, }, { "rate limiting", []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "3", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "4", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "5", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "6", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "3", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "4", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "5", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "6", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, }, []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "3", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "4", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "5", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "3", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "4", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "5", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, }, false, }, @@ -121,9 +123,9 @@ func TestLogRecoverer_Clean(t *testing.T) { { "clean expired", []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, - {WorkID: "3", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "3")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, + {WorkID: "3", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "3")}, }, map[string]visitedRecord{ "1": visitedRecord{time.Now(), ocr2keepers.UpkeepPayload{ @@ -164,9 +166,9 @@ func TestLogRecoverer_Clean(t *testing.T) { ocr2keepers.UnknownState, }, []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, - {WorkID: "4", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "4")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, + {WorkID: "4", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "4")}, }, []string{"1", "2", "4"}, }, @@ -583,7 +585,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a filter is not found for the upkeep ID, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), }, skipFilter: true, expectErr: true, @@ -592,7 +594,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if an error is encountered fetching the latest block, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 0, @@ -615,7 +617,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if an error is encountered fetching the tx receipt, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 0, @@ -643,7 +645,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if the tx block is nil, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 0, @@ -671,7 +673,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log trigger extension block number is 0, and the block number on the tx receipt is not recoverable, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 0, @@ -701,7 +703,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block is not recoverable, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 200, @@ -731,7 +733,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block has does not match, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 200, @@ -763,7 +765,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block is recoverable, when the upkeep state reader errors, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 80, @@ -798,7 +800,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block is recoverable, when the upkeep state reader returns a non recoverable state, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 80, @@ -835,7 +837,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block is recoverable, when the filter address is empty, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 80, @@ -875,7 +877,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block is recoverable, when the log poller returns an error fetching logs, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 80, @@ -910,7 +912,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "if a log block is recoverable, when logs cannot be found for an upkeep ID, an error is returned", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ BlockNumber: 80, @@ -949,7 +951,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "happy path with empty check data", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: func() ocr2keepers.Trigger { t := ocr2keepers.NewTrigger( ocr2keepers.BlockNumber(80), @@ -1000,7 +1002,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { { name: "happy path with check data", proposal: ocr2keepers.CoordinatedBlockProposal{ - UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "123"), Trigger: func() ocr2keepers.Trigger { t := ocr2keepers.NewTrigger( ocr2keepers.BlockNumber(80), @@ -1061,7 +1063,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { filterStore.AddActiveUpkeeps(upkeepFilter{ addr: []byte("test"), topics: []common.Hash{common.HexToHash("0x1"), common.HexToHash("0x2"), common.HexToHash("0x3"), common.HexToHash("0x4")}, - upkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "123").BigInt(), + upkeepID: core.GenUpkeepID(types2.LogTrigger, "123").BigInt(), }) } @@ -1111,34 +1113,34 @@ func TestLogRecoverer_pending(t *testing.T) { name: "add new and existing", maxPerUpkeep: 10, exist: []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, }, new: []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, }, errored: []bool{false, false}, want: []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "2")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "2")}, }, }, { name: "exceed limits for upkeep", maxPerUpkeep: 3, exist: []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "3", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "3", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, }, new: []ocr2keepers.UpkeepPayload{ - {WorkID: "4", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, + {WorkID: "4", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, }, errored: []bool{true}, want: []ocr2keepers.UpkeepPayload{ - {WorkID: "1", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "2", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, - {WorkID: "3", UpkeepID: core.GenUpkeepID(ocr2keepers.LogTrigger, "1")}, + {WorkID: "1", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "2", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, + {WorkID: "3", UpkeepID: core.GenUpkeepID(types2.LogTrigger, "1")}, }, }, } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go index d3a5167ef72..d24442b6ee9 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go @@ -10,11 +10,12 @@ import ( "net/http" "time" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) @@ -73,7 +74,7 @@ type MercuryData struct { } type MercuryConfigProvider interface { - Credentials() *models.MercuryCredentials + Credentials() *types.MercuryCredentials IsUpkeepAllowed(string) (interface{}, bool) SetUpkeepAllowed(string, interface{}, time.Duration) GetPluginRetry(string) (interface{}, bool) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index 27aafeca553..a0dba9c8ac3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -15,8 +15,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 31b6597bb23..c7bff2eac7a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/pkg/errors" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -20,13 +22,12 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02" @@ -37,8 +38,8 @@ type MockMercuryConfigProvider struct { mock.Mock } -func (m *MockMercuryConfigProvider) Credentials() *models.MercuryCredentials { - mc := &models.MercuryCredentials{ +func (m *MockMercuryConfigProvider) Credentials() *types.MercuryCredentials { + mc := &types.MercuryCredentials{ LegacyURL: "https://google.old.com", URL: "https://google.com", Username: "FakeClientID", diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go index 686b2199ebc..6c07c383504 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go @@ -11,13 +11,14 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" "github.com/stretchr/testify/mock" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -41,8 +42,8 @@ func NewMockMercuryConfigProvider() *MockMercuryConfigProvider { } } -func (m *MockMercuryConfigProvider) Credentials() *models.MercuryCredentials { - mc := &models.MercuryCredentials{ +func (m *MockMercuryConfigProvider) Credentials() *types.MercuryCredentials { + mc := &types.MercuryCredentials{ LegacyURL: "https://google.old.com", URL: "https://google.com", Username: "FakeClientID", diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go index 4a22ae477e6..a7742c04872 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go @@ -9,13 +9,14 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" "github.com/stretchr/testify/mock" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks" @@ -40,8 +41,8 @@ func NewMockMercuryConfigProvider() *MockMercuryConfigProvider { } } -func (m *MockMercuryConfigProvider) Credentials() *models.MercuryCredentials { - mc := &models.MercuryCredentials{ +func (m *MockMercuryConfigProvider) Credentials() *types.MercuryCredentials { + mc := &types.MercuryCredentials{ LegacyURL: "https://google.old.com", URL: "https://google.com", Username: "FakeClientID", diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go index 37c458ae6eb..4854f517c46 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go @@ -3,7 +3,9 @@ package evm import ( "context" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" @@ -36,13 +38,13 @@ func (b *payloadBuilder) BuildPayloads(ctx context.Context, proposals ...ocr2kee var checkData []byte var err error switch core.GetUpkeepType(proposal.UpkeepID) { - case ocr2keepers.LogTrigger: + case types.LogTrigger: checkData, err = b.recoverer.GetProposalData(ctx, proposal) if err != nil { b.lggr.Warnw("failed to get log proposal data", "err", err, "upkeepID", proposal.UpkeepID, "trigger", proposal.Trigger) continue } - case ocr2keepers.ConditionTrigger: + case types.ConditionTrigger: // Empty checkData for conditionals } payload, err = core.NewUpkeepPayload(proposal.UpkeepID.BigInt(), proposal.Trigger, checkData) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go index 7e55267f6d3..7cd63dd308a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go @@ -5,10 +5,12 @@ import ( "math/big" "testing" + types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + types "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -33,7 +35,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, proposals: []types.CoordinatedBlockProposal{ { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "abc"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "abc"), WorkID: "workID1", Trigger: types.Trigger{ BlockNumber: 1, @@ -41,7 +43,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, }, { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "def"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "def"), WorkID: "workID2", Trigger: types.Trigger{ BlockNumber: 2, @@ -56,7 +58,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, wantPayloads: []types.UpkeepPayload{ { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "abc"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "abc"), WorkID: "714f83255c5b562823725748c4a75777c9b78ea8c5ba72ea819926a1fecd389e", Trigger: types.Trigger{ BlockNumber: 1, @@ -65,7 +67,7 @@ func TestNewPayloadBuilder(t *testing.T) { CheckData: []byte{1, 2, 3}, }, { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "def"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "def"), WorkID: "3956daa0378d6a761fe972ee00fe98338f17fb6b7865c1d49a8a416cd85977b8", Trigger: types.Trigger{ BlockNumber: 2, @@ -79,12 +81,12 @@ func TestNewPayloadBuilder(t *testing.T) { name: "for an inactive log trigger upkeep, an empty payload is added to the list of payloads", activeList: &mockActiveUpkeepList{ IsActiveFn: func(id *big.Int) bool { - return core.GenUpkeepID(types.LogTrigger, "ghi").BigInt().Cmp(id) != 0 + return core.GenUpkeepID(types2.LogTrigger, "ghi").BigInt().Cmp(id) != 0 }, }, proposals: []types.CoordinatedBlockProposal{ { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "abc"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "abc"), WorkID: "workID1", Trigger: types.Trigger{ BlockNumber: 1, @@ -92,7 +94,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, }, { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "def"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "def"), WorkID: "workID2", Trigger: types.Trigger{ BlockNumber: 2, @@ -100,7 +102,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, }, { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "ghi"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "ghi"), WorkID: "workID3", Trigger: types.Trigger{ BlockNumber: 3, @@ -115,7 +117,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, wantPayloads: []types.UpkeepPayload{ { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "abc"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "abc"), WorkID: "714f83255c5b562823725748c4a75777c9b78ea8c5ba72ea819926a1fecd389e", Trigger: types.Trigger{ BlockNumber: 1, @@ -124,7 +126,7 @@ func TestNewPayloadBuilder(t *testing.T) { CheckData: []byte{1, 2, 3}, }, { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "def"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "def"), WorkID: "3956daa0378d6a761fe972ee00fe98338f17fb6b7865c1d49a8a416cd85977b8", Trigger: types.Trigger{ BlockNumber: 2, @@ -144,7 +146,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, proposals: []types.CoordinatedBlockProposal{ { - UpkeepID: core.GenUpkeepID(types.LogTrigger, "abc"), + UpkeepID: core.GenUpkeepID(types2.LogTrigger, "abc"), WorkID: "workID1", Trigger: types.Trigger{ BlockNumber: 1, @@ -170,7 +172,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, proposals: []types.CoordinatedBlockProposal{ { - UpkeepID: core.GenUpkeepID(types.ConditionTrigger, "def"), + UpkeepID: core.GenUpkeepID(types2.ConditionTrigger, "def"), WorkID: "workID1", Trigger: types.Trigger{ BlockNumber: 1, @@ -180,7 +182,7 @@ func TestNewPayloadBuilder(t *testing.T) { }, wantPayloads: []types.UpkeepPayload{ { - UpkeepID: core.GenUpkeepID(types.ConditionTrigger, "def"), + UpkeepID: core.GenUpkeepID(types2.ConditionTrigger, "def"), WorkID: "58f2f231792448679a75bac6efc2af4ba731901f0cb93a44a366525751cbabfb", Trigger: types.Trigger{ BlockNumber: 1, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go index 39862bb7539..fd7bfa91d7f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go @@ -9,6 +9,10 @@ import ( "sync" "time" + types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -19,7 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -27,7 +31,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" @@ -81,7 +84,7 @@ func NewEvmRegistry( addr common.Address, client legacyevm.Chain, registry *iregistry21.IKeeperRegistryMaster, - mc *models.MercuryCredentials, + mc *types.MercuryCredentials, al ActiveUpkeepList, logEventProvider logprovider.LogEventProvider, packer encoding.Packer, @@ -129,14 +132,14 @@ var upkeepStateEvents = []common.Hash{ } type MercuryConfig struct { - cred *models.MercuryCredentials + cred *types.MercuryCredentials Abi abi.ABI // AllowListCache stores the upkeeps privileges. In 2.1, this only includes a JSON bytes for allowed to use mercury AllowListCache *cache.Cache pluginRetryCache *cache.Cache } -func NewMercuryConfig(credentials *models.MercuryCredentials, abi abi.ABI) *MercuryConfig { +func NewMercuryConfig(credentials *types.MercuryCredentials, abi abi.ABI) *MercuryConfig { return &MercuryConfig{ cred: credentials, Abi: abi, @@ -145,7 +148,7 @@ func NewMercuryConfig(credentials *models.MercuryCredentials, abi abi.ABI) *Merc } } -func (c *MercuryConfig) Credentials() *models.MercuryCredentials { +func (c *MercuryConfig) Credentials() *types.MercuryCredentials { return c.cred } @@ -295,7 +298,7 @@ func (r *EvmRegistry) refreshActiveUpkeeps() error { continue } switch core.GetUpkeepType(*uid) { - case ocr2keepers.LogTrigger: + case types2.LogTrigger: logTriggerIDs = append(logTriggerIDs, id) default: } @@ -517,7 +520,7 @@ func (r *EvmRegistry) removeFromActive(id *big.Int) { uid.FromBigInt(id) trigger := core.GetUpkeepType(*uid) switch trigger { - case ocr2keepers.LogTrigger: + case types2.LogTrigger: if err := r.logEventProvider.UnregisterFilter(id); err != nil { r.lggr.Warnw("failed to unregister log filter", "upkeepID", id.String()) } @@ -585,7 +588,7 @@ func (r *EvmRegistry) updateTriggerConfig(id *big.Int, cfg []byte, logBlock uint uid := &ocr2keepers.UpkeepIdentifier{} uid.FromBigInt(id) switch core.GetUpkeepType(*uid) { - case ocr2keepers.LogTrigger: + case types2.LogTrigger: if len(cfg) == 0 { fetched, err := r.fetchTriggerConfig(id) if err != nil { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go index 33d4a7157be..6475b3ef7df 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go @@ -6,11 +6,13 @@ import ( "math/big" "strings" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" @@ -197,7 +199,7 @@ func (r *EvmRegistry) checkUpkeeps(ctx context.Context, payloads []ocr2keepers.U uid := &ocr2keepers.UpkeepIdentifier{} uid.FromBigInt(upkeepId) switch core.GetUpkeepType(*uid) { - case ocr2keepers.LogTrigger: + case types.LogTrigger: reason, state, retryable := r.verifyLogExists(upkeepId, p) if reason != encoding.UpkeepFailureReasonNone || state != encoding.NoPipelineError { results[i] = encoding.GetIneligibleCheckResultWithoutPerformData(p, reason, state, retryable) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go index 9867a58201b..e6b61be8d0a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go @@ -8,6 +8,10 @@ import ( "sync/atomic" "testing" + types3 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + + types2 "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" @@ -16,7 +20,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -25,7 +29,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks" @@ -376,9 +379,9 @@ func TestRegistry_VerifyLogExists(t *testing.T) { func TestRegistry_CheckUpkeeps(t *testing.T) { lggr := logger.TestLogger(t) - uid0 := core.GenUpkeepID(ocr2keepers.UpkeepType(0), "p0") - uid1 := core.GenUpkeepID(ocr2keepers.UpkeepType(1), "p1") - uid2 := core.GenUpkeepID(ocr2keepers.UpkeepType(1), "p2") + uid0 := core.GenUpkeepID(types3.UpkeepType(0), "p0") + uid1 := core.GenUpkeepID(types3.UpkeepType(1), "p1") + uid2 := core.GenUpkeepID(types3.UpkeepType(1), "p2") extension1 := &ocr2keepers.LogTriggerExtension{ TxHash: common.HexToHash("0xc8def8abdcf3a4eaaf6cc13bff3e4e2a7168d86ea41dbbf97451235aa76c3651"), @@ -536,9 +539,9 @@ func TestRegistry_CheckUpkeeps(t *testing.T) { } func TestRegistry_SimulatePerformUpkeeps(t *testing.T) { - uid0 := core.GenUpkeepID(ocr2keepers.UpkeepType(0), "p0") - uid1 := core.GenUpkeepID(ocr2keepers.UpkeepType(1), "p1") - uid2 := core.GenUpkeepID(ocr2keepers.UpkeepType(1), "p2") + uid0 := core.GenUpkeepID(types3.UpkeepType(0), "p0") + uid1 := core.GenUpkeepID(types3.UpkeepType(1), "p1") + uid2 := core.GenUpkeepID(types3.UpkeepType(1), "p2") extension1 := &ocr2keepers.LogTriggerExtension{ TxHash: common.HexToHash("0xc8def8abdcf3a4eaaf6cc13bff3e4e2a7168d86ea41dbbf97451235aa76c3651"), @@ -682,7 +685,7 @@ func setupEVMRegistry(t *testing.T) *EvmRegistry { headFunc: func(ocr2keepers.BlockKey) {}, chLog: make(chan logpoller.Log, 1000), mercury: &MercuryConfig{ - cred: &models.MercuryCredentials{ + cred: &types2.MercuryCredentials{ LegacyURL: "https://google.old.com", URL: "https://google.com", Username: "FakeClientID", diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go index 0d097ceadb4..dc48c3d75f6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go @@ -8,14 +8,14 @@ import ( "testing" "time" + types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" coreTypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - types3 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -211,7 +211,7 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { { name: "an error is returned when fetching indexed logs for IKeeperRegistryMasterUpkeepUnpaused errors", ids: []*big.Int{ - core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), }, logEventProvider: &mockLogEventProvider{ RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { @@ -234,8 +234,8 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { { name: "an error is returned when fetching indexed logs for IKeeperRegistryMasterUpkeepTriggerConfigSet errors", ids: []*big.Int{ - core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), - core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.ConditionTrigger, "abc").BigInt(), big.NewInt(-1), }, logEventProvider: &mockLogEventProvider{ @@ -259,8 +259,8 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { { name: "an error is returned when parsing the logs using the registry errors", ids: []*big.Int{ - core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), - core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.ConditionTrigger, "abc").BigInt(), big.NewInt(-1), }, logEventProvider: &mockLogEventProvider{ @@ -288,8 +288,8 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { { name: "an error is returned when registering the filter errors", ids: []*big.Int{ - core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), - core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.ConditionTrigger, "abc").BigInt(), big.NewInt(-1), }, logEventProvider: &mockLogEventProvider{ @@ -319,11 +319,11 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { if log.BlockNumber == 1 { return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ TriggerConfig: []byte{1, 2, 3}, - Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), }, nil } return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ - Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), }, nil }, GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -341,9 +341,9 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { { name: "log trigger upkeeps are refreshed without error", ids: []*big.Int{ - core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), - core.GenUpkeepID(types.LogTrigger, "def").BigInt(), - core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types2.LogTrigger, "def").BigInt(), + core.GenUpkeepID(types2.ConditionTrigger, "abc").BigInt(), big.NewInt(-1), }, logEventProvider: &mockLogEventProvider{ @@ -372,12 +372,12 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { if log.BlockNumber == 1 { return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ - Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), TriggerConfig: []byte{1, 2, 3}, }, nil } return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ - Id: core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "def").BigInt(), }, nil }, GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -395,7 +395,7 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { ids: func() []*big.Int { res := []*big.Int{} for i := 0; i < logTriggerRefreshBatchSize*3; i++ { - res = append(res, core.GenUpkeepID(types.LogTrigger, fmt.Sprintf("%d", i)).BigInt()) + res = append(res, core.GenUpkeepID(types2.LogTrigger, fmt.Sprintf("%d", i)).BigInt()) } return res }(), @@ -424,12 +424,12 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { if log.BlockNumber == 1 { return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ - Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), TriggerConfig: []byte{1, 2, 3}, }, nil } return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ - Id: core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "def").BigInt(), }, nil }, GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { @@ -447,7 +447,7 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { ids: func() []*big.Int { res := []*big.Int{} for i := 0; i < logTriggerRefreshBatchSize+3; i++ { - res = append(res, core.GenUpkeepID(types.LogTrigger, fmt.Sprintf("%d", i)).BigInt()) + res = append(res, core.GenUpkeepID(types2.LogTrigger, fmt.Sprintf("%d", i)).BigInt()) } return res }(), @@ -478,12 +478,12 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { if log.BlockNumber == 1 { return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ - Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "abc").BigInt(), TriggerConfig: []byte{1, 2, 3}, }, nil } return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ - Id: core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + Id: core.GenUpkeepID(types2.LogTrigger, "def").BigInt(), }, nil }, GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go index 71ccee872ff..5fe21b08724 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go @@ -1,136 +1,31 @@ package evm import ( - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - - "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) type AutomationServices interface { - Registry() *EvmRegistry - Encoder() ocr2keepers.Encoder - TransmitEventProvider() *transmit.EventProvider - BlockSubscriber() *BlockSubscriber - PayloadBuilder() ocr2keepers.PayloadBuilder - UpkeepStateStore() upkeepstate.UpkeepStateStore - LogEventProvider() logprovider.LogEventProvider - LogRecoverer() logprovider.LogRecoverer - UpkeepProvider() ocr2keepers.ConditionalUpkeepProvider Keyring() ocr3types.OnchainKeyring[plugin.AutomationReportInfo] } -func New(addr common.Address, client legacyevm.Chain, mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, lggr logger.Logger, db *sqlx.DB, dbCfg pg.QConfig) (AutomationServices, error) { - registryContract, err := iregistry21.NewIKeeperRegistryMaster(addr, client.Client()) - if err != nil { - return nil, fmt.Errorf("%w: failed to create caller for address and backend", ErrInitializationFailure) - } - // lookback blocks for transmit event is hard coded and should provide ample time for logs - // to be detected in most cases - var transmitLookbackBlocks int64 = 250 - transmitEventProvider, err := transmit.NewTransmitEventProvider(lggr, client.LogPoller(), addr, client.Client(), transmitLookbackBlocks) - if err != nil { - return nil, err - } +func New(keyring ocrtypes.OnchainKeyring) (AutomationServices, error) { services := new(automationServices) - services.transmitEventProvider = transmitEventProvider - - packer := encoding.NewAbiPacker() - services.encoder = encoding.NewReportEncoder(packer) - - finalityDepth := client.Config().EVM().FinalityDepth() - - orm := upkeepstate.NewORM(client.ID(), db, lggr, dbCfg) - scanner := upkeepstate.NewPerformedEventsScanner(lggr, client.LogPoller(), addr, finalityDepth) - services.upkeepState = upkeepstate.NewUpkeepStateStore(orm, lggr, scanner) - - logProvider, logRecoverer := logprovider.New(lggr, client.LogPoller(), client.Client(), services.upkeepState, finalityDepth) - services.logProvider = logProvider - services.logRecoverer = logRecoverer - services.blockSub = NewBlockSubscriber(client.HeadBroadcaster(), client.LogPoller(), finalityDepth, lggr) services.keyring = NewOnchainKeyringV3Wrapper(keyring) - al := NewActiveUpkeepList() - services.payloadBuilder = NewPayloadBuilder(al, logRecoverer, lggr) - - services.reg = NewEvmRegistry(lggr, addr, client, - registryContract, mc, al, services.logProvider, - packer, services.blockSub, finalityDepth) - - services.upkeepProvider = NewUpkeepProvider(al, services.blockSub, client.LogPoller()) - return services, nil } type automationServices struct { - reg *EvmRegistry - encoder ocr2keepers.Encoder - transmitEventProvider *transmit.EventProvider - blockSub *BlockSubscriber - payloadBuilder ocr2keepers.PayloadBuilder - upkeepState upkeepstate.UpkeepStateStore - logProvider logprovider.LogEventProvider - logRecoverer logprovider.LogRecoverer - upkeepProvider *upkeepProvider - keyring *onchainKeyringV3Wrapper + keyring *onchainKeyringV3Wrapper } var _ AutomationServices = &automationServices{} -func (f *automationServices) Registry() *EvmRegistry { - return f.reg -} - -func (f *automationServices) Encoder() ocr2keepers.Encoder { - return f.encoder -} - -func (f *automationServices) TransmitEventProvider() *transmit.EventProvider { - return f.transmitEventProvider -} - -func (f *automationServices) BlockSubscriber() *BlockSubscriber { - return f.blockSub -} - -func (f *automationServices) PayloadBuilder() ocr2keepers.PayloadBuilder { - return f.payloadBuilder -} - -func (f *automationServices) UpkeepStateStore() upkeepstate.UpkeepStateStore { - return f.upkeepState -} - -func (f *automationServices) LogEventProvider() logprovider.LogEventProvider { - return f.logProvider -} - -func (f *automationServices) LogRecoverer() logprovider.LogRecoverer { - return f.logRecoverer -} - -func (f *automationServices) UpkeepProvider() ocr2keepers.ConditionalUpkeepProvider { - return f.upkeepProvider -} - func (f *automationServices) Keyring() ocr3types.OnchainKeyring[plugin.AutomationReportInfo] { return f.keyring } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go index 2c0055482ed..814e86040b3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go @@ -3,7 +3,7 @@ package transmit import ( "sync" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) // transmitEventCache holds a ring buffer of the last visited blocks (transmit block), diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache_test.go index 7f4a1296567..e7b7ca800ab 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" ) func TestTransmitEventCache_Sanity(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding.go index 17d3cd439f8..89dbb52c0e3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go index da32376aab6..97aed4706ec 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go @@ -3,10 +3,12 @@ package transmit import ( "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" @@ -14,7 +16,7 @@ import ( ) func TestTransmitEventLog(t *testing.T) { - uid := core.GenUpkeepID(ocr2keepers.ConditionTrigger, "111") + uid := core.GenUpkeepID(types.ConditionTrigger, "111") tests := []struct { name string diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go index f66711c2b71..eb8dc1793c1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go @@ -6,9 +6,11 @@ import ( "fmt" "sync" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -20,7 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) -var _ ocr2keepers.TransmitEventProvider = &EventProvider{} +var _ types.TransmitEventProvider = &EventProvider{} type logParser func(registry *iregistry21.IKeeperRegistryMaster, log logpoller.Log) (transmitEventLog, error) @@ -189,7 +191,7 @@ func (c *EventProvider) processLogs(latestBlock int64, logs ...logpoller.Log) ([ triggerW.BlockHash, ) switch core.GetUpkeepType(*upkeepId) { - case ocr2keepers.LogTrigger: + case types.LogTrigger: trigger.LogTriggerExtension = &ocr2keepers.LogTriggerExtension{} trigger.LogTriggerExtension.TxHash = triggerW.TxHash trigger.LogTriggerExtension.Index = triggerW.LogIndex diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go index 950ec810402..89a49f07807 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go @@ -5,12 +5,14 @@ import ( "runtime" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -107,7 +109,7 @@ func TestTransmitEventProvider_ProcessLogs(t *testing.T) { provider, err := NewTransmitEventProvider(logger.TestLogger(t), lp, common.HexToAddress("0x"), client, 250) require.NoError(t, err) - id := core.GenUpkeepID(ocr2keepers.LogTrigger, "1111111111111111") + id := core.GenUpkeepID(types.LogTrigger, "1111111111111111") tests := []struct { name string diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go index b30215e4fbf..01f136aaa6b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go @@ -4,7 +4,8 @@ import ( "context" "fmt" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" @@ -32,7 +33,7 @@ func (p *upkeepProvider) GetActiveUpkeeps(_ context.Context) ([]ocr2keepers.Upke return nil, fmt.Errorf("no latest block found when fetching active upkeeps") } var payloads []ocr2keepers.UpkeepPayload - for _, uid := range p.activeUpkeeps.View(ocr2keepers.ConditionTrigger) { + for _, uid := range p.activeUpkeeps.View(types.ConditionTrigger) { payload, err := core.NewUpkeepPayload( uid, ocr2keepers.NewTrigger(latestBlock.Number, latestBlock.Hash), diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go index 81ea3913188..cad2d77411d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go @@ -5,9 +5,11 @@ import ( "sync/atomic" "testing" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -29,7 +31,7 @@ func TestUpkeepProvider_GetActiveUpkeeps(t *testing.T) { { "empty", &mockActiveUpkeepList{ - ViewFn: func(upkeepType ...ocr2keepers.UpkeepType) []*big.Int { + ViewFn: func(upkeepType ...types.UpkeepType) []*big.Int { return []*big.Int{} }, }, @@ -40,7 +42,7 @@ func TestUpkeepProvider_GetActiveUpkeeps(t *testing.T) { { "happy flow", &mockActiveUpkeepList{ - ViewFn: func(upkeepType ...ocr2keepers.UpkeepType) []*big.Int { + ViewFn: func(upkeepType ...types.UpkeepType) []*big.Int { return []*big.Int{ big.NewInt(1), big.NewInt(2), @@ -65,7 +67,7 @@ func TestUpkeepProvider_GetActiveUpkeeps(t *testing.T) { { "latest block not found", &mockActiveUpkeepList{ - ViewFn: func(upkeepType ...ocr2keepers.UpkeepType) []*big.Int { + ViewFn: func(upkeepType ...types.UpkeepType) []*big.Int { return []*big.Int{ big.NewInt(1), big.NewInt(2), @@ -100,11 +102,11 @@ func TestUpkeepProvider_GetActiveUpkeeps(t *testing.T) { type mockActiveUpkeepList struct { ActiveUpkeepList - ViewFn func(...ocr2keepers.UpkeepType) []*big.Int + ViewFn func(...types.UpkeepType) []*big.Int IsActiveFn func(id *big.Int) bool } -func (l *mockActiveUpkeepList) View(u ...ocr2keepers.UpkeepType) []*big.Int { +func (l *mockActiveUpkeepList) View(u ...types.UpkeepType) []*big.Int { return l.ViewFn(u...) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go index 19b3c46f502..9410374d7ca 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go @@ -8,7 +8,7 @@ import ( "sync" "time" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go index 7108e1b64c4..3912e2a99c6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 199bbac0536..53fff8751c3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -15,13 +15,12 @@ import ( ocr2keepers20coordinator "github.com/smartcontractkit/chainlink-automation/pkg/v2/coordinator" ocr2keepers20polling "github.com/smartcontractkit/chainlink-automation/pkg/v2/observer/polling" ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" - ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers21 "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" evmregistry20 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20" evmregistry21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" evmregistry21transmit "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" @@ -44,9 +43,9 @@ var ( ErrNoChainFromSpec = fmt.Errorf("could not create chain from spec") ) -func EVMProvider(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, spec job.Job, ethKeystore keystore.Eth) (evmrelay.OCR2KeeperProvider, error) { +func EVMProvider(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, spec job.Job, ethKeystore keystore.Eth, dbCfg pg.QConfig) (evmrelay.OCR2KeeperProvider, error) { oSpec := spec.OCR2OracleSpec - ocr2keeperRelayer := evmrelay.NewOCR2KeeperRelayer(db, chain, lggr.Named("OCR2KeeperRelayer"), ethKeystore) + ocr2keeperRelayer := evmrelay.NewOCR2KeeperRelayer(db, chain, lggr.Named("OCR2KeeperRelayer"), ethKeystore, dbCfg) keeperProvider, err := ocr2keeperRelayer.NewOCR2KeeperProvider( types.RelayArgs{ @@ -73,6 +72,7 @@ func EVMDependencies20( lggr logger.Logger, chain legacyevm.Chain, ethKeystore keystore.Eth, + dbCfg pg.QConfig, ) (evmrelay.OCR2KeeperProvider, *evmregistry20.EvmRegistry, Encoder20, *evmregistry20.LogProvider, error) { var err error @@ -80,7 +80,7 @@ func EVMDependencies20( var registry *evmregistry20.EvmRegistry // the provider will be returned as a dependency - if keeperProvider, err = EVMProvider(db, chain, lggr, spec, ethKeystore); err != nil { + if keeperProvider, err = EVMProvider(db, chain, lggr, spec, ethKeystore, dbCfg); err != nil { return nil, nil, nil, nil, err } @@ -109,25 +109,9 @@ func FilterNamesFromSpec20(spec *job.OCR2OracleSpec) (names []string, err error) } func EVMDependencies21( - spec job.Job, - db *sqlx.DB, - lggr logger.Logger, - chain legacyevm.Chain, - mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, - dbCfg pg.QConfig, ) (evmregistry21.AutomationServices, error) { - var err error - - oSpec := spec.OCR2OracleSpec - - rAddr := ethkey.MustEIP55Address(oSpec.ContractID).Address() - services, err := evmregistry21.New(rAddr, chain, mc, keyring, lggr, db, dbCfg) - if err != nil { - return nil, err - } - - return services, err + return evmregistry21.New(keyring) } func FilterNamesFromSpec21(spec *job.OCR2OracleSpec) (names []string, err error) { diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index d6a8c584fab..633ca69ea67 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -498,7 +498,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp func (r *Relayer) NewAutomationProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.AutomationProvider, error) { lggr := r.lggr.Named("AutomationProvider").Named(rargs.ExternalJobID.String()) - ocr2keeperRelayer := NewOCR2KeeperRelayer(r.db, r.chain, lggr.Named("OCR2KeeperRelayer"), r.ks.Eth()) + ocr2keeperRelayer := NewOCR2KeeperRelayer(r.db, r.chain, lggr.Named("OCR2KeeperRelayer"), r.ks.Eth(), r.pgCfg) return ocr2keeperRelayer.NewOCR2KeeperProvider(rargs, pargs) } diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 55c4d78e7b4..34daf55b835 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -6,6 +6,17 @@ import ( "fmt" "strings" + iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" + evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + + "github.com/smartcontractkit/chainlink-common/pkg/types/automation" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" @@ -27,8 +38,9 @@ import ( ) var ( - _ OCR2KeeperRelayer = (*ocr2keeperRelayer)(nil) - _ OCR2KeeperProvider = (*ocr2keeperProvider)(nil) + _ OCR2KeeperRelayer = (*ocr2keeperRelayer)(nil) + _ OCR2KeeperProvider = (*ocr2keeperProvider)(nil) + ErrInitializationFailure = fmt.Errorf("failed to initialize registry") ) // OCR2KeeperProviderOpts is the custom options to create a keeper provider @@ -41,6 +53,15 @@ type OCR2KeeperProviderOpts struct { // OCR2KeeperProvider provides all components needed for a OCR2Keeper plugin. type OCR2KeeperProvider interface { commontypes.Plugin + Registry() automation.Registry + Encoder() automation.Encoder + TransmitEventProvider() automation.EventProvider + BlockSubscriber() automation.BlockSubscriber + PayloadBuilder() automation.PayloadBuilder + UpkeepStateStore() automation.UpkeepStateStore + LogEventProvider() automation.LogEventProvider + LogRecoverer() automation.LogRecoverer + UpkeepProvider() automation.ConditionalUpkeepProvider } // OCR2KeeperRelayer contains the relayer and instantiating functions for OCR2Keeper providers. @@ -54,15 +75,17 @@ type ocr2keeperRelayer struct { chain legacyevm.Chain lggr logger.Logger ethKeystore keystore.Eth + dbCfg pg.QConfig } // NewOCR2KeeperRelayer is the constructor of ocr2keeperRelayer -func NewOCR2KeeperRelayer(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, ethKeystore keystore.Eth) OCR2KeeperRelayer { +func NewOCR2KeeperRelayer(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, ethKeystore keystore.Eth, dbCfg pg.QConfig) OCR2KeeperRelayer { return &ocr2keeperRelayer{ db: db, chain: chain, lggr: lggr, ethKeystore: ethKeystore, + dbCfg: dbCfg, } } @@ -78,10 +101,53 @@ func (r *ocr2keeperRelayer) NewOCR2KeeperProvider(rargs commontypes.RelayArgs, p return nil, err } - return &ocr2keeperProvider{ - configWatcher: cfgWatcher, - contractTransmitter: contractTransmitter, - }, nil + client := r.chain + + services := new(ocr2keeperProvider) + services.configWatcher = cfgWatcher + services.contractTransmitter = contractTransmitter + + addr := ethkey.MustEIP55Address(rargs.ContractID).Address() + + registryContract, err := iregistry21.NewIKeeperRegistryMaster(addr, client.Client()) + if err != nil { + return nil, fmt.Errorf("%w: failed to create caller for address and backend", ErrInitializationFailure) + } + // lookback blocks for transmit event is hard coded and should provide ample time for logs + // to be detected in most cases + var transmitLookbackBlocks int64 = 250 + transmitEventProvider, err := transmit.NewTransmitEventProvider(r.lggr, client.LogPoller(), addr, client.Client(), transmitLookbackBlocks) + if err != nil { + return nil, err + } + + services.transmitEventProvider = transmitEventProvider + + packer := encoding.NewAbiPacker() + services.encoder = encoding.NewReportEncoder(packer) + + finalityDepth := client.Config().EVM().FinalityDepth() + + orm := upkeepstate.NewORM(client.ID(), r.db, r.lggr, r.dbCfg) + scanner := upkeepstate.NewPerformedEventsScanner(r.lggr, client.LogPoller(), addr, finalityDepth) + services.upkeepStateStore = upkeepstate.NewUpkeepStateStore(orm, r.lggr, scanner) + + logProvider, logRecoverer := logprovider.New(r.lggr, client.LogPoller(), client.Client(), services.upkeepStateStore, finalityDepth) + services.logEventProvider = logProvider + services.logRecoverer = logRecoverer + blockSubscriber := evm.NewBlockSubscriber(client.HeadBroadcaster(), client.LogPoller(), finalityDepth, r.lggr) + services.blockSubscriber = blockSubscriber + + al := evm.NewActiveUpkeepList() + services.payloadBuilder = evm.NewPayloadBuilder(al, logRecoverer, r.lggr) + + services.registry = evm.NewEvmRegistry(r.lggr, addr, client, + registryContract, rargs.MercuryCredentials, al, logProvider, + packer, blockSubscriber, finalityDepth) + + services.conditionalUpkeepProvider = evm.NewUpkeepProvider(al, blockSubscriber, client.LogPoller()) + + return services, nil } type ocr3keeperProviderContractTransmitter struct { @@ -120,7 +186,16 @@ func (t *ocr3keeperProviderContractTransmitter) FromAccount() (ocrtypes.Account, type ocr2keeperProvider struct { *configWatcher - contractTransmitter ContractTransmitter + contractTransmitter ContractTransmitter + registry automation.Registry + encoder automation.Encoder + transmitEventProvider automation.EventProvider + blockSubscriber automation.BlockSubscriber + payloadBuilder automation.PayloadBuilder + upkeepStateStore automation.UpkeepStateStore + logEventProvider automation.LogEventProvider + logRecoverer automation.LogRecoverer + conditionalUpkeepProvider automation.ConditionalUpkeepProvider } func (c *ocr2keeperProvider) ContractTransmitter() ocrtypes.ContractTransmitter { @@ -175,3 +250,39 @@ func newOCR2KeeperConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rarg rargs.New, ), nil } + +func (c *ocr2keeperProvider) Registry() automation.Registry { + return c.registry +} + +func (c *ocr2keeperProvider) Encoder() automation.Encoder { + return c.encoder +} + +func (c *ocr2keeperProvider) TransmitEventProvider() automation.EventProvider { + return c.transmitEventProvider +} + +func (c *ocr2keeperProvider) BlockSubscriber() automation.BlockSubscriber { + return c.blockSubscriber +} + +func (c *ocr2keeperProvider) PayloadBuilder() automation.PayloadBuilder { + return c.payloadBuilder +} + +func (c *ocr2keeperProvider) UpkeepStateStore() automation.UpkeepStateStore { + return c.upkeepStateStore +} + +func (c *ocr2keeperProvider) LogEventProvider() automation.LogEventProvider { + return c.logEventProvider +} + +func (c *ocr2keeperProvider) LogRecoverer() automation.LogRecoverer { + return c.logRecoverer +} + +func (c *ocr2keeperProvider) UpkeepProvider() automation.ConditionalUpkeepProvider { + return c.conditionalUpkeepProvider +} diff --git a/go.md b/go.md index 090221a89fd..2a893c2a55e 100644 --- a/go.md +++ b/go.md @@ -46,6 +46,7 @@ flowchart LR click tdh2/go/tdh2 href "https://github.com/smartcontractkit/tdh2" chainlink/v2 --> wsrpc click wsrpc href "https://github.com/smartcontractkit/wsrpc" + chainlink-automation --> chainlink-common chainlink-automation --> libocr chainlink-common --> libocr chainlink-cosmos --> chainlink-common diff --git a/go.mod b/go.mod index fd1ffe4760a..2d27bc575fb 100644 --- a/go.mod +++ b/go.mod @@ -64,8 +64,8 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b + github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d diff --git a/go.sum b/go.sum index 0e2b917f251..6db671e7dec 100644 --- a/go.sum +++ b/go.sum @@ -1148,10 +1148,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= -github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b h1:PRuvHgPka1gIGPKASuJHvqUvYWhZO7z5B/kKapjkZaM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 h1:3cWO2/lFVDul5SVTgl4/RX/GXcT8Zq5NGMPeNEz09tY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d6f1fa94c6d..9df6d54bd23 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,8 +24,8 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b + github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 github.com/smartcontractkit/chainlink-testing-framework v1.22.4 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f4129568536..98351519d13 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1481,10 +1481,10 @@ 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/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= -github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b h1:PRuvHgPka1gIGPKASuJHvqUvYWhZO7z5B/kKapjkZaM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240108182329-237f56daad6b/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 h1:3cWO2/lFVDul5SVTgl4/RX/GXcT8Zq5NGMPeNEz09tY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From 958e2c62d61fb4a2d1c1c07644d12f5b4c43f3a9 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 17 Jan 2024 21:58:22 -0600 Subject: [PATCH 166/234] bump geth; replace common.Address.Hash() calls; restore standard log (#11806) * bump geth; replace common.Address.Hash() calls; restore standard log * run make generate --------- Co-authored-by: James Walker --- core/chains/evm/logpoller/log_poller_test.go | 4 +- core/chains/evm/logpoller/orm_test.go | 14 ++--- core/chains/evm/utils/utils.go | 6 +++ core/cmd/shell.go | 7 +++ ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- core/scripts/go.mod | 23 ++++---- core/scripts/go.sum | 52 +++++++++---------- core/services/functions/orm_test.go | 3 +- core/services/keystore/keys/ethkey/address.go | 4 +- .../v21/logprovider/filter_test.go | 14 ++--- .../evmregistry/v21/mercury/mercury_test.go | 2 +- .../vrf_coordinator_interface_test.go | 2 +- ...rf_coordinator_solidity_crosscheck_test.go | 2 +- go.mod | 25 +++++---- go.sum | 52 +++++++++---------- integration-tests/go.mod | 21 ++++---- integration-tests/go.sum | 48 ++++++++--------- 21 files changed, 154 insertions(+), 135 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 4bfc403e341..2508e676e6c 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -71,14 +71,14 @@ func populateDatabase(t testing.TB, o *logpoller.DbORM, chainID *big.Int) (commo EventSig: event1, Topics: [][]byte{event1[:], logpoller.EvmWord(uint64(i + 1000*j)).Bytes()}, Address: addr, - TxHash: utils.RandomAddress().Hash(), + TxHash: utils.RandomHash(), Data: logpoller.EvmWord(uint64(i + 1000*j)).Bytes(), CreatedAt: blockTimestamp, }) } require.NoError(t, o.InsertLogs(logs)) - require.NoError(t, o.InsertBlock(utils.RandomAddress().Hash(), int64((j+1)*1000-1), startDate.Add(time.Duration(j*1000)*time.Hour), 0)) + require.NoError(t, o.InsertBlock(utils.RandomHash(), int64((j+1)*1000-1), startDate.Add(time.Duration(j*1000)*time.Hour), 0)) } return event1, address1, address2 diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index b8468d28e98..0af62ebd547 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -1106,7 +1106,7 @@ func TestSelectLatestBlockNumberEventSigsAddrsWithConfs(t *testing.T) { GenLog(th.ChainID, 2, 2, utils.RandomAddress().String(), event2[:], address2), GenLog(th.ChainID, 2, 3, utils.RandomAddress().String(), event2[:], address2), })) - require.NoError(t, th.ORM.InsertBlock(utils.RandomAddress().Hash(), 3, time.Now(), 1)) + require.NoError(t, th.ORM.InsertBlock(utils.RandomHash(), 3, time.Now(), 1)) tests := []struct { name string @@ -1205,9 +1205,9 @@ func TestSelectLogsCreatedAfter(t *testing.T) { GenLogWithTimestamp(th.ChainID, 2, 2, utils.RandomAddress().String(), event[:], address, block2ts), GenLogWithTimestamp(th.ChainID, 1, 3, utils.RandomAddress().String(), event[:], address, block3ts), })) - require.NoError(t, th.ORM.InsertBlock(utils.RandomAddress().Hash(), 1, block1ts, 0)) - require.NoError(t, th.ORM.InsertBlock(utils.RandomAddress().Hash(), 2, block2ts, 1)) - require.NoError(t, th.ORM.InsertBlock(utils.RandomAddress().Hash(), 3, block3ts, 2)) + require.NoError(t, th.ORM.InsertBlock(utils.RandomHash(), 1, block1ts, 0)) + require.NoError(t, th.ORM.InsertBlock(utils.RandomHash(), 2, block2ts, 1)) + require.NoError(t, th.ORM.InsertBlock(utils.RandomHash(), 3, block3ts, 2)) type expectedLog struct { block int64 @@ -1309,7 +1309,7 @@ func TestNestedLogPollerBlocksQuery(t *testing.T) { require.Len(t, logs, 0) // Persist block - require.NoError(t, th.ORM.InsertBlock(utils.RandomAddress().Hash(), 10, time.Now(), 0)) + require.NoError(t, th.ORM.InsertBlock(utils.RandomHash(), 10, time.Now(), 0)) // Check if query actually works well with provided dataset logs, err = th.ORM.SelectIndexedLogs(address, event, 1, []common.Hash{event}, logpoller.Unconfirmed) @@ -1542,12 +1542,12 @@ func Benchmark_LogsDataWordBetween(b *testing.B) { EventSig: commitReportAccepted, Topics: [][]byte{}, Address: commitStoreAddress, - TxHash: utils.RandomAddress().Hash(), + TxHash: utils.RandomHash(), Data: data, CreatedAt: time.Now(), }) } - require.NoError(b, o.InsertBlock(utils.RandomAddress().Hash(), int64(numberOfReports*numberOfMessagesPerReport), time.Now(), int64(numberOfReports*numberOfMessagesPerReport))) + require.NoError(b, o.InsertBlock(utils.RandomHash(), int64(numberOfReports*numberOfMessagesPerReport), time.Now(), int64(numberOfReports*numberOfMessagesPerReport))) require.NoError(b, o.InsertLogs(dbLogs)) b.ResetTimer() diff --git a/core/chains/evm/utils/utils.go b/core/chains/evm/utils/utils.go index 97aaf3b2bd4..6784d33cdb7 100644 --- a/core/chains/evm/utils/utils.go +++ b/core/chains/evm/utils/utils.go @@ -33,6 +33,12 @@ func RandomAddress() common.Address { return common.BytesToAddress(b) } +func RandomHash() common.Hash { + b := make([]byte, 32) + _, _ = rand.Read(b) // Assignment for errcheck. Only used in tests so we can ignore. + return common.BytesToHash(b) +} + // IsEmptyAddress checks that the address is empty, synonymous with the zero // account/address. No logs can come from this address, as there is no contract // present there. diff --git a/core/cmd/shell.go b/core/cmd/shell.go index b2298ab399d..5ca938b1b40 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net" "net/http" "net/url" @@ -56,6 +57,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/plugins" ) +func init() { + // hack to undo geth's disruption of the std default logger + // remove with geth v1.13.10 + slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, nil))) +} + var ( initGlobalsOnce sync.Once prometheus *ginprom.Prometheus diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 0a77e57c88f..132f38507ad 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.2 +GETH_VERSION: 1.13.8 functions: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.bin 3c972870b0afeb6d73a29ebb182f24956a2cebb127b21c4f867d1ecf19a762db functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.abi ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.bin 6beec092fbb3b619dfe69f1ad23392b0bbaf00327b335e4080f921c7122a57e4 functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 0dd8e4cbb92..3e8d6420e57 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.2 +GETH_VERSION: 1.13.8 aggregator_v2v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.bin 95e8814b408bb05bf21742ef580d98698b7db6a9bac6a35c3de12b23aec4ee28 aggregator_v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.bin 351b55d3b0f04af67db6dfb5c92f1c64479400ca1fec77afc20bc0ce65cb49ab authorized_forwarder: ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.bin 8ea76c883d460f8353a45a493f2aebeb5a2d9a7b4619d1bc4fff5fb590bb3e10 diff --git a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 29f0deb7b2c..d54d3fb8cdf 100644 --- a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.2 +GETH_VERSION: 1.13.8 errored_verifier: ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.abi ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.bin 510d18a58bfda646be35e46491baf73041eb333a349615465b20e2b5b41c5f73 exposed_verifier: ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.bin 6932cea8f2738e874d3ec9e1a4231d2421704030c071d9e15dd2f7f08482c246 fee_manager: ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.bin 1b852df75bfabcc2b57539e84309cd57f9e693a2bb6b25a50e4a6101ccf32c49 diff --git a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt index ff9bfa2fea4..3268bb55bd7 100644 --- a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.2 +GETH_VERSION: 1.13.8 burn_mint_erc677: ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.abi ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.bin 405c9016171e614b17e10588653ef8d33dcea21dd569c3fddc596a46fcff68a3 erc20: ../../../contracts/solc/v0.8.19/ERC20/ERC20.abi ../../../contracts/solc/v0.8.19/ERC20/ERC20.bin 5b1a93d9b24f250e49a730c96335a8113c3f7010365cba578f313b483001d4fc link_token: ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.abi ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.bin c0ef9b507103aae541ebc31d87d051c2764ba9d843076b30ec505d37cdfffaba diff --git a/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt index cded1f0c5e5..6d5b5c22c58 100644 --- a/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.12.2 +GETH_VERSION: 1.13.8 entry_point: ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.abi ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.bin 2cb4bb2ba3efa8df3dfb0a57eb3727d17b68fe202682024fa7cfb4faf026833e greeter: ../../../contracts/solc/v0.8.15/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c greeter_wrapper: ../../../contracts/solc/v0.8.15/Greeter/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 1efcd5275d2..7c49c66782a 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -8,7 +8,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 - github.com/ethereum/go-ethereum v1.12.2 + github.com/ethereum/go-ethereum v1.13.8 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.4.0 github.com/jmoiron/sqlx v1.3.5 @@ -50,14 +50,14 @@ require ( github.com/Depado/ginprom v1.8.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/VictoriaMetrics/fastcache v1.10.0 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect @@ -70,13 +70,14 @@ require ( github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cometbft/cometbft v0.37.2 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.10.0 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/cosmos-sdk v0.47.4 // indirect @@ -87,7 +88,8 @@ require ( github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -102,7 +104,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/esote/minmaxheap v1.0.0 // indirect - github.com/ethereum/c-kzg-4844 v0.3.1 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 // indirect @@ -111,6 +113,7 @@ require ( github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/getsentry/sentry-go v0.19.0 // indirect github.com/gin-contrib/cors v1.5.0 // indirect @@ -131,7 +134,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/go-webauthn/webauthn v0.9.4 // indirect github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -172,7 +174,7 @@ require ( github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -294,7 +296,7 @@ require ( go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.6.0 // indirect - golang.org/x/crypto v0.16.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect @@ -314,7 +316,6 @@ require ( gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect pgregory.net/rapid v0.5.5 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index cf504cf6047..a6875f319ca 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -114,8 +114,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= -github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -164,8 +164,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= @@ -211,7 +211,6 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= @@ -240,17 +239,19 @@ github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOG github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= @@ -262,8 +263,8 @@ github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4 github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -302,8 +303,10 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -379,10 +382,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= -github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= -github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -425,6 +428,8 @@ github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89 github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= @@ -499,8 +504,6 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-webauthn/webauthn v0.9.4 h1:YxvHSqgUyc5AK2pZbqkWWR55qKeDPhP8zLDr6lpIc2g= github.com/go-webauthn/webauthn v0.9.4/go.mod h1:LqupCtzSef38FcxzaklmOn7AykGKhAhr9xlRbdbgnTw= @@ -733,8 +736,8 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= @@ -1441,8 +1444,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1643,7 +1646,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1914,8 +1916,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/core/services/functions/orm_test.go b/core/services/functions/orm_test.go index 459b1f56a60..ca92aafcb0e 100644 --- a/core/services/functions/orm_test.go +++ b/core/services/functions/orm_test.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -47,7 +48,7 @@ func createRequest(t *testing.T, orm functions.ORM) (functions.RequestID, common func createRequestWithTimestamp(t *testing.T, orm functions.ORM, ts time.Time) (functions.RequestID, common.Hash) { id := newRequestID() - txHash := testutils.NewAddress().Hash() + txHash := utils.RandomHash() newReq := &functions.Request{ RequestID: id, RequestTxHash: &txHash, diff --git a/core/services/keystore/keys/ethkey/address.go b/core/services/keystore/keys/ethkey/address.go index 1b26413f634..0d93a4cdb29 100644 --- a/core/services/keystore/keys/ethkey/address.go +++ b/core/services/keystore/keys/ethkey/address.go @@ -53,10 +53,10 @@ func EIP55AddressFromAddress(a common.Address) EIP55Address { func (a EIP55Address) Bytes() []byte { return a.Address().Bytes() } // Big returns a big.Int representation -func (a EIP55Address) Big() *big.Int { return a.Address().Hash().Big() } +func (a EIP55Address) Big() *big.Int { return a.Address().Big() } // Hash returns the Hash -func (a EIP55Address) Hash() common.Hash { return a.Address().Hash() } +func (a EIP55Address) Hash() common.Hash { return common.BytesToHash(a.Bytes()) } // Address returns EIP55Address as a go-ethereum Address type func (a EIP55Address) Address() common.Address { return common.HexToAddress(a.String()) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_test.go index 8b3b836f87f..75684d17bd8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_test.go @@ -74,7 +74,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "no selector configured - all logs are returned", upkeepFilter{ selector: 0, - topics: []common.Hash{contractAddress.Hash(), emptyTopic, emptyTopic, emptyTopic}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), emptyTopic, emptyTopic, emptyTopic}, upkeepID: uid, }, []logpoller.Log{ @@ -90,7 +90,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "selector is 1 - topics 1 is used to filter logs", upkeepFilter{ selector: 1, - topics: []common.Hash{contractAddress.Hash(), common.HexToHash(topic10), emptyTopic, emptyTopic}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), common.HexToHash(topic10), emptyTopic, emptyTopic}, upkeepID: uid, }, []logpoller.Log{ @@ -105,7 +105,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "selector is 2 - topic 2 is used to filter logs", upkeepFilter{ selector: 2, - topics: []common.Hash{contractAddress.Hash(), emptyTopic, common.HexToHash(topic21), emptyTopic}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), emptyTopic, common.HexToHash(topic21), emptyTopic}, upkeepID: uid, }, []logpoller.Log{ @@ -120,7 +120,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "selector is 3 - topics 1 2 are used to filter logs", upkeepFilter{ selector: 3, - topics: []common.Hash{contractAddress.Hash(), common.HexToHash(topic10), common.HexToHash(topic21), emptyTopic}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), common.HexToHash(topic10), common.HexToHash(topic21), emptyTopic}, upkeepID: uid, }, []logpoller.Log{ @@ -137,7 +137,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "selector is 4 - topic 3 is used to filter logs", upkeepFilter{ selector: 4, - topics: []common.Hash{contractAddress.Hash(), emptyTopic, emptyTopic, common.HexToHash(topic31)}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), emptyTopic, emptyTopic, common.HexToHash(topic31)}, upkeepID: uid, }, []logpoller.Log{ @@ -152,7 +152,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "selector is 5 - topics 1 3 are used to filter logs", upkeepFilter{ selector: 5, - topics: []common.Hash{contractAddress.Hash(), common.HexToHash(topic11), emptyTopic, common.HexToHash(topic31)}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), common.HexToHash(topic11), emptyTopic, common.HexToHash(topic31)}, upkeepID: uid, }, []logpoller.Log{ @@ -170,7 +170,7 @@ func TestUpkeepFilter_Select(t *testing.T) { "selector is 7 - topics 1 2 3 are used to filter logs", upkeepFilter{ selector: 7, - topics: []common.Hash{contractAddress.Hash(), common.HexToHash(topic10), common.HexToHash(topic20), common.HexToHash(topic30)}, + topics: []common.Hash{common.BytesToHash(contractAddress.Bytes()), common.HexToHash(topic10), common.HexToHash(topic20), common.HexToHash(topic30)}, upkeepID: uid, }, []logpoller.Log{ diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go index c7e22d45878..ce82ec7ae8f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go @@ -80,7 +80,7 @@ func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { { name: "failure - unpack error", data: []byte{1, 2, 3, 4}, - err: errors.New("unpack error: invalid data for unpacking"), + err: errors.New("unpack error: invalid identifier, have 0x01020304 want 0xf055e4a2"), }, } diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go index 2601f800e9e..47c47af7c9b 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go @@ -31,7 +31,7 @@ var ( Data: append(append(append(append( keyHash.Bytes(), common.BigToHash(seed).Bytes()...), - sender.Hash().Bytes()...), + common.BytesToHash(sender.Bytes()).Bytes()...), common.BigToHash(fee).Bytes()...), requestID.Bytes()...), Topics: []common.Hash{{}, jobID}, diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go index e758689593a..946365e31a4 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_solidity_crosscheck_test.go @@ -170,7 +170,7 @@ func TestRandomnessRequestLog(t *testing.T) { golangSeed := utils.MustHash(string(append(append(append( keyHash[:], common.BigToHash(hardcodedSeed).Bytes()...), - tc.consumerAddress.Hash().Bytes()...), + common.BytesToHash(tc.consumerAddress.Bytes()).Bytes()...), common.BigToHash(nonce).Bytes()...))) assert.Equal(t, golangSeed, common.BigToHash((log.Seed)), "VRFCoordinator logged different actual input seed than expected by golang code!") assert.Equal(t, jobID, log.JobID, "VRFCoordinator logged different JobID from randomness request!") diff --git a/go.mod b/go.mod index 2d27bc575fb..882a63e8591 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/cosmos/cosmos-sdk v0.47.4 github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e github.com/esote/minmaxheap v1.0.0 - github.com/ethereum/go-ethereum v1.12.2 + github.com/ethereum/go-ethereum v1.13.8 github.com/fatih/color v1.16.0 github.com/fxamacker/cbor/v2 v2.5.0 github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 @@ -90,7 +90,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.16.0 + golang.org/x/crypto v0.17.0 golang.org/x/exp v0.0.0-20231127185646-65229373498e golang.org/x/sync v0.5.0 golang.org/x/term v0.15.0 @@ -122,13 +122,14 @@ require ( github.com/CosmWasm/wasmvm v1.2.4 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/VictoriaMetrics/fastcache v1.10.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect @@ -139,12 +140,13 @@ require ( github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.10.0 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect @@ -154,7 +156,8 @@ require ( github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.3.0 // indirect @@ -166,12 +169,13 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect - github.com/ethereum/c-kzg-4844 v0.3.1 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gagliardetto/binary v0.7.1 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect @@ -185,7 +189,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect @@ -219,7 +222,7 @@ require ( github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/huin/goupnp v1.3.0 // indirect @@ -308,6 +311,7 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect golang.org/x/arch v0.6.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect @@ -317,7 +321,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect pgregory.net/rapid v0.5.5 // indirect diff --git a/go.sum b/go.sum index 6db671e7dec..e27fc213632 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= -github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= @@ -168,8 +168,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= @@ -197,7 +197,6 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= @@ -226,17 +225,19 @@ github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOG github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= @@ -248,8 +249,8 @@ github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4 github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -288,8 +289,10 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -361,10 +364,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= -github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= -github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -407,6 +410,8 @@ github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89 github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= @@ -481,8 +486,6 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -718,8 +721,8 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= @@ -1427,8 +1430,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1626,7 +1629,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1901,8 +1903,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 9df6d54bd23..56f4ce0351f 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -9,7 +9,7 @@ require ( cosmossdk.io/errors v1.0.0 github.com/K-Phoen/grabana v0.21.17 github.com/cli/go-gh/v2 v2.0.0 - github.com/ethereum/go-ethereum v1.12.2 + github.com/ethereum/go-ethereum v1.13.8 github.com/go-resty/resty/v2 v2.7.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.4.0 @@ -82,7 +82,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.1 // indirect - github.com/VictoriaMetrics/fastcache v1.10.0 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect @@ -95,7 +95,7 @@ require ( github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/buger/jsonparser v1.1.1 // indirect @@ -115,13 +115,14 @@ require ( github.com/cli/safeexec v1.0.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cometbft/cometbft v0.37.2 // indirect github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.10.0 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/containerd v1.7.7 // indirect github.com/containerd/log v0.1.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect @@ -136,7 +137,8 @@ require ( github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect - github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.3.0 // indirect @@ -156,7 +158,7 @@ require ( github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/esote/minmaxheap v1.0.0 // indirect - github.com/ethereum/c-kzg-4844 v0.3.1 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect @@ -172,6 +174,7 @@ require ( github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/getsentry/sentry-go v0.19.0 // indirect github.com/gin-contrib/sessions v0.0.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -199,7 +202,6 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/go-webauthn/webauthn v0.9.4 // indirect github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -261,7 +263,7 @@ require ( github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/huin/goupnp v1.3.0 // indirect @@ -449,7 +451,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.28.2 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 98351519d13..788029b0136 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -146,8 +146,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56 h1:HItfr1XKD/4xnsJE56m3uxnkMQ9lbg8xDnkf9qoZCH0= github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56/go.mod h1:ICriE9bLX5CLxL9OFQ2N+2N+f+803LNJ1utJb1+Inx0= -github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= -github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd5wAKUHEO/k= @@ -214,8 +214,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= @@ -252,7 +252,6 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= @@ -296,17 +295,19 @@ github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOG github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= -github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= -github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= @@ -318,8 +319,8 @@ github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4 github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/containerd v1.7.7 h1:QOC2K4A42RQpcrZyptP6z9EJZnlHfHJUfZrAAHe15q4= github.com/containerd/containerd v1.7.7/go.mod h1:3c4XZv6VeT9qgf9GMTxNTMFxGJrGpI2vz1yk4ye+YY8= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= @@ -368,8 +369,10 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -453,10 +456,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= -github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= -github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -513,6 +516,8 @@ github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89 github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= @@ -625,8 +630,6 @@ github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSM github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -953,8 +956,8 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= @@ -2029,7 +2032,6 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2320,8 +2322,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= From 400e292bfe6b91d7a1e9cd3d375c13562b1e65bc Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 18 Jan 2024 06:47:32 -0600 Subject: [PATCH 167/234] bump libocr 6359502 (#11799) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7c49c66782a..50c7ea8bb00 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -22,7 +22,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 + github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index a6875f319ca..e4e7406126c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1185,8 +1185,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 h1:AA7vf29c6lFsZm+MtIEtXtg6VUOQV6waJo5MUuHfRjQ= -github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7/go.mod h1:WcuWFMskcGK0MYZuH5hEhGJOzdJRUFeNEM4PAKlejI4= +github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 h1:3y9WsXkZ5lxFrmfH7DQHs/q308lylKId5l/3VC0QAdM= +github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1/go.mod h1:kC0qmVPUaVkFqGiZMNhmRmjdphuUmeyLEdlWFOQzFWI= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/go.mod b/go.mod index 882a63e8591..a5aa4eb23dc 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 - github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 + github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wsrpc v0.7.2 diff --git a/go.sum b/go.sum index e27fc213632..5578bf8ff31 100644 --- a/go.sum +++ b/go.sum @@ -1171,8 +1171,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 h1:AA7vf29c6lFsZm+MtIEtXtg6VUOQV6waJo5MUuHfRjQ= -github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7/go.mod h1:WcuWFMskcGK0MYZuH5hEhGJOzdJRUFeNEM4PAKlejI4= +github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 h1:3y9WsXkZ5lxFrmfH7DQHs/q308lylKId5l/3VC0QAdM= +github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1/go.mod h1:kC0qmVPUaVkFqGiZMNhmRmjdphuUmeyLEdlWFOQzFWI= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 56f4ce0351f..c4bd01defca 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -29,7 +29,7 @@ require ( github.com/smartcontractkit/chainlink-testing-framework v1.22.4 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 + github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.4.0 github.com/spf13/cobra v1.6.1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 788029b0136..1bfd4be9a4e 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1506,8 +1506,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 h1:AA7vf29c6lFsZm+MtIEtXtg6VUOQV6waJo5MUuHfRjQ= -github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7/go.mod h1:WcuWFMskcGK0MYZuH5hEhGJOzdJRUFeNEM4PAKlejI4= +github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 h1:3y9WsXkZ5lxFrmfH7DQHs/q308lylKId5l/3VC0QAdM= +github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1/go.mod h1:kC0qmVPUaVkFqGiZMNhmRmjdphuUmeyLEdlWFOQzFWI= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= From 3e91b244abb9443c0ed688cda1f8ed221cc84608 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 18 Jan 2024 08:25:12 -0500 Subject: [PATCH 168/234] [TT-821] Optimize Integration Test Runtimes (#11795) * Experiment with OCR2 runtimes * Fixes workflow dispatch * Add back linea * More power to the engines --- .github/workflows/integration-tests.yml | 52 ++++++++++++------------ .github/workflows/live-testnet-tests.yml | 7 ++++ 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index d852520d8e1..c11928a4f7b 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -129,7 +129,7 @@ jobs: needs: [changes, enforce-ctf-version] steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -142,7 +142,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Chainlink Image - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: ./.github/actions/build-chainlink-image with: tag_suffix: ${{ matrix.image.tag-suffix }} @@ -164,7 +164,7 @@ jobs: needs: [changes] steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -177,7 +177,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Test Image - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: ./.github/actions/build-test-image with: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} @@ -228,7 +228,7 @@ jobs: fi eth-smoke-tests-matrix-automation: - if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} + if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} environment: integration permissions: checks: write @@ -250,7 +250,7 @@ jobs: name: ETH Smoke Tests ${{ matrix.product.name }} steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -274,7 +274,7 @@ jobs: fi ## Run this step when changes that require tests to be run are made - name: Run Tests - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 @@ -297,7 +297,7 @@ jobs: QA_KUBECONFIG: "" eth-smoke-tests-matrix: - if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} + if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} environment: integration permissions: checks: write @@ -315,7 +315,7 @@ jobs: matrix: product: - name: cron - nodes: 1 + nodes: 2 os: ubuntu-latest pyroscope_env: "" - name: flux @@ -323,17 +323,17 @@ jobs: os: ubuntu-latest pyroscope_env: "" - name: ocr - nodes: 1 + nodes: 2 os: ubuntu-latest file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 - nodes: 1 + nodes: 4 os: ubuntu-latest file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 - nodes: 1 + nodes: 4 os: ubuntu-latest pyroscope_env: ci-smoke-ocr2-plugins-evm-simulated tag_suffix: "-plugins" @@ -342,15 +342,15 @@ jobs: os: ubuntu-latest pyroscope_env: "" - name: vrf - nodes: 1 + nodes: 2 os: ubuntu-latest pyroscope_env: ci-smoke-vrf-evm-simulated - name: vrfv2 - nodes: 1 + nodes: 2 os: ubuntu-latest pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus - nodes: 1 + nodes: 3 os: ubuntu-latest pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr @@ -365,7 +365,7 @@ jobs: name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -441,7 +441,7 @@ jobs: docker logs otel-collector ## Run this step when changes that require tests to be run are made - name: Run Tests - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 @@ -772,7 +772,7 @@ jobs: FORCE_COLOR: 1 steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -788,7 +788,7 @@ jobs: repository: smartcontractkit/chainlink-solana ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Build contracts - if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' + if: (needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch') && needs.solana-test-image-exists.outputs.exists == 'false' uses: smartcontractkit/chainlink-solana/.github/actions/build_contract_artifacts@21675b3a7dcdff8e790391708d4763020cace21e # stable action on December 18 2023 with: ref: ${{ needs.get_solana_sha.outputs.sha }} @@ -813,7 +813,7 @@ jobs: CONTRACT_ARTIFACTS_PATH: contracts/target/deploy steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' + if: (needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch') && needs.solana-test-image-exists.outputs.exists == 'false' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -822,13 +822,13 @@ jobs: this-job-name: Solana Build Test Image continue-on-error: true - name: Checkout the repo - if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' + if: (needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch') && needs.solana-test-image-exists.outputs.exists == 'false' uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Build Test Image - if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' + if: (needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch') && needs.solana-test-image-exists.outputs.exists == 'false' uses: ./.github/actions/build-test-image with: tag: ${{ needs.get_solana_sha.outputs.sha }} @@ -864,7 +864,7 @@ jobs: CONTRACT_ARTIFACTS_PATH: contracts/target/deploy steps: - name: Collect Metrics - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: @@ -879,7 +879,7 @@ jobs: repository: smartcontractkit/chainlink-solana ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Run Setup - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: go_mod_path: ./integration-tests/go.mod @@ -892,7 +892,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Pull Artfacts - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' run: | IMAGE_NAME=${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-solana-tests:${{ needs.get_solana_sha.outputs.sha }} # Pull the Docker image @@ -908,7 +908,7 @@ jobs: # Remove the created container docker rm "$CONTAINER_ID" - name: Run Tests - if: needs.changes.outputs.src == 'true' + if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: export ENV_JOB_IMAGE=${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-solana-tests:${{ needs.get_solana_sha.outputs.sha }} && make test_smoke diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index da5bd59f76a..c970751779d 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -21,6 +21,13 @@ env: INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com MOD_CACHE_VERSION: 2 CHAINLINK_NODE_FUNDING: .5 + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} + LOGSTREAM_LOG_TARGETS: loki + GRAFANA_URL: ${{ vars.GRAFANA_URL }} + RUN_ID: ${{ github.run_id }} CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} From 286c8c01c13ab048301195e6a24cbf5da18cc290 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:07:04 -0500 Subject: [PATCH 169/234] Re-add network policies back to helm chart and fix node egress (#11771) * Re-add helm network policies from prev commit: 48002f273655c179b6254803c047f6ccbaf52c17^1 * Fix chainlink node networkpolicy egress ports * Test removing network policies * Revert "Test removing network policies" This reverts commit 8260e0fab5add3a6fed0a15bfedf62802d40572f. * Use proper syntax * Fix syntax * Delete egress rules on node np * Create network policy for runner template * Set default np to ingress only * Fix EOF * Fix indent * Include default egress rules * Rename network policy * Avoid hardcode * test load and chaos, fix reorg --------- Co-authored-by: skudasov --- charts/chainlink-cluster/devspace.yaml | 4 +-- .../templates/chainlink-db-networkpolicy.yaml | 23 ++++++++++++++++ .../chainlink-node-networkpolicy.yaml | 19 +++++++++++++ .../templates/geth-deployment.yaml | 2 ++ .../templates/geth-networkpolicy.yaml | 25 +++++++++++++++++ .../templates/mockserver-networkpolicy.yaml | 23 ++++++++++++++++ .../templates/networkpolicy-default-deny.yaml | 27 +++++++++++++++++++ .../templates/runner-networkpolicy.yaml | 19 +++++++++++++ 8 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/geth-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml create mode 100644 charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml create mode 100644 charts/chainlink-cluster/templates/runner-networkpolicy.yaml diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index 902925b278e..0157ce7451c 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -21,8 +21,8 @@ pipelines: run_dependencies --all ensure_pull_secrets --all build_images ---var DOCKER_DEFAULT_PLATFORM=linux/amd64 --all -t $(git rev-parse --short HEAD) - kubectl annotate namespace ${DEVSPACE_NAMESPACE} janitor/ttl=${NS_TTL} - kubectl label namespace/${DEVSPACE_NAMESPACE} network=crib + kubectl annotate namespace ${DEVSPACE_NAMESPACE} janitor/ttl=${NS_TTL} || true + kubectl label namespace/${DEVSPACE_NAMESPACE} network=crib || true create_deployments --all echo "Namespace ${DEVSPACE_NAMESPACE} will be deleted in ${NS_TTL}" purge: diff --git a/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml b/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml new file mode 100644 index 00000000000..e5d029b7865 --- /dev/null +++ b/charts/chainlink-cluster/templates/chainlink-db-networkpolicy.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-db +spec: + podSelector: + matchLabels: + app: {{ $.Release.Name }}-db + policyTypes: + - Ingress + ingress: + - from: + # Allow all node pods to access the database pods. + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + # Allow all runner pods to access the database pods. + - podSelector: + matchLabels: + app: runner + ports: + - protocol: TCP + port: 5432 diff --git a/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml b/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml new file mode 100644 index 00000000000..321bc531626 --- /dev/null +++ b/charts/chainlink-cluster/templates/chainlink-node-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-node +spec: + podSelector: + matchLabels: + app: {{ $.Release.Name }} + policyTypes: + - Ingress + ingress: + # Allow all ingress traffic between the node pods and from runner pod. + - from: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + - podSelector: + matchLabels: + app: runner diff --git a/charts/chainlink-cluster/templates/geth-deployment.yaml b/charts/chainlink-cluster/templates/geth-deployment.yaml index 6948c4df288..8d2d4d3c76c 100644 --- a/charts/chainlink-cluster/templates/geth-deployment.yaml +++ b/charts/chainlink-cluster/templates/geth-deployment.yaml @@ -65,6 +65,8 @@ spec: - '--miner.etherbase' - '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' - '--ipcdisable' + - '--http.api' + - 'admin,debug,web3,eth,txpool,personal,miner,net' - '--http' - '--http.vhosts' - '*' diff --git a/charts/chainlink-cluster/templates/geth-networkpolicy.yaml b/charts/chainlink-cluster/templates/geth-networkpolicy.yaml new file mode 100644 index 00000000000..5be59136251 --- /dev/null +++ b/charts/chainlink-cluster/templates/geth-networkpolicy.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-geth +spec: + podSelector: + matchLabels: + app: geth + policyTypes: + - Ingress + ingress: + - from: + # Allow http and websocket connections from the node pods. + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + # Allow http and websocket connections from the runner pods. + - podSelector: + matchLabels: + app: runner + ports: + - protocol: TCP + port: 8544 + - protocol: TCP + port: 8546 diff --git a/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml b/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml new file mode 100644 index 00000000000..074b1ab089a --- /dev/null +++ b/charts/chainlink-cluster/templates/mockserver-networkpolicy.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-mockserver +spec: + podSelector: + matchLabels: + app: mockserver + policyTypes: + - Ingress + ingress: + - from: + # Allow http traffic from the node pods. + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + # Allow http traffic from the runner pods. + - podSelector: + matchLabels: + app: runner + ports: + - protocol: TCP + port: 1080 diff --git a/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml b/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml new file mode 100644 index 00000000000..e2a4407be6a --- /dev/null +++ b/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml @@ -0,0 +1,27 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny +spec: + podSelector: + matchLabels: {} + policyTypes: + - Ingress + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: "{{ $.Release.Namespace }}" + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + podSelector: + matchLabels: + k8s-app: kube-dns + ports: + - protocol: TCP + port: 53 + - protocol: UDP + port: 53 diff --git a/charts/chainlink-cluster/templates/runner-networkpolicy.yaml b/charts/chainlink-cluster/templates/runner-networkpolicy.yaml new file mode 100644 index 00000000000..2bb6ac98625 --- /dev/null +++ b/charts/chainlink-cluster/templates/runner-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ $.Release.Name }}-runner +spec: + podSelector: + matchLabels: + app: runner + policyTypes: + - Ingress + ingress: + # Allow all ingress traffic between the node pods and from runner pod. + - from: + - podSelector: + matchLabels: + app: {{ $.Release.Name }} + - podSelector: + matchLabels: + app: runner From 9c22655006bf58422556840dca234a18d82eacd2 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:57:26 -0500 Subject: [PATCH 170/234] Add role label to nodes in chart (#11817) * Add role label to nodes in chart * Change back var * Fix typo * Implicitly make the first node the bootstrap node to avoid new values --- .../templates/chainlink-node-deployment.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml index 16156ef4ffc..884cf0e535b 100644 --- a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml @@ -1,4 +1,4 @@ -{{- range $cfg := .Values.chainlink.nodes }} +{{- range $index, $cfg := .Values.chainlink.nodes }} apiVersion: apps/v1 kind: Deployment metadata: @@ -18,6 +18,10 @@ spec: app: {{ $.Release.Name }} instance: {{ $cfg.name }} release: {{ $.Release.Name }} + # Used for testing. Role value should either be: bootstrap or node. + # There should only be one "bootstrap" node, the rest should be "node". + # Here we set the first node to be bootstrap, the rest to be node. + role: {{ if eq $index 0 }}bootstrap{{ else }}node{{ end }} {{- range $key, $value := $.Values.labels }} {{ $key }}: {{ $value | quote }} {{- end }} From d805141fa8ab258e149b587bb74f8bd5e796497d Mon Sep 17 00:00:00 2001 From: Amir Y Date: Fri, 19 Jan 2024 15:04:33 +0200 Subject: [PATCH 171/234] Align log buffer to work with logs limits config (#11781) * Align log buffer to work with logs limits config: - Used FastExecLogsHigh - limit the number of logs we save in a block for an upkeep - Used FastExecLogsHigh * NumOfLogUpkeeps - limit the amount of logs we save in a block for all upkeeps - Ensured these values can be set dynamically for the buffer, as the configs might change over time * reduce numOfLogUpkeeps * added test for dynamic limits * remove redundant test * increase default FastExecLogsHigh (32 instead of 10) * Revert "reduce numOfLogUpkeeps" This reverts commit f422a54af75bd112b9a21838a78d881778446385. --- .../evmregistry/v21/logprovider/buffer.go | 33 +++++++------ .../v21/logprovider/buffer_test.go | 46 ++++++++++++++++++- .../evmregistry/v21/logprovider/provider.go | 2 +- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go index afdb882cee5..9f11a1fca01 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go @@ -15,10 +15,12 @@ import ( ) var ( - // maxLogsPerUpkeepInBlock is the maximum number of logs allowed per upkeep in a block. - maxLogsPerUpkeepInBlock = 32 - // maxLogsPerBlock is the maximum number of blocks in the buffer. - maxLogsPerBlock = 1024 + // defaultFastExecLogsHigh is the default upper bound / maximum number of logs that Automation is committed to process for each upkeep, + // based on available capacity, i.e. if there are no logs from other upkeeps. + // Used by Log buffer to limit the number of logs we are saving in memory for each upkeep in a block + defaultFastExecLogsHigh = 32 + // defaultNumOfLogUpkeeps is the default number of log upkeeps supported by the registry. + defaultNumOfLogUpkeeps = 50 ) // fetchedLog holds the log and the ID of the upkeep @@ -143,20 +145,20 @@ type logEventBuffer struct { // size is the number of blocks supported by the buffer size int32 - maxBlockLogs, maxUpkeepLogsPerBlock int + numOfLogUpkeeps, fastExecLogsHigh uint32 // blocks is the circular buffer of fetched blocks blocks []fetchedBlock // latestBlock is the latest block number seen latestBlock int64 } -func newLogEventBuffer(lggr logger.Logger, size, maxBlockLogs, maxUpkeepLogsPerBlock int) *logEventBuffer { +func newLogEventBuffer(lggr logger.Logger, size, numOfLogUpkeeps, fastExecLogsHigh int) *logEventBuffer { return &logEventBuffer{ - lggr: lggr.Named("KeepersRegistry.LogEventBuffer"), - size: int32(size), - blocks: make([]fetchedBlock, size), - maxBlockLogs: maxBlockLogs, - maxUpkeepLogsPerBlock: maxUpkeepLogsPerBlock, + lggr: lggr.Named("KeepersRegistry.LogEventBuffer"), + size: int32(size), + blocks: make([]fetchedBlock, size), + numOfLogUpkeeps: uint32(numOfLogUpkeeps), + fastExecLogsHigh: uint32(fastExecLogsHigh), } } @@ -168,6 +170,11 @@ func (b *logEventBuffer) bufferSize() int { return int(atomic.LoadInt32(&b.size)) } +func (b *logEventBuffer) SetLimits(numOfLogUpkeeps, fastExecLogsHigh int) { + atomic.StoreUint32(&b.numOfLogUpkeeps, uint32(numOfLogUpkeeps)) + atomic.StoreUint32(&b.fastExecLogsHigh, uint32(fastExecLogsHigh)) +} + // enqueue adds logs (if not exist) to the buffer, returning the number of logs added // minus the number of logs dropped. func (b *logEventBuffer) enqueue(id *big.Int, logs ...logpoller.Log) int { @@ -176,8 +183,8 @@ func (b *logEventBuffer) enqueue(id *big.Int, logs ...logpoller.Log) int { lggr := b.lggr.With("id", id.String()) - maxBlockLogs := b.maxBlockLogs - maxUpkeepLogs := b.maxUpkeepLogsPerBlock + maxBlockLogs := int(atomic.LoadUint32(&b.fastExecLogsHigh) * atomic.LoadUint32(&b.numOfLogUpkeeps)) + maxUpkeepLogs := int(atomic.LoadUint32(&b.fastExecLogsHigh)) latestBlock := b.latestBlockSeen() added, dropped := 0, 0 diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go index c90a0f34a1b..dca43ca14ac 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go @@ -187,7 +187,7 @@ func TestLogEventBuffer_EnqueueDequeue(t *testing.T) { }) t.Run("enqueue logs overflow", func(t *testing.T) { - buf := newLogEventBuffer(logger.TestLogger(t), 2, 2, 10) + buf := newLogEventBuffer(logger.TestLogger(t), 2, 2, 2) require.Equal(t, 2, buf.enqueue(big.NewInt(1), logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 0}, @@ -199,6 +199,50 @@ func TestLogEventBuffer_EnqueueDequeue(t *testing.T) { buf.lock.Unlock() }) + t.Run("enqueue logs overflow with dynamic limits", func(t *testing.T) { + buf := newLogEventBuffer(logger.TestLogger(t), 2, 10, 2) + + require.Equal(t, 2, buf.enqueue(big.NewInt(1), + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 0}, + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 1}, + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 2}, + )) + buf.SetLimits(10, 3) + require.Equal(t, 3, buf.enqueue(big.NewInt(1), + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 0}, + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 1}, + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 2}, + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 3}, + )) + + buf.lock.Lock() + defer buf.lock.Unlock() + require.Equal(t, 2, len(buf.blocks[0].logs)) + require.Equal(t, 3, len(buf.blocks[1].logs)) + }) + + t.Run("enqueue logs overflow with dynamic limits", func(t *testing.T) { + buf := newLogEventBuffer(logger.TestLogger(t), 2, 10, 2) + + require.Equal(t, 2, buf.enqueue(big.NewInt(1), + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 0}, + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 1}, + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 2}, + logpoller.Log{BlockNumber: 1, TxHash: common.HexToHash("0x1"), LogIndex: 3}, + )) + buf.SetLimits(10, 3) + require.Equal(t, 3, buf.enqueue(big.NewInt(1), + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 0}, + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 1}, + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 2}, + logpoller.Log{BlockNumber: 2, TxHash: common.HexToHash("0x21"), LogIndex: 3}, + )) + + buf.lock.Lock() + defer buf.lock.Unlock() + require.Equal(t, 2, len(buf.blocks[0].logs)) + }) + t.Run("enqueue block overflow", func(t *testing.T) { buf := newLogEventBuffer(logger.TestLogger(t), 3, 2, 10) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go index 4840fa10fa1..d1360faaf6d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go @@ -109,7 +109,7 @@ func NewLogProvider(lggr logger.Logger, poller logpoller.LogPoller, packer LogDa threadCtrl: utils.NewThreadControl(), lggr: lggr.Named("KeepersRegistry.LogEventProvider"), packer: packer, - buffer: newLogEventBuffer(lggr, int(opts.LookbackBlocks), maxLogsPerBlock, maxLogsPerUpkeepInBlock), + buffer: newLogEventBuffer(lggr, int(opts.LookbackBlocks), defaultNumOfLogUpkeeps, defaultFastExecLogsHigh), poller: poller, opts: opts, filterStore: filterStore, From 93deaf66615255c21c0ed6c7039a588f4516ef90 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Fri, 19 Jan 2024 11:37:04 -0300 Subject: [PATCH 172/234] updaete ctf version (#11793) --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c4bd01defca..53135cfea7c 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -26,7 +26,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 - github.com/smartcontractkit/chainlink-testing-framework v1.22.4 + github.com/smartcontractkit/chainlink-testing-framework v1.22.6 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 1bfd4be9a4e..09c9979d578 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1498,8 +1498,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f31 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= -github.com/smartcontractkit/chainlink-testing-framework v1.22.4 h1:617FyrN/wQNFLsGDofqKAQ5/P+swmIPo8sIJZqBPvsQ= -github.com/smartcontractkit/chainlink-testing-framework v1.22.4/go.mod h1:FBRC6elqaqO8jiMZMfa3UKrvwzZhMGUtYqVTGYmfFrA= +github.com/smartcontractkit/chainlink-testing-framework v1.22.6 h1:5kWMlo99RY/ys4EWGMPsEg1sfY67f5YQogI8PohdRvw= +github.com/smartcontractkit/chainlink-testing-framework v1.22.6/go.mod h1:FBRC6elqaqO8jiMZMfa3UKrvwzZhMGUtYqVTGYmfFrA= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= From 0e5019987a0145904fd377ea063f7dbd720bd84f Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Fri, 19 Jan 2024 11:37:14 -0300 Subject: [PATCH 173/234] Log stream improvements (#11791) * use default docker hooks from CTF, pass logstream as functional option when creating a new container * bump ctf * use tagged ctf version * use tagged CTF version * use log stream from env component, not cl node --- integration-tests/docker/test_env/cl_node.go | 25 +------------------ integration-tests/docker/test_env/test_env.go | 17 ++----------- .../docker/test_env/test_env_builder.go | 2 +- 3 files changed, 4 insertions(+), 40 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 1466d03c6f1..fefc9ca8838 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -52,12 +52,8 @@ type ClNode struct { UserEmail string `json:"userEmail"` UserPassword string `json:"userPassword"` AlwaysPullImage bool `json:"-"` - PostStartsHooks []tc.ContainerHook `json:"-"` - PostStopsHooks []tc.ContainerHook `json:"-"` - PreTerminatesHooks []tc.ContainerHook `json:"-"` t *testing.T l zerolog.Logger - ls *logstream.LogStream } type ClNodeOption = func(c *ClNode) @@ -97,7 +93,7 @@ func WithDbContainerName(name string) ClNodeOption { func WithLogStream(ls *logstream.LogStream) ClNodeOption { return func(c *ClNode) { - c.ls = ls + c.LogStream = ls } } @@ -150,25 +146,6 @@ func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *ch return n, nil } -func (n *ClNode) SetDefaultHooks() { - n.PostStartsHooks = []tc.ContainerHook{ - func(ctx context.Context, c tc.Container) error { - if n.ls != nil { - return n.ls.ConnectContainer(ctx, c, "cl-node") - } - return nil - }, - } - n.PostStopsHooks = []tc.ContainerHook{ - func(ctx context.Context, c tc.Container) error { - if n.ls != nil { - return n.ls.DisconnectContainer(c) - } - return nil - }, - } -} - func (n *ClNode) SetTestLogger(t *testing.T) { n.l = logging.GetTestLogger(t) n.t = t diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index c27078dc16f..4ffa8cb3a88 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -1,7 +1,6 @@ package test_env import ( - "context" "encoding/json" "fmt" "math/big" @@ -66,7 +65,7 @@ func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTest te.Cfg = cfg if cfg.MockAdapter.ContainerName != "" { n := []string{te.Network.Name} - te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName)) + te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName), test_env.WithLogStream(te.LogStream)) } return te } @@ -154,7 +153,7 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i if te.Cfg != nil && te.Cfg.ClCluster != nil { te.ClCluster = te.Cfg.ClCluster } else { - opts = append(opts, WithSecrets(secretsConfig)) + opts = append(opts, WithSecrets(secretsConfig), WithLogStream(te.LogStream)) te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { ocrNode, err := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, opts...) @@ -172,18 +171,6 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i } } - if te.LogStream != nil { - for _, node := range te.ClCluster.Nodes { - node.ls = te.LogStream - } - if te.MockAdapter != nil { - err := te.LogStream.ConnectContainer(context.Background(), te.MockAdapter.Container, "mock-adapter") - if err != nil { - return err - } - } - } - // Start/attach node containers return te.ClCluster.Start() } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index dd5e2643bcd..336a70ded2b 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -238,7 +238,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { return nil, fmt.Errorf("test environment builder failed: %w", fmt.Errorf("cannot start mock adapter without a network")) } - b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.Network.Name}, "") + b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.Network.Name}, "", test_env.WithLogStream(b.te.LogStream)) err = b.te.StartMockAdapter() if err != nil { From ed0b614d1b091e3cf6db67fe0b09503ce3cf93b9 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Fri, 19 Jan 2024 12:09:47 -0300 Subject: [PATCH 174/234] add test summary + loki to automation smoke test; use summary action (#11814) * add test summary + loki to automation smoke test; use summary action * update version in comment --- .github/workflows/integration-tests.yml | 36 +++++++++++-------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index c11928a4f7b..3f6c669d679 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -280,6 +280,13 @@ jobs: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} + LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} + GRAFANA_URL: ${{ vars.GRAFANA_URL }} + GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} + RUN_ID: ${{ github.run_id }} with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -295,6 +302,11 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: ./integration-tests/smoke/ eth-smoke-tests-matrix: if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} @@ -495,28 +507,12 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: trace-data - path: ./integration-tests/smoke/traces/trace-data.json + path: ./integration-tests/smoke/traces/trace-data.json - name: Print failed test summary if: always() - run: | - directory="./integration-tests/smoke/.test_summary" - files=("$directory"/*) - if [ -d "$directory" ]; then - echo "Test summary folder found" - if [ ${#files[@]} -gt 0 ]; then - first_file="${files[0]}" - echo "Name of the first test summary file: $(basename "$first_file")" - echo "### Failed Test Execution Logs Dashboard (over VPN):" >> $GITHUB_STEP_SUMMARY - cat "$first_file" | jq -r '.loki[] | "* [\(.test_name)](\(.value))"' >> $GITHUB_STEP_SUMMARY - if [ ${#files[@]} -gt 1 ]; then - echo "Found more than one test summary file. This is incorrect, there should be only one file" - fi - else - echo "Test summary directory is empty. This should not happen" - fi - else - echo "No test summary folder found. If no test failed or log collection wasn't explicitly requested this is correct. Exiting" - fi + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: ./integration-tests/smoke/ ### Used to check the required checks box when the matrix completes eth-smoke-tests: From 6133df8a2a8b527155a8a822d2924d5ca4bfd122 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Fri, 19 Jan 2024 18:19:42 -0500 Subject: [PATCH 175/234] Hotfix attempt for Canary : v2.8.0-rc0 upgrade fails on Polygon. (#11828) * Remove start lock + add logging * Add loggs * Update tracker.go * Update tracker.go * Update common/txmgr/txmgr.go Co-authored-by: Jordan Krage * Update logging --------- Co-authored-by: Jordan Krage --- common/txmgr/tracker.go | 18 +++++++++++++----- common/txmgr/txmgr.go | 2 ++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/common/txmgr/tracker.go b/common/txmgr/tracker.go index 3ef2fc07208..8b66668c41e 100644 --- a/common/txmgr/tracker.go +++ b/common/txmgr/tracker.go @@ -92,19 +92,22 @@ func NewTracker[ } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(_ context.Context) (err error) { - tr.lock.Lock() - defer tr.lock.Unlock() + tr.lggr.Info("Abandoned transaction tracking enabled") return tr.StartOnce("Tracker", func() error { return tr.startInternal() }) } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) startInternal() (err error) { + tr.lock.Lock() + defer tr.lock.Unlock() + tr.ctx, tr.ctxCancel = context.WithCancel(context.Background()) if err := tr.setEnabledAddresses(); err != nil { return fmt.Errorf("failed to set enabled addresses: %w", err) } + tr.lggr.Info("Enabled addresses set") if err := tr.trackAbandonedTxes(tr.ctx); err != nil { return fmt.Errorf("failed to track abandoned txes: %w", err) @@ -112,9 +115,11 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) startIntern tr.isStarted = true if len(tr.txCache) == 0 { - tr.lggr.Infow("no abandoned txes found, skipping runLoop") + tr.lggr.Info("no abandoned txes found, skipping runLoop") return nil } + + tr.lggr.Infof("%d abandoned txes found, starting runLoop", len(tr.txCache)) tr.wg.Add(1) go tr.runLoop() return nil @@ -129,7 +134,7 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() err } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) closeInternal() error { - tr.lggr.Infow("stopping tracker") + tr.lggr.Info("stopping tracker") if !tr.isStarted { return fmt.Errorf("tracker not started") } @@ -159,7 +164,7 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() { } } case <-ttlExceeded.C: - tr.lggr.Infow("ttl exceeded") + tr.lggr.Info("ttl exceeded") tr.MarkAllTxesFatal(tr.ctx) return case <-tr.ctx.Done(): @@ -211,6 +216,7 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) trackAbando return fmt.Errorf("tracker already started") } + tr.lggr.Info("Retrieving non fatal transactions from txStore") nonFatalTxes, err := tr.txStore.GetNonFatalTransactions(ctx, tr.chainID) if err != nil { return fmt.Errorf("failed to get non fatal txes from txStore: %w", err) @@ -239,6 +245,8 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HandleTxesB } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleTxesByState(ctx context.Context, blockHeight int64) error { + tr.lggr.Info("Handling transactions by state") + for id, atx := range tr.txCache { tx, err := tr.txStore.GetTxByID(ctx, atx.id) if err != nil { diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 25c3b9beb35..358554947cc 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -191,10 +191,12 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return fmt.Errorf("Txm: Estimator failed to start: %w", err) } + b.logger.Info("Txm starting tracker") if err := ms.Start(ctx, b.tracker); err != nil { return fmt.Errorf("Txm: Tracker failed to start: %w", err) } + b.logger.Info("Txm starting runLoop") b.wg.Add(1) go b.runLoop() <-b.chSubbed From e4bde648582d55806ab7e0f8d4ea05a721ca120d Mon Sep 17 00:00:00 2001 From: chris-de-leon-cll <147140544+chris-de-leon-cll@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:19:57 -0800 Subject: [PATCH 176/234] [DEPLOY-512]: Adds L2EP Foundry Tests (#11683) * adds l2ep test README and an example test file * adds scroll validator foundry tests * Updates L2EP contracts README, adds ScrollSequencerUptimeFeed test cases, and refactors test cases by using a L2EPTest helper * Updates README, fixes test commands in comments, finish ScrollSequencerUptimeFeed test cases, adds ScrollCrossDomainForwarder skeleton * updates README, adds ScrollCrossDomainForwarder tests, adds better test case grouping and more comments * Adds ScrollCrossDomainGovernor foundry tests and adds small comment to ScrollCrossDomainForwarder helper function * fixes style errors and updates README * reorganizing l2ep files and adding OptimismValidator foundry tests * adds OptimismSequencerUptimeFeed foundry tests * adds OptimismCrossDomainGovernor and OptimismCrossDomainForwarder foundry tests * adds ArbitrumValidator foundry test * fixes forge import path for L2EPTest file * renames L2EPTest.sol to L2EPTest.t.sol, updates README, and fixes foundry import path for Example test * fixes styling issues for mock L2EP contracts * fixes paths to mock contracts in scroll hardhat tests * formatting files * adds ArbitrumSequencerUptimeFeed test * adds ArbitrumCrossDomainGovernor foundry tests * adds ArbitrumCrossDomainForwarder foundry tests * refactors test helper functions * adds requested changes * running formatter * renames scroll tests, moves helper variables to L2EPTest, adds wrappers script for L2EP, updates README * updates imports to comply with style guide --- .github/workflows/solidity-foundry.yml | 2 +- contracts/GNUmakefile | 2 +- contracts/gas-snapshots/l2ep.gas-snapshot | 146 +++++ contracts/remappings.txt | 7 +- .../scripts/native_solc_compile_all_l2ep | 70 +++ contracts/src/v0.8/l2ep/README.md | 148 +++++ .../l2ep/test/mocks/MockAggregatorV2V3.sol | 56 ++ .../optimism/MockOVMCrossDomainMessenger.sol | 32 ++ .../scroll/MockScrollCrossDomainMessenger.sol | 36 ++ .../MockScrollL1CrossDomainMessenger.sol | 0 .../MockScrollL2CrossDomainMessenger.sol | 4 +- .../src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol | 82 +++ .../ArbitrumCrossDomainForwarder.t.sol | 159 ++++++ .../ArbitrumCrossDomainGovernor.t.sol | 258 +++++++++ .../ArbitrumSequencerUptimeFeed.t.sol | 444 +++++++++++++++ .../v1_0_0/arbitrum/ArbitrumValidator.t.sol | 87 +++ .../OptimismCrossDomainForwarder.t.sol | 187 +++++++ .../OptimismCrossDomainGovernor.t.sol | 294 ++++++++++ .../OptimismSequencerUptimeFeed.t.sol | 524 ++++++++++++++++++ .../v1_0_0/optimism/OptimismValidator.t.sol | 108 ++++ .../scroll/ScrollCrossDomainForwarder.t.sol | 191 +++++++ .../scroll/ScrollCrossDomainGovernor.t.sol | 305 ++++++++++ .../scroll/ScrollSequencerUptimeFeed.t.sol | 524 ++++++++++++++++++ .../test/v1_0_0/scroll/ScrollValidator.t.sol | 117 ++++ .../src/v0.8/tests/MockArbitrumInbox.sol | 77 +-- .../MockOptimismL1CrossDomainMessenger.sol | 2 +- .../messaging/iOVM_CrossDomainMessenger.sol | 7 +- .../dev/ScrollSequencerUptimeFeed.test.ts | 2 +- .../test/v0.8/dev/ScrollValidator.test.ts | 2 +- 29 files changed, 3821 insertions(+), 52 deletions(-) create mode 100644 contracts/gas-snapshots/l2ep.gas-snapshot create mode 100755 contracts/scripts/native_solc_compile_all_l2ep create mode 100644 contracts/src/v0.8/l2ep/README.md create mode 100644 contracts/src/v0.8/l2ep/test/mocks/MockAggregatorV2V3.sol create mode 100644 contracts/src/v0.8/l2ep/test/mocks/optimism/MockOVMCrossDomainMessenger.sol create mode 100644 contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollCrossDomainMessenger.sol rename contracts/src/v0.8/l2ep/test/mocks/{ => scroll}/MockScrollL1CrossDomainMessenger.sol (100%) rename contracts/src/v0.8/l2ep/test/mocks/{ => scroll}/MockScrollL2CrossDomainMessenger.sol (90%) create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol create mode 100644 contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollValidator.t.sol diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index b629106972e..447018eb62e 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - product: [vrf, automation, llo-feeds, functions, shared] + product: [vrf, automation, llo-feeds, l2ep, functions, shared] needs: [changes] name: Foundry Tests ${{ matrix.product }} # See https://github.com/foundry-rs/foundry/issues/3827 diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile index 8ec536d520f..f666014c48f 100644 --- a/contracts/GNUmakefile +++ b/contracts/GNUmakefile @@ -1,6 +1,6 @@ # ALL_FOUNDRY_PRODUCTS contains a list of all products that have a foundry # profile defined and use the Foundry snapshots. -ALL_FOUNDRY_PRODUCTS = llo-feeds functions shared +ALL_FOUNDRY_PRODUCTS = l2ep llo-feeds functions shared # To make a snapshot for a specific product, either set the `FOUNDRY_PROFILE` env var # or call the target with `FOUNDRY_PROFILE=product` diff --git a/contracts/gas-snapshots/l2ep.gas-snapshot b/contracts/gas-snapshots/l2ep.gas-snapshot new file mode 100644 index 00000000000..569af2fe661 --- /dev/null +++ b/contracts/gas-snapshots/l2ep.gas-snapshot @@ -0,0 +1,146 @@ +ArbitrumCrossDomainForwarder_AcceptL1Ownership:test_CallableByPendingL1Owner() (gas: 37312) +ArbitrumCrossDomainForwarder_AcceptL1Ownership:test_NotCallableByNonPendingOwners() (gas: 12963) +ArbitrumCrossDomainForwarder_Constructor:test_InitialState() (gas: 18431) +ArbitrumCrossDomainForwarder_Forward:test_Forward() (gas: 47601) +ArbitrumCrossDomainForwarder_Forward:test_ForwardRevert() (gas: 22151) +ArbitrumCrossDomainForwarder_Forward:test_NotCallableByUnknownAddress() (gas: 16048) +ArbitrumCrossDomainForwarder_TransferL1Ownership:test_CallableByL1Owner() (gas: 41408) +ArbitrumCrossDomainForwarder_TransferL1Ownership:test_CallableByL1OwnerOrZeroAddress() (gas: 19312) +ArbitrumCrossDomainForwarder_TransferL1Ownership:test_NotCallableByL2Owner() (gas: 18323) +ArbitrumCrossDomainForwarder_TransferL1Ownership:test_NotCallableByNonOwners() (gas: 13200) +ArbitrumCrossDomainGovernor_AcceptL1Ownership:test_CallableByPendingL1Owner() (gas: 37312) +ArbitrumCrossDomainGovernor_AcceptL1Ownership:test_NotCallableByNonPendingOwners() (gas: 12963) +ArbitrumCrossDomainGovernor_Constructor:test_InitialState() (gas: 18454) +ArbitrumCrossDomainGovernor_Forward:test_CallableByL2Owner() (gas: 49720) +ArbitrumCrossDomainGovernor_Forward:test_Forward() (gas: 47658) +ArbitrumCrossDomainGovernor_Forward:test_ForwardRevert() (gas: 24348) +ArbitrumCrossDomainGovernor_Forward:test_NotCallableByUnknownAddress() (gas: 18247) +ArbitrumCrossDomainGovernor_ForwardDelegate:test_BubbleUpRevert() (gas: 19386) +ArbitrumCrossDomainGovernor_ForwardDelegate:test_CallableByCrossDomainMessengerAddressOrL1Owner() (gas: 60617) +ArbitrumCrossDomainGovernor_ForwardDelegate:test_CallableByL2Owner() (gas: 62723) +ArbitrumCrossDomainGovernor_ForwardDelegate:test_NotCallableByUnknownAddress() (gas: 18237) +ArbitrumCrossDomainGovernor_ForwardDelegate:test_RevertsBatchWhenOneCallFails() (gas: 64110) +ArbitrumCrossDomainGovernor_TransferL1Ownership:test_CallableByL1Owner() (gas: 41408) +ArbitrumCrossDomainGovernor_TransferL1Ownership:test_CallableByL1OwnerOrZeroAddress() (gas: 19312) +ArbitrumCrossDomainGovernor_TransferL1Ownership:test_NotCallableByL2Owner() (gas: 18323) +ArbitrumCrossDomainGovernor_TransferL1Ownership:test_NotCallableByNonOwners() (gas: 13200) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetAnswer() (gas: 92118) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetRoundData() (gas: 92673) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetTimestamp() (gas: 92039) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestAnswer() (gas: 89813) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRound() (gas: 89705) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRoundData() (gas: 90246) +ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestTimestamp() (gas: 89690) +ArbitrumSequencerUptimeFeed_AggregatorV3Interface:test_AggregatorV3Interface() (gas: 98825) +ArbitrumSequencerUptimeFeed_AggregatorV3Interface:test_Return0WhenRoundDoesNotExistYet() (gas: 18309) +ArbitrumSequencerUptimeFeed_Constants:test_InitialState() (gas: 5684) +ArbitrumSequencerUptimeFeed_GasCosts:test_GasCosts() (gas: 97495) +ArbitrumSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() (gas: 602711) +ArbitrumSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() (gas: 573802) +ArbitrumSequencerUptimeFeed_UpdateStatus:test_IgnoreOutOfOrderUpdates() (gas: 98976) +ArbitrumSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 15776) +ArbitrumSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndNoTimeChange() (gas: 113269) +ArbitrumSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndTimeChange() (gas: 113329) +ArbitrumValidator_Validate:test_PostSequencerOffline() (gas: 69068) +OptimismCrossDomainForwarder_AcceptL1Ownership:test_CallableByPendingL1Owner() (gas: 46888) +OptimismCrossDomainForwarder_AcceptL1Ownership:test_NotCallableByNonPendingOwners() (gas: 22155) +OptimismCrossDomainForwarder_Constructor:test_InitialState() (gas: 18266) +OptimismCrossDomainForwarder_Forward:test_Forward() (gas: 58025) +OptimismCrossDomainForwarder_Forward:test_ForwardRevert() (gas: 32546) +OptimismCrossDomainForwarder_Forward:test_NotCallableByUnknownAddress() (gas: 13859) +OptimismCrossDomainForwarder_TransferL1Ownership:test_CallableByL1Owner() (gas: 48886) +OptimismCrossDomainForwarder_TransferL1Ownership:test_CallableByL1OwnerOrZeroAddress() (gas: 28767) +OptimismCrossDomainForwarder_TransferL1Ownership:test_NotCallableByL2Owner() (gas: 16134) +OptimismCrossDomainForwarder_TransferL1Ownership:test_NotCallableByNonOwners() (gas: 11011) +OptimismCrossDomainGovernor_AcceptL1Ownership:test_CallableByPendingL1Owner() (gas: 46888) +OptimismCrossDomainGovernor_AcceptL1Ownership:test_NotCallableByNonPendingOwners() (gas: 22155) +OptimismCrossDomainGovernor_Constructor:test_InitialState() (gas: 18289) +OptimismCrossDomainGovernor_Forward:test_CallableByL2Owner() (gas: 47557) +OptimismCrossDomainGovernor_Forward:test_Forward() (gas: 58096) +OptimismCrossDomainGovernor_Forward:test_ForwardRevert() (gas: 32627) +OptimismCrossDomainGovernor_Forward:test_NotCallableByUnknownAddress() (gas: 16061) +OptimismCrossDomainGovernor_ForwardDelegate:test_BubbleUpRevert() (gas: 29181) +OptimismCrossDomainGovernor_ForwardDelegate:test_CallableByCrossDomainMessengerAddressOrL1Owner() (gas: 72695) +OptimismCrossDomainGovernor_ForwardDelegate:test_CallableByL2Owner() (gas: 72685) +OptimismCrossDomainGovernor_ForwardDelegate:test_NotCallableByUnknownAddress() (gas: 16051) +OptimismCrossDomainGovernor_ForwardDelegate:test_RevertsBatchWhenOneCallFails() (gas: 75908) +OptimismCrossDomainGovernor_TransferL1Ownership:test_CallableByL1Owner() (gas: 48886) +OptimismCrossDomainGovernor_TransferL1Ownership:test_CallableByL1OwnerOrZeroAddress() (gas: 28767) +OptimismCrossDomainGovernor_TransferL1Ownership:test_NotCallableByL2Owner() (gas: 16134) +OptimismCrossDomainGovernor_TransferL1Ownership:test_NotCallableByNonOwners() (gas: 11011) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetAnswer() (gas: 59095) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetRoundData() (gas: 59635) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetTimestamp() (gas: 58950) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestAnswer() (gas: 56887) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRound() (gas: 56773) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRoundData() (gas: 57309) +OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestTimestamp() (gas: 56740) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_AggregatorV3Interface() (gas: 65617) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetAnswerWhenRoundDoesNotExistYet() (gas: 18039) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetRoundDataWhenRoundDoesNotExistYet() (gas: 18257) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetTimestampWhenRoundDoesNotExistYet() (gas: 17963) +OptimismSequencerUptimeFeed_Constructor:test_InitialState() (gas: 21078) +OptimismSequencerUptimeFeed_GasCosts:test_GasCosts() (gas: 67197) +OptimismSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() (gas: 597640) +OptimismSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() (gas: 573807) +OptimismSequencerUptimeFeed_UpdateStatus:test_IgnoreOutOfOrderUpdates() (gas: 66532) +OptimismSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 13560) +OptimismSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() (gas: 23967) +OptimismSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenNoChange() (gas: 74035) +OptimismSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndNoTimeChange() (gas: 96155) +OptimismSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndTimeChange() (gas: 96215) +OptimismValidator_SetGasLimit:test_CorrectlyUpdatesTheGasLimit() (gas: 15503) +OptimismValidator_Validate:test_PostSequencerOffline() (gas: 74813) +OptimismValidator_Validate:test_PostSequencerStatusWhenThereIsNotStatusChange() (gas: 74869) +OptimismValidator_Validate:test_RevertsIfCalledByAnAccountWithNoAccess() (gas: 15563) +ScrollCrossDomainForwarder_AcceptL1Ownership:test_CallableByPendingL1Owner() (gas: 46988) +ScrollCrossDomainForwarder_AcceptL1Ownership:test_NotCallableByNonPendingOwners() (gas: 22207) +ScrollCrossDomainForwarder_Constructor:test_InitialState() (gas: 17930) +ScrollCrossDomainForwarder_Forward:test_Forward() (gas: 58092) +ScrollCrossDomainForwarder_Forward:test_ForwardRevert() (gas: 32619) +ScrollCrossDomainForwarder_Forward:test_NotCallableByUnknownAddress() (gas: 13859) +ScrollCrossDomainForwarder_TransferL1Ownership:test_CallableByL1Owner() (gas: 48952) +ScrollCrossDomainForwarder_TransferL1Ownership:test_CallableByL1OwnerOrZeroAddress() (gas: 28833) +ScrollCrossDomainForwarder_TransferL1Ownership:test_NotCallableByL2Owner() (gas: 16134) +ScrollCrossDomainForwarder_TransferL1Ownership:test_NotCallableByNonOwners() (gas: 11011) +ScrollCrossDomainGovernor_AcceptL1Ownership:test_CallableByPendingL1Owner() (gas: 46988) +ScrollCrossDomainGovernor_AcceptL1Ownership:test_NotCallableByNonPendingOwners() (gas: 22207) +ScrollCrossDomainGovernor_Constructor:test_InitialState() (gas: 17953) +ScrollCrossDomainGovernor_Forward:test_CallableByL2Owner() (gas: 47552) +ScrollCrossDomainGovernor_Forward:test_Forward() (gas: 58158) +ScrollCrossDomainGovernor_Forward:test_ForwardRevert() (gas: 32697) +ScrollCrossDomainGovernor_Forward:test_NotCallableByUnknownAddress() (gas: 16058) +ScrollCrossDomainGovernor_ForwardDelegate:test_BubbleUpRevert() (gas: 29248) +ScrollCrossDomainGovernor_ForwardDelegate:test_CallableByCrossDomainMessengerAddressOrL1Owner() (gas: 72756) +ScrollCrossDomainGovernor_ForwardDelegate:test_CallableByL2Owner() (gas: 72746) +ScrollCrossDomainGovernor_ForwardDelegate:test_NotCallableByUnknownAddress() (gas: 16048) +ScrollCrossDomainGovernor_ForwardDelegate:test_RevertsBatchWhenOneCallFails() (gas: 75970) +ScrollCrossDomainGovernor_TransferL1Ownership:test_CallableByL1Owner() (gas: 48952) +ScrollCrossDomainGovernor_TransferL1Ownership:test_CallableByL1OwnerOrZeroAddress() (gas: 28833) +ScrollCrossDomainGovernor_TransferL1Ownership:test_NotCallableByL2Owner() (gas: 16134) +ScrollCrossDomainGovernor_TransferL1Ownership:test_NotCallableByNonOwners() (gas: 11011) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetAnswer() (gas: 57250) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetRoundData() (gas: 57780) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForGetTimestamp() (gas: 57105) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestAnswer() (gas: 54888) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRound() (gas: 54768) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRoundData() (gas: 55473) +ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestTimestamp() (gas: 54758) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_AggregatorV3Interface() (gas: 63903) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetAnswerWhenRoundDoesNotExistYet() (gas: 18035) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetRoundDataWhenRoundDoesNotExistYet() (gas: 18253) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetTimestampWhenRoundDoesNotExistYet() (gas: 17959) +ScrollSequencerUptimeFeed_Constructor:test_InitialState() (gas: 21085) +ScrollSequencerUptimeFeed_GasCosts:test_GasCosts() (gas: 64888) +ScrollSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() (gas: 597491) +ScrollSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() (gas: 573807) +ScrollSequencerUptimeFeed_UpdateStatus:test_IgnoreOutOfOrderUpdates() (gas: 64417) +ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 13560) +ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() (gas: 23967) +ScrollSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenNoChange() (gas: 71618) +ScrollSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndNoTimeChange() (gas: 92018) +ScrollSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndTimeChange() (gas: 92078) +ScrollValidator_SetGasLimit:test_CorrectlyUpdatesTheGasLimit() (gas: 15503) +ScrollValidator_Validate:test_PostSequencerOffline() (gas: 75094) +ScrollValidator_Validate:test_PostSequencerStatusWhenThereIsNotStatusChange() (gas: 75156) +ScrollValidator_Validate:test_RevertsIfCalledByAnAccountWithNoAccess() (gas: 15563) \ No newline at end of file diff --git a/contracts/remappings.txt b/contracts/remappings.txt index f0ac4993c2c..a9d24257659 100644 --- a/contracts/remappings.txt +++ b/contracts/remappings.txt @@ -1,6 +1,7 @@ -ds-test/=foundry-lib/forge-std/lib/ds-test/src -forge-std/=foundry-lib/forge-std/src +ds-test/=foundry-lib/forge-std/lib/ds-test/src/ +forge-std/=foundry-lib/forge-std/src/ @openzeppelin/=node_modules/@openzeppelin/ hardhat/=node_modules/hardhat/ -@eth-optimism/=node_modules/@eth-optimism +@eth-optimism/=node_modules/@eth-optimism/ +@scroll-tech/=node_modules/@scroll-tech/ diff --git a/contracts/scripts/native_solc_compile_all_l2ep b/contracts/scripts/native_solc_compile_all_l2ep new file mode 100755 index 00000000000..1b9f5fb611d --- /dev/null +++ b/contracts/scripts/native_solc_compile_all_l2ep @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +########### +# Logging # +########### + +set -e + +echo " ┌──────────────────────────────────────────────┐" +echo " │ Compiling L2EP contracts... │" +echo " └──────────────────────────────────────────────┘" + +###################### +# Helper Variable(s) # +###################### + +export SOLC_VERSION="0.8.19" + +SCRIPTPATH="$( + cd "$(dirname "$0")" >/dev/null 2>&1 + pwd -P +)" + +ROOT="$( + cd "$(dirname "$0")" >/dev/null 2>&1 + cd ../ && pwd -P +)" + +###################### +# Helper Function(s) # +###################### + +compileContract() { + local optimize_runs=1000000 + local version="$1" + local srcpath="$2" + solc \ + @openzeppelin/=$ROOT/node_modules/@openzeppelin/ \ + @eth-optimism/=$ROOT/node_modules/@eth-optimism/ \ + @scroll-tech/=$ROOT/node_modules/@scroll-tech/ \ + --overwrite --optimize --optimize-runs $optimize_runs --metadata-hash none \ + -o $ROOT/solc/v$SOLC_VERSION/l2ep/"$version" \ + --abi --bin \ + --allow-paths $ROOT/src/v0.8,$ROOT/node_modules \ + $ROOT/src/v0.8/l2ep/"$srcpath" +} + +################# +# Version 1.0.0 # +################# + +python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt + +solc-select install $SOLC_VERSION +solc-select use $SOLC_VERSION + +compileContract v1_0_0 dev/arbitrum/ArbitrumValidator.sol +compileContract v1_0_0 dev/arbitrum/ArbitrumSequencerUptimeFeed.sol +compileContract v1_0_0 dev/arbitrum/ArbitrumCrossDomainForwarder.sol +compileContract v1_0_0 dev/arbitrum/ArbitrumCrossDomainGovernor.sol + +compileContract v1_0_0 dev/optimism/OptimismValidator.sol +compileContract v1_0_0 dev/optimism/OptimismSequencerUptimeFeed.sol +compileContract v1_0_0 dev/optimism/OptimismCrossDomainForwarder.sol +compileContract v1_0_0 dev/optimism/OptimismCrossDomainGovernor.sol + +compileContract v1_0_0 dev/scroll/ScrollValidator.sol +compileContract v1_0_0 dev/scroll/ScrollSequencerUptimeFeed.sol +compileContract v1_0_0 dev/scroll/ScrollCrossDomainForwarder.sol +compileContract v1_0_0 dev/scroll/ScrollCrossDomainGovernor.sol diff --git a/contracts/src/v0.8/l2ep/README.md b/contracts/src/v0.8/l2ep/README.md new file mode 100644 index 00000000000..21a537c4fe7 --- /dev/null +++ b/contracts/src/v0.8/l2ep/README.md @@ -0,0 +1,148 @@ +# Overview + +This folder contains the source code and tests for the Layer 2 +Emergency Protocol (L2EP) contracts. It is organized as follows: + +```text +. +├─/dev (stores the latest source code for L2EP) +├─/test (stores the Foundry tests for L2EP) +``` + +## The `/dev` Folder + +The `/dev` folder contains subfolders for each chain that +has an L2EP solution implemented for it (e.g. `/scroll`, `/arbitrum`, +`/optimism`). It also contains a subfolder named `/interfaces`, +which stores shared interface types between all the supported +contracts. The top-level contracts (e.g. `CrossDomainOwnable.sol`) +serve as either abstract or parent contracts that are meant +to be reused for each indiviudal chain. + +## The `/test` Folder + +This folder is arranged as follows: + +- `/mocks`: used for both Foundry test cases and Hardhat test cases (NOTE: +Hardhat test cases should be considered deprecated at this point) + +- `/[version]`: test cases for a specific version of the L2EP contracts + +### Testing Conventions and Methodology + +By convention, each testing file should end in `.t.sol` (this is a standard +that other projects have also adopted). Each testing file in this folder +follows a similar structure. + +```text +TestFile.t.sol + | + |--- Base Contract (inherits L2EPTest contract) + | + |--- Child Contract 1 (inherits base contract) + | | + | |--- Test Function + | | + | |--- ... + | + | + |--- Child Contract 2 (inherits base contract) + | | + | |--- Test Function + | | + | |--- ... + | + | + ... +``` + +All test files contain a base contract defined at the top of the file. This +base contract inherits from a contract called `L2EPTest`. The `L2EPTest` +contract and base contracts have no test cases. Instead, the `L2EPTest` +contract is meant to store data/functions that will be reused among all +the base contracts. Similarly, the base contract is meant to store data +and/or functions that will be reused by any contracts that inherit it. +As such, each test file will define separate child contracts, and each +will inherit from the base contract + define its own set of tests. + +The base contract defines a `setUp` function which is automatically called +exactly once before ***each*** of the tests are run in an inheriting contract. +The `setUp` function typically deploys a fresh set of test contracts so that +tests can run independently of each other. Alongside the `setUp` function, +the base contract can also define variables, constants, events, etc. that +are meant to be reused per test. + +The name of the base contract follows the following convention: + +```text +Test +``` + +The child contract names follow a similar convention: + +```text +_ +``` + +Each test function within the child contract complies +with the following naming pattern: + +```text +test_ +``` + +### Running Foundry Tests + +#### Usage + +First make sure you are in the contracts directory: + +```sh +# Assuming you are currently in the /chainlink directory +cd ./contracts +``` + +If you already have foundry installed, you can use the following command +to run all L2EP tests: + +```sh +FOUNDRY_PROFILE=l2ep forge test -vvv +``` + +To run a specific L2EP test, you can use a variation of the following command: + +```sh +FOUNDRY_PROFILE=l2ep forge test -vvv --match-path ./src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol +``` + +Or alternatively: + +```sh +FOUNDRY_PROFILE=l2ep forge test -vvv --match-contract ScrollSequencerUptimeFeed +``` + +If you prefer, you can also export `FOUNDRY_PROFILE` so that it doesn't need +to be provided before every command: + +```sh +# Export foundry profile +export FOUNDRY_PROFILE=l2ep + +# Run all tests +forge test -vvv + +# Run all tests and generate a gas snapshot +make snapshot +``` + +A full list of flags for `forge test` can be found [here](https://book.getfoundry.sh/reference/forge/forge-test). + +#### Coverage + +First ensure that the correct files are being evaluated. For example, if only +v1 contracts are, being evaluated then temporarily change the L2EP profile in +`./foundry.toml`. + +```sh +forge coverage +``` diff --git a/contracts/src/v0.8/l2ep/test/mocks/MockAggregatorV2V3.sol b/contracts/src/v0.8/l2ep/test/mocks/MockAggregatorV2V3.sol new file mode 100644 index 00000000000..c4e2f710300 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/mocks/MockAggregatorV2V3.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {AggregatorV2V3Interface} from "../../../shared/interfaces/AggregatorV2V3Interface.sol"; + +contract MockAggregatorV2V3 is AggregatorV2V3Interface { + function latestAnswer() external pure returns (int256) { + return 0; + } + + function latestTimestamp() external pure returns (uint256) { + return 0; + } + + function latestRound() external pure returns (uint256) { + return 0; + } + + function getAnswer(uint256) external pure returns (int256) { + return 0; + } + + function getTimestamp(uint256 roundId) external pure returns (uint256) { + return roundId; + } + + function decimals() external pure returns (uint8) { + return 0; + } + + function description() external pure returns (string memory) { + return ""; + } + + function version() external pure returns (uint256) { + return 0; + } + + function getRoundData( + uint80 + ) + external + pure + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + return (0, 0, 0, 0, 0); + } + + function latestRoundData() + external + pure + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + return (73786976294838220258, 96800000000, 163826896, 1638268960, 73786976294838220258); + } +} diff --git a/contracts/src/v0.8/l2ep/test/mocks/optimism/MockOVMCrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/optimism/MockOVMCrossDomainMessenger.sol new file mode 100644 index 00000000000..3a45cba347a --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/mocks/optimism/MockOVMCrossDomainMessenger.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.6 <0.9.0; + +import {iOVM_CrossDomainMessenger} from "../../../../vendor/@eth-optimism/contracts/v0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol"; + +import {Address} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; + +contract MockOVMCrossDomainMessenger is iOVM_CrossDomainMessenger { + address internal s_mockMessageSender; + + constructor(address sender) { + s_mockMessageSender = sender; + } + + function xDomainMessageSender() external view override returns (address) { + return s_mockMessageSender; + } + + function _setMockMessageSender(address sender) external { + s_mockMessageSender = sender; + } + + /** + * Sends a cross domain message to the target messenger. + * @param _target Target contract address. + * @param _message Message to send to the target. + */ + function sendMessage(address _target, bytes calldata _message, uint32) external override { + Address.functionCall(_target, _message, "sendMessage reverted"); + } +} diff --git a/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollCrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollCrossDomainMessenger.sol new file mode 100644 index 00000000000..37244910b81 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollCrossDomainMessenger.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.16; + +import {IScrollMessenger} from "@scroll-tech/contracts/libraries/IScrollMessenger.sol"; + +import {Address} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; + +contract MockScrollCrossDomainMessenger is IScrollMessenger { + address internal s_mockMessageSender; + + constructor(address sender) { + s_mockMessageSender = sender; + } + + function xDomainMessageSender() external view override returns (address) { + return s_mockMessageSender; + } + + function _setMockMessageSender(address sender) external { + s_mockMessageSender = sender; + } + + /// @notice Send cross chain message from L1 to L2 or L2 to L1. + /// @param _target The address of account who receive the message. + /// @param _message The content of the message. + function sendMessage(address _target, uint256, bytes calldata _message, uint256) external payable override { + Address.functionCall(_target, _message, "sendMessage reverted"); + } + + /// @notice Send cross chain message from L1 to L2 or L2 to L1. + /// @param _target The address of account who receive the message. + /// @param _message The content of the message. + function sendMessage(address _target, uint256, bytes calldata _message, uint256, address) external payable override { + Address.functionCall(_target, _message, "sendMessage reverted"); + } +} diff --git a/contracts/src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL1CrossDomainMessenger.sol similarity index 100% rename from contracts/src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol rename to contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL1CrossDomainMessenger.sol diff --git a/contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL2CrossDomainMessenger.sol similarity index 90% rename from contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol rename to contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL2CrossDomainMessenger.sol index f63faa35179..66400b7d305 100644 --- a/contracts/src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol +++ b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL2CrossDomainMessenger.sol @@ -40,11 +40,11 @@ contract MockScrollL2CrossDomainMessenger is IL2ScrollMessenger { bytes calldata message ) external override {} - /// Needed for testing + /// Needed for backwards compatibility in Hardhat tests function setSender(address newSender) external { s_sender = newSender; } - /// Needed for testing + /// Needed for backwards compatibility in Hardhat tests receive() external payable {} } diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol new file mode 100644 index 00000000000..561e32be1a2 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/L2EPTest.t.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {Greeter} from "../../../tests/Greeter.sol"; + +import {MultiSend} from "../../../vendor/MultiSend.sol"; +import {Test} from "forge-std/Test.sol"; + +contract L2EPTest is Test { + /// Helper variable(s) + address internal s_strangerAddr = vm.addr(0x1); + address internal s_l1OwnerAddr = vm.addr(0x2); + address internal s_eoaValidator = vm.addr(0x3); + address internal s_deployerAddr = vm.addr(0x4); + + /// @param expectedGasUsage - the expected gas usage + /// @param startGasUsage - the gas usage before the code of interest is run + /// @param finalGasUsage - the gas usage after the code of interest is run + /// @param deviation - the amount of gas that the actual usage is allowed to deviate by (e.g. (expectedGas - deviation) <= actualGasUsage <= (expectedGas + deviation)) + function assertGasUsageIsCloseTo( + uint256 expectedGasUsage, + uint256 startGasUsage, + uint256 finalGasUsage, + uint256 deviation + ) public { + uint256 gasUsed = (startGasUsage - finalGasUsage) * tx.gasprice; + assertLe(gasUsed, expectedGasUsage + deviation); + assertGe(gasUsed, expectedGasUsage - deviation); + } + + /// @param selector - the function selector + /// @param greeterAddr - the address of the Greeter contract + /// @param message - the new greeting message, which will be passed as an argument to Greeter#setGreeting + /// @return a 2-layer encoding such that decoding the first layer provides the CrossDomainForwarder#forward + /// function selector and the corresponding arguments to the forward function, and decoding the + /// second layer provides the Greeter#setGreeting function selector and the corresponding + /// arguments to the set greeting function (which in this case is the input message) + function encodeCrossDomainSetGreetingMsg( + bytes4 selector, + address greeterAddr, + string memory message + ) public pure returns (bytes memory) { + return abi.encodeWithSelector(selector, greeterAddr, abi.encodeWithSelector(Greeter.setGreeting.selector, message)); + } + + /// @param selector - the function selector + /// @param multiSendAddr - the address of the MultiSend contract + /// @param encodedTxs - an encoded list of transactions (e.g. abi.encodePacked(encodeMultiSendTx("some data"), ...)) + /// @return a 2-layer encoding such that decoding the first layer provides the CrossDomainGoverner#forwardDelegate + /// function selector and the corresponding arguments to the forwardDelegate function, and decoding the + /// second layer provides the MultiSend#multiSend function selector and the corresponding + /// arguments to the multiSend function (which in this case is the input encodedTxs) + function encodeCrossDomainMultiSendMsg( + bytes4 selector, + address multiSendAddr, + bytes memory encodedTxs + ) public pure returns (bytes memory) { + return + abi.encodeWithSelector(selector, multiSendAddr, abi.encodeWithSelector(MultiSend.multiSend.selector, encodedTxs)); + } + + /// @param greeterAddr - the address of the greeter contract + /// @param data - the transaction data string + /// @return an encoded transaction structured as specified in the MultiSend#multiSend comments + function encodeMultiSendTx(address greeterAddr, bytes memory data) public pure returns (bytes memory) { + bytes memory txData = abi.encodeWithSelector(Greeter.setGreeting.selector, data); + return + abi.encodePacked( + uint8(0), // operation + greeterAddr, // to + uint256(0), // value + uint256(txData.length), // data length + txData // data as bytes + ); + } + + /// @param l1Address - Address on L1 + /// @return an Arbitrum L2 address + function toArbitrumL2AliasAddress(address l1Address) public pure returns (address) { + return address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111)); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol new file mode 100644 index 00000000000..be3851c5b5d --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainForwarder.t.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ArbitrumCrossDomainForwarder} from "../../../dev/arbitrum/ArbitrumCrossDomainForwarder.sol"; +import {Greeter} from "../../../../tests/Greeter.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract ArbitrumCrossDomainForwarderTest is L2EPTest { + /// Helper variable(s) + address internal s_crossDomainMessengerAddr = toArbitrumL2AliasAddress(s_l1OwnerAddr); + address internal s_newOwnerCrossDomainMessengerAddr = toArbitrumL2AliasAddress(s_strangerAddr); + + /// Contracts + ArbitrumCrossDomainForwarder internal s_arbitrumCrossDomainForwarder; + Greeter internal s_greeter; + + /// Events + event L1OwnershipTransferRequested(address indexed from, address indexed to); + event L1OwnershipTransferred(address indexed from, address indexed to); + + /// Setup + function setUp() public { + // Deploys contracts + vm.startPrank(s_l1OwnerAddr); + s_arbitrumCrossDomainForwarder = new ArbitrumCrossDomainForwarder(s_l1OwnerAddr); + s_greeter = new Greeter(address(s_arbitrumCrossDomainForwarder)); + vm.stopPrank(); + } +} + +contract ArbitrumCrossDomainForwarder_Constructor is ArbitrumCrossDomainForwarderTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // it should set the owner correctly + assertEq(s_arbitrumCrossDomainForwarder.owner(), s_l1OwnerAddr); + + // it should set the l1Owner correctly + assertEq(s_arbitrumCrossDomainForwarder.l1Owner(), s_l1OwnerAddr); + + // it should set the crossdomain messenger correctly + assertEq(s_arbitrumCrossDomainForwarder.crossDomainMessenger(), s_crossDomainMessengerAddr); + + // it should set the typeAndVersion correctly + assertEq(s_arbitrumCrossDomainForwarder.typeAndVersion(), "ArbitrumCrossDomainForwarder 1.0.0"); + } +} + +contract ArbitrumCrossDomainForwarder_Forward is ArbitrumCrossDomainForwarderTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_arbitrumCrossDomainForwarder.forward(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_Forward() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_arbitrumCrossDomainForwarder.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, greeting) + ); + + // Checks that the greeter got the message + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should revert when contract call reverts + function test_ForwardRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Sends an invalid message + vm.expectRevert("Invalid greeting length"); + s_arbitrumCrossDomainForwarder.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, "") + ); + } +} + +contract ArbitrumCrossDomainForwarder_TransferL1Ownership is ArbitrumCrossDomainForwarderTest { + /// @notice it should not be callable by non-owners + function test_NotCallableByNonOwners() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_arbitrumCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should not be callable by L2 owner + function test_NotCallableByL2Owner() public { + vm.startPrank(s_l1OwnerAddr); + assertEq(s_arbitrumCrossDomainForwarder.owner(), s_l1OwnerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_arbitrumCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner + function test_CallableByL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_arbitrumCrossDomainForwarder.l1Owner(), s_strangerAddr); + + // Sends the message + s_arbitrumCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner to zero address + function test_CallableByL1OwnerOrZeroAddress() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_arbitrumCrossDomainForwarder.l1Owner(), address(0)); + + // Sends the message + s_arbitrumCrossDomainForwarder.transferL1Ownership(address(0)); + } +} + +contract ArbitrumCrossDomainForwarder_AcceptL1Ownership is ArbitrumCrossDomainForwarderTest { + /// @notice it should not be callable by non pending-owners + function test_NotCallableByNonPendingOwners() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Sends the message + vm.expectRevert("Must be proposed L1 owner"); + s_arbitrumCrossDomainForwarder.acceptL1Ownership(); + } + + /// @notice it should be callable by pending L1 owner + function test_CallableByPendingL1Owner() public { + // Request ownership transfer + vm.startPrank(s_crossDomainMessengerAddr); + s_arbitrumCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + + // Prepares expected event payload + vm.expectEmit(); + emit L1OwnershipTransferred(s_l1OwnerAddr, s_strangerAddr); + + // Accepts ownership transfer request + vm.startPrank(s_newOwnerCrossDomainMessengerAddr); + s_arbitrumCrossDomainForwarder.acceptL1Ownership(); + + // Asserts that the ownership was actually transferred + assertEq(s_arbitrumCrossDomainForwarder.l1Owner(), s_strangerAddr); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol new file mode 100644 index 00000000000..c5b8adaf7d2 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumCrossDomainGovernor.t.sol @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ArbitrumCrossDomainGovernor} from "../../../dev/arbitrum/ArbitrumCrossDomainGovernor.sol"; +import {Greeter} from "../../../../tests/Greeter.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +import {MultiSend} from "../../../../vendor/MultiSend.sol"; + +contract ArbitrumCrossDomainGovernorTest is L2EPTest { + /// Helper variable(s) + address internal s_crossDomainMessengerAddr = toArbitrumL2AliasAddress(s_l1OwnerAddr); + address internal s_newOwnerCrossDomainMessengerAddr = toArbitrumL2AliasAddress(s_strangerAddr); + + /// Contracts + ArbitrumCrossDomainGovernor internal s_arbitrumCrossDomainGovernor; + MultiSend internal s_multiSend; + Greeter internal s_greeter; + + /// Events + event L1OwnershipTransferRequested(address indexed from, address indexed to); + event L1OwnershipTransferred(address indexed from, address indexed to); + + /// Setup + function setUp() public { + // Deploys contracts + vm.startPrank(s_l1OwnerAddr); + s_arbitrumCrossDomainGovernor = new ArbitrumCrossDomainGovernor(s_l1OwnerAddr); + s_greeter = new Greeter(address(s_arbitrumCrossDomainGovernor)); + s_multiSend = new MultiSend(); + vm.stopPrank(); + } +} + +contract ArbitrumCrossDomainGovernor_Constructor is ArbitrumCrossDomainGovernorTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // it should set the owner correctly + assertEq(s_arbitrumCrossDomainGovernor.owner(), s_l1OwnerAddr); + + // it should set the l1Owner correctly + assertEq(s_arbitrumCrossDomainGovernor.l1Owner(), s_l1OwnerAddr); + + // it should set the crossdomain messenger correctly + assertEq(s_arbitrumCrossDomainGovernor.crossDomainMessenger(), s_crossDomainMessengerAddr); + + // it should set the typeAndVersion correctly + assertEq(s_arbitrumCrossDomainGovernor.typeAndVersion(), "ArbitrumCrossDomainGovernor 1.0.0"); + } +} + +contract ArbitrumCrossDomainGovernor_Forward is ArbitrumCrossDomainGovernorTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger or owner"); + s_arbitrumCrossDomainGovernor.forward(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_Forward() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_arbitrumCrossDomainGovernor.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, greeting) + ); + + // Checks that the greeter got the message + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should be callable by L2 owner + function test_CallableByL2Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_arbitrumCrossDomainGovernor.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, greeting) + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should revert when contract call reverts + function test_ForwardRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Sends an invalid message + vm.expectRevert("Invalid greeting length"); + s_arbitrumCrossDomainGovernor.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, "") + ); + } +} + +contract ArbitrumCrossDomainGovernor_ForwardDelegate is ArbitrumCrossDomainGovernorTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger or owner"); + s_arbitrumCrossDomainGovernor.forwardDelegate(address(s_multiSend), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_CallableByCrossDomainMessengerAddressOrL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Sends the message + s_arbitrumCrossDomainGovernor.forwardDelegate( + address(s_multiSend), + abi.encodeWithSelector( + MultiSend.multiSend.selector, + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "bar")) + ) + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), "bar"); + } + + /// @notice it should be callable by L2 owner + function test_CallableByL2Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Sends the message + s_arbitrumCrossDomainGovernor.forwardDelegate( + address(s_multiSend), + abi.encodeWithSelector( + MultiSend.multiSend.selector, + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "bar")) + ) + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), "bar"); + } + + /// @notice it should revert batch when one call fails + function test_RevertsBatchWhenOneCallFails() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Sends an invalid message (empty transaction data is not allowed) + vm.expectRevert("Governor delegatecall reverted"); + s_arbitrumCrossDomainGovernor.forwardDelegate( + address(s_multiSend), + abi.encodeWithSelector( + MultiSend.multiSend.selector, + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "")) + ) + ); + + // Checks that the greeter message is unchanged + assertEq(s_greeter.greeting(), ""); + } + + /// @notice it should bubble up revert when contract call reverts + function test_BubbleUpRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Sends an invalid message (empty transaction data is not allowed) + vm.expectRevert("Greeter: revert triggered"); + s_arbitrumCrossDomainGovernor.forwardDelegate( + address(s_greeter), + abi.encodeWithSelector(Greeter.triggerRevert.selector) + ); + } +} + +contract ArbitrumCrossDomainGovernor_TransferL1Ownership is ArbitrumCrossDomainGovernorTest { + /// @notice it should not be callable by non-owners + function test_NotCallableByNonOwners() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_arbitrumCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should not be callable by L2 owner + function test_NotCallableByL2Owner() public { + vm.startPrank(s_l1OwnerAddr); + assertEq(s_arbitrumCrossDomainGovernor.owner(), s_l1OwnerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_arbitrumCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner + function test_CallableByL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_arbitrumCrossDomainGovernor.l1Owner(), s_strangerAddr); + + // Sends the message + s_arbitrumCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner to zero address + function test_CallableByL1OwnerOrZeroAddress() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_arbitrumCrossDomainGovernor.l1Owner(), address(0)); + + // Sends the message + s_arbitrumCrossDomainGovernor.transferL1Ownership(address(0)); + } +} + +contract ArbitrumCrossDomainGovernor_AcceptL1Ownership is ArbitrumCrossDomainGovernorTest { + /// @notice it should not be callable by non pending-owners + function test_NotCallableByNonPendingOwners() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_crossDomainMessengerAddr); + + // Sends the message + vm.expectRevert("Must be proposed L1 owner"); + s_arbitrumCrossDomainGovernor.acceptL1Ownership(); + } + + /// @notice it should be callable by pending L1 owner + function test_CallableByPendingL1Owner() public { + // Request ownership transfer + vm.startPrank(s_crossDomainMessengerAddr); + s_arbitrumCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + + // Prepares expected event payload + vm.expectEmit(); + emit L1OwnershipTransferred(s_l1OwnerAddr, s_strangerAddr); + + // Accepts ownership transfer request + vm.startPrank(s_newOwnerCrossDomainMessengerAddr); + s_arbitrumCrossDomainGovernor.acceptL1Ownership(); + + // Asserts that the ownership was actually transferred + assertEq(s_arbitrumCrossDomainGovernor.l1Owner(), s_strangerAddr); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol new file mode 100644 index 00000000000..054c49b1605 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {SimpleWriteAccessController} from "../../../../shared/access/SimpleWriteAccessController.sol"; +import {ArbitrumSequencerUptimeFeed} from "../../../dev/arbitrum/ArbitrumSequencerUptimeFeed.sol"; +import {MockAggregatorV2V3} from "../../mocks/MockAggregatorV2V3.sol"; +import {FeedConsumer} from "../../../../tests/FeedConsumer.sol"; +import {Flags} from "../../../dev/Flags.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract ArbitrumSequencerUptimeFeedTest is L2EPTest { + /// Constants + uint256 internal constant GAS_USED_DEVIATION = 100; + + /// Helper variable(s) + address internal s_l2MessengerAddr = toArbitrumL2AliasAddress(s_l1OwnerAddr); + + /// L2EP contracts + ArbitrumSequencerUptimeFeed internal s_arbitrumSequencerUptimeFeed; + SimpleWriteAccessController internal s_accessController; + MockAggregatorV2V3 internal s_l1GasFeed; + Flags internal s_flags; + + /// Events + event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); + event RoundUpdated(int256 status, uint64 updatedAt); + event Initialized(); + + /// Setup + function setUp() public { + vm.startPrank(s_deployerAddr, s_deployerAddr); + + s_accessController = new SimpleWriteAccessController(); + s_flags = new Flags(address(s_accessController), address(s_accessController)); + s_arbitrumSequencerUptimeFeed = new ArbitrumSequencerUptimeFeed(address(s_flags), s_l1OwnerAddr); + + s_accessController.addAccess(address(s_arbitrumSequencerUptimeFeed)); + s_accessController.addAccess(address(s_flags)); + s_accessController.addAccess(s_deployerAddr); + s_flags.addAccess(address(s_arbitrumSequencerUptimeFeed)); + + vm.expectEmit(); + emit Initialized(); + s_arbitrumSequencerUptimeFeed.initialize(); + + vm.stopPrank(); + } +} + +contract ArbitrumSequencerUptimeFeed_Constants is ArbitrumSequencerUptimeFeedTest { + /// @notice it should have the correct value for FLAG_L2_SEQ_OFFLINE' + function test_InitialState() public { + assertEq(s_arbitrumSequencerUptimeFeed.FLAG_L2_SEQ_OFFLINE(), 0xa438451D6458044c3c8CD2f6f31c91ac882A6d91); + } +} + +contract ArbitrumSequencerUptimeFeed_UpdateStatus is ArbitrumSequencerUptimeFeedTest { + /// @notice it should revert if called by an address that is not the L2 Cross Domain Messenger + function test_RevertIfNotL2CrossDomainMessengerAddr() public { + // Sets msg.sender and tx.origin to an unauthorized address + vm.startPrank(s_strangerAddr, s_strangerAddr); + + // Tries to update the status from an unauthorized account + vm.expectRevert(abi.encodeWithSelector(ArbitrumSequencerUptimeFeed.InvalidSender.selector)); + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(1)); + } + + /// @notice it should update status when status has changed and incoming timestamp is newer than the latest + function test_UpdateStatusWhenStatusChangeAndTimeChange() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Submits a status update + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp(); + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_arbitrumSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Submit another status update, different status, newer timestamp should update + timestamp = timestamp + 200; + vm.expectEmit(); + emit AnswerUpdated(0, 3, timestamp); + s_arbitrumSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 0); + assertEq(s_arbitrumSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + } + + /// @notice it should update status when status has changed and incoming timestamp is the same as latest + function test_UpdateStatusWhenStatusChangeAndNoTimeChange() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Fetches the latest timestamp + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp(); + + // Submits a status update + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_arbitrumSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Submit another status update, different status, same timestamp should update + vm.expectEmit(); + emit AnswerUpdated(0, 3, timestamp); + s_arbitrumSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 0); + assertEq(s_arbitrumSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + } + + /// @notice it should ignore out-of-order updates + function test_IgnoreOutOfOrderUpdates() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Submits a status update + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 10000; + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_arbitrumSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Update with different status, but stale timestamp, should be ignored + timestamp = timestamp - 1000; + vm.expectEmit(false, false, false, false); + emit UpdateIgnored(true, 0, true, 0); // arguments are dummy values + // TODO: how can we check that an AnswerUpdated event was NOT emitted + s_arbitrumSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + } +} + +contract ArbitrumSequencerUptimeFeed_AggregatorV3Interface is ArbitrumSequencerUptimeFeedTest { + /// @notice it should return valid answer from getRoundData and latestRoundData + function test_AggregatorV3Interface() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables + uint80 roundId; + int256 answer; + uint256 startedAt; + uint256 updatedAt; + uint80 answeredInRound; + + // Checks initial state + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_arbitrumSequencerUptimeFeed.latestRoundData(); + assertEq(roundId, 1); + assertEq(answer, 0); + assertEq(answeredInRound, roundId); + assertEq(startedAt, updatedAt); + + // Submits status update with different status and newer timestamp, should update + uint256 timestamp = startedAt + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_arbitrumSequencerUptimeFeed.getRoundData(2); + assertEq(roundId, 2); + assertEq(answer, 1); + assertEq(answeredInRound, roundId); + assertEq(startedAt, timestamp); + assertLe(updatedAt, startedAt); + + // Saves round 2 data + uint80 roundId2 = roundId; + int256 answer2 = answer; + uint256 startedAt2 = startedAt; + uint256 updatedAt2 = updatedAt; + uint80 answeredInRound2 = answeredInRound; + + // Checks that last round is still returning the correct data + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_arbitrumSequencerUptimeFeed.getRoundData(1); + assertEq(roundId, 1); + assertEq(answer, 0); + assertEq(answeredInRound, roundId); + assertEq(startedAt, updatedAt); + + // Assert latestRoundData corresponds to latest round id + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_arbitrumSequencerUptimeFeed.latestRoundData(); + assertEq(roundId2, roundId); + assertEq(answer2, answer); + assertEq(startedAt2, startedAt); + assertEq(updatedAt2, updatedAt); + assertEq(answeredInRound2, answeredInRound); + } + + /// @notice it should revert from #getRoundData when round does not yet exist (future roundId) + function test_Return0WhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) = s_arbitrumSequencerUptimeFeed.getRoundData(2); + + // Validates round data + assertEq(roundId, 2); + assertEq(answer, 0); + assertEq(startedAt, 0); + assertEq(updatedAt, 0); + assertEq(answeredInRound, 2); + } +} + +contract ArbitrumSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions is ArbitrumSequencerUptimeFeedTest { + /// @notice it should disallow reads on AggregatorV2V3Interface functions when consuming contract is not whitelisted + function test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() public { + // Deploys a FeedConsumer contract + FeedConsumer feedConsumer = new FeedConsumer(address(s_arbitrumSequencerUptimeFeed)); + + // Sanity - consumer is not whitelisted + assertEq(s_arbitrumSequencerUptimeFeed.checkEnabled(), true); + assertEq(s_arbitrumSequencerUptimeFeed.hasAccess(address(feedConsumer), abi.encode("")), false); + + // Asserts reads are not possible from consuming contract + vm.expectRevert("No access"); + feedConsumer.latestAnswer(); + vm.expectRevert("No access"); + feedConsumer.latestRoundData(); + } + + /// @notice it should allow reads on AggregatorV2V3Interface functions when consuming contract is whitelisted + function test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() public { + // Deploys a FeedConsumer contract + FeedConsumer feedConsumer = new FeedConsumer(address(s_arbitrumSequencerUptimeFeed)); + + // Whitelist consumer + vm.startPrank(s_deployerAddr, s_deployerAddr); + s_arbitrumSequencerUptimeFeed.addAccess(address(feedConsumer)); + + // Sanity - consumer is whitelisted + assertEq(s_arbitrumSequencerUptimeFeed.checkEnabled(), true); + assertEq(s_arbitrumSequencerUptimeFeed.hasAccess(address(feedConsumer), abi.encode("")), true); + + // Asserts reads are possible from consuming contract + (uint80 roundId, int256 answer, , , ) = feedConsumer.latestRoundData(); + assertEq(feedConsumer.latestAnswer(), 0); + assertEq(roundId, 1); + assertEq(answer, 0); + } +} + +contract ArbitrumSequencerUptimeFeed_GasCosts is ArbitrumSequencerUptimeFeedTest { + /// @notice it should consume a known amount of gas for updates + function test_GasCosts() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Assert initial conditions + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp(); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 0); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed; + uint256 gasStart; + uint256 gasFinal; + + // measures gas used for no update + expectedGasUsed = 5507; // NOTE: used to be 28300 in hardhat tests + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.updateStatus(false, uint64(timestamp + 1000)); + gasFinal = gasleft(); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 0); + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + + // measures gas used for update + expectedGasUsed = 68198; // NOTE: used to be 93015 in hardhat tests + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp + 1000)); + gasFinal = gasleft(); + assertEq(s_arbitrumSequencerUptimeFeed.latestAnswer(), 1); + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } +} + +contract ArbitrumSequencerUptimeFeed_AggregatorInterfaceGasCosts is ArbitrumSequencerUptimeFeedTest { + /// @notice it should consume a known amount of gas for getRoundData(uint80) + function test_GasUsageForGetRoundData() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 4658; // NOTE: used to be 31157 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.getRoundData(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestRoundData() + function test_GasUsageForLatestRoundData() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 2154; // NOTE: used to be 28523 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.latestRoundData(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestAnswer() + function test_GasUsageForLatestAnswer() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1722; // NOTE: used to be 28329 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.latestAnswer(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestTimestamp() + function test_GasUsageForLatestTimestamp() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1652; // NOTE: used to be 28229 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.latestTimestamp(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestRound() + function test_GasUsageForLatestRound() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1632; // NOTE: used to be 28245 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.latestRound(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for getAnswer() + function test_GasUsageForGetAnswer() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 4059; // NOTE: used to be 30799 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.getAnswer(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for getTimestamp() + function test_GasUsageForGetTimestamp() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l2MessengerAddr, s_l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 4024; // NOTE: used to be 30753 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_arbitrumSequencerUptimeFeed.latestTimestamp() + 1000; + s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_arbitrumSequencerUptimeFeed.getTimestamp(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol new file mode 100644 index 00000000000..504635540ce --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumValidator.t.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; + +import {SimpleWriteAccessController} from "../../../../shared/access/SimpleWriteAccessController.sol"; +import {ArbitrumSequencerUptimeFeed} from "../../../dev/arbitrum/ArbitrumSequencerUptimeFeed.sol"; +import {ArbitrumValidator} from "../../../dev/arbitrum/ArbitrumValidator.sol"; +import {MockArbitrumInbox} from "../../../../tests/MockArbitrumInbox.sol"; +import {MockAggregatorV2V3} from "../../mocks/MockAggregatorV2V3.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract ArbitrumValidatorTest is L2EPTest { + /// Helper constants + address internal constant L2_SEQ_STATUS_RECORDER_ADDRESS = 0x491B1dDA0A8fa069bbC1125133A975BF4e85a91b; + uint256 internal constant GAS_PRICE_BID = 1000000; + uint256 internal constant BASE_FEE = 14000000000; + uint256 internal constant MAX_GAS = 1000000; + + /// L2EP contracts + AccessControllerInterface internal s_accessController; + MockArbitrumInbox internal s_mockArbitrumInbox; + ArbitrumValidator internal s_arbitrumValidator; + MockAggregatorV2V3 internal s_l1GasFeed; + + /// Events + event RetryableTicketNoRefundAliasRewriteCreated( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes data + ); + + /// Setup + function setUp() public { + s_accessController = new SimpleWriteAccessController(); + s_mockArbitrumInbox = new MockArbitrumInbox(); + s_l1GasFeed = new MockAggregatorV2V3(); + s_arbitrumValidator = new ArbitrumValidator( + address(s_mockArbitrumInbox), + L2_SEQ_STATUS_RECORDER_ADDRESS, + address(s_accessController), + MAX_GAS, + GAS_PRICE_BID, + BASE_FEE, + address(s_l1GasFeed), + ArbitrumValidator.PaymentStrategy.L1 + ); + } +} + +contract ArbitrumValidator_Validate is ArbitrumValidatorTest { + /// @notice it post sequencer offline + function test_PostSequencerOffline() public { + // Gives access to the s_eoaValidator + s_arbitrumValidator.addAccess(s_eoaValidator); + + // Gets the ArbitrumValidator L2 address + address arbitrumValidatorL2Addr = toArbitrumL2AliasAddress(address(s_arbitrumValidator)); + + // Sets block.timestamp to a later date, funds the ArbitrumValidator contract, and sets msg.sender and tx.origin + uint256 futureTimestampInSeconds = block.timestamp + 5000; + vm.warp(futureTimestampInSeconds); + vm.deal(address(s_arbitrumValidator), 1 ether); + vm.startPrank(s_eoaValidator); + + // Sets up the expected event data + vm.expectEmit(); + emit RetryableTicketNoRefundAliasRewriteCreated( + L2_SEQ_STATUS_RECORDER_ADDRESS, // destAddr + 0, // arbTxCallValue + 25312000000000, // maxSubmissionCost + arbitrumValidatorL2Addr, // submissionRefundAddress + arbitrumValidatorL2Addr, // valueRefundAddress + MAX_GAS, // maxGas + GAS_PRICE_BID, // gasPriceBid + abi.encodeWithSelector(ArbitrumSequencerUptimeFeed.updateStatus.selector, true, futureTimestampInSeconds) // data + ); + + // Runs the function (which produces the event to test) + s_arbitrumValidator.validate(0, 0, 1, 1); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol new file mode 100644 index 00000000000..d5c482dce98 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainForwarder.t.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {OptimismCrossDomainForwarder} from "../../../dev/optimism/OptimismCrossDomainForwarder.sol"; +import {MockOVMCrossDomainMessenger} from "../../mocks/optimism/MockOVMCrossDomainMessenger.sol"; +import {Greeter} from "../../../../tests/Greeter.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract OptimismCrossDomainForwarderTest is L2EPTest { + /// Contracts + MockOVMCrossDomainMessenger internal s_mockOptimismCrossDomainMessenger; + OptimismCrossDomainForwarder internal s_optimismCrossDomainForwarder; + Greeter internal s_greeter; + + /// Events + event L1OwnershipTransferRequested(address indexed from, address indexed to); + event L1OwnershipTransferred(address indexed from, address indexed to); + + /// Setup + function setUp() public { + // Deploys contracts + vm.startPrank(s_l1OwnerAddr); + s_mockOptimismCrossDomainMessenger = new MockOVMCrossDomainMessenger(s_l1OwnerAddr); + s_optimismCrossDomainForwarder = new OptimismCrossDomainForwarder( + s_mockOptimismCrossDomainMessenger, + s_l1OwnerAddr + ); + s_greeter = new Greeter(address(s_optimismCrossDomainForwarder)); + vm.stopPrank(); + } +} + +contract OptimismCrossDomainForwarder_Constructor is OptimismCrossDomainForwarderTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // it should set the owner correctly + assertEq(s_optimismCrossDomainForwarder.owner(), s_l1OwnerAddr); + + // it should set the l1Owner correctly + assertEq(s_optimismCrossDomainForwarder.l1Owner(), s_l1OwnerAddr); + + // it should set the crossdomain messenger correctly + assertEq(s_optimismCrossDomainForwarder.crossDomainMessenger(), address(s_mockOptimismCrossDomainMessenger)); + + // it should set the typeAndVersion correctly + assertEq(s_optimismCrossDomainForwarder.typeAndVersion(), "OptimismCrossDomainForwarder 1.0.0"); + } +} + +contract OptimismCrossDomainForwarder_Forward is OptimismCrossDomainForwarderTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_optimismCrossDomainForwarder.forward(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_Forward() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + encodeCrossDomainSetGreetingMsg(s_optimismCrossDomainForwarder.forward.selector, address(s_greeter), greeting), // message + 0 // gas limit + ); + + // Checks that the greeter got the message + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should revert when contract call reverts + function test_ForwardRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message + vm.expectRevert("Invalid greeting length"); + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + encodeCrossDomainSetGreetingMsg(s_optimismCrossDomainForwarder.forward.selector, address(s_greeter), ""), // message + 0 // gas limit + ); + } +} + +contract OptimismCrossDomainForwarder_TransferL1Ownership is OptimismCrossDomainForwarderTest { + /// @notice it should not be callable by non-owners + function test_NotCallableByNonOwners() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_optimismCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should not be callable by L2 owner + function test_NotCallableByL2Owner() public { + vm.startPrank(s_l1OwnerAddr); + assertEq(s_optimismCrossDomainForwarder.owner(), s_l1OwnerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_optimismCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner + function test_CallableByL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_optimismCrossDomainForwarder.l1Owner(), s_strangerAddr); + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + abi.encodeWithSelector(s_optimismCrossDomainForwarder.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by current L1 owner to zero address + function test_CallableByL1OwnerOrZeroAddress() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_optimismCrossDomainForwarder.l1Owner(), address(0)); + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + abi.encodeWithSelector(s_optimismCrossDomainForwarder.transferL1Ownership.selector, address(0)), // message + 0 // gas limit + ); + } +} + +contract OptimismCrossDomainForwarder_AcceptL1Ownership is OptimismCrossDomainForwarderTest { + /// @notice it should not be callable by non pending-owners + function test_NotCallableByNonPendingOwners() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends the message + vm.expectRevert("Must be proposed L1 owner"); + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + abi.encodeWithSelector(s_optimismCrossDomainForwarder.acceptL1Ownership.selector), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by pending L1 owner + function test_CallableByPendingL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Request ownership transfer + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + abi.encodeWithSelector(s_optimismCrossDomainForwarder.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Sets a mock message sender + s_mockOptimismCrossDomainMessenger._setMockMessageSender(s_strangerAddr); + + // Prepares expected event payload + vm.expectEmit(); + emit L1OwnershipTransferred(s_l1OwnerAddr, s_strangerAddr); + + // Accepts ownership transfer request + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainForwarder), // target + abi.encodeWithSelector(s_optimismCrossDomainForwarder.acceptL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Asserts that the ownership was actually transferred + assertEq(s_optimismCrossDomainForwarder.l1Owner(), s_strangerAddr); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol new file mode 100644 index 00000000000..e1a5aef95a1 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismCrossDomainGovernor.t.sol @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {OptimismCrossDomainGovernor} from "../../../dev/optimism/OptimismCrossDomainGovernor.sol"; +import {MockOVMCrossDomainMessenger} from "../../mocks/optimism/MockOVMCrossDomainMessenger.sol"; +import {Greeter} from "../../../../tests/Greeter.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +import {MultiSend} from "../../../../vendor/MultiSend.sol"; + +contract OptimismCrossDomainGovernorTest is L2EPTest { + /// Contracts + MockOVMCrossDomainMessenger internal s_mockOptimismCrossDomainMessenger; + OptimismCrossDomainGovernor internal s_optimismCrossDomainGovernor; + MultiSend internal s_multiSend; + Greeter internal s_greeter; + + /// Events + event L1OwnershipTransferRequested(address indexed from, address indexed to); + event L1OwnershipTransferred(address indexed from, address indexed to); + + /// Setup + function setUp() public { + // Deploys contracts + vm.startPrank(s_l1OwnerAddr); + s_mockOptimismCrossDomainMessenger = new MockOVMCrossDomainMessenger(s_l1OwnerAddr); + s_optimismCrossDomainGovernor = new OptimismCrossDomainGovernor(s_mockOptimismCrossDomainMessenger, s_l1OwnerAddr); + s_greeter = new Greeter(address(s_optimismCrossDomainGovernor)); + s_multiSend = new MultiSend(); + vm.stopPrank(); + } +} + +contract OptimismCrossDomainGovernor_Constructor is OptimismCrossDomainGovernorTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // it should set the owner correctly + assertEq(s_optimismCrossDomainGovernor.owner(), s_l1OwnerAddr); + + // it should set the l1Owner correctly + assertEq(s_optimismCrossDomainGovernor.l1Owner(), s_l1OwnerAddr); + + // it should set the crossdomain messenger correctly + assertEq(s_optimismCrossDomainGovernor.crossDomainMessenger(), address(s_mockOptimismCrossDomainMessenger)); + + // it should set the typeAndVersion correctly + assertEq(s_optimismCrossDomainGovernor.typeAndVersion(), "OptimismCrossDomainGovernor 1.0.0"); + } +} + +contract OptimismCrossDomainGovernor_Forward is OptimismCrossDomainGovernorTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger or owner"); + s_optimismCrossDomainGovernor.forward(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_Forward() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + encodeCrossDomainSetGreetingMsg(s_optimismCrossDomainGovernor.forward.selector, address(s_greeter), greeting), // message + 0 // gas limit + ); + + // Checks that the greeter got the message + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should revert when contract call reverts + function test_ForwardRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message + vm.expectRevert("Invalid greeting length"); + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + encodeCrossDomainSetGreetingMsg(s_optimismCrossDomainGovernor.forward.selector, address(s_greeter), ""), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by L2 owner + function test_CallableByL2Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_optimismCrossDomainGovernor.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, greeting) + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), greeting); + } +} + +contract OptimismCrossDomainGovernor_ForwardDelegate is OptimismCrossDomainGovernorTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger or owner"); + s_optimismCrossDomainGovernor.forwardDelegate(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_CallableByCrossDomainMessengerAddressOrL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + encodeCrossDomainMultiSendMsg( + s_optimismCrossDomainGovernor.forwardDelegate.selector, + address(s_multiSend), + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "bar")) + ), // message + 0 // gas limit + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), "bar"); + } + + /// @notice it should be callable by L2 owner + function test_CallableByL2Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + encodeCrossDomainMultiSendMsg( + s_optimismCrossDomainGovernor.forwardDelegate.selector, + address(s_multiSend), + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "bar")) + ), // message + 0 // gas limit + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), "bar"); + } + + /// @notice it should revert batch when one call fails + function test_RevertsBatchWhenOneCallFails() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message (empty transaction data is not allowed) + vm.expectRevert("Governor delegatecall reverted"); + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + encodeCrossDomainMultiSendMsg( + s_optimismCrossDomainGovernor.forwardDelegate.selector, + address(s_multiSend), + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "")) + ), // message + 0 // gas limit + ); + + // Checks that the greeter message is unchanged + assertEq(s_greeter.greeting(), ""); + } + + /// @notice it should bubble up revert when contract call reverts + function test_BubbleUpRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message (empty transaction data is not allowed) + vm.expectRevert("Greeter: revert triggered"); + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + abi.encodeWithSelector( + OptimismCrossDomainGovernor.forwardDelegate.selector, + address(s_greeter), + abi.encodeWithSelector(Greeter.triggerRevert.selector) + ), // message + 0 // gas limit + ); + } +} + +contract OptimismCrossDomainGovernor_TransferL1Ownership is OptimismCrossDomainGovernorTest { + /// @notice it should not be callable by non-owners + function test_NotCallableByNonOwners() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_optimismCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should not be callable by L2 owner + function test_NotCallableByL2Owner() public { + vm.startPrank(s_l1OwnerAddr); + assertEq(s_optimismCrossDomainGovernor.owner(), s_l1OwnerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_optimismCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner + function test_CallableByL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_optimismCrossDomainGovernor.l1Owner(), s_strangerAddr); + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + abi.encodeWithSelector(s_optimismCrossDomainGovernor.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by current L1 owner to zero address + function test_CallableByL1OwnerOrZeroAddress() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_optimismCrossDomainGovernor.l1Owner(), address(0)); + + // Sends the message + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + abi.encodeWithSelector(s_optimismCrossDomainGovernor.transferL1Ownership.selector, address(0)), // message + 0 // gas limit + ); + } +} + +contract OptimismCrossDomainGovernor_AcceptL1Ownership is OptimismCrossDomainGovernorTest { + /// @notice it should not be callable by non pending-owners + function test_NotCallableByNonPendingOwners() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends the message + vm.expectRevert("Must be proposed L1 owner"); + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + abi.encodeWithSelector(s_optimismCrossDomainGovernor.acceptL1Ownership.selector), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by pending L1 owner + function test_CallableByPendingL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Request ownership transfer + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + abi.encodeWithSelector(s_optimismCrossDomainGovernor.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Sets a mock message sender + s_mockOptimismCrossDomainMessenger._setMockMessageSender(s_strangerAddr); + + // Prepares expected event payload + vm.expectEmit(); + emit L1OwnershipTransferred(s_l1OwnerAddr, s_strangerAddr); + + // Accepts ownership transfer request + s_mockOptimismCrossDomainMessenger.sendMessage( + address(s_optimismCrossDomainGovernor), // target + abi.encodeWithSelector(s_optimismCrossDomainGovernor.acceptL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Asserts that the ownership was actually transferred + assertEq(s_optimismCrossDomainGovernor.l1Owner(), s_strangerAddr); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol new file mode 100644 index 00000000000..b4aa32ce69b --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {MockOptimismL1CrossDomainMessenger} from "../../../../tests/MockOptimismL1CrossDomainMessenger.sol"; +import {MockOptimismL2CrossDomainMessenger} from "../../../../tests/MockOptimismL2CrossDomainMessenger.sol"; +import {OptimismSequencerUptimeFeed} from "../../../dev/optimism/OptimismSequencerUptimeFeed.sol"; +import {FeedConsumer} from "../../../../tests/FeedConsumer.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract OptimismSequencerUptimeFeedTest is L2EPTest { + /// Constants + uint256 internal constant GAS_USED_DEVIATION = 100; + + /// L2EP contracts + MockOptimismL1CrossDomainMessenger internal s_mockOptimismL1CrossDomainMessenger; + MockOptimismL2CrossDomainMessenger internal s_mockOptimismL2CrossDomainMessenger; + OptimismSequencerUptimeFeed internal s_optimismSequencerUptimeFeed; + + /// Events + event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); + event RoundUpdated(int256 status, uint64 updatedAt); + + /// Setup + function setUp() public { + // Deploys contracts + s_mockOptimismL1CrossDomainMessenger = new MockOptimismL1CrossDomainMessenger(); + s_mockOptimismL2CrossDomainMessenger = new MockOptimismL2CrossDomainMessenger(); + s_optimismSequencerUptimeFeed = new OptimismSequencerUptimeFeed( + s_l1OwnerAddr, + address(s_mockOptimismL2CrossDomainMessenger), + false + ); + + // Sets mock sender in mock L2 messenger contract + s_mockOptimismL2CrossDomainMessenger.setSender(s_l1OwnerAddr); + } +} + +contract OptimismSequencerUptimeFeed_Constructor is OptimismSequencerUptimeFeedTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Checks L1 sender + address actualL1Addr = s_optimismSequencerUptimeFeed.l1Sender(); + assertEq(actualL1Addr, s_l1OwnerAddr); + + // Checks latest round data + (uint80 roundId, int256 answer, , , ) = s_optimismSequencerUptimeFeed.latestRoundData(); + assertEq(roundId, 1); + assertEq(answer, 0); + } +} + +contract OptimismSequencerUptimeFeed_UpdateStatus is OptimismSequencerUptimeFeedTest { + /// @notice it should revert if called by an address that is not the L2 Cross Domain Messenger + function test_RevertIfNotL2CrossDomainMessengerAddr() public { + // Sets msg.sender and tx.origin to an unauthorized address + vm.startPrank(s_strangerAddr, s_strangerAddr); + + // Tries to update the status from an unauthorized account + vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.InvalidSender.selector)); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(1)); + } + + /// @notice it should revert if called by an address that is not the L2 Cross Domain Messenger and is not the L1 sender + function test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() public { + // Sets msg.sender and tx.origin to an unauthorized address + vm.startPrank(s_strangerAddr, s_strangerAddr); + + // Sets mock sender in mock L2 messenger contract + s_mockOptimismL2CrossDomainMessenger.setSender(s_strangerAddr); + + // Tries to update the status from an unauthorized account + vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.InvalidSender.selector)); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(1)); + } + + /// @notice it should update status when status has not changed and incoming timestamp is the same as latest + function test_UpdateStatusWhenNoChange() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Fetches the latest timestamp + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp(); + + // Submits a status update + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Stores the current round data before updating it + ( + uint80 roundIdBeforeUpdate, + int256 answerBeforeUpdate, + uint256 startedAtBeforeUpdate, + , + uint80 answeredInRoundBeforeUpdate + ) = s_optimismSequencerUptimeFeed.latestRoundData(); + + // Submit another status update with the same status + vm.expectEmit(); + emit RoundUpdated(1, uint64(block.timestamp)); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp + 200)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Stores the current round data after updating it + ( + uint80 roundIdAfterUpdate, + int256 answerAfterUpdate, + uint256 startedAtAfterUpdate, + uint256 updatedAtAfterUpdate, + uint80 answeredInRoundAfterUpdate + ) = s_optimismSequencerUptimeFeed.latestRoundData(); + + // Verifies the latest round data has been properly updated + assertEq(roundIdAfterUpdate, roundIdBeforeUpdate); + assertEq(answerAfterUpdate, answerBeforeUpdate); + assertEq(startedAtAfterUpdate, startedAtBeforeUpdate); + assertEq(answeredInRoundAfterUpdate, answeredInRoundBeforeUpdate); + assertEq(updatedAtAfterUpdate, block.timestamp); + } + + /// @notice it should update status when status has changed and incoming timestamp is newer than the latest + function test_UpdateStatusWhenStatusChangeAndTimeChange() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Submits a status update + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp(); + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Submit another status update, different status, newer timestamp should update + timestamp = timestamp + 200; + vm.expectEmit(); + emit AnswerUpdated(0, 3, timestamp); + s_optimismSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 0); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + } + + /// @notice it should update status when status has changed and incoming timestamp is the same as latest + function test_UpdateStatusWhenStatusChangeAndNoTimeChange() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Fetches the latest timestamp + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp(); + + // Submits a status update + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Submit another status update, different status, same timestamp should update + vm.expectEmit(); + emit AnswerUpdated(0, 3, timestamp); + s_optimismSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 0); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + } + + /// @notice it should ignore out-of-order updates + function test_IgnoreOutOfOrderUpdates() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Submits a status update + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 10000; + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_optimismSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Update with different status, but stale timestamp, should be ignored + timestamp = timestamp - 1000; + vm.expectEmit(false, false, false, false); + emit UpdateIgnored(true, 0, true, 0); // arguments are dummy values + // TODO: how can we check that an AnswerUpdated event was NOT emitted + s_optimismSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + } +} + +contract OptimismSequencerUptimeFeed_AggregatorV3Interface is OptimismSequencerUptimeFeedTest { + /// @notice it should return valid answer from getRoundData and latestRoundData + function test_AggregatorV3Interface() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables + uint80 roundId; + int256 answer; + uint256 startedAt; + uint256 updatedAt; + uint80 answeredInRound; + + // Checks initial state + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_optimismSequencerUptimeFeed.latestRoundData(); + assertEq(roundId, 1); + assertEq(answer, 0); + assertEq(answeredInRound, roundId); + assertEq(startedAt, updatedAt); + + // Submits status update with different status and newer timestamp, should update + uint256 timestamp = startedAt + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_optimismSequencerUptimeFeed.getRoundData(2); + assertEq(roundId, 2); + assertEq(answer, 1); + assertEq(answeredInRound, roundId); + assertEq(startedAt, timestamp); + assertLe(updatedAt, startedAt); + + // Saves round 2 data + uint80 roundId2 = roundId; + int256 answer2 = answer; + uint256 startedAt2 = startedAt; + uint256 updatedAt2 = updatedAt; + uint80 answeredInRound2 = answeredInRound; + + // Checks that last round is still returning the correct data + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_optimismSequencerUptimeFeed.getRoundData(1); + assertEq(roundId, 1); + assertEq(answer, 0); + assertEq(answeredInRound, roundId); + assertEq(startedAt, updatedAt); + + // Assert latestRoundData corresponds to latest round id + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_optimismSequencerUptimeFeed.latestRoundData(); + assertEq(roundId2, roundId); + assertEq(answer2, answer); + assertEq(startedAt2, startedAt); + assertEq(updatedAt2, updatedAt); + assertEq(answeredInRound2, answeredInRound); + } + + /// @notice it should revert from #getRoundData when round does not yet exist (future roundId) + function test_RevertGetRoundDataWhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.NoDataPresent.selector)); + s_optimismSequencerUptimeFeed.getRoundData(2); + } + + /// @notice it should revert from #getAnswer when round does not yet exist (future roundId) + function test_RevertGetAnswerWhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.NoDataPresent.selector)); + s_optimismSequencerUptimeFeed.getAnswer(2); + } + + /// @notice it should revert from #getTimestamp when round does not yet exist (future roundId) + function test_RevertGetTimestampWhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.NoDataPresent.selector)); + s_optimismSequencerUptimeFeed.getTimestamp(2); + } +} + +contract OptimismSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions is OptimismSequencerUptimeFeedTest { + /// @notice it should disallow reads on AggregatorV2V3Interface functions when consuming contract is not whitelisted + function test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() public { + // Deploys a FeedConsumer contract + FeedConsumer feedConsumer = new FeedConsumer(address(s_optimismSequencerUptimeFeed)); + + // Sanity - consumer is not whitelisted + assertEq(s_optimismSequencerUptimeFeed.checkEnabled(), true); + assertEq(s_optimismSequencerUptimeFeed.hasAccess(address(feedConsumer), abi.encode("")), false); + + // Asserts reads are not possible from consuming contract + vm.expectRevert("No access"); + feedConsumer.latestAnswer(); + vm.expectRevert("No access"); + feedConsumer.latestRoundData(); + } + + /// @notice it should allow reads on AggregatorV2V3Interface functions when consuming contract is whitelisted + function test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() public { + // Deploys a FeedConsumer contract + FeedConsumer feedConsumer = new FeedConsumer(address(s_optimismSequencerUptimeFeed)); + + // Whitelist consumer + s_optimismSequencerUptimeFeed.addAccess(address(feedConsumer)); + + // Sanity - consumer is whitelisted + assertEq(s_optimismSequencerUptimeFeed.checkEnabled(), true); + assertEq(s_optimismSequencerUptimeFeed.hasAccess(address(feedConsumer), abi.encode("")), true); + + // Asserts reads are possible from consuming contract + (uint80 roundId, int256 answer, , , ) = feedConsumer.latestRoundData(); + assertEq(feedConsumer.latestAnswer(), 0); + assertEq(roundId, 1); + assertEq(answer, 0); + } +} + +contract OptimismSequencerUptimeFeed_GasCosts is OptimismSequencerUptimeFeedTest { + /// @notice it should consume a known amount of gas for updates + function test_GasCosts() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Assert initial conditions + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp(); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 0); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed; + uint256 gasStart; + uint256 gasFinal; + + // measures gas used for no update + expectedGasUsed = 10197; // NOTE: used to be 38594 in hardhat tests + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.updateStatus(false, uint64(timestamp + 1000)); + gasFinal = gasleft(); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 0); + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + + // measures gas used for update + expectedGasUsed = 33348; // NOTE: used to be 60170 in hardhat tests + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp + 1000)); + gasFinal = gasleft(); + assertEq(s_optimismSequencerUptimeFeed.latestAnswer(), 1); + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } +} + +contract OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts is OptimismSequencerUptimeFeedTest { + /// @notice it should consume a known amount of gas for getRoundData(uint80) + function test_GasUsageForGetRoundData() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 4504; // NOTE: used to be 30952 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.getRoundData(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestRoundData() + function test_GasUsageForLatestRoundData() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 2154; // NOTE: used to be 28523 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.latestRoundData(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestAnswer() + function test_GasUsageForLatestAnswer() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1722; // NOTE: used to be 28329 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.latestAnswer(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestTimestamp() + function test_GasUsageForLatestTimestamp() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1598; // NOTE: used to be 28229 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.latestTimestamp(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestRound() + function test_GasUsageForLatestRound() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1632; // NOTE: used to be 28245 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.latestRound(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for getAnswer() + function test_GasUsageForGetAnswer() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 3929; // NOTE: used to be 30682 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.getAnswer(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for getTimestamp() + function test_GasUsageForGetTimestamp() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockOptimismL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 3817; // NOTE: used to be 30570 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_optimismSequencerUptimeFeed.latestTimestamp() + 1000; + s_optimismSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_optimismSequencerUptimeFeed.getTimestamp(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol new file mode 100644 index 00000000000..9364396817a --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismValidator.t.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {MockOptimismL1CrossDomainMessenger} from "../../../../tests/MockOptimismL1CrossDomainMessenger.sol"; +import {MockOptimismL2CrossDomainMessenger} from "../../../../tests/MockOptimismL2CrossDomainMessenger.sol"; +import {OptimismSequencerUptimeFeed} from "../../../dev/optimism/OptimismSequencerUptimeFeed.sol"; +import {OptimismValidator} from "../../../dev/optimism/OptimismValidator.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract OptimismValidatorTest is L2EPTest { + /// Helper constants + address internal constant L2_SEQ_STATUS_RECORDER_ADDRESS = 0x491B1dDA0A8fa069bbC1125133A975BF4e85a91b; + uint32 internal constant INIT_GAS_LIMIT = 1900000; + + /// L2EP contracts + MockOptimismL1CrossDomainMessenger internal s_mockOptimismL1CrossDomainMessenger; + MockOptimismL2CrossDomainMessenger internal s_mockOptimismL2CrossDomainMessenger; + OptimismSequencerUptimeFeed internal s_optimismSequencerUptimeFeed; + OptimismValidator internal s_optimismValidator; + + /// Events + event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); + + /// Setup + function setUp() public { + s_mockOptimismL1CrossDomainMessenger = new MockOptimismL1CrossDomainMessenger(); + s_mockOptimismL2CrossDomainMessenger = new MockOptimismL2CrossDomainMessenger(); + + s_optimismSequencerUptimeFeed = new OptimismSequencerUptimeFeed( + address(s_mockOptimismL1CrossDomainMessenger), + address(s_mockOptimismL2CrossDomainMessenger), + true + ); + + s_optimismValidator = new OptimismValidator( + address(s_mockOptimismL1CrossDomainMessenger), + address(s_optimismSequencerUptimeFeed), + INIT_GAS_LIMIT + ); + } +} + +contract OptimismValidator_SetGasLimit is OptimismValidatorTest { + /// @notice it correctly updates the gas limit + function test_CorrectlyUpdatesTheGasLimit() public { + uint32 newGasLimit = 2000000; + assertEq(s_optimismValidator.getGasLimit(), INIT_GAS_LIMIT); + s_optimismValidator.setGasLimit(newGasLimit); + assertEq(s_optimismValidator.getGasLimit(), newGasLimit); + } +} + +contract OptimismValidator_Validate is OptimismValidatorTest { + /// @notice it reverts if called by account with no access + function test_RevertsIfCalledByAnAccountWithNoAccess() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("No access"); + s_optimismValidator.validate(0, 0, 1, 1); + } + + /// @notice it posts sequencer status when there is not status change + function test_PostSequencerStatusWhenThereIsNotStatusChange() public { + // Gives access to the s_eoaValidator + s_optimismValidator.addAccess(s_eoaValidator); + + // Sets block.timestamp to a later date + uint256 futureTimestampInSeconds = block.timestamp + 5000; + vm.startPrank(s_eoaValidator); + vm.warp(futureTimestampInSeconds); + + // Sets up the expected event data + vm.expectEmit(false, false, false, true); + emit SentMessage( + L2_SEQ_STATUS_RECORDER_ADDRESS, // target + address(s_optimismValidator), // sender + abi.encodeWithSelector(OptimismSequencerUptimeFeed.updateStatus.selector, false, futureTimestampInSeconds), // message + 0, // nonce + INIT_GAS_LIMIT // gas limit + ); + + // Runs the function (which produces the event to test) + s_optimismValidator.validate(0, 0, 0, 0); + } + + /// @notice it post sequencer offline + function test_PostSequencerOffline() public { + // Gives access to the s_eoaValidator + s_optimismValidator.addAccess(s_eoaValidator); + + // Sets block.timestamp to a later date + uint256 futureTimestampInSeconds = block.timestamp + 10000; + vm.startPrank(s_eoaValidator); + vm.warp(futureTimestampInSeconds); + + // Sets up the expected event data + vm.expectEmit(false, false, false, true); + emit SentMessage( + L2_SEQ_STATUS_RECORDER_ADDRESS, // target + address(s_optimismValidator), // sender + abi.encodeWithSelector(OptimismSequencerUptimeFeed.updateStatus.selector, true, futureTimestampInSeconds), // message + 0, // nonce + INIT_GAS_LIMIT // gas limit + ); + + // Runs the function (which produces the event to test) + s_optimismValidator.validate(0, 0, 1, 1); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol new file mode 100644 index 00000000000..f921fa9242e --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainForwarder.t.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {MockScrollCrossDomainMessenger} from "../../mocks/scroll/MockScrollCrossDomainMessenger.sol"; +import {ScrollCrossDomainForwarder} from "../../../dev/scroll/ScrollCrossDomainForwarder.sol"; +import {Greeter} from "../../../../tests/Greeter.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract ScrollCrossDomainForwarderTest is L2EPTest { + /// Contracts + MockScrollCrossDomainMessenger internal s_mockScrollCrossDomainMessenger; + ScrollCrossDomainForwarder internal s_scrollCrossDomainForwarder; + Greeter internal s_greeter; + + /// Events + event L1OwnershipTransferRequested(address indexed from, address indexed to); + event L1OwnershipTransferred(address indexed from, address indexed to); + + /// Setup + function setUp() public { + // Deploys contracts + vm.startPrank(s_l1OwnerAddr); + s_mockScrollCrossDomainMessenger = new MockScrollCrossDomainMessenger(s_l1OwnerAddr); + s_scrollCrossDomainForwarder = new ScrollCrossDomainForwarder(s_mockScrollCrossDomainMessenger, s_l1OwnerAddr); + s_greeter = new Greeter(address(s_scrollCrossDomainForwarder)); + vm.stopPrank(); + } +} + +contract ScrollCrossDomainForwarder_Constructor is ScrollCrossDomainForwarderTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // it should set the owner correctly + assertEq(s_scrollCrossDomainForwarder.owner(), s_l1OwnerAddr); + + // it should set the l1Owner correctly + assertEq(s_scrollCrossDomainForwarder.l1Owner(), s_l1OwnerAddr); + + // it should set the crossdomain messenger correctly + assertEq(s_scrollCrossDomainForwarder.crossDomainMessenger(), address(s_mockScrollCrossDomainMessenger)); + + // it should set the typeAndVersion correctly + assertEq(s_scrollCrossDomainForwarder.typeAndVersion(), "ScrollCrossDomainForwarder 1.0.0"); + } +} + +contract ScrollCrossDomainForwarder_Forward is ScrollCrossDomainForwarderTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_scrollCrossDomainForwarder.forward(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_Forward() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + encodeCrossDomainSetGreetingMsg(s_scrollCrossDomainForwarder.forward.selector, address(s_greeter), greeting), // message + 0 // gas limit + ); + + // Checks that the greeter got the message + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should revert when contract call reverts + function test_ForwardRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message + vm.expectRevert("Invalid greeting length"); + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + encodeCrossDomainSetGreetingMsg(s_scrollCrossDomainForwarder.forward.selector, address(s_greeter), ""), // message + 0 // gas limit + ); + } +} + +contract ScrollCrossDomainForwarder_TransferL1Ownership is ScrollCrossDomainForwarderTest { + /// @notice it should not be callable by non-owners + function test_NotCallableByNonOwners() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_scrollCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should not be callable by L2 owner + function test_NotCallableByL2Owner() public { + vm.startPrank(s_l1OwnerAddr); + assertEq(s_scrollCrossDomainForwarder.owner(), s_l1OwnerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_scrollCrossDomainForwarder.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner + function test_CallableByL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_scrollCrossDomainForwarder.l1Owner(), s_strangerAddr); + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainForwarder.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by current L1 owner to zero address + function test_CallableByL1OwnerOrZeroAddress() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_scrollCrossDomainForwarder.l1Owner(), address(0)); + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainForwarder.transferL1Ownership.selector, address(0)), // message + 0 // gas limit + ); + } +} + +contract ScrollCrossDomainForwarder_AcceptL1Ownership is ScrollCrossDomainForwarderTest { + /// @notice it should not be callable by non pending-owners + function test_NotCallableByNonPendingOwners() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends the message + vm.expectRevert("Must be proposed L1 owner"); + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainForwarder.acceptL1Ownership.selector), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by pending L1 owner + function test_CallableByPendingL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Request ownership transfer + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainForwarder.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Sets a mock message sender + s_mockScrollCrossDomainMessenger._setMockMessageSender(s_strangerAddr); + + // Prepares expected event payload + vm.expectEmit(); + emit L1OwnershipTransferred(s_l1OwnerAddr, s_strangerAddr); + + // Accepts ownership transfer request + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainForwarder), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainForwarder.acceptL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Asserts that the ownership was actually transferred + assertEq(s_scrollCrossDomainForwarder.l1Owner(), s_strangerAddr); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol new file mode 100644 index 00000000000..9c444604946 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollCrossDomainGovernor.t.sol @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {MockScrollCrossDomainMessenger} from "../../mocks/scroll/MockScrollCrossDomainMessenger.sol"; +import {ScrollCrossDomainGovernor} from "../../../dev/scroll/ScrollCrossDomainGovernor.sol"; +import {Greeter} from "../../../../tests/Greeter.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +import {MultiSend} from "../../../../vendor/MultiSend.sol"; + +contract ScrollCrossDomainGovernorTest is L2EPTest { + /// Contracts + MockScrollCrossDomainMessenger internal s_mockScrollCrossDomainMessenger; + ScrollCrossDomainGovernor internal s_scrollCrossDomainGovernor; + MultiSend internal s_multiSend; + Greeter internal s_greeter; + + /// Events + event L1OwnershipTransferRequested(address indexed from, address indexed to); + event L1OwnershipTransferred(address indexed from, address indexed to); + + /// Setup + function setUp() public { + // Deploys contracts + vm.startPrank(s_l1OwnerAddr); + s_mockScrollCrossDomainMessenger = new MockScrollCrossDomainMessenger(s_l1OwnerAddr); + s_scrollCrossDomainGovernor = new ScrollCrossDomainGovernor(s_mockScrollCrossDomainMessenger, s_l1OwnerAddr); + s_greeter = new Greeter(address(s_scrollCrossDomainGovernor)); + s_multiSend = new MultiSend(); + vm.stopPrank(); + } +} + +contract ScrollCrossDomainGovernor_Constructor is ScrollCrossDomainGovernorTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // it should set the owner correctly + assertEq(s_scrollCrossDomainGovernor.owner(), s_l1OwnerAddr); + + // it should set the l1Owner correctly + assertEq(s_scrollCrossDomainGovernor.l1Owner(), s_l1OwnerAddr); + + // it should set the crossdomain messenger correctly + assertEq(s_scrollCrossDomainGovernor.crossDomainMessenger(), address(s_mockScrollCrossDomainMessenger)); + + // it should set the typeAndVersion correctly + assertEq(s_scrollCrossDomainGovernor.typeAndVersion(), "ScrollCrossDomainGovernor 1.0.0"); + } +} + +contract ScrollCrossDomainGovernor_Forward is ScrollCrossDomainGovernorTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger or owner"); + s_scrollCrossDomainGovernor.forward(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_Forward() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + encodeCrossDomainSetGreetingMsg(s_scrollCrossDomainGovernor.forward.selector, address(s_greeter), greeting), // message + 0 // gas limit + ); + + // Checks that the greeter got the message + assertEq(s_greeter.greeting(), greeting); + } + + /// @notice it should revert when contract call reverts + function test_ForwardRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message + vm.expectRevert("Invalid greeting length"); + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + encodeCrossDomainSetGreetingMsg(s_scrollCrossDomainGovernor.forward.selector, address(s_greeter), ""), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by L2 owner + function test_CallableByL2Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Defines the cross domain message to send + string memory greeting = "hello"; + + // Sends the message + s_scrollCrossDomainGovernor.forward( + address(s_greeter), + abi.encodeWithSelector(s_greeter.setGreeting.selector, greeting) + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), greeting); + } +} + +contract ScrollCrossDomainGovernor_ForwardDelegate is ScrollCrossDomainGovernorTest { + /// @notice it should not be callable by unknown address + function test_NotCallableByUnknownAddress() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger or owner"); + s_scrollCrossDomainGovernor.forwardDelegate(address(s_greeter), abi.encode("")); + } + + /// @notice it should be callable by crossdomain messenger address / L1 owner + function test_CallableByCrossDomainMessengerAddressOrL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + encodeCrossDomainMultiSendMsg( + s_scrollCrossDomainGovernor.forwardDelegate.selector, + address(s_multiSend), + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "bar")) + ), // message + 0 // gas limit + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), "bar"); + } + + /// @notice it should be callable by L2 owner + function test_CallableByL2Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_l1OwnerAddr); + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + encodeCrossDomainMultiSendMsg( + s_scrollCrossDomainGovernor.forwardDelegate.selector, + address(s_multiSend), + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "bar")) + ), // message + 0 // gas limit + ); + + // Checks that the greeter message was updated + assertEq(s_greeter.greeting(), "bar"); + } + + /// @notice it should revert batch when one call fails + function test_RevertsBatchWhenOneCallFails() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message (empty transaction data is not allowed) + vm.expectRevert("Governor delegatecall reverted"); + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + encodeCrossDomainMultiSendMsg( + s_scrollCrossDomainGovernor.forwardDelegate.selector, + address(s_multiSend), + abi.encodePacked(encodeMultiSendTx(address(s_greeter), "foo"), encodeMultiSendTx(address(s_greeter), "")) + ), // message + 0 // gas limit + ); + + // Checks that the greeter message is unchanged + assertEq(s_greeter.greeting(), ""); + } + + /// @notice it should bubble up revert when contract call reverts + function test_BubbleUpRevert() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends an invalid message (empty transaction data is not allowed) + vm.expectRevert("Greeter: revert triggered"); + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + abi.encodeWithSelector( + ScrollCrossDomainGovernor.forwardDelegate.selector, + address(s_greeter), + abi.encodeWithSelector(Greeter.triggerRevert.selector) + ), // message + 0 // gas limit + ); + } +} + +contract ScrollCrossDomainGovernor_TransferL1Ownership is ScrollCrossDomainGovernorTest { + /// @notice it should not be callable by non-owners + function test_NotCallableByNonOwners() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_scrollCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should not be callable by L2 owner + function test_NotCallableByL2Owner() public { + vm.startPrank(s_l1OwnerAddr); + assertEq(s_scrollCrossDomainGovernor.owner(), s_l1OwnerAddr); + vm.expectRevert("Sender is not the L2 messenger"); + s_scrollCrossDomainGovernor.transferL1Ownership(s_strangerAddr); + } + + /// @notice it should be callable by current L1 owner + function test_CallableByL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_scrollCrossDomainGovernor.l1Owner(), s_strangerAddr); + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainGovernor.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by current L1 owner to zero address + function test_CallableByL1OwnerOrZeroAddress() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Defines the cross domain message to send + vm.expectEmit(); + emit L1OwnershipTransferRequested(s_scrollCrossDomainGovernor.l1Owner(), address(0)); + + // Sends the message + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainGovernor.transferL1Ownership.selector, address(0)), // message + 0 // gas limit + ); + } +} + +contract ScrollCrossDomainGovernor_AcceptL1Ownership is ScrollCrossDomainGovernorTest { + /// @notice it should not be callable by non pending-owners + function test_NotCallableByNonPendingOwners() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Sends the message + vm.expectRevert("Must be proposed L1 owner"); + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainGovernor.acceptL1Ownership.selector), // message + 0 // gas limit + ); + } + + /// @notice it should be callable by pending L1 owner + function test_CallableByPendingL1Owner() public { + // Sets msg.sender and tx.origin + vm.startPrank(s_strangerAddr); + + // Request ownership transfer + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainGovernor.transferL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Sets a mock message sender + s_mockScrollCrossDomainMessenger._setMockMessageSender(s_strangerAddr); + + // Prepares expected event payload + vm.expectEmit(); + emit L1OwnershipTransferred(s_l1OwnerAddr, s_strangerAddr); + + // Accepts ownership transfer request + s_mockScrollCrossDomainMessenger.sendMessage( + address(s_scrollCrossDomainGovernor), // target + 0, // value + abi.encodeWithSelector(s_scrollCrossDomainGovernor.acceptL1Ownership.selector, s_strangerAddr), // message + 0 // gas limit + ); + + // Asserts that the ownership was actually transferred + assertEq(s_scrollCrossDomainGovernor.l1Owner(), s_strangerAddr); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol new file mode 100644 index 00000000000..85195816b90 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {MockScrollL1CrossDomainMessenger} from "../../mocks/scroll/MockScrollL1CrossDomainMessenger.sol"; +import {MockScrollL2CrossDomainMessenger} from "../../mocks/scroll/MockScrollL2CrossDomainMessenger.sol"; +import {ScrollSequencerUptimeFeed} from "../../../dev/scroll/ScrollSequencerUptimeFeed.sol"; +import {FeedConsumer} from "../../../../tests/FeedConsumer.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract ScrollSequencerUptimeFeedTest is L2EPTest { + /// Constants + uint256 internal constant GAS_USED_DEVIATION = 100; + + /// L2EP contracts + MockScrollL1CrossDomainMessenger internal s_mockScrollL1CrossDomainMessenger; + MockScrollL2CrossDomainMessenger internal s_mockScrollL2CrossDomainMessenger; + ScrollSequencerUptimeFeed internal s_scrollSequencerUptimeFeed; + + /// Events + event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); + event RoundUpdated(int256 status, uint64 updatedAt); + + /// Setup + function setUp() public { + // Deploys contracts + s_mockScrollL1CrossDomainMessenger = new MockScrollL1CrossDomainMessenger(); + s_mockScrollL2CrossDomainMessenger = new MockScrollL2CrossDomainMessenger(); + s_scrollSequencerUptimeFeed = new ScrollSequencerUptimeFeed( + s_l1OwnerAddr, + address(s_mockScrollL2CrossDomainMessenger), + false + ); + + // Sets mock sender in mock L2 messenger contract + s_mockScrollL2CrossDomainMessenger.setSender(s_l1OwnerAddr); + } +} + +contract ScrollSequencerUptimeFeed_Constructor is ScrollSequencerUptimeFeedTest { + /// @notice it should have been deployed with the correct initial state + function test_InitialState() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Checks L1 sender + address actualL1Addr = s_scrollSequencerUptimeFeed.l1Sender(); + assertEq(actualL1Addr, s_l1OwnerAddr); + + // Checks latest round data + (uint80 roundId, int256 answer, , , ) = s_scrollSequencerUptimeFeed.latestRoundData(); + assertEq(roundId, 1); + assertEq(answer, 0); + } +} + +contract ScrollSequencerUptimeFeed_UpdateStatus is ScrollSequencerUptimeFeedTest { + /// @notice it should revert if called by an address that is not the L2 Cross Domain Messenger + function test_RevertIfNotL2CrossDomainMessengerAddr() public { + // Sets msg.sender and tx.origin to an unauthorized address + vm.startPrank(s_strangerAddr, s_strangerAddr); + + // Tries to update the status from an unauthorized account + vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.InvalidSender.selector)); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(1)); + } + + /// @notice it should revert if called by an address that is not the L2 Cross Domain Messenger and is not the L1 sender + function test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() public { + // Sets msg.sender and tx.origin to an unauthorized address + vm.startPrank(s_strangerAddr, s_strangerAddr); + + // Sets mock sender in mock L2 messenger contract + s_mockScrollL2CrossDomainMessenger.setSender(s_strangerAddr); + + // Tries to update the status from an unauthorized account + vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.InvalidSender.selector)); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(1)); + } + + /// @notice it should update status when status has not changed and incoming timestamp is the same as latest + function test_UpdateStatusWhenNoChange() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Fetches the latest timestamp + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp(); + + // Submits a status update + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Stores the current round data before updating it + ( + uint80 roundIdBeforeUpdate, + int256 answerBeforeUpdate, + uint256 startedAtBeforeUpdate, + , + uint80 answeredInRoundBeforeUpdate + ) = s_scrollSequencerUptimeFeed.latestRoundData(); + + // Submit another status update with the same status + vm.expectEmit(); + emit RoundUpdated(1, uint64(block.timestamp)); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp + 200)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Stores the current round data after updating it + ( + uint80 roundIdAfterUpdate, + int256 answerAfterUpdate, + uint256 startedAtAfterUpdate, + uint256 updatedAtAfterUpdate, + uint80 answeredInRoundAfterUpdate + ) = s_scrollSequencerUptimeFeed.latestRoundData(); + + // Verifies the latest round data has been properly updated + assertEq(roundIdAfterUpdate, roundIdBeforeUpdate); + assertEq(answerAfterUpdate, answerBeforeUpdate); + assertEq(startedAtAfterUpdate, startedAtBeforeUpdate); + assertEq(answeredInRoundAfterUpdate, answeredInRoundBeforeUpdate); + assertEq(updatedAtAfterUpdate, block.timestamp); + } + + /// @notice it should update status when status has changed and incoming timestamp is newer than the latest + function test_UpdateStatusWhenStatusChangeAndTimeChange() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Submits a status update + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp(); + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Submit another status update, different status, newer timestamp should update + timestamp = timestamp + 200; + vm.expectEmit(); + emit AnswerUpdated(0, 3, timestamp); + s_scrollSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 0); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + } + + /// @notice it should update status when status has changed and incoming timestamp is the same as latest + function test_UpdateStatusWhenStatusChangeAndNoTimeChange() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Fetches the latest timestamp + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp(); + + // Submits a status update + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Submit another status update, different status, same timestamp should update + vm.expectEmit(); + emit AnswerUpdated(0, 3, timestamp); + s_scrollSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 0); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + } + + /// @notice it should ignore out-of-order updates + function test_IgnoreOutOfOrderUpdates() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Submits a status update + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 10000; + vm.expectEmit(); + emit AnswerUpdated(1, 2, timestamp); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 1); + assertEq(s_scrollSequencerUptimeFeed.latestTimestamp(), uint64(timestamp)); + + // Update with different status, but stale timestamp, should be ignored + timestamp = timestamp - 1000; + vm.expectEmit(false, false, false, false); + emit UpdateIgnored(true, 0, true, 0); // arguments are dummy values + // TODO: how can we check that an AnswerUpdated event was NOT emitted + s_scrollSequencerUptimeFeed.updateStatus(false, uint64(timestamp)); + } +} + +contract ScrollSequencerUptimeFeed_AggregatorV3Interface is ScrollSequencerUptimeFeedTest { + /// @notice it should return valid answer from getRoundData and latestRoundData + function test_AggregatorV3Interface() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables + uint80 roundId; + int256 answer; + uint256 startedAt; + uint256 updatedAt; + uint80 answeredInRound; + + // Checks initial state + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_scrollSequencerUptimeFeed.latestRoundData(); + assertEq(roundId, 1); + assertEq(answer, 0); + assertEq(answeredInRound, roundId); + assertEq(startedAt, updatedAt); + + // Submits status update with different status and newer timestamp, should update + uint256 timestamp = startedAt + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_scrollSequencerUptimeFeed.getRoundData(2); + assertEq(roundId, 2); + assertEq(answer, 1); + assertEq(answeredInRound, roundId); + assertEq(startedAt, timestamp); + assertLe(updatedAt, startedAt); + + // Saves round 2 data + uint80 roundId2 = roundId; + int256 answer2 = answer; + uint256 startedAt2 = startedAt; + uint256 updatedAt2 = updatedAt; + uint80 answeredInRound2 = answeredInRound; + + // Checks that last round is still returning the correct data + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_scrollSequencerUptimeFeed.getRoundData(1); + assertEq(roundId, 1); + assertEq(answer, 0); + assertEq(answeredInRound, roundId); + assertEq(startedAt, updatedAt); + + // Assert latestRoundData corresponds to latest round id + (roundId, answer, startedAt, updatedAt, answeredInRound) = s_scrollSequencerUptimeFeed.latestRoundData(); + assertEq(roundId2, roundId); + assertEq(answer2, answer); + assertEq(startedAt2, startedAt); + assertEq(updatedAt2, updatedAt); + assertEq(answeredInRound2, answeredInRound); + } + + /// @notice it should revert from #getRoundData when round does not yet exist (future roundId) + function test_RevertGetRoundDataWhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.NoDataPresent.selector)); + s_scrollSequencerUptimeFeed.getRoundData(2); + } + + /// @notice it should revert from #getAnswer when round does not yet exist (future roundId) + function test_RevertGetAnswerWhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.NoDataPresent.selector)); + s_scrollSequencerUptimeFeed.getAnswer(2); + } + + /// @notice it should revert from #getTimestamp when round does not yet exist (future roundId) + function test_RevertGetTimestampWhenRoundDoesNotExistYet() public { + // Sets msg.sender and tx.origin to a valid address + vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); + + // Gets data from a round that has not happened yet + vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.NoDataPresent.selector)); + s_scrollSequencerUptimeFeed.getTimestamp(2); + } +} + +contract ScrollSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions is ScrollSequencerUptimeFeedTest { + /// @notice it should disallow reads on AggregatorV2V3Interface functions when consuming contract is not whitelisted + function test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() public { + // Deploys a FeedConsumer contract + FeedConsumer feedConsumer = new FeedConsumer(address(s_scrollSequencerUptimeFeed)); + + // Sanity - consumer is not whitelisted + assertEq(s_scrollSequencerUptimeFeed.checkEnabled(), true); + assertEq(s_scrollSequencerUptimeFeed.hasAccess(address(feedConsumer), abi.encode("")), false); + + // Asserts reads are not possible from consuming contract + vm.expectRevert("No access"); + feedConsumer.latestAnswer(); + vm.expectRevert("No access"); + feedConsumer.latestRoundData(); + } + + /// @notice it should allow reads on AggregatorV2V3Interface functions when consuming contract is whitelisted + function test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() public { + // Deploys a FeedConsumer contract + FeedConsumer feedConsumer = new FeedConsumer(address(s_scrollSequencerUptimeFeed)); + + // Whitelist consumer + s_scrollSequencerUptimeFeed.addAccess(address(feedConsumer)); + + // Sanity - consumer is whitelisted + assertEq(s_scrollSequencerUptimeFeed.checkEnabled(), true); + assertEq(s_scrollSequencerUptimeFeed.hasAccess(address(feedConsumer), abi.encode("")), true); + + // Asserts reads are possible from consuming contract + (uint80 roundId, int256 answer, , , ) = feedConsumer.latestRoundData(); + assertEq(feedConsumer.latestAnswer(), 0); + assertEq(roundId, 1); + assertEq(answer, 0); + } +} + +contract ScrollSequencerUptimeFeed_GasCosts is ScrollSequencerUptimeFeedTest { + /// @notice it should consume a known amount of gas for updates + function test_GasCosts() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Assert initial conditions + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp(); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 0); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed; + uint256 gasStart; + uint256 gasFinal; + + // measures gas used for no update + expectedGasUsed = 10197; // NOTE: used to be 38594 in hardhat tests + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.updateStatus(false, uint64(timestamp + 1000)); + gasFinal = gasleft(); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 0); + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + + // measures gas used for update + expectedGasUsed = 31644; // NOTE: used to be 58458 in hardhat tests + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp + 1000)); + gasFinal = gasleft(); + assertEq(s_scrollSequencerUptimeFeed.latestAnswer(), 1); + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } +} + +contract ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts is ScrollSequencerUptimeFeedTest { + /// @notice it should consume a known amount of gas for getRoundData(uint80) + function test_GasUsageForGetRoundData() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 4504; // NOTE: used to be 30952 in hardhat tesst + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.getRoundData(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestRoundData() + function test_GasUsageForLatestRoundData() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 2154; // NOTE: used to be 28523 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.latestRoundData(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestAnswer() + function test_GasUsageForLatestAnswer() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1566; // NOTE: used to be 28229 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.latestAnswer(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestTimestamp() + function test_GasUsageForLatestTimestamp() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1459; // NOTE: used to be 28129 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.latestTimestamp(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for latestRound() + function test_GasUsageForLatestRound() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 1470; // NOTE: used to be 28145 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.latestRound(); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for getAnswer() + function test_GasUsageForGetAnswer() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 3929; // NOTE: used to be 30682 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.getAnswer(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } + + /// @notice it should consume a known amount of gas for getTimestamp() + function test_GasUsageForGetTimestamp() public { + // Sets msg.sender and tx.origin to a valid address + address l2MessengerAddr = address(s_mockScrollL2CrossDomainMessenger); + vm.startPrank(l2MessengerAddr, l2MessengerAddr); + + // Defines helper variables for measuring gas usage + uint256 expectedGasUsed = 3817; // NOTE: used to be 30570 in hardhat tests + uint256 gasStart; + uint256 gasFinal; + + // Initializes a round + uint256 timestamp = s_scrollSequencerUptimeFeed.latestTimestamp() + 1000; + s_scrollSequencerUptimeFeed.updateStatus(true, uint64(timestamp)); + + // Measures gas usage + gasStart = gasleft(); + s_scrollSequencerUptimeFeed.getTimestamp(1); + gasFinal = gasleft(); + + // Checks that gas usage is within expected range + assertGasUsageIsCloseTo(expectedGasUsed, gasStart, gasFinal, GAS_USED_DEVIATION); + } +} diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollValidator.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollValidator.t.sol new file mode 100644 index 00000000000..969c78c72ef --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollValidator.t.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {MockScrollL1CrossDomainMessenger} from "../../mocks/scroll/MockScrollL1CrossDomainMessenger.sol"; +import {MockScrollL2CrossDomainMessenger} from "../../mocks/scroll/MockScrollL2CrossDomainMessenger.sol"; +import {ScrollSequencerUptimeFeed} from "../../../dev/scroll/ScrollSequencerUptimeFeed.sol"; +import {ScrollValidator} from "../../../dev/scroll/ScrollValidator.sol"; +import {L2EPTest} from "../L2EPTest.t.sol"; + +contract ScrollValidatorTest is L2EPTest { + /// Helper constants + address internal constant L2_SEQ_STATUS_RECORDER_ADDRESS = 0x491B1dDA0A8fa069bbC1125133A975BF4e85a91b; + uint32 internal constant INIT_GAS_LIMIT = 1900000; + + /// L2EP contracts + MockScrollL1CrossDomainMessenger internal s_mockScrollL1CrossDomainMessenger; + MockScrollL2CrossDomainMessenger internal s_mockScrollL2CrossDomainMessenger; + ScrollSequencerUptimeFeed internal s_scrollSequencerUptimeFeed; + ScrollValidator internal s_scrollValidator; + + /// https://github.com/scroll-tech/scroll/blob/03089eaeee1193ff44c532c7038611ae123e7ef3/contracts/src/libraries/IScrollMessenger.sol#L22 + event SentMessage( + address indexed sender, + address indexed target, + uint256 value, + uint256 messageNonce, + uint256 gasLimit, + bytes message + ); + + /// Setup + function setUp() public { + s_mockScrollL1CrossDomainMessenger = new MockScrollL1CrossDomainMessenger(); + s_mockScrollL2CrossDomainMessenger = new MockScrollL2CrossDomainMessenger(); + + s_scrollSequencerUptimeFeed = new ScrollSequencerUptimeFeed( + address(s_mockScrollL1CrossDomainMessenger), + address(s_mockScrollL2CrossDomainMessenger), + true + ); + + s_scrollValidator = new ScrollValidator( + address(s_mockScrollL1CrossDomainMessenger), + address(s_scrollSequencerUptimeFeed), + INIT_GAS_LIMIT + ); + } +} + +contract ScrollValidator_SetGasLimit is ScrollValidatorTest { + /// @notice it correctly updates the gas limit + function test_CorrectlyUpdatesTheGasLimit() public { + uint32 newGasLimit = 2000000; + assertEq(s_scrollValidator.getGasLimit(), INIT_GAS_LIMIT); + s_scrollValidator.setGasLimit(newGasLimit); + assertEq(s_scrollValidator.getGasLimit(), newGasLimit); + } +} + +contract ScrollValidator_Validate is ScrollValidatorTest { + /// @notice it reverts if called by account with no access + function test_RevertsIfCalledByAnAccountWithNoAccess() public { + vm.startPrank(s_strangerAddr); + vm.expectRevert("No access"); + s_scrollValidator.validate(0, 0, 1, 1); + } + + /// @notice it posts sequencer status when there is not status change + function test_PostSequencerStatusWhenThereIsNotStatusChange() public { + // Gives access to the s_eoaValidator + s_scrollValidator.addAccess(s_eoaValidator); + + // Sets block.timestamp to a later date + uint256 futureTimestampInSeconds = block.timestamp + 5000; + vm.startPrank(s_eoaValidator); + vm.warp(futureTimestampInSeconds); + + // Sets up the expected event data + vm.expectEmit(false, false, false, true); + emit SentMessage( + address(s_scrollValidator), // sender + L2_SEQ_STATUS_RECORDER_ADDRESS, // target + 0, // value + 0, // nonce + INIT_GAS_LIMIT, // gas limit + abi.encodeWithSelector(ScrollSequencerUptimeFeed.updateStatus.selector, false, futureTimestampInSeconds) // message + ); + + // Runs the function (which produces the event to test) + s_scrollValidator.validate(0, 0, 0, 0); + } + + /// @notice it post sequencer offline + function test_PostSequencerOffline() public { + // Gives access to the s_eoaValidator + s_scrollValidator.addAccess(s_eoaValidator); + + // Sets block.timestamp to a later date + uint256 futureTimestampInSeconds = block.timestamp + 10000; + vm.startPrank(s_eoaValidator); + vm.warp(futureTimestampInSeconds); + + // Sets up the expected event data + vm.expectEmit(false, false, false, true); + emit SentMessage( + address(s_scrollValidator), // sender + L2_SEQ_STATUS_RECORDER_ADDRESS, // target + 0, // value + 0, // nonce + INIT_GAS_LIMIT, // gas limit + abi.encodeWithSelector(ScrollSequencerUptimeFeed.updateStatus.selector, true, futureTimestampInSeconds) // message + ); + + // Runs the function (which produces the event to test) + s_scrollValidator.validate(0, 0, 1, 1); + } +} diff --git a/contracts/src/v0.8/tests/MockArbitrumInbox.sol b/contracts/src/v0.8/tests/MockArbitrumInbox.sol index cd85ed4d6ea..445a361b309 100644 --- a/contracts/src/v0.8/tests/MockArbitrumInbox.sol +++ b/contracts/src/v0.8/tests/MockArbitrumInbox.sol @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + import {IInbox} from "../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol"; import {IBridge} from "../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol"; @@ -13,46 +16,46 @@ contract MockArbitrumInbox is IInbox { bytes data ); - function sendL2Message(bytes calldata messageData) external override returns (uint256) { + function sendL2Message(bytes calldata /* messageData */) external pure override returns (uint256) { return 0; } function sendUnsignedTransaction( - uint256 maxGas, - uint256 gasPriceBid, - uint256 nonce, - address destAddr, - uint256 amount, - bytes calldata data - ) external override returns (uint256) { + uint256 /* maxGas */, + uint256 /* gasPriceBid */, + uint256 /* nonce */, + address /* destAddr */, + uint256 /* amount */, + bytes calldata /* data */ + ) external pure override returns (uint256) { return 0; } function sendContractTransaction( - uint256 maxGas, - uint256 gasPriceBid, - address destAddr, - uint256 amount, - bytes calldata data - ) external override returns (uint256) { + uint256 /* maxGas */, + uint256 /* gasPriceBid */, + address /* destAddr */, + uint256 /* amount */, + bytes calldata /* data */ + ) external pure override returns (uint256) { return 0; } function sendL1FundedUnsignedTransaction( - uint256 maxGas, - uint256 gasPriceBid, - uint256 nonce, - address destAddr, - bytes calldata data + uint256 /* maxGas */, + uint256 /* gasPriceBid */, + uint256 /* nonce */, + address /* destAddr */, + bytes calldata /* data */ ) external payable override returns (uint256) { return 0; } function sendL1FundedContractTransaction( - uint256 maxGas, - uint256 gasPriceBid, - address destAddr, - bytes calldata data + uint256 /* maxGas */, + uint256 /* gasPriceBid */, + address /* destAddr */, + bytes calldata /* data */ ) external payable override returns (uint256) { return 0; } @@ -81,32 +84,32 @@ contract MockArbitrumInbox is IInbox { } function createRetryableTicket( - address destAddr, - uint256 arbTxCallValue, - uint256 maxSubmissionCost, - address submissionRefundAddress, - address valueRefundAddress, - uint256 maxGas, - uint256 gasPriceBid, - bytes calldata data + address /* destAddr */, + uint256 /* arbTxCallValue */, + uint256 /* maxSubmissionCost */, + address /* submissionRefundAddress */, + address /* valueRefundAddress */, + uint256 /* maxGas */, + uint256 /* gasPriceBid */, + bytes calldata /* data */ ) external payable override returns (uint256) { return 0; } - function depositEth(address destAddr) external payable override returns (uint256) { + function depositEth(address /* destAddr */) external payable override returns (uint256) { return 0; } function depositEthRetryable( - address destAddr, - uint256 maxSubmissionCost, - uint256 maxGas, - uint256 maxGasPrice + address /* destAddr */, + uint256 /* maxSubmissionCost */, + uint256 /* maxGas */, + uint256 /* maxGasPrice */ ) external payable override returns (uint256) { return 0; } - function bridge() external view override returns (IBridge) { + function bridge() external pure override returns (IBridge) { return IBridge(address(0)); } diff --git a/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol b/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol index 3184e7bb4a5..a92ff8fb556 100644 --- a/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol +++ b/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol @@ -8,7 +8,7 @@ contract MockOptimismL1CrossDomainMessenger is IL1CrossDomainMessenger { uint256 private s_nonce; // slither-disable-next-line external-function - function xDomainMessageSender() public view returns (address) { + function xDomainMessageSender() public pure returns (address) { return address(0); } diff --git a/contracts/src/v0.8/vendor/@eth-optimism/contracts/v0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol b/contracts/src/v0.8/vendor/@eth-optimism/contracts/v0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol index 6d74788e352..8b5aad82e5d 100644 --- a/contracts/src/v0.8/vendor/@eth-optimism/contracts/v0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol +++ b/contracts/src/v0.8/vendor/@eth-optimism/contracts/v0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9.0; /** @@ -28,9 +29,5 @@ interface iOVM_CrossDomainMessenger { * @param _message Message to send to the target. * @param _gasLimit Gas limit for the provided message. */ - function sendMessage( - address _target, - bytes calldata _message, - uint32 _gasLimit - ) external; + function sendMessage(address _target, bytes calldata _message, uint32 _gasLimit) external; } diff --git a/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts b/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts index b294032e73d..1d93497b9fa 100644 --- a/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts +++ b/contracts/test/v0.8/dev/ScrollSequencerUptimeFeed.test.ts @@ -21,7 +21,7 @@ describe('ScrollSequencerUptimeFeed', () => { dummy = accounts[3] const l2CrossDomainMessengerFactory = await ethers.getContractFactory( - 'src/v0.8/l2ep/test/mocks/MockScrollL2CrossDomainMessenger.sol:MockScrollL2CrossDomainMessenger', + 'src/v0.8/l2ep/test/mocks/scroll/MockScrollL2CrossDomainMessenger.sol:MockScrollL2CrossDomainMessenger', deployer, ) diff --git a/contracts/test/v0.8/dev/ScrollValidator.test.ts b/contracts/test/v0.8/dev/ScrollValidator.test.ts index 866d52b202f..c5ec59c5c99 100644 --- a/contracts/test/v0.8/dev/ScrollValidator.test.ts +++ b/contracts/test/v0.8/dev/ScrollValidator.test.ts @@ -30,7 +30,7 @@ describe('ScrollValidator', () => { // Scroll Messenger contract on L1 const mockScrollL1CrossDomainMessengerFactory = await ethers.getContractFactory( - 'src/v0.8/l2ep/test/mocks/MockScrollL1CrossDomainMessenger.sol:MockScrollL1CrossDomainMessenger', + 'src/v0.8/l2ep/test/mocks/scroll/MockScrollL1CrossDomainMessenger.sol:MockScrollL1CrossDomainMessenger', ) mockScrollL1CrossDomainMessenger = await mockScrollL1CrossDomainMessengerFactory.deploy() From f8a47b97500eaa91fdee7170d337d8a759999020 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski <32773226+smickovskid@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:34:40 +0100 Subject: [PATCH 177/234] Added Liea support (#11784) --- .github/workflows/automation-benchmark-tests.yml | 1 + integration-tests/benchmark/keeper_test.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index efe6d2eb59d..b2e5ad1638e 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -26,6 +26,7 @@ on: - SEPOLIA - BASE_GOERLI - ARBITRUM_SEPOLIA + - LINEA_GOERLI TestInputs: description: TestInputs required: false diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 6d398c685e0..015dff1126c 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -295,6 +295,12 @@ var networkConfig = map[string]NetworkConfig{ deltaStage: 20 * time.Second, funding: big.NewFloat(ChainlinkNodeFunding), }, + "LineaGoerli": { + upkeepSLA: int64(120), + blockTime: time.Second, + deltaStage: 20 * time.Second, + funding: big.NewFloat(ChainlinkNodeFunding), + }, } func getEnv(key, fallback string) string { From b1640404ab73530946bdead5db7e29cc93ed6d8e Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 22 Jan 2024 14:00:30 +0000 Subject: [PATCH 178/234] pipeline/task.bridge: do not cache invalid external adapter response objects. (#11725) * pipeline/task.bridge: check for external adapter status in response object * pipeline/task.bridge: external adapter response object tests * pipeline: move ea specific utilities into an internal package and add unit tests * pipeline/task.bridge: rebase fix for commonconfig * Update core/services/pipeline/internal/eautils/eautils.go Co-authored-by: Jordan Krage * pipeline/task.bridge: ea status error can also be an object --------- Co-authored-by: Jordan Krage --- .../pipeline/internal/eautils/eautils.go | 39 +++++ .../pipeline/internal/eautils/eautils_test.go | 61 ++++++++ core/services/pipeline/task.bridge.go | 9 +- core/services/pipeline/task.bridge_test.go | 139 +++++++++++++++++- core/services/pipeline/task.http_test.go | 7 +- 5 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 core/services/pipeline/internal/eautils/eautils.go create mode 100644 core/services/pipeline/internal/eautils/eautils_test.go diff --git a/core/services/pipeline/internal/eautils/eautils.go b/core/services/pipeline/internal/eautils/eautils.go new file mode 100644 index 00000000000..30faa826b22 --- /dev/null +++ b/core/services/pipeline/internal/eautils/eautils.go @@ -0,0 +1,39 @@ +package eautils + +import ( + "encoding/json" + "net/http" +) + +type AdapterStatus struct { + ErrorMessage *string `json:"errorMessage"` + Error any `json:"error"` + StatusCode *int `json:"statusCode"` + ProviderStatusCode *int `json:"providerStatusCode"` +} + +func BestEffortExtractEAStatus(responseBytes []byte) (code int, ok bool) { + var status AdapterStatus + err := json.Unmarshal(responseBytes, &status) + if err != nil { + return 0, false + } + + if status.StatusCode == nil { + return 0, false + } + + if *status.StatusCode != http.StatusOK { + return *status.StatusCode, true + } + + if status.ProviderStatusCode != nil && *status.ProviderStatusCode != http.StatusOK { + return *status.ProviderStatusCode, true + } + + if status.Error != nil { + return http.StatusInternalServerError, true + } + + return *status.StatusCode, true +} diff --git a/core/services/pipeline/internal/eautils/eautils_test.go b/core/services/pipeline/internal/eautils/eautils_test.go new file mode 100644 index 00000000000..80183b80d2b --- /dev/null +++ b/core/services/pipeline/internal/eautils/eautils_test.go @@ -0,0 +1,61 @@ +package eautils + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBestEffortExtractEAStatus(t *testing.T) { + tests := []struct { + name string + arg []byte + expectCode int + expectOk bool + }{ + { + name: "invalid object", + arg: []byte(`{"error": "invalid json object" `), + expectCode: 0, + expectOk: false, + }, + { + name: "no status code in object", + arg: []byte(`{}`), + expectCode: 0, + expectOk: false, + }, + { + name: "invalid status code", + arg: []byte(`{"statusCode":400}`), + expectCode: http.StatusBadRequest, + expectOk: true, + }, + { + name: "invalid provider status code", + arg: []byte(`{"statusCode":200, "providerStatusCode":500}`), + expectCode: http.StatusInternalServerError, + expectOk: true, + }, + { + name: "valid statuses with error message", + arg: []byte(`{"statusCode":200, "providerStatusCode":200, "error": "unexpected error"}`), + expectCode: http.StatusInternalServerError, + expectOk: true, + }, + { + name: "valid status code", + arg: []byte(`{"statusCode":200}`), + expectCode: http.StatusOK, + expectOk: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + code, ok := BestEffortExtractEAStatus(tt.arg) + assert.Equal(t, tt.expectCode, code) + assert.Equal(t, tt.expectOk, ok) + }) + } +} diff --git a/core/services/pipeline/task.bridge.go b/core/services/pipeline/task.bridge.go index f9490ea791d..1da34d19134 100644 --- a/core/services/pipeline/task.bridge.go +++ b/core/services/pipeline/task.bridge.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/internal/eautils" ) // NOTE: These metrics generate a new label per bridge, this should be safe @@ -167,7 +168,13 @@ func (t *BridgeTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, inp var cachedResponse bool responseBytes, statusCode, headers, elapsed, err := makeHTTPRequest(requestCtx, lggr, "POST", url, reqHeaders, requestData, t.httpClient, t.config.DefaultHTTPLimit()) - if err != nil { + + // check for external adapter response object status + if code, ok := eautils.BestEffortExtractEAStatus(responseBytes); ok { + statusCode = code + } + + if err != nil || statusCode != http.StatusOK { promBridgeErrors.WithLabelValues(t.Name).Inc() if cacheTTL == 0 { return Result{Error: err}, RunInfo{IsRetryable: isRetryableHTTPError(statusCode, err)} diff --git a/core/services/pipeline/task.bridge_test.go b/core/services/pipeline/task.bridge_test.go index bf1e63d6314..7673add1e35 100644 --- a/core/services/pipeline/task.bridge_test.go +++ b/core/services/pipeline/task.bridge_test.go @@ -19,7 +19,6 @@ import ( "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -32,6 +31,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/internal/eautils" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -59,11 +59,43 @@ type adapterResponseData struct { // adapterResponse is the HTTP response as defined by the external adapter: // https://github.com/smartcontractkit/bnc-adapter type adapterResponse struct { - Data adapterResponseData `json:"data"` - ErrorMessage null.String `json:"errorMessage"` + eautils.AdapterStatus + Data adapterResponseData `json:"data"` } -func (pr adapterResponse) Result() *decimal.Decimal { +func (pr *adapterResponse) SetStatusCode(code int) { + pr.StatusCode = &code +} + +func (pr *adapterResponse) UnsetStatusCode() { + pr.StatusCode = nil +} + +func (pr *adapterResponse) SetProviderStatusCode(code int) { + pr.ProviderStatusCode = &code +} + +func (pr *adapterResponse) UnsetProviderStatusCode() { + pr.ProviderStatusCode = nil +} + +func (pr *adapterResponse) SetError(msg string) { + pr.Error = msg +} + +func (pr *adapterResponse) UnsetError() { + pr.Error = nil +} + +func (pr *adapterResponse) SetErrorMessage(msg string) { + pr.ErrorMessage = &msg +} + +func (pr *adapterResponse) UnsetErrorMessage() { + pr.ErrorMessage = nil +} + +func (pr *adapterResponse) Result() *decimal.Decimal { return pr.Data.Result } @@ -295,7 +327,7 @@ func TestBridgeTask_DoesNotReturnStaleResults(t *testing.T) { task.HelperSetDependencies(cfg.JobPipeline(), cfg.WebServer(), orm, specID, uuid.UUID{}, c) // Insert entry 1m in the past, stale value, should not be used in case of EA failure. - err = queryer.ExecQ(`INSERT INTO bridge_last_value(dot_id, spec_id, value, finished_at) + err = queryer.ExecQ(`INSERT INTO bridge_last_value(dot_id, spec_id, value, finished_at) VALUES($1, $2, $3, $4) ON CONFLICT ON CONSTRAINT bridge_last_value_pkey DO UPDATE SET value = $3, finished_at = $4;`, task.DotID(), specID, big.NewInt(9700).Bytes(), time.Now().Add(-1*time.Minute)) require.NoError(t, err) @@ -786,9 +818,10 @@ func TestBridgeTask_ErrorMessage(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusTooManyRequests) - err := json.NewEncoder(w).Encode(adapterResponse{ - ErrorMessage: null.StringFrom("could not hit data fetcher"), - }) + + resp := &adapterResponse{} + resp.SetErrorMessage("could not hit data fetcher") + err := json.NewEncoder(w).Encode(resp) require.NoError(t, err) }) @@ -1016,3 +1049,93 @@ func TestBridgeTask_Headers(t *testing.T) { assert.Equal(t, []string{"Content-Length", "38", "Content-Type", "footype", "User-Agent", "Go-http-client/1.1", "X-Header-1", "foo", "X-Header-2", "bar"}, allHeaders(headers)) }) } + +func TestBridgeTask_AdapterResponseStatusFailure(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c.WebServer.BridgeCacheTTL = commonconfig.MustNewDuration(1 * time.Minute) + }) + + testAdapterResponse := &adapterResponse{ + Data: adapterResponseData{Result: &decimal.Zero}, + } + + queryer := pg.NewQ(db, logger.TestLogger(t), cfg.Database()) + s1 := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + err := json.NewEncoder(w).Encode(testAdapterResponse) + require.NoError(t, err) + })) + defer s1.Close() + + feedURL, err := url.ParseRequestURI(s1.URL) + require.NoError(t, err) + + orm := bridges.NewORM(db, logger.TestLogger(t), cfg.Database()) + _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{URL: feedURL.String()}, cfg.Database()) + + task := pipeline.BridgeTask{ + BaseTask: pipeline.NewBaseTask(0, "bridge", nil, nil, 0), + Name: bridge.Name.String(), + RequestData: btcUSDPairing, + } + c := clhttptest.NewTestLocalOnlyHTTPClient() + trORM := pipeline.NewORM(db, logger.TestLogger(t), cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) + specID, err := trORM.CreateSpec(pipeline.Pipeline{}, *models.NewInterval(5 * time.Minute), pg.WithParentCtx(testutils.Context(t))) + require.NoError(t, err) + task.HelperSetDependencies(cfg.JobPipeline(), cfg.WebServer(), orm, specID, uuid.UUID{}, c) + + // Insert entry 1m in the past, stale value, should not be used in case of EA failure. + err = queryer.ExecQ(`INSERT INTO bridge_last_value(dot_id, spec_id, value, finished_at) + VALUES($1, $2, $3, $4) ON CONFLICT ON CONSTRAINT bridge_last_value_pkey + DO UPDATE SET value = $3, finished_at = $4;`, task.DotID(), specID, big.NewInt(9700).Bytes(), time.Now()) + require.NoError(t, err) + + vars := pipeline.NewVarsFrom( + map[string]interface{}{ + "jobRun": map[string]interface{}{ + "meta": map[string]interface{}{ + "shouldFail": true, + }, + }, + }, + ) + + // expect all external adapter response status failures to be served from the cache + testAdapterResponse.SetStatusCode(http.StatusBadRequest) + result, runInfo := task.Run(testutils.Context(t), logger.TestLogger(t), vars, nil) + + require.NoError(t, result.Error) + require.NotNil(t, result.Value) + require.False(t, runInfo.IsRetryable) + require.False(t, runInfo.IsPending) + + testAdapterResponse.SetStatusCode(http.StatusOK) + testAdapterResponse.SetProviderStatusCode(http.StatusBadRequest) + result, runInfo = task.Run(testutils.Context(t), logger.TestLogger(t), vars, nil) + + require.NoError(t, result.Error) + require.NotNil(t, result.Value) + require.False(t, runInfo.IsRetryable) + require.False(t, runInfo.IsPending) + + testAdapterResponse.SetStatusCode(http.StatusOK) + testAdapterResponse.SetProviderStatusCode(http.StatusOK) + testAdapterResponse.SetError("some error") + result, runInfo = task.Run(testutils.Context(t), logger.TestLogger(t), vars, nil) + + require.NoError(t, result.Error) + require.NotNil(t, result.Value) + require.False(t, runInfo.IsRetryable) + require.False(t, runInfo.IsPending) + + testAdapterResponse.SetStatusCode(http.StatusInternalServerError) + result, runInfo = task.Run(testutils.Context(t), logger.TestLogger(t), vars, nil) + + require.NoError(t, result.Error) + require.NotNil(t, result.Value) + require.False(t, runInfo.IsRetryable) + require.False(t, runInfo.IsPending) +} diff --git a/core/services/pipeline/task.http_test.go b/core/services/pipeline/task.http_test.go index c0dd93df430..36ccc147a78 100644 --- a/core/services/pipeline/task.http_test.go +++ b/core/services/pipeline/task.http_test.go @@ -14,7 +14,6 @@ import ( "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -264,9 +263,9 @@ func TestHTTPTask_ErrorMessage(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusTooManyRequests) - err := json.NewEncoder(w).Encode(adapterResponse{ - ErrorMessage: null.StringFrom("could not hit data fetcher"), - }) + resp := &adapterResponse{} + resp.SetErrorMessage("could not hit data fetcher") + err := json.NewEncoder(w).Encode(resp) require.NoError(t, err) }) From 41f2497830959a79513d3e80bd5195bdef8b950b Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:21:06 +0100 Subject: [PATCH 179/234] Fix RPC name override (#11813) * Fix rpc name override * use configured name without additional checks * multi-node metrics test * Fix typo --- core/chains/legacyevm/chain.go | 6 +- testdata/scripts/metrics/multi-node.txtar | 78 +++++++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 testdata/scripts/metrics/multi-node.txtar diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 0e0e1e65aca..92936299cdb 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -474,15 +474,13 @@ func newEthClientFromCfg(cfg evmconfig.NodePool, noNewHeadsThreshold time.Durati var sendonlys []commonclient.SendOnlyNode[*big.Int, evmclient.RPCCLient] for i, node := range nodes { if node.SendOnly != nil && *node.SendOnly { - name := fmt.Sprintf("eth-sendonly-rpc-%d", i) - rpc := evmclient.NewRPCClient(lggr, empty, (*url.URL)(node.HTTPURL), name, int32(i), chainID, + rpc := evmclient.NewRPCClient(lggr, empty, (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, commonclient.Secondary) sendonly := commonclient.NewSendOnlyNode[*big.Int, evmclient.RPCCLient](lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) sendonlys = append(sendonlys, sendonly) } else { - name := fmt.Sprintf("eth-primary-rpc-%d", i) - rpc := evmclient.NewRPCClient(lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), name, int32(i), + rpc := evmclient.NewRPCClient(lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, commonclient.Primary) primaryNode := commonclient.NewNode[*big.Int, *evmtypes.Head, evmclient.RPCCLient](cfg, noNewHeadsThreshold, lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, *node.Order, diff --git a/testdata/scripts/metrics/multi-node.txtar b/testdata/scripts/metrics/multi-node.txtar new file mode 100644 index 00000000000..c3928160443 --- /dev/null +++ b/testdata/scripts/metrics/multi-node.txtar @@ -0,0 +1,78 @@ +# Check that metrics specified in the expected_metrics are present in /metrics response +# start node +exec sh -c 'eval "echo \"$(cat config.toml.tmpl)\" > config.toml"' +exec chainlink node -c config.toml start -p password -a creds & + +# ensure node is up and running +env NODEURL=http://localhost:$PORT +exec curl --retry 10 --retry-max-time 60 --retry-connrefused $NODEURL + + +# Check +chmod 700 ./script.sh +exec sh -c './script.sh' + +-- script.sh -- + +maxRetries=5 +for retriesNum in $(seq 1 $maxRetries); do + passedAllChecks=true + curl $NODEURL/metrics > metrics.txt + while IFS= read -r expectedMetric; do + grep -q $expectedMetric metrics.txt && continue + + if [[ $retriesNum -ge $maxRetries ]]; then + cat metrics.txt + echo "FAIL Expected metric $expectedMetric to be present in GET /metrics response" + exit 1 + fi + + echo "Metric $expectedMetric is not present in GET /metrics response - retrying after 5s" + passedAllChecks=false + sleep 5 + break + done < expected_metrics.txt + + $passedAllChecks && break +done + +-- testdb.txt -- +CL_DATABASE_URL +-- testport.txt -- +PORT + +-- password -- +T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ +-- creds -- +notreal@fakeemail.ch +fj293fbBnlQ!f9vNs + +-- config.toml.tmpl -- +[Webserver] +HTTPPort = $PORT + +[[EVM]] +ChainID = '68472' + +[[EVM.Nodes]] +Name = 'BlueEVMPrimaryNode' +WSURL = 'wss://primaryfoo.bar/ws' +HTTPURL = 'https://primaryfoo.bar' + +[[EVM.Nodes]] +Name = 'YellowEVMPrimaryNode' +WSURL = 'wss://sendonlyfoo.bar/ws' +HTTPURL = 'https://sendonlyfoo.bar' +SendOnly = true + +-- expected_metrics.txt -- +evm_pool_rpc_node_dials_total{evmChainID="68472",nodeName="BlueEVMPrimaryNode"} +evm_pool_rpc_node_dials_total{evmChainID="68472",nodeName="YellowEVMPrimaryNode"} +multi_node_states{chainId="68472",network="EVM",state="Alive"} +multi_node_states{chainId="68472",network="EVM",state="Closed"} +multi_node_states{chainId="68472",network="EVM",state="Dialed"} +multi_node_states{chainId="68472",network="EVM",state="InvalidChainID"} +multi_node_states{chainId="68472",network="EVM",state="OutOfSync"} +multi_node_states{chainId="68472",network="EVM",state="Undialed"} +multi_node_states{chainId="68472",network="EVM",state="Unreachable"} +multi_node_states{chainId="68472",network="EVM",state="Unusable"} \ No newline at end of file From f0543e6d8ad919e2f97fd074b316de167ecdbb98 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Mon, 22 Jan 2024 16:16:43 +0100 Subject: [PATCH 180/234] labels for automatic chaos testing (#11830) --- .../templates/chainlink-db-deployment.yaml | 8 ++++++ .../templates/chainlink-node-deployment.yaml | 25 ++++++++++++++----- .../templates/geth-deployment.yaml | 8 ++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/charts/chainlink-cluster/templates/chainlink-db-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-db-deployment.yaml index f335130ea9f..91924ba5005 100644 --- a/charts/chainlink-cluster/templates/chainlink-db-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-db-deployment.yaml @@ -24,12 +24,20 @@ spec: selector: matchLabels: app: {{ $.Release.Name }}-db + # Used for testing. + # havoc-component-group and havoc-network-group are used by "havoc" chaos testing tool + havoc-component-group: db + havoc-network-group: db instance: {{ $cfg.name }}-db release: {{ $.Release.Name }} template: metadata: labels: app: {{ $.Release.Name }}-db + # Used for testing. + # havoc-component-group and havoc-network-group are used by "havoc" chaos testing tool + havoc-component-group: db + havoc-network-group: db instance: {{ $cfg.name }}-db release: {{ $.Release.Name }} {{- range $key, $value := $.Values.labels }} diff --git a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml index 884cf0e535b..0ce16fd475b 100644 --- a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ $.Release.Name }}-{{ $cfg.name }} + name: {{ if eq $index 0 }}{{ $.Release.Name }}-{{ $cfg.name }}-bootstrap{{ else }}{{ $.Release.Name }}-{{ $cfg.name }}{{ end }} spec: strategy: # Need to recreate the pod to deal with lease lock held by old pod. @@ -10,18 +10,31 @@ spec: selector: matchLabels: app: {{ $.Release.Name }} + # Used for testing. + # havoc-component-group and havoc-network-group are used by "havoc" chaos testing tool + {{ if eq $index 0 }}{{ else }} + havoc-component-group: node + {{ end }} + {{ if eq $index 0 }}{{ else }} + havoc-network-group: {{ if gt $index 2 }}"1"{{ else }}"2"{{ end }} + {{ end }} instance: {{ $cfg.name }} release: {{ $.Release.Name }} template: metadata: labels: app: {{ $.Release.Name }} + # Used for testing. + # havoc-component-group and havoc-network-group are used by "havoc" chaos testing tool + {{ if eq $index 0 }}{{ else }} + havoc-component-group: node + {{ end }} + {{ if eq $index 0 }}{{ else }} + havoc-network-group: {{ if gt $index 2 }}"1"{{ else }}"2"{{ end }} + {{ end }} + instance: {{ $cfg.name }} release: {{ $.Release.Name }} - # Used for testing. Role value should either be: bootstrap or node. - # There should only be one "bootstrap" node, the rest should be "node". - # Here we set the first node to be bootstrap, the rest to be node. - role: {{ if eq $index 0 }}bootstrap{{ else }}node{{ end }} {{- range $key, $value := $.Values.labels }} {{ $key }}: {{ $value | quote }} {{- end }} @@ -43,7 +56,7 @@ spec: {{- toYaml $.Values.chainlink.securityContext | nindent 12 }} image: {{ default "public.ecr.aws/chainlink/chainlink" $cfg.image }} imagePullPolicy: Always - command: ["bash", "-c", "while ! pg_isready -U postgres --host {{ $.Release.Name }}-db-{{ $cfg.name }} --port 5432; do echo \"waiting for database to start\"; sleep 1; done && chainlink -c /etc/node-secrets-volume/default.toml -c /etc/node-secrets-volume/overrides.toml -secrets /etc/node-secrets-volume/secrets.toml node start -d -p /etc/node-secrets-volume/node-password -a /etc/node-secrets-volume/apicredentials --vrfpassword=/etc/node-secrets-volume/apicredentials"] + command: [ "bash", "-c", "while ! pg_isready -U postgres --host {{ $.Release.Name }}-db-{{ $cfg.name }} --port 5432; do echo \"waiting for database to start\"; sleep 1; done && chainlink -c /etc/node-secrets-volume/default.toml -c /etc/node-secrets-volume/overrides.toml -secrets /etc/node-secrets-volume/secrets.toml node start -d -p /etc/node-secrets-volume/node-password -a /etc/node-secrets-volume/apicredentials --vrfpassword=/etc/node-secrets-volume/apicredentials" ] ports: - name: access containerPort: {{ $.Values.chainlink.web_port }} diff --git a/charts/chainlink-cluster/templates/geth-deployment.yaml b/charts/chainlink-cluster/templates/geth-deployment.yaml index 8d2d4d3c76c..abc7853d978 100644 --- a/charts/chainlink-cluster/templates/geth-deployment.yaml +++ b/charts/chainlink-cluster/templates/geth-deployment.yaml @@ -7,11 +7,19 @@ spec: selector: matchLabels: app: geth + # Used for testing. + # havoc-component-group and havoc-network-group are used by "havoc" chaos testing tool + havoc-component-group: "blockchain" + havoc-network-group: "blockchain" release: {{ .Release.Name }} template: metadata: labels: app: geth + # Used for testing. + # havoc-component-group and havoc-network-group are used by "havoc" chaos testing tool + havoc-component-group: "blockchain" + havoc-network-group: "blockchain" release: {{ .Release.Name }} annotations: {{- range $key, $value := .Values.podAnnotations }} From dbcba88fcc12d479ddc12f44c0bcaac7caf259ab Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:27:52 -0500 Subject: [PATCH 181/234] Support custom ingress rules on the chart to support external Ingress (#11829) * Support custom ingress rules on the chart to allow external Ingress to reach pods * Simplify values to be passed in with the helm --set flag --- .../templates/networkpolicy-default-deny.yaml | 27 ------------ .../templates/networkpolicy-default.yaml | 41 +++++++++++++++++++ charts/chainlink-cluster/values.yaml | 9 ++++ 3 files changed, 50 insertions(+), 27 deletions(-) delete mode 100644 charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml create mode 100644 charts/chainlink-cluster/templates/networkpolicy-default.yaml diff --git a/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml b/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml deleted file mode 100644 index e2a4407be6a..00000000000 --- a/charts/chainlink-cluster/templates/networkpolicy-default-deny.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: default-deny -spec: - podSelector: - matchLabels: {} - policyTypes: - - Ingress - - Egress - egress: - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: "{{ $.Release.Namespace }}" - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: kube-system - podSelector: - matchLabels: - k8s-app: kube-dns - ports: - - protocol: TCP - port: 53 - - protocol: UDP - port: 53 diff --git a/charts/chainlink-cluster/templates/networkpolicy-default.yaml b/charts/chainlink-cluster/templates/networkpolicy-default.yaml new file mode 100644 index 00000000000..f2d9416cf15 --- /dev/null +++ b/charts/chainlink-cluster/templates/networkpolicy-default.yaml @@ -0,0 +1,41 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default +spec: + podSelector: + matchLabels: {} + policyTypes: + - Ingress + - Egress + ingress: + {{- if and .Values.networkPolicyDefault.ingress.allowCustomCidrs (not (empty .Values.networkPolicyDefault.ingress.customCidrs)) }} + # Using a comma separated list to make it easy to pass in with: + # `helm template ... --set networkPolicyDefault.ingress.customCidrs=...` + {{- $cidrs := splitList "," .Values.networkPolicyDefault.ingress.customCidrs }} + - from: + {{- range $cidr := $cidrs }} + - ipBlock: + cidr: {{ $cidr | quote }} + {{- end }} + {{- else }} + # Deny all ingress if no rules are specified. Rules can still be specified in other templates. + - {} + {{- end }} + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: "{{ $.Release.Namespace }}" + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + podSelector: + matchLabels: + k8s-app: kube-dns + ports: + - protocol: TCP + port: 53 + - protocol: UDP + port: 53 diff --git a/charts/chainlink-cluster/values.yaml b/charts/chainlink-cluster/values.yaml index 9394bda1213..24914a40a91 100644 --- a/charts/chainlink-cluster/values.yaml +++ b/charts/chainlink-cluster/values.yaml @@ -283,3 +283,12 @@ podAnnotations: nodeSelector: tolerations: affinity: + +# Configure the default network policy. +networkPolicyDefault: + ingress: + allowCustomCidrs: false + # String of comma separated CIDRs + customCidrs: null + # Example: + # customCidrs: "10.0.0.0/16,192.168.0.1/24" From 091a9a31a29ada9e49125d3c6e143450f885c8a6 Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Mon, 22 Jan 2024 15:46:26 -0500 Subject: [PATCH 182/234] Add chain reader and codec EVM implementation (#11842) * Implement skeleton interfaces, structs, & methods for ChainReader EVM POC - Read ChainReader config in from RelayConfig - Add some initialization and validation relay skeletons - Use medianProviderWrapper instead of passing medianContract separately This avoids us having to modify the signature of NewMedianFactory, which would require further modifications to all non-evm repos and chainlink-relay - Add chain_reader_test.go with some basic relay tests Co-authored-by: Jordan Krage - Add chain reader config validation - Add chain reader config validation tests - Add config for chain reader median contract to cr validation testcases - Add unimplemented Encode(), Decode(), GetMaxEncodingSize(), GetMaxDecodingSize() - Add ChainReader() method to mock provider for plugin test - Rename relaymercury.ChainReader to MercuryChainReader, resolve name collisions - Add tests for errors during ChainReader construction - Propagate InvalidConfig & any other errors back to client We should ignore Unsupported until node ops have been given ample time to migrate to the new job spec (including a section for ChainReader config) so that we can remove the old product-specific MedianContract component from MedianProvider. All other errors we can immediately start passing back to the client, letting the core node decide how to handle them (eg. displaying an "invalid job spec" message to the UI if the RelayConfig was invalid or the ContractID missing) * Fix lint * Fix go imports lint * Fix go.mod require got split into two Co-authored-by: Jordan Krage * Run make gomodtidy * Add tickets to chainreader TODOs * Log when we're falling back to internal MedianContract We had this before, at some point it accidentally got dropped * Pass contractID, relayConfig instead of relayOpts Also: move test for invalid contract ID to evm_test.go, & add mismatched ChainID test while we're here since it's pretty similar * Revert get solana sha workflow to ref develop Co-authored-by: Jordan Krage * Update err handling for when chain reader config is missing * Update solana repo hash * Improve chain reader config validation tests * Run go mod tidy * Update solana repo go mod reference and tidy * fix logs dir for Solana tests * fix solana logs path once more * print absolue log folder location * get abs folder in a correct way * do it some other way * update solana logs dir in github workflow * Add chain Reader enabled log to NewMedianServices * Update chainlink-solana ref and tidy * Add logs and handle disabled chain reader when median loop is enabled * Update go mod solana repo refs * Improve error messages in evm chain reader and add test cases * Update minor comment * Run tidy * Remove unneeded chain reader codec methods * Remove chain reader return values as they are out of scope for this PR * Add types for the evm * Create a codec entry with from args, allowing us to do type checking and to have structs to use with abi.Arguments's Pack function * Craetes a function to get the max size for abi.Argumetns, given any outermost slices have N elements * Add codec and make chain reader use it. Run the interface tests for both chain reader and codec. * Add modifiers to evm chain reader and codec * Add modifiers to evm chain reader and codec * small fixes after merging * Small cleanups and comments * fix after merge * add events * Don't pass ctx around in tests * Support multiple contract names and mapping them to multiple contracts. * Upadte chain reader interface * update go mod * Fix address type and start the chain reader * Update go mod references * add epoch converting * Simplify chain reader's bindings. * Add smoke test and feature test for chain reader. * lint again * Update solana to point to newer other ones in its own integration tests... * linter * . * newer solana again * newer solana again... * Fix bug with unnamed return params and invalid params containing the same name multiple times * Add generated files to sonar exclude * Update sonar again * Methods per sonar suggestions * Reanme function and type per PR feedback * Generate abi, bin & wrapper for chainreader test with gethwrappers * Update generated-wrapper-dependency-versions-do-not-edit.txt Also: remove extra unused files * prettier -w ChainReaderTestContract.sol * Update to use the ErrSliceWrongLen * Move test contract from contracts/tests to contrats/shared/test * Fix SQ coverage exclusions * Use modifier for time * Update go.mod * .github/workflows: dedupe log artifact name with tag_suffix * [TT-792] Add Resources for New OCR2 Tests (#11702) * Add Resources for New OCR2 Tests * More power to the engines * Log everything * Chill on the power now * Fix missing container env * Update common * Fix broken test * Add encoder defs for events * Revert "Add encoder defs for events" This reverts commit dd99acc5afb9a206130a6a150a832e592400bbc3. * revert the change to codec entry for event and simplify how it's done * Add Marshal method for custom codec types. * Updated common, still seeing not found, not sure why... * Update feeds * Update feeds and common to remove accidental debugging log line * Update feeds to remove accidental log line and to add more logging to warning messages * Fixup go mod * Update feeds * Remove json tag that had no business being where it was in codec entry * Fix small lint error, likely detected from better linter in merge from dev * Add fuzz test for codec * Support indexed events * PR feedback, fix typo, clear up comment, revert making a function public * Refactored function for getting event input. * --fix and add comments * use maps instaed fo ToCamelCase to allow consistnacy, also safer if customers have foo and foo_ both as varibles. * Scope native and checked type conversion to a single struct and add tests to verify that NewAt is safe and fuzz tests for sanity around it too * Chain Reader config improvements (#11679) * core/services/job: remove JSONConfig.Bytes hack * Shorten chain reader cfg abi in median test, change readType string * minimize abi; pretty formatting; use text marshaller * integrations-tests/client: use TOML for relayconfig * minimize chain reader median contract abi in features ocr2 test * pretty ABI JSON * add ModifiersConfig wrapper to include type field * fix TestOCR2TaskJobSpec_String --------- Co-authored-by: ilija * Template must in types gen and small json rename for mods in chain reader def fields * Update test contract for lint, fix toml test files after renaming the mod in prior commit * Update common for a rename and fix tests that I broke a couple commits ago * Move test contract * Commit with fixed contract for codec test and add small check to if statement for arg len * small fix to encoder, and fix config in feature test * Remove debug line since smoke test is passing now and I need to be ready to merge * Fix bad merge conflict * small PR fixes * Couple more error fixes * update common * update feeds to point to main * Fix typo * Update Solana repo refs * Pretty chain reader test solitidty file * Update solana * Update solana in integation tests * Needed to update more than just integation tests for solana. * Update Solana ref to fix relayConfig * Update Solana ref again * Update solana and starknet to point to the develop branches. * update relayer for starknet --------- Co-authored-by: Domino Valdano <2644901+reductionista@users.noreply.github.com> Co-authored-by: ilija Co-authored-by: ilija42 <57732589+ilija42@users.noreply.github.com> Co-authored-by: Jordan Krage Co-authored-by: Bartek Tofel Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Adam Hamrick --- .github/workflows/ci-core.yml | 2 +- .github/workflows/integration-tests.yml | 6 +- .gitignore | 3 + common/txmgr/confirmer.go | 3 +- .../scripts/native_solc_compile_all_shared | 1 + .../test/helpers/ChainReaderTestContract.sol | 117 + .../chain_reader_example.go | 830 +++++ ...rapper-dependency-versions-do-not-edit.txt | 1 + core/gethwrappers/go_generate.go | 4 +- .../features/ocr2/features_ocr2_test.go | 502 ++- core/scripts/go.mod | 10 +- core/scripts/go.sum | 24 +- core/services/job/models.go | 3 +- core/services/job/models_test.go | 190 ++ core/services/job/testdata/compact.toml | 34 + core/services/job/testdata/pretty.toml | 149 + core/services/ocr2/plugins/median/plugin.go | 68 - core/services/ocr2/plugins/median/services.go | 84 +- core/services/relay/evm/binding.go | 15 + core/services/relay/evm/bindings.go | 61 + core/services/relay/evm/chain_reader.go | 292 +- core/services/relay/evm/chain_reader_test.go | 601 ++-- core/services/relay/evm/codec.go | 130 + core/services/relay/evm/codec_fuzz_test.go | 12 + core/services/relay/evm/codec_test.go | 210 ++ core/services/relay/evm/decoder.go | 102 + core/services/relay/evm/encoder.go | 145 + core/services/relay/evm/event_binding.go | 272 ++ core/services/relay/evm/evm.go | 35 +- core/services/relay/evm/functions.go | 4 + .../relay/evm/mercury/v1/data_source_test.go | 1 - core/services/relay/evm/mercury_provider.go | 8 + core/services/relay/evm/method_binding.go | 66 + core/services/relay/evm/ocr2keeper.go | 4 + core/services/relay/evm/ocr2vrf.go | 8 + core/services/relay/evm/parsed_types.go | 50 + core/services/relay/evm/types/abi_types.go | 66 + .../relay/evm/types/byte_types_gen.go | 300 ++ core/services/relay/evm/types/codec_entry.go | 251 ++ .../relay/evm/types/codec_entry_test.go | 284 ++ .../relay/evm/types/gen/bytes.go.tmpl | 14 + .../services/relay/evm/types/gen/ints.go.tmpl | 74 + core/services/relay/evm/types/gen/main.go | 73 + .../services/relay/evm/types/int_types_gen.go | 2710 +++++++++++++++++ .../relay/evm/types/int_types_test.go | 54 + core/services/relay/evm/types/size_helper.go | 69 + .../relay/evm/types/size_helper_test.go | 263 ++ core/services/relay/evm/types/types.go | 105 +- go.mod | 14 +- go.sum | 21 +- integration-tests/actions/ocr2_helpers.go | 9 +- .../actions/ocr2_helpers_local.go | 40 + integration-tests/client/chainlink_models.go | 38 +- .../client/chainlink_models_test.go | 135 + integration-tests/docker/test_env/cl_node.go | 1 + integration-tests/go.mod | 10 +- integration-tests/go.sum | 24 +- .../smoke/forwarders_ocr2_test.go | 2 +- integration-tests/smoke/ocr2_test.go | 24 +- plugins/medianpoc/plugin_test.go | 5 + sonar-project.properties | 2 +- tools/bin/go_core_fuzz | 36 + 62 files changed, 7955 insertions(+), 716 deletions(-) create mode 100644 contracts/src/v0.8/shared/test/helpers/ChainReaderTestContract.sol create mode 100644 core/gethwrappers/generated/chain_reader_example/chain_reader_example.go create mode 100644 core/services/job/testdata/compact.toml create mode 100644 core/services/job/testdata/pretty.toml delete mode 100644 core/services/ocr2/plugins/median/plugin.go create mode 100644 core/services/relay/evm/binding.go create mode 100644 core/services/relay/evm/bindings.go create mode 100644 core/services/relay/evm/codec.go create mode 100644 core/services/relay/evm/codec_fuzz_test.go create mode 100644 core/services/relay/evm/codec_test.go create mode 100644 core/services/relay/evm/decoder.go create mode 100644 core/services/relay/evm/encoder.go create mode 100644 core/services/relay/evm/event_binding.go create mode 100644 core/services/relay/evm/method_binding.go create mode 100644 core/services/relay/evm/parsed_types.go create mode 100644 core/services/relay/evm/types/abi_types.go create mode 100644 core/services/relay/evm/types/byte_types_gen.go create mode 100644 core/services/relay/evm/types/codec_entry.go create mode 100644 core/services/relay/evm/types/codec_entry_test.go create mode 100644 core/services/relay/evm/types/gen/bytes.go.tmpl create mode 100644 core/services/relay/evm/types/gen/ints.go.tmpl create mode 100644 core/services/relay/evm/types/gen/main.go create mode 100644 core/services/relay/evm/types/int_types_gen.go create mode 100644 core/services/relay/evm/types/int_types_test.go create mode 100644 core/services/relay/evm/types/size_helper.go create mode 100644 core/services/relay/evm/types/size_helper_test.go create mode 100644 integration-tests/client/chainlink_models_test.go create mode 100755 tools/bin/go_core_fuzz diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 0d9d2912718..30534873134 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false matrix: - cmd: ["go_core_tests", "go_core_race_tests"] + cmd: ["go_core_tests", "go_core_race_tests", "go_core_fuzz"] name: Core Tests (${{ matrix.cmd }}) runs-on: ubuntu20.04-64cores-256GB env: diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 3f6c669d679..8ff6c3d4cd6 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -340,12 +340,12 @@ jobs: file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 - nodes: 4 + nodes: 6 os: ubuntu-latest file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 - nodes: 4 + nodes: 6 os: ubuntu-latest pyroscope_env: ci-smoke-ocr2-plugins-evm-simulated tag_suffix: "-plugins" @@ -472,7 +472,7 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - artifacts_name: ${{ matrix.product.name }}-test-logs + artifacts_name: ${{ matrix.product.name }}${{ matrix.product.tag_suffix }}-test-logs artifacts_location: ./integration-tests/smoke/logs/ publish_check_name: ${{ matrix.product.name }} token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 2f68ec2c945..45ec5eb2bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,6 @@ tools/flakeytests/coverage.txt .test_summary/ .run.id + +# Fuzz tests can create these files +**/testdata/fuzz/* diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index f10481fef56..d55f982c11f 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -14,10 +14,11 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" + commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" + "github.com/smartcontractkit/chainlink-common/pkg/chains/label" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - commonhex "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/client" diff --git a/contracts/scripts/native_solc_compile_all_shared b/contracts/scripts/native_solc_compile_all_shared index 9178237b8a5..eeaa9902346 100755 --- a/contracts/scripts/native_solc_compile_all_shared +++ b/contracts/scripts/native_solc_compile_all_shared @@ -32,3 +32,4 @@ compileContract shared/token/ERC677/BurnMintERC677.sol compileContract shared/token/ERC677/LinkToken.sol compileContract shared/mocks/WERC20Mock.sol compileContract vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol +compileContract shared/test/helpers/ChainReaderTestContract.sol diff --git a/contracts/src/v0.8/shared/test/helpers/ChainReaderTestContract.sol b/contracts/src/v0.8/shared/test/helpers/ChainReaderTestContract.sol new file mode 100644 index 00000000000..050f5fb390e --- /dev/null +++ b/contracts/src/v0.8/shared/test/helpers/ChainReaderTestContract.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8; + +struct TestStruct { + int32 Field; + string DifferentField; + uint8 OracleId; + uint8[32] OracleIds; + address Account; + address[] Accounts; + int192 BigField; + MidLevelTestStruct NestedStruct; +} + +struct MidLevelTestStruct { + bytes2 FixedBytes; + InnerTestStruct Inner; +} + +struct InnerTestStruct { + int64 IntVal; + string S; +} + +contract LatestValueHolder { + event Triggered( + int32 indexed field, + string differentField, + uint8 oracleId, + uint8[32] oracleIds, + address Account, + address[] Accounts, + int192 bigField, + MidLevelTestStruct nestedStruct + ); + + event TriggeredEventWithDynamicTopic(string indexed fieldHash, string field); + + // First topic is event hash + event TriggeredWithFourTopics(int32 indexed field1, int32 indexed field2, int32 indexed field3); + + TestStruct[] private s_seen; + uint64[] private s_arr; + + constructor() { + // See chain_reader_interface_tests.go in chainlink-relay + s_arr.push(3); + s_arr.push(4); + } + + function addTestStruct( + int32 field, + string calldata differentField, + uint8 oracleId, + uint8[32] calldata oracleIds, + address account, + address[] calldata accounts, + int192 bigField, + MidLevelTestStruct calldata nestedStruct + ) public { + s_seen.push(TestStruct(field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct)); + } + + function returnSeen( + int32 field, + string calldata differentField, + uint8 oracleId, + uint8[32] calldata oracleIds, + address account, + address[] calldata accounts, + int192 bigField, + MidLevelTestStruct calldata nestedStruct + ) public pure returns (TestStruct memory) { + return TestStruct(field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct); + } + + function getElementAtIndex(uint256 i) public view returns (TestStruct memory) { + // See chain_reader_interface_tests.go in chainlink-relay + return s_seen[i - 1]; + } + + function getPrimitiveValue() public pure returns (uint64) { + // See chain_reader_interface_tests.go in chainlink-relay + return 3; + } + + function getDifferentPrimitiveValue() public pure returns (uint64) { + // See chain_reader_interface_tests.go in chainlink-relay + return 1990; + } + + function getSliceValue() public view returns (uint64[] memory) { + return s_arr; + } + + function triggerEvent( + int32 field, + string calldata differentField, + uint8 oracleId, + uint8[32] calldata oracleIds, + address account, + address[] calldata accounts, + int192 bigField, + MidLevelTestStruct calldata nestedStruct + ) public { + emit Triggered(field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct); + } + + function triggerEventWithDynamicTopic(string calldata field) public { + emit TriggeredEventWithDynamicTopic(field, field); + } + + // first topic is the event signature + function triggerWithFourTopics(int32 field1, int32 field2, int32 field3) public { + emit TriggeredWithFourTopics(field1, field2, field3); + } +} diff --git a/core/gethwrappers/generated/chain_reader_example/chain_reader_example.go b/core/gethwrappers/generated/chain_reader_example/chain_reader_example.go new file mode 100644 index 00000000000..f6dce0bb6c3 --- /dev/null +++ b/core/gethwrappers/generated/chain_reader_example/chain_reader_example.go @@ -0,0 +1,830 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package chain_reader_example + +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 InnerTestStruct struct { + IntVal int64 + S string +} + +type MidLevelTestStruct struct { + FixedBytes [2]byte + Inner InnerTestStruct +} + +type TestStruct struct { + Field int32 + DifferentField string + OracleId uint8 + OracleIds [32]uint8 + Account common.Address + Accounts []common.Address + BigField *big.Int + NestedStruct MidLevelTestStruct +} + +var LatestValueHolderMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"Triggered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"string\",\"name\":\"fieldHash\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"field\",\"type\":\"string\"}],\"name\":\"TriggeredEventWithDynamicTopic\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field1\",\"type\":\"int32\"},{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field2\",\"type\":\"int32\"},{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field3\",\"type\":\"int32\"}],\"name\":\"TriggeredWithFourTopics\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"addTestStruct\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDifferentPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"getElementAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSliceValue\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"returnSeen\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"triggerEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"field\",\"type\":\"string\"}],\"name\":\"triggerEventWithDynamicTopic\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field1\",\"type\":\"int32\"},{\"internalType\":\"int32\",\"name\":\"field2\",\"type\":\"int32\"},{\"internalType\":\"int32\",\"name\":\"field3\",\"type\":\"int32\"}],\"name\":\"triggerWithFourTopics\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50600180548082018255600082905260048082047fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6908101805460086003958616810261010090810a8088026001600160401b0391820219909416939093179093558654808801909755848704909301805496909516909202900a91820291021990921691909117905561176c806100a96000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c80637f002d6711610076578063dbfd73321161005b578063dbfd73321461013e578063ef4e1ced14610151578063f6f871c81461015857600080fd5b80637f002d671461010e578063ab5e0b381461012157600080fd5b80632c45576f146100a85780633272b66c146100d157806349eac2ac146100e6578063679004a4146100f9575b600080fd5b6100bb6100b6366004610baa565b61016b565b6040516100c89190610d09565b60405180910390f35b6100e46100df366004610e48565b610446565b005b6100e46100f4366004610f5d565b61049b565b61010161079e565b6040516100c8919061104f565b6100e461011c366004610f5d565b61082a565b6107c65b60405167ffffffffffffffff90911681526020016100c8565b6100e461014c36600461109d565b610881565b6003610125565b6100bb610166366004610f5d565b6108be565b6101736109c7565b60006101806001846110e0565b815481106101905761019061111a565b6000918252602091829020604080516101008101909152600a90920201805460030b825260018101805492939192918401916101cb90611149565b80601f01602080910402602001604051908101604052809291908181526020018280546101f790611149565b80156102445780601f1061021957610100808354040283529160200191610244565b820191906000526020600020905b81548152906001019060200180831161022757829003601f168201915b5050509183525050600282015460ff166020808301919091526040805161040081018083529190930192916003850191826000855b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161027957505050928452505050600482015473ffffffffffffffffffffffffffffffffffffffff16602080830191909152600583018054604080518285028101850182528281529401939283018282801561033257602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610307575b5050509183525050600682015460170b6020808301919091526040805180820182526007808601805460f01b7fffff0000000000000000000000000000000000000000000000000000000000001683528351808501855260088801805490930b815260098801805495909701969395919486830194919392840191906103b790611149565b80601f01602080910402602001604051908101604052809291908181526020018280546103e390611149565b80156104305780601f1061040557610100808354040283529160200191610430565b820191906000526020600020905b81548152906001019060200180831161041357829003601f168201915b5050509190925250505090525090525092915050565b8181604051610456929190611196565b60405180910390207f3d969732b1bbbb9f1d7eb9f3f14e4cb50a74d950b3ef916a397b85dfbab93c67838360405161048f9291906111ef565b60405180910390a25050565b60006040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b9183908390808284376000920191909152505050815273ffffffffffffffffffffffffffffffffffffffff8816602080830191909152604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b602082015260400161058d846112ec565b905281546001808201845560009384526020938490208351600a9093020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9093169290921782559282015191929091908201906105f39082611446565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff90921691909117905560608201516106419060038301906020610a16565b5060808201516004820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905560a082015180516106a8916005840191602090910190610aa9565b5060c08201516006820180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905560e082015180516007830180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660f09290921c91909117815560208083015180516008860180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff90921691909117815591810151909190600986019061078b9082611446565b5050505050505050505050505050505050565b6060600180548060200260200160405190810160405280929190818152602001828054801561082057602002820191906000526020600020906000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116107db5790505b5050505050905090565b8960030b7f7188419dcd8b51877b71766f075f3626586c0ff190e7d056aa65ce9acb649a3d8a8a8a8a8a8a8a8a8a60405161086d999897969594939291906116a5565b60405180910390a250505050505050505050565b8060030b8260030b8460030b7f91c80dc390f3d041b3a04b0099b19634499541ea26972250986ee4b24a12fac560405160405180910390a4505050565b6108c66109c7565b6040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b9183908390808284376000920191909152505050815273ffffffffffffffffffffffffffffffffffffffff8816602080830191909152604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b60208201526040016109b6846112ec565b90529b9a5050505050505050505050565b60408051610100810182526000808252606060208301819052928201529081016109ef610b23565b8152600060208201819052606060408301819052820152608001610a11610b42565b905290565b600183019183908215610a995791602002820160005b83821115610a6a57835183826101000a81548160ff021916908360ff1602179055509260200192600101602081600001049283019260010302610a2c565b8015610a975782816101000a81549060ff0219169055600101602081600001049283019260010302610a6a565b505b50610aa5929150610b95565b5090565b828054828255906000526020600020908101928215610a99579160200282015b82811115610a9957825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190610ac9565b6040518061040001604052806020906020820280368337509192915050565b604051806040016040528060007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001610a116040518060400160405280600060070b8152602001606081525090565b5b80821115610aa55760008155600101610b96565b600060208284031215610bbc57600080fd5b5035919050565b6000815180845260005b81811015610be957602081850181015186830182015201610bcd565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b8060005b6020808210610c3a5750610c51565b825160ff1685529384019390910190600101610c2b565b50505050565b600081518084526020808501945080840160005b83811015610c9d57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101610c6b565b509495945050505050565b7fffff00000000000000000000000000000000000000000000000000000000000081511682526000602082015160406020850152805160070b60408501526020810151905060406060850152610d016080850182610bc3565b949350505050565b60208152610d1d60208201835160030b9052565b600060208301516104e0806040850152610d3b610500850183610bc3565b91506040850151610d51606086018260ff169052565b506060850151610d646080860182610c27565b50608085015173ffffffffffffffffffffffffffffffffffffffff1661048085015260a08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840381016104a0870152610dc18483610c57565b935060c08701519150610dda6104c087018360170b9052565b60e0870151915080868503018387015250610df58382610ca8565b9695505050505050565b60008083601f840112610e1157600080fd5b50813567ffffffffffffffff811115610e2957600080fd5b602083019150836020828501011115610e4157600080fd5b9250929050565b60008060208385031215610e5b57600080fd5b823567ffffffffffffffff811115610e7257600080fd5b610e7e85828601610dff565b90969095509350505050565b8035600381900b8114610e9c57600080fd5b919050565b803560ff81168114610e9c57600080fd5b806104008101831015610ec457600080fd5b92915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610e9c57600080fd5b60008083601f840112610f0057600080fd5b50813567ffffffffffffffff811115610f1857600080fd5b6020830191508360208260051b8501011115610e4157600080fd5b8035601781900b8114610e9c57600080fd5b600060408284031215610f5757600080fd5b50919050565b6000806000806000806000806000806104e08b8d031215610f7d57600080fd5b610f868b610e8a565b995060208b013567ffffffffffffffff80821115610fa357600080fd5b610faf8e838f01610dff565b909b509950899150610fc360408e01610ea1565b9850610fd28e60608f01610eb2565b9750610fe16104608e01610eca565b96506104808d0135915080821115610ff857600080fd5b6110048e838f01610eee565b90965094508491506110196104a08e01610f33565b93506104c08d013591508082111561103057600080fd5b5061103d8d828e01610f45565b9150509295989b9194979a5092959850565b6020808252825182820181905260009190848201906040850190845b8181101561109157835167ffffffffffffffff168352928401929184019160010161106b565b50909695505050505050565b6000806000606084860312156110b257600080fd5b6110bb84610e8a565b92506110c960208501610e8a565b91506110d760408501610e8a565b90509250925092565b81810381811115610ec4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600181811c9082168061115d57607f821691505b602082108103610f57577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000610d016020830184866111a6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561125557611255611203565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156112a2576112a2611203565b604052919050565b80357fffff00000000000000000000000000000000000000000000000000000000000081168114610e9c57600080fd5b8035600781900b8114610e9c57600080fd5b6000604082360312156112fe57600080fd5b611306611232565b61130f836112aa565b815260208084013567ffffffffffffffff8082111561132d57600080fd5b81860191506040823603121561134257600080fd5b61134a611232565b611353836112da565b8152838301358281111561136657600080fd5b929092019136601f84011261137a57600080fd5b82358281111561138c5761138c611203565b6113bc857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161125b565b925080835236858286010111156113d257600080fd5b8085850186850137600090830185015280840191909152918301919091525092915050565b601f82111561144157600081815260208120601f850160051c8101602086101561141e5750805b601f850160051c820191505b8181101561143d5782815560010161142a565b5050505b505050565b815167ffffffffffffffff81111561146057611460611203565b6114748161146e8454611149565b846113f7565b602080601f8311600181146114c757600084156114915750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561143d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015611514578886015182559484019460019091019084016114f5565b508582101561155057878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b8183526000602080850194508260005b85811015610c9d5773ffffffffffffffffffffffffffffffffffffffff61159683610eca565b1687529582019590820190600101611570565b7fffff0000000000000000000000000000000000000000000000000000000000006115d3826112aa565b168252600060208201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261160d57600080fd5b60406020850152820161161f816112da565b60070b604085015260208101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe182360301811261165c57600080fd5b0160208101903567ffffffffffffffff81111561167857600080fd5b80360382131561168757600080fd5b6040606086015261169c6080860182846111a6565b95945050505050565b60006104c08083526116ba8184018c8e6111a6565b9050602060ff808c1682860152604085018b60005b848110156116f457836116e183610ea1565b16835291840191908401906001016116cf565b505050505073ffffffffffffffffffffffffffffffffffffffff881661044084015282810361046084015261172a818789611560565b905061173c61048084018660170b9052565b8281036104a084015261174f81856115a9565b9c9b50505050505050505050505056fea164736f6c6343000813000a", +} + +var LatestValueHolderABI = LatestValueHolderMetaData.ABI + +var LatestValueHolderBin = LatestValueHolderMetaData.Bin + +func DeployLatestValueHolder(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *LatestValueHolder, error) { + parsed, err := LatestValueHolderMetaData.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(LatestValueHolderBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &LatestValueHolder{address: address, abi: *parsed, LatestValueHolderCaller: LatestValueHolderCaller{contract: contract}, LatestValueHolderTransactor: LatestValueHolderTransactor{contract: contract}, LatestValueHolderFilterer: LatestValueHolderFilterer{contract: contract}}, nil +} + +type LatestValueHolder struct { + address common.Address + abi abi.ABI + LatestValueHolderCaller + LatestValueHolderTransactor + LatestValueHolderFilterer +} + +type LatestValueHolderCaller struct { + contract *bind.BoundContract +} + +type LatestValueHolderTransactor struct { + contract *bind.BoundContract +} + +type LatestValueHolderFilterer struct { + contract *bind.BoundContract +} + +type LatestValueHolderSession struct { + Contract *LatestValueHolder + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type LatestValueHolderCallerSession struct { + Contract *LatestValueHolderCaller + CallOpts bind.CallOpts +} + +type LatestValueHolderTransactorSession struct { + Contract *LatestValueHolderTransactor + TransactOpts bind.TransactOpts +} + +type LatestValueHolderRaw struct { + Contract *LatestValueHolder +} + +type LatestValueHolderCallerRaw struct { + Contract *LatestValueHolderCaller +} + +type LatestValueHolderTransactorRaw struct { + Contract *LatestValueHolderTransactor +} + +func NewLatestValueHolder(address common.Address, backend bind.ContractBackend) (*LatestValueHolder, error) { + abi, err := abi.JSON(strings.NewReader(LatestValueHolderABI)) + if err != nil { + return nil, err + } + contract, err := bindLatestValueHolder(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &LatestValueHolder{address: address, abi: abi, LatestValueHolderCaller: LatestValueHolderCaller{contract: contract}, LatestValueHolderTransactor: LatestValueHolderTransactor{contract: contract}, LatestValueHolderFilterer: LatestValueHolderFilterer{contract: contract}}, nil +} + +func NewLatestValueHolderCaller(address common.Address, caller bind.ContractCaller) (*LatestValueHolderCaller, error) { + contract, err := bindLatestValueHolder(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &LatestValueHolderCaller{contract: contract}, nil +} + +func NewLatestValueHolderTransactor(address common.Address, transactor bind.ContractTransactor) (*LatestValueHolderTransactor, error) { + contract, err := bindLatestValueHolder(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &LatestValueHolderTransactor{contract: contract}, nil +} + +func NewLatestValueHolderFilterer(address common.Address, filterer bind.ContractFilterer) (*LatestValueHolderFilterer, error) { + contract, err := bindLatestValueHolder(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &LatestValueHolderFilterer{contract: contract}, nil +} + +func bindLatestValueHolder(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := LatestValueHolderMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_LatestValueHolder *LatestValueHolderRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _LatestValueHolder.Contract.LatestValueHolderCaller.contract.Call(opts, result, method, params...) +} + +func (_LatestValueHolder *LatestValueHolderRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LatestValueHolder.Contract.LatestValueHolderTransactor.contract.Transfer(opts) +} + +func (_LatestValueHolder *LatestValueHolderRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _LatestValueHolder.Contract.LatestValueHolderTransactor.contract.Transact(opts, method, params...) +} + +func (_LatestValueHolder *LatestValueHolderCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _LatestValueHolder.Contract.contract.Call(opts, result, method, params...) +} + +func (_LatestValueHolder *LatestValueHolderTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LatestValueHolder.Contract.contract.Transfer(opts) +} + +func (_LatestValueHolder *LatestValueHolderTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _LatestValueHolder.Contract.contract.Transact(opts, method, params...) +} + +func (_LatestValueHolder *LatestValueHolderCaller) GetDifferentPrimitiveValue(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _LatestValueHolder.contract.Call(opts, &out, "getDifferentPrimitiveValue") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_LatestValueHolder *LatestValueHolderSession) GetDifferentPrimitiveValue() (uint64, error) { + return _LatestValueHolder.Contract.GetDifferentPrimitiveValue(&_LatestValueHolder.CallOpts) +} + +func (_LatestValueHolder *LatestValueHolderCallerSession) GetDifferentPrimitiveValue() (uint64, error) { + return _LatestValueHolder.Contract.GetDifferentPrimitiveValue(&_LatestValueHolder.CallOpts) +} + +func (_LatestValueHolder *LatestValueHolderCaller) GetElementAtIndex(opts *bind.CallOpts, i *big.Int) (TestStruct, error) { + var out []interface{} + err := _LatestValueHolder.contract.Call(opts, &out, "getElementAtIndex", i) + + if err != nil { + return *new(TestStruct), err + } + + out0 := *abi.ConvertType(out[0], new(TestStruct)).(*TestStruct) + + return out0, err + +} + +func (_LatestValueHolder *LatestValueHolderSession) GetElementAtIndex(i *big.Int) (TestStruct, error) { + return _LatestValueHolder.Contract.GetElementAtIndex(&_LatestValueHolder.CallOpts, i) +} + +func (_LatestValueHolder *LatestValueHolderCallerSession) GetElementAtIndex(i *big.Int) (TestStruct, error) { + return _LatestValueHolder.Contract.GetElementAtIndex(&_LatestValueHolder.CallOpts, i) +} + +func (_LatestValueHolder *LatestValueHolderCaller) GetPrimitiveValue(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _LatestValueHolder.contract.Call(opts, &out, "getPrimitiveValue") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_LatestValueHolder *LatestValueHolderSession) GetPrimitiveValue() (uint64, error) { + return _LatestValueHolder.Contract.GetPrimitiveValue(&_LatestValueHolder.CallOpts) +} + +func (_LatestValueHolder *LatestValueHolderCallerSession) GetPrimitiveValue() (uint64, error) { + return _LatestValueHolder.Contract.GetPrimitiveValue(&_LatestValueHolder.CallOpts) +} + +func (_LatestValueHolder *LatestValueHolderCaller) GetSliceValue(opts *bind.CallOpts) ([]uint64, error) { + var out []interface{} + err := _LatestValueHolder.contract.Call(opts, &out, "getSliceValue") + + if err != nil { + return *new([]uint64), err + } + + out0 := *abi.ConvertType(out[0], new([]uint64)).(*[]uint64) + + return out0, err + +} + +func (_LatestValueHolder *LatestValueHolderSession) GetSliceValue() ([]uint64, error) { + return _LatestValueHolder.Contract.GetSliceValue(&_LatestValueHolder.CallOpts) +} + +func (_LatestValueHolder *LatestValueHolderCallerSession) GetSliceValue() ([]uint64, error) { + return _LatestValueHolder.Contract.GetSliceValue(&_LatestValueHolder.CallOpts) +} + +func (_LatestValueHolder *LatestValueHolderCaller) ReturnSeen(opts *bind.CallOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (TestStruct, error) { + var out []interface{} + err := _LatestValueHolder.contract.Call(opts, &out, "returnSeen", field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) + + if err != nil { + return *new(TestStruct), err + } + + out0 := *abi.ConvertType(out[0], new(TestStruct)).(*TestStruct) + + return out0, err + +} + +func (_LatestValueHolder *LatestValueHolderSession) ReturnSeen(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (TestStruct, error) { + return _LatestValueHolder.Contract.ReturnSeen(&_LatestValueHolder.CallOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderCallerSession) ReturnSeen(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (TestStruct, error) { + return _LatestValueHolder.Contract.ReturnSeen(&_LatestValueHolder.CallOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderTransactor) AddTestStruct(opts *bind.TransactOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _LatestValueHolder.contract.Transact(opts, "addTestStruct", field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderSession) AddTestStruct(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _LatestValueHolder.Contract.AddTestStruct(&_LatestValueHolder.TransactOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderTransactorSession) AddTestStruct(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _LatestValueHolder.Contract.AddTestStruct(&_LatestValueHolder.TransactOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderTransactor) TriggerEvent(opts *bind.TransactOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _LatestValueHolder.contract.Transact(opts, "triggerEvent", field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderSession) TriggerEvent(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _LatestValueHolder.Contract.TriggerEvent(&_LatestValueHolder.TransactOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderTransactorSession) TriggerEvent(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _LatestValueHolder.Contract.TriggerEvent(&_LatestValueHolder.TransactOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +func (_LatestValueHolder *LatestValueHolderTransactor) TriggerEventWithDynamicTopic(opts *bind.TransactOpts, field string) (*types.Transaction, error) { + return _LatestValueHolder.contract.Transact(opts, "triggerEventWithDynamicTopic", field) +} + +func (_LatestValueHolder *LatestValueHolderSession) TriggerEventWithDynamicTopic(field string) (*types.Transaction, error) { + return _LatestValueHolder.Contract.TriggerEventWithDynamicTopic(&_LatestValueHolder.TransactOpts, field) +} + +func (_LatestValueHolder *LatestValueHolderTransactorSession) TriggerEventWithDynamicTopic(field string) (*types.Transaction, error) { + return _LatestValueHolder.Contract.TriggerEventWithDynamicTopic(&_LatestValueHolder.TransactOpts, field) +} + +func (_LatestValueHolder *LatestValueHolderTransactor) TriggerWithFourTopics(opts *bind.TransactOpts, field1 int32, field2 int32, field3 int32) (*types.Transaction, error) { + return _LatestValueHolder.contract.Transact(opts, "triggerWithFourTopics", field1, field2, field3) +} + +func (_LatestValueHolder *LatestValueHolderSession) TriggerWithFourTopics(field1 int32, field2 int32, field3 int32) (*types.Transaction, error) { + return _LatestValueHolder.Contract.TriggerWithFourTopics(&_LatestValueHolder.TransactOpts, field1, field2, field3) +} + +func (_LatestValueHolder *LatestValueHolderTransactorSession) TriggerWithFourTopics(field1 int32, field2 int32, field3 int32) (*types.Transaction, error) { + return _LatestValueHolder.Contract.TriggerWithFourTopics(&_LatestValueHolder.TransactOpts, field1, field2, field3) +} + +type LatestValueHolderTriggeredIterator struct { + Event *LatestValueHolderTriggered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LatestValueHolderTriggeredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LatestValueHolderTriggered) + 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(LatestValueHolderTriggered) + 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 *LatestValueHolderTriggeredIterator) Error() error { + return it.fail +} + +func (it *LatestValueHolderTriggeredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LatestValueHolderTriggered struct { + Field int32 + DifferentField string + OracleId uint8 + OracleIds [32]uint8 + Account common.Address + Accounts []common.Address + BigField *big.Int + NestedStruct MidLevelTestStruct + Raw types.Log +} + +func (_LatestValueHolder *LatestValueHolderFilterer) FilterTriggered(opts *bind.FilterOpts, field []int32) (*LatestValueHolderTriggeredIterator, error) { + + var fieldRule []interface{} + for _, fieldItem := range field { + fieldRule = append(fieldRule, fieldItem) + } + + logs, sub, err := _LatestValueHolder.contract.FilterLogs(opts, "Triggered", fieldRule) + if err != nil { + return nil, err + } + return &LatestValueHolderTriggeredIterator{contract: _LatestValueHolder.contract, event: "Triggered", logs: logs, sub: sub}, nil +} + +func (_LatestValueHolder *LatestValueHolderFilterer) WatchTriggered(opts *bind.WatchOpts, sink chan<- *LatestValueHolderTriggered, field []int32) (event.Subscription, error) { + + var fieldRule []interface{} + for _, fieldItem := range field { + fieldRule = append(fieldRule, fieldItem) + } + + logs, sub, err := _LatestValueHolder.contract.WatchLogs(opts, "Triggered", fieldRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LatestValueHolderTriggered) + if err := _LatestValueHolder.contract.UnpackLog(event, "Triggered", 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 (_LatestValueHolder *LatestValueHolderFilterer) ParseTriggered(log types.Log) (*LatestValueHolderTriggered, error) { + event := new(LatestValueHolderTriggered) + if err := _LatestValueHolder.contract.UnpackLog(event, "Triggered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LatestValueHolderTriggeredEventWithDynamicTopicIterator struct { + Event *LatestValueHolderTriggeredEventWithDynamicTopic + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LatestValueHolderTriggeredEventWithDynamicTopicIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LatestValueHolderTriggeredEventWithDynamicTopic) + 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(LatestValueHolderTriggeredEventWithDynamicTopic) + 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 *LatestValueHolderTriggeredEventWithDynamicTopicIterator) Error() error { + return it.fail +} + +func (it *LatestValueHolderTriggeredEventWithDynamicTopicIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LatestValueHolderTriggeredEventWithDynamicTopic struct { + FieldHash common.Hash + Field string + Raw types.Log +} + +func (_LatestValueHolder *LatestValueHolderFilterer) FilterTriggeredEventWithDynamicTopic(opts *bind.FilterOpts, fieldHash []string) (*LatestValueHolderTriggeredEventWithDynamicTopicIterator, error) { + + var fieldHashRule []interface{} + for _, fieldHashItem := range fieldHash { + fieldHashRule = append(fieldHashRule, fieldHashItem) + } + + logs, sub, err := _LatestValueHolder.contract.FilterLogs(opts, "TriggeredEventWithDynamicTopic", fieldHashRule) + if err != nil { + return nil, err + } + return &LatestValueHolderTriggeredEventWithDynamicTopicIterator{contract: _LatestValueHolder.contract, event: "TriggeredEventWithDynamicTopic", logs: logs, sub: sub}, nil +} + +func (_LatestValueHolder *LatestValueHolderFilterer) WatchTriggeredEventWithDynamicTopic(opts *bind.WatchOpts, sink chan<- *LatestValueHolderTriggeredEventWithDynamicTopic, fieldHash []string) (event.Subscription, error) { + + var fieldHashRule []interface{} + for _, fieldHashItem := range fieldHash { + fieldHashRule = append(fieldHashRule, fieldHashItem) + } + + logs, sub, err := _LatestValueHolder.contract.WatchLogs(opts, "TriggeredEventWithDynamicTopic", fieldHashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LatestValueHolderTriggeredEventWithDynamicTopic) + if err := _LatestValueHolder.contract.UnpackLog(event, "TriggeredEventWithDynamicTopic", 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 (_LatestValueHolder *LatestValueHolderFilterer) ParseTriggeredEventWithDynamicTopic(log types.Log) (*LatestValueHolderTriggeredEventWithDynamicTopic, error) { + event := new(LatestValueHolderTriggeredEventWithDynamicTopic) + if err := _LatestValueHolder.contract.UnpackLog(event, "TriggeredEventWithDynamicTopic", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LatestValueHolderTriggeredWithFourTopicsIterator struct { + Event *LatestValueHolderTriggeredWithFourTopics + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LatestValueHolderTriggeredWithFourTopicsIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LatestValueHolderTriggeredWithFourTopics) + 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(LatestValueHolderTriggeredWithFourTopics) + 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 *LatestValueHolderTriggeredWithFourTopicsIterator) Error() error { + return it.fail +} + +func (it *LatestValueHolderTriggeredWithFourTopicsIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LatestValueHolderTriggeredWithFourTopics struct { + Field1 int32 + Field2 int32 + Field3 int32 + Raw types.Log +} + +func (_LatestValueHolder *LatestValueHolderFilterer) FilterTriggeredWithFourTopics(opts *bind.FilterOpts, field1 []int32, field2 []int32, field3 []int32) (*LatestValueHolderTriggeredWithFourTopicsIterator, error) { + + var field1Rule []interface{} + for _, field1Item := range field1 { + field1Rule = append(field1Rule, field1Item) + } + var field2Rule []interface{} + for _, field2Item := range field2 { + field2Rule = append(field2Rule, field2Item) + } + var field3Rule []interface{} + for _, field3Item := range field3 { + field3Rule = append(field3Rule, field3Item) + } + + logs, sub, err := _LatestValueHolder.contract.FilterLogs(opts, "TriggeredWithFourTopics", field1Rule, field2Rule, field3Rule) + if err != nil { + return nil, err + } + return &LatestValueHolderTriggeredWithFourTopicsIterator{contract: _LatestValueHolder.contract, event: "TriggeredWithFourTopics", logs: logs, sub: sub}, nil +} + +func (_LatestValueHolder *LatestValueHolderFilterer) WatchTriggeredWithFourTopics(opts *bind.WatchOpts, sink chan<- *LatestValueHolderTriggeredWithFourTopics, field1 []int32, field2 []int32, field3 []int32) (event.Subscription, error) { + + var field1Rule []interface{} + for _, field1Item := range field1 { + field1Rule = append(field1Rule, field1Item) + } + var field2Rule []interface{} + for _, field2Item := range field2 { + field2Rule = append(field2Rule, field2Item) + } + var field3Rule []interface{} + for _, field3Item := range field3 { + field3Rule = append(field3Rule, field3Item) + } + + logs, sub, err := _LatestValueHolder.contract.WatchLogs(opts, "TriggeredWithFourTopics", field1Rule, field2Rule, field3Rule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LatestValueHolderTriggeredWithFourTopics) + if err := _LatestValueHolder.contract.UnpackLog(event, "TriggeredWithFourTopics", 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 (_LatestValueHolder *LatestValueHolderFilterer) ParseTriggeredWithFourTopics(log types.Log) (*LatestValueHolderTriggeredWithFourTopics, error) { + event := new(LatestValueHolderTriggeredWithFourTopics) + if err := _LatestValueHolder.contract.UnpackLog(event, "TriggeredWithFourTopics", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_LatestValueHolder *LatestValueHolder) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _LatestValueHolder.abi.Events["Triggered"].ID: + return _LatestValueHolder.ParseTriggered(log) + case _LatestValueHolder.abi.Events["TriggeredEventWithDynamicTopic"].ID: + return _LatestValueHolder.ParseTriggeredEventWithDynamicTopic(log) + case _LatestValueHolder.abi.Events["TriggeredWithFourTopics"].ID: + return _LatestValueHolder.ParseTriggeredWithFourTopics(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (LatestValueHolderTriggered) Topic() common.Hash { + return common.HexToHash("0x7188419dcd8b51877b71766f075f3626586c0ff190e7d056aa65ce9acb649a3d") +} + +func (LatestValueHolderTriggeredEventWithDynamicTopic) Topic() common.Hash { + return common.HexToHash("0x3d969732b1bbbb9f1d7eb9f3f14e4cb50a74d950b3ef916a397b85dfbab93c67") +} + +func (LatestValueHolderTriggeredWithFourTopics) Topic() common.Hash { + return common.HexToHash("0x91c80dc390f3d041b3a04b0099b19634499541ea26972250986ee4b24a12fac5") +} + +func (_LatestValueHolder *LatestValueHolder) Address() common.Address { + return _LatestValueHolder.address +} + +type LatestValueHolderInterface interface { + GetDifferentPrimitiveValue(opts *bind.CallOpts) (uint64, error) + + GetElementAtIndex(opts *bind.CallOpts, i *big.Int) (TestStruct, error) + + GetPrimitiveValue(opts *bind.CallOpts) (uint64, error) + + GetSliceValue(opts *bind.CallOpts) ([]uint64, error) + + ReturnSeen(opts *bind.CallOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (TestStruct, error) + + AddTestStruct(opts *bind.TransactOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) + + TriggerEvent(opts *bind.TransactOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account common.Address, accounts []common.Address, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) + + TriggerEventWithDynamicTopic(opts *bind.TransactOpts, field string) (*types.Transaction, error) + + TriggerWithFourTopics(opts *bind.TransactOpts, field1 int32, field2 int32, field3 int32) (*types.Transaction, error) + + FilterTriggered(opts *bind.FilterOpts, field []int32) (*LatestValueHolderTriggeredIterator, error) + + WatchTriggered(opts *bind.WatchOpts, sink chan<- *LatestValueHolderTriggered, field []int32) (event.Subscription, error) + + ParseTriggered(log types.Log) (*LatestValueHolderTriggered, error) + + FilterTriggeredEventWithDynamicTopic(opts *bind.FilterOpts, fieldHash []string) (*LatestValueHolderTriggeredEventWithDynamicTopicIterator, error) + + WatchTriggeredEventWithDynamicTopic(opts *bind.WatchOpts, sink chan<- *LatestValueHolderTriggeredEventWithDynamicTopic, fieldHash []string) (event.Subscription, error) + + ParseTriggeredEventWithDynamicTopic(log types.Log) (*LatestValueHolderTriggeredEventWithDynamicTopic, error) + + FilterTriggeredWithFourTopics(opts *bind.FilterOpts, field1 []int32, field2 []int32, field3 []int32) (*LatestValueHolderTriggeredWithFourTopicsIterator, error) + + WatchTriggeredWithFourTopics(opts *bind.WatchOpts, sink chan<- *LatestValueHolderTriggeredWithFourTopics, field1 []int32, field2 []int32, field3 []int32) (event.Subscription, error) + + ParseTriggeredWithFourTopics(log types.Log) (*LatestValueHolderTriggeredWithFourTopics, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 3e8d6420e57..ea8d1a020e2 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -11,6 +11,7 @@ batch_blockhash_store: ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBloc batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.bin d0a54963260d8c1f1bbd984b758285e6027cfb5a7e42701bcb562ab123219332 batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin 7bb76ae241cf1b37b41920830b836cb99f1ad33efd7435ca2398ff6cd2fe5d48 blockhash_store: ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.abi ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.bin 12b0662f1636a341c8863bdec7a20f2ddd97c3a4fd1a7ae353fe316609face4e +chain_reader_example: ../../contracts/solc/v0.8.19/ChainReaderTestContract/LatestValueHolder.abi ../../contracts/solc/v0.8.19/ChainReaderTestContract/LatestValueHolder.bin de88c7e68de36b96aa2bec844bdc96fcd7c9017b38e25062b3b9f9cec42c814f chain_specific_util_helper: ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.bin 5f10664e31abc768f4a37901cae7a3bef90146180f97303e5a1bde5a08d84595 consumer_wrapper: ../../contracts/solc/v0.7/Consumer/Consumer.abi ../../contracts/solc/v0.7/Consumer/Consumer.bin 894d1cbd920dccbd36d92918c1037c6ded34f66f417ccb18ec3f33c64ef83ec5 cron_upkeep_factory_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeepFactory.abi - dacb0f8cdf54ae9d2781c5e720fc314b32ed5e58eddccff512c75d6067292cd7 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index f5aa4451bb4..9bd3f2fd41a 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -139,7 +139,9 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.bin MockAggregatorProxy mock_aggregator_proxy // Log tester -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin LogEmitter log_emitter + +// ChainReader test contract +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/ChainReaderTestContract/LatestValueHolder.abi ../../contracts/solc/v0.8.19/ChainReaderTestContract/LatestValueHolder.bin LatestValueHolder chain_reader_example // Chainlink Functions //go:generate go generate ./functions diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 863451de733..002f1d192ae 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -35,6 +35,7 @@ import ( ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" @@ -188,50 +189,62 @@ func setupNodeOCR2( func TestIntegration_OCR2(t *testing.T) { t.Parallel() - owner, b, ocrContractAddress, ocrContract := setupOCR2Contracts(t) - - lggr := logger.TestLogger(t) - bootstrapNodePort := freeport.GetOne(t) - bootstrapNode := setupNodeOCR2(t, owner, bootstrapNodePort, false /* useForwarders */, b, nil) - - var ( - oracles []confighelper2.OracleIdentityExtra - transmitters []common.Address - kbs []ocr2key.KeyBundle - apps []*cltest.TestApplication - ) - ports := freeport.GetN(t, 4) - for i := 0; i < 4; i++ { - node := setupNodeOCR2(t, owner, ports[i], false /* useForwarders */, b, []commontypes.BootstrapperLocator{ - // Supply the bootstrap IP and port as a V2 peer address - {PeerID: bootstrapNode.peerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, - }) - - kbs = append(kbs, node.keybundle) - apps = append(apps, node.app) - transmitters = append(transmitters, node.transmitter) - oracles = append(oracles, confighelper2.OracleIdentityExtra{ - OracleIdentity: confighelper2.OracleIdentity{ - OnchainPublicKey: node.keybundle.PublicKey(), - TransmitAccount: ocrtypes2.Account(node.transmitter.String()), - OffchainPublicKey: node.keybundle.OffchainPublicKey(), - PeerID: node.peerID, - }, - ConfigEncryptionPublicKey: node.keybundle.ConfigEncryptionPublicKey(), - }) - } + for _, test := range []struct { + name string + chainReaderAndCodec bool + }{ + {"legacy", false}, + {"chain-reader", true}, + } { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + owner, b, ocrContractAddress, ocrContract := setupOCR2Contracts(t) + + lggr := logger.TestLogger(t) + bootstrapNodePort := freeport.GetOne(t) + bootstrapNode := setupNodeOCR2(t, owner, bootstrapNodePort, false /* useForwarders */, b, nil) + + var ( + oracles []confighelper2.OracleIdentityExtra + transmitters []common.Address + kbs []ocr2key.KeyBundle + apps []*cltest.TestApplication + ) + ports := freeport.GetN(t, 4) + for i := 0; i < 4; i++ { + node := setupNodeOCR2(t, owner, ports[i], false /* useForwarders */, b, []commontypes.BootstrapperLocator{ + // Supply the bootstrap IP and port as a V2 peer address + {PeerID: bootstrapNode.peerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, + }) + + kbs = append(kbs, node.keybundle) + apps = append(apps, node.app) + transmitters = append(transmitters, node.transmitter) + + oracles = append(oracles, confighelper2.OracleIdentityExtra{ + OracleIdentity: confighelper2.OracleIdentity{ + OnchainPublicKey: node.keybundle.PublicKey(), + TransmitAccount: ocrtypes2.Account(node.transmitter.String()), + OffchainPublicKey: node.keybundle.OffchainPublicKey(), + PeerID: node.peerID, + }, + ConfigEncryptionPublicKey: node.keybundle.ConfigEncryptionPublicKey(), + }) + } - tick := time.NewTicker(1 * time.Second) - defer tick.Stop() - go func() { - for range tick.C { - b.Commit() - } - }() + tick := time.NewTicker(1 * time.Second) + defer tick.Stop() + go func() { + for range tick.C { + b.Commit() + } + }() - blockBeforeConfig := initOCR2(t, lggr, b, ocrContract, owner, bootstrapNode, oracles, transmitters, transmitters, func(blockNum int64) string { - return fmt.Sprintf(` + blockBeforeConfig := initOCR2(t, lggr, b, ocrContract, owner, bootstrapNode, oracles, transmitters, transmitters, func(blockNum int64) string { + return fmt.Sprintf(` type = "bootstrap" name = "bootstrap" relay = "evm" @@ -241,54 +254,189 @@ contractID = "%s" chainID = 1337 fromBlock = %d `, ocrContractAddress, blockNum) - }) - - var jids []int32 - var servers, slowServers = make([]*httptest.Server, 4), make([]*httptest.Server, 4) - // We expect metadata of: - // latestAnswer:nil // First call - // latestAnswer:0 - // latestAnswer:10 - // latestAnswer:20 - // latestAnswer:30 - var metaLock sync.Mutex - expectedMeta := map[string]struct{}{ - "0": {}, "10": {}, "20": {}, "30": {}, - } - for i := 0; i < 4; i++ { - s := i - require.NoError(t, apps[i].Start(testutils.Context(t))) - - // API speed is > observation timeout set in ContractSetConfigArgsForIntegrationTest - slowServers[i] = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - time.Sleep(5 * time.Second) - res.WriteHeader(http.StatusOK) - _, err := res.Write([]byte(`{"data":10}`)) - require.NoError(t, err) - })) - t.Cleanup(slowServers[s].Close) - servers[i] = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - b, err := io.ReadAll(req.Body) - require.NoError(t, err) - var m bridges.BridgeMetaDataJSON - require.NoError(t, json.Unmarshal(b, &m)) - if m.Meta.LatestAnswer != nil && m.Meta.UpdatedAt != nil { - metaLock.Lock() - delete(expectedMeta, m.Meta.LatestAnswer.String()) - metaLock.Unlock() + }) + + var jids []int32 + var servers, slowServers = make([]*httptest.Server, 4), make([]*httptest.Server, 4) + // We expect metadata of: + // latestAnswer:nil // First call + // latestAnswer:0 + // latestAnswer:10 + // latestAnswer:20 + // latestAnswer:30 + var metaLock sync.Mutex + expectedMeta := map[string]struct{}{ + "0": {}, "10": {}, "20": {}, "30": {}, } - res.WriteHeader(http.StatusOK) - _, err = res.Write([]byte(`{"data":10}`)) - require.NoError(t, err) - })) - t.Cleanup(servers[s].Close) - u, _ := url.Parse(servers[i].URL) - require.NoError(t, apps[i].BridgeORM().CreateBridgeType(&bridges.BridgeType{ - Name: bridges.BridgeName(fmt.Sprintf("bridge%d", i)), - URL: models.WebURL(*u), - })) - - ocrJob, err := validate.ValidatedOracleSpecToml(apps[i].Config.OCR2(), apps[i].Config.Insecure(), fmt.Sprintf(` + returnData := int(10) + for i := 0; i < 4; i++ { + s := i + require.NoError(t, apps[i].Start(testutils.Context(t))) + + // API speed is > observation timeout set in ContractSetConfigArgsForIntegrationTest + slowServers[i] = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Second) + var result string + metaLock.Lock() + result = fmt.Sprintf(`{"data":%d}`, returnData) + metaLock.Unlock() + res.WriteHeader(http.StatusOK) + t.Logf("Slow Bridge %d returning data:10", s) + _, err := res.Write([]byte(result)) + require.NoError(t, err) + })) + t.Cleanup(slowServers[s].Close) + servers[i] = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + b, err := io.ReadAll(req.Body) + require.NoError(t, err) + var m bridges.BridgeMetaDataJSON + require.NoError(t, json.Unmarshal(b, &m)) + var result string + metaLock.Lock() + result = fmt.Sprintf(`{"data":%d}`, returnData) + metaLock.Unlock() + if m.Meta.LatestAnswer != nil && m.Meta.UpdatedAt != nil { + t.Logf("Bridge %d deleting %s, from request body: %s", s, m.Meta.LatestAnswer, b) + metaLock.Lock() + delete(expectedMeta, m.Meta.LatestAnswer.String()) + metaLock.Unlock() + } + res.WriteHeader(http.StatusOK) + _, err = res.Write([]byte(result)) + require.NoError(t, err) + })) + t.Cleanup(servers[s].Close) + u, _ := url.Parse(servers[i].URL) + require.NoError(t, apps[i].BridgeORM().CreateBridgeType(&bridges.BridgeType{ + Name: bridges.BridgeName(fmt.Sprintf("bridge%d", i)), + URL: models.WebURL(*u), + })) + + var chainReaderSpec string + if test.chainReaderAndCodec { + chainReaderSpec = ` +[relayConfig.chainReader.contracts.median] +contractABI = ''' +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "round", + "type": "uint8" + } + ], + "name": "RoundRequested", + "type": "event" + }, + { + "inputs": [], + "name": "latestTransmissionDetails", + "outputs": [ + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "round", + "type": "uint8" + }, + { + "internalType": "int192", + "name": "latestAnswer_", + "type": "int192" + }, + { + "internalType": "uint64", + "name": "latestTimestamp_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] +''' + +[relayConfig.chainReader.contracts.median.configs] +LatestRoundRequested = ''' +{ + "chainSpecificName": "RoundRequested", + "readType": "event" +} +''' +LatestTransmissionDetails = ''' +{ + "chainSpecificName": "latestTransmissionDetails", + "outputModifications": [ + { + "Fields": [ + "LatestTimestamp_" + ], + "type": "epoch to time" + }, + { + "Fields": { + "LatestAnswer_": "LatestAnswer", + "LatestTimestamp_": "LatestTimestamp" + }, + "type": "rename" + } + ] +} +''' + +[relayConfig.codec.configs.MedianReport] +typeABI = ''' +[ + { + "Name": "Timestamp", + "Type": "uint32" + }, + { + "Name": "Observers", + "Type": "bytes32" + }, + { + "Name": "Observations", + "Type": "int192[]" + }, + { + "Name": "JuelsPerFeeCoin", + "Type": "int192" + } +] +''' +` + } + ocrJob, err := validate.ValidatedOracleSpecToml(apps[i].Config.OCR2(), apps[i].Config.Insecure(), fmt.Sprintf(` type = "offchainreporting2" relay = "evm" schemaVersion = 1 @@ -315,9 +463,12 @@ observationSource = """ answer1 [type=median index=0]; """ + [relayConfig] chainID = 1337 fromBlock = %d +%s + [pluginConfig] juelsPerFeeCoinSource = """ // data source 1 @@ -335,72 +486,131 @@ juelsPerFeeCoinSource = """ answer1 [type=median index=0]; """ -`, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, blockBeforeConfig.Number().Int64(), fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) - require.NoError(t, err) - err = apps[i].AddJobV2(testutils.Context(t), &ocrJob) - require.NoError(t, err) - jids = append(jids, ocrJob.ID) - } +`, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, blockBeforeConfig.Number().Int64(), chainReaderSpec, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) + require.NoError(t, err) + err = apps[i].AddJobV2(testutils.Context(t), &ocrJob) + require.NoError(t, err) + jids = append(jids, ocrJob.ID) + } - // Assert that all the OCR jobs get a run with valid values eventually. - var wg sync.WaitGroup - for i := 0; i < 4; i++ { - ic := i - wg.Add(1) - go func() { - defer wg.Done() - // Want at least 2 runs so we see all the metadata. - pr := cltest.WaitForPipelineComplete(t, ic, jids[ic], 2, 7, apps[ic].JobORM(), 2*time.Minute, 5*time.Second) - jb, err := pr[0].Outputs.MarshalJSON() + // Watch for OCR2AggregatorTransmitted events + start := uint64(0) + txEvents := make(chan *ocr2aggregator.OCR2AggregatorTransmitted) + _, err := ocrContract.WatchTransmitted(&bind.WatchOpts{Start: &start, Context: testutils.Context(t)}, txEvents) require.NoError(t, err) - assert.Equal(t, []byte(fmt.Sprintf("[\"%d\"]", 10*ic)), jb, "pr[0] %+v pr[1] %+v", pr[0], pr[1]) + newTxEvents := make(chan *ocr2aggregator.OCR2AggregatorNewTransmission) + _, err = ocrContract.WatchNewTransmission(&bind.WatchOpts{Start: &start, Context: testutils.Context(t)}, newTxEvents, []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) require.NoError(t, err) - }() - } - wg.Wait() - // 4 oracles reporting 0, 10, 20, 30. Answer should be 20 (results[4/2]). - gomega.NewGomegaWithT(t).Eventually(func() string { - answer, err := ocrContract.LatestAnswer(nil) - require.NoError(t, err) - return answer.String() - }, 1*time.Minute, 200*time.Millisecond).Should(gomega.Equal("20")) + go func() { + var newTxEvent *ocr2aggregator.OCR2AggregatorNewTransmission + select { + case txEvent := <-txEvents: + t.Logf("txEvent: %v", txEvent) + if newTxEvent != nil { + assert.Equal(t, txEvent.Epoch, uint32(newTxEvent.EpochAndRound.Uint64())) + } + case newTxEvent = <-newTxEvents: + t.Logf("newTxEvent: %v", newTxEvent) + } + }() - for _, app := range apps { - jobs, _, err := app.JobORM().FindJobs(0, 1000) - require.NoError(t, err) - // No spec errors - for _, j := range jobs { - ignore := 0 - for i := range j.JobSpecErrors { - // Non-fatal timing related error, ignore for testing. - if strings.Contains(j.JobSpecErrors[i].Description, "leader's phase conflicts tGrace timeout") { - ignore++ + for trial := 0; trial < 2; trial++ { + var retVal int + + metaLock.Lock() + returnData = 10 * (trial + 1) + retVal = returnData + for i := 0; i < 4; i++ { + expectedMeta[fmt.Sprintf("%d", returnData*i)] = struct{}{} + } + metaLock.Unlock() + + // Assert that all the OCR jobs get a run with valid values eventually. + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + ic := i + wg.Add(1) + go func() { + defer wg.Done() + completedRuns, err2 := apps[ic].JobORM().FindPipelineRunIDsByJobID(jids[ic], 0, 1000) + require.NoError(t, err2) + // Want at least 2 runs so we see all the metadata. + pr := cltest.WaitForPipelineComplete(t, ic, jids[ic], len(completedRuns)+2, 7, apps[ic].JobORM(), 2*time.Minute, 5*time.Second) + jb, err2 := pr[0].Outputs.MarshalJSON() + require.NoError(t, err2) + assert.Equal(t, []byte(fmt.Sprintf("[\"%d\"]", retVal*ic)), jb, "pr[0] %+v pr[1] %+v", pr[0], pr[1]) + require.NoError(t, err2) + }() + } + wg.Wait() + + // Trail #1: 4 oracles reporting 0, 10, 20, 30. Answer should be 20 (results[4/2]). + // Trial #2: 4 oracles reporting 0, 20, 40, 60. Answer should be 40 (results[4/2]). + gomega.NewGomegaWithT(t).Eventually(func() string { + answer, err2 := ocrContract.LatestAnswer(nil) + require.NoError(t, err2) + return answer.String() + }, 1*time.Minute, 200*time.Millisecond).Should(gomega.Equal(fmt.Sprintf("%d", 2*retVal))) + + for _, app := range apps { + jobs, _, err2 := app.JobORM().FindJobs(0, 1000) + require.NoError(t, err2) + // No spec errors + for _, j := range jobs { + ignore := 0 + for i := range j.JobSpecErrors { + // Non-fatal timing related error, ignore for testing. + if strings.Contains(j.JobSpecErrors[i].Description, "leader's phase conflicts tGrace timeout") { + ignore++ + } + } + require.Len(t, j.JobSpecErrors, ignore) + } + } + em := map[string]struct{}{} + metaLock.Lock() + maps.Copy(em, expectedMeta) + metaLock.Unlock() + assert.Len(t, em, 0, "expected metadata %v", em) + + t.Logf("======= Summary =======") + roundId, err2 := ocrContract.LatestRound(nil) + require.NoError(t, err2) + for i := 0; i <= int(roundId.Int64()); i++ { + roundData, err3 := ocrContract.GetRoundData(nil, big.NewInt(int64(i))) + require.NoError(t, err3) + t.Logf("RoundId: %d, AnsweredInRound: %d, Answer: %d, StartedAt: %v, UpdatedAt: %v", roundData.RoundId, roundData.AnsweredInRound, roundData.Answer, roundData.StartedAt, roundData.UpdatedAt) + } + + expectedAnswer := big.NewInt(2 * int64(retVal)) + + // Assert we can read the latest config digest and epoch after a report has been submitted. + contractABI, err2 := abi.JSON(strings.NewReader(ocr2aggregator.OCR2AggregatorABI)) + require.NoError(t, err2) + apps[0].GetRelayers().LegacyEVMChains().Slice() + ct, err2 := evm.NewOCRContractTransmitter(ocrContractAddress, apps[0].GetRelayers().LegacyEVMChains().Slice()[0].Client(), contractABI, nil, apps[0].GetRelayers().LegacyEVMChains().Slice()[0].LogPoller(), lggr, nil) + require.NoError(t, err2) + configDigest, epoch, err2 := ct.LatestConfigDigestAndEpoch(testutils.Context(t)) + require.NoError(t, err2) + details, err2 := ocrContract.LatestConfigDetails(nil) + require.NoError(t, err2) + assert.True(t, bytes.Equal(configDigest[:], details.ConfigDigest[:])) + digestAndEpoch, err2 := ocrContract.LatestConfigDigestAndEpoch(nil) + require.NoError(t, err2) + assert.Equal(t, digestAndEpoch.Epoch, epoch) + latestTransmissionDetails, err2 := ocrContract.LatestTransmissionDetails(nil) + require.NoError(t, err2) + assert.Equal(t, expectedAnswer, latestTransmissionDetails.LatestAnswer) + require.NoError(t, err2) + newTransmissionEvents, err2 := ocrContract.FilterTransmitted(&bind.FilterOpts{Start: 0, End: nil}) + require.NoError(t, err2) + for newTransmissionEvents.Next() { + assert.Equal(t, 3, newTransmissionEvents.Event.Epoch) } } - require.Len(t, j.JobSpecErrors, ignore) - } + }) } - em := map[string]struct{}{} - metaLock.Lock() - maps.Copy(em, expectedMeta) - metaLock.Unlock() - assert.Len(t, em, 0, "expected metadata %v", em) - - // Assert we can read the latest config digest and epoch after a report has been submitted. - contractABI, err := abi.JSON(strings.NewReader(ocr2aggregator.OCR2AggregatorABI)) - require.NoError(t, err) - apps[0].GetRelayers().LegacyEVMChains().Slice() - ct, err := evm.NewOCRContractTransmitter(ocrContractAddress, apps[0].GetRelayers().LegacyEVMChains().Slice()[0].Client(), contractABI, nil, apps[0].GetRelayers().LegacyEVMChains().Slice()[0].LogPoller(), lggr, nil) - require.NoError(t, err) - configDigest, epoch, err := ct.LatestConfigDigestAndEpoch(testutils.Context(t)) - require.NoError(t, err) - details, err := ocrContract.LatestConfigDetails(nil) - require.NoError(t, err) - assert.True(t, bytes.Equal(configDigest[:], details.ConfigDigest[:])) - digestAndEpoch, err := ocrContract.LatestConfigDigestAndEpoch(nil) - require.NoError(t, err) - assert.Equal(t, digestAndEpoch.Epoch, epoch) } func initOCR2(t *testing.T, lggr logger.Logger, b *backends.SimulatedBackend, diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 50c7ea8bb00..7d82e2b4d75 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 @@ -244,11 +244,11 @@ require ( github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect - github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index e4e7406126c..2142547c1ea 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1167,18 +1167,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 h1:3cWO2/lFVDul5SVTgl4/RX/GXcT8Zq5NGMPeNEz09tY= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a h1:lgM0yPo0KqSntLY4Y42RAH3avdv+Kyne8n+VM7cwlxo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 h1:ziqC+WW/2/UI6w3DShy7HGzJMWWLIYHT5ev2Qaa3h6I= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 h1:1BcjXuviSAKttOX7BZoVHRZZGfxqoA2+AL8tykmkdoc= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8/go.mod h1:vy1L7NybTy2F/Yv7BOh+oZBa1MACD6gzd1+DkcSkfp8= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba h1:6rnQrD8NaLfLOPHszW1hbpviqpU8011gzdZk6wKP1xY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= @@ -1789,8 +1789,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= -google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= +google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/core/services/job/models.go b/core/services/job/models.go index 31032d58e0e..17bac545be1 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -277,7 +277,8 @@ func (s *OCROracleSpec) SetID(value string) error { return nil } -// JSONConfig is a Go mapping for JSON based database properties. +// JSONConfig is a map for config properties which are encoded as JSON in the database by implementing +// sql.Scanner and driver.Valuer. type JSONConfig map[string]interface{} // Bytes returns the raw bytes diff --git a/core/services/job/models_test.go b/core/services/job/models_test.go index ddbb815e730..fa15e3b1b22 100644 --- a/core/services/job/models_test.go +++ b/core/services/job/models_test.go @@ -1,10 +1,20 @@ package job import ( + _ "embed" "reflect" "testing" + "time" + "github.com/pelletier/go-toml/v2" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/services/relay" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestOCR2OracleSpec_RelayIdentifier(t *testing.T) { @@ -71,3 +81,183 @@ func TestOCR2OracleSpec_RelayIdentifier(t *testing.T) { }) } } + +var ( + //go:embed testdata/compact.toml + compact string + //go:embed testdata/pretty.toml + pretty string +) + +func TestOCR2OracleSpec(t *testing.T) { + val := OCR2OracleSpec{ + Relay: relay.EVM, + PluginType: types.Median, + ContractID: "foo", + OCRKeyBundleID: null.StringFrom("bar"), + TransmitterID: null.StringFrom("baz"), + ContractConfigConfirmations: 1, + ContractConfigTrackerPollInterval: *models.NewInterval(time.Second), + RelayConfig: map[string]interface{}{ + "chainID": 1337, + "fromBlock": 42, + "chainReader": evmtypes.ChainReaderConfig{ + Contracts: map[string]evmtypes.ChainContractReader{ + "median": { + ContractABI: `[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "round", + "type": "uint8" + } + ], + "name": "RoundRequested", + "type": "event" + }, + { + "inputs": [], + "name": "latestTransmissionDetails", + "outputs": [ + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "round", + "type": "uint8" + }, + { + "internalType": "int192", + "name": "latestAnswer_", + "type": "int192" + }, + { + "internalType": "uint64", + "name": "latestTimestamp_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] +`, + Configs: map[string]*evmtypes.ChainReaderDefinition{ + "LatestTransmissionDetails": { + ChainSpecificName: "latestTransmissionDetails", + OutputModifications: codec.ModifiersConfig{ + &codec.EpochToTimeModifierConfig{ + Fields: []string{"LatestTimestamp_"}, + }, + &codec.RenameModifierConfig{ + Fields: map[string]string{ + "LatestAnswer_": "LatestAnswer", + "LatestTimestamp_": "LatestTimestamp", + }, + }, + }, + }, + "LatestRoundRequested": { + ChainSpecificName: "RoundRequested", + ReadType: evmtypes.Event, + }, + }, + }, + }, + }, + "codec": evmtypes.CodecConfig{ + Configs: map[string]evmtypes.ChainCodecConfig{ + "MedianReport": { + TypeABI: `[ + { + "Name": "Timestamp", + "Type": "uint32" + }, + { + "Name": "Observers", + "Type": "bytes32" + }, + { + "Name": "Observations", + "Type": "int192[]" + }, + { + "Name": "JuelsPerFeeCoin", + "Type": "int192" + } +] +`, + }, + }, + }, + }, + PluginConfig: map[string]interface{}{"juelsPerFeeCoinSource": ` // data source 1 + ds1 [type=bridge name="%s"]; + ds1_parse [type=jsonparse path="data"]; + ds1_multiply [type=multiply times=2]; + + // data source 2 + ds2 [type=http method=GET url="%s"]; + ds2_parse [type=jsonparse path="data"]; + ds2_multiply [type=multiply times=2]; + + ds1 -> ds1_parse -> ds1_multiply -> answer1; + ds2 -> ds2_parse -> ds2_multiply -> answer1; + + answer1 [type=median index=0]; +`, + }, + } + + t.Run("marshal", func(t *testing.T) { + gotB, err := toml.Marshal(val) + require.NoError(t, err) + t.Log("marshaled:", string(gotB)) + require.Equal(t, compact, string(gotB)) + }) + + t.Run("round-trip", func(t *testing.T) { + var gotVal OCR2OracleSpec + require.NoError(t, toml.Unmarshal([]byte(compact), &gotVal)) + gotB, err := toml.Marshal(gotVal) + require.NoError(t, err) + require.Equal(t, compact, string(gotB)) + t.Run("pretty", func(t *testing.T) { + var gotVal OCR2OracleSpec + require.NoError(t, toml.Unmarshal([]byte(pretty), &gotVal)) + gotB, err := toml.Marshal(gotVal) + require.NoError(t, err) + t.Log("marshaled compact:", string(gotB)) + require.Equal(t, compact, string(gotB)) + }) + }) +} diff --git a/core/services/job/testdata/compact.toml b/core/services/job/testdata/compact.toml new file mode 100644 index 00000000000..9f0f54027d2 --- /dev/null +++ b/core/services/job/testdata/compact.toml @@ -0,0 +1,34 @@ +contractID = 'foo' +relay = 'evm' +chainID = '' +p2pv2Bootstrappers = [] +ocrKeyBundleID = 'bar' +monitoringEndpoint = '' +transmitterID = 'baz' +blockchainTimeout = '0s' +contractConfigTrackerPollInterval = '1s' +contractConfigConfirmations = 1 +pluginType = 'median' +captureEATelemetry = false +captureAutomationCustomTelemetry = false + +[relayConfig] +chainID = 1337 +fromBlock = 42 + +[relayConfig.chainReader] +[relayConfig.chainReader.contracts] +[relayConfig.chainReader.contracts.median] +contractABI = "[\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"requester\",\n \"type\": \"address\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"bytes32\",\n \"name\": \"configDigest\",\n \"type\": \"bytes32\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint32\",\n \"name\": \"epoch\",\n \"type\": \"uint32\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint8\",\n \"name\": \"round\",\n \"type\": \"uint8\"\n }\n ],\n \"name\": \"RoundRequested\",\n \"type\": \"event\"\n },\n {\n \"inputs\": [],\n \"name\": \"latestTransmissionDetails\",\n \"outputs\": [\n {\n \"internalType\": \"bytes32\",\n \"name\": \"configDigest\",\n \"type\": \"bytes32\"\n },\n {\n \"internalType\": \"uint32\",\n \"name\": \"epoch\",\n \"type\": \"uint32\"\n },\n {\n \"internalType\": \"uint8\",\n \"name\": \"round\",\n \"type\": \"uint8\"\n },\n {\n \"internalType\": \"int192\",\n \"name\": \"latestAnswer_\",\n \"type\": \"int192\"\n },\n {\n \"internalType\": \"uint64\",\n \"name\": \"latestTimestamp_\",\n \"type\": \"uint64\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n }\n]\n" + +[relayConfig.chainReader.contracts.median.configs] +LatestRoundRequested = "{\n \"chainSpecificName\": \"RoundRequested\",\n \"readType\": \"event\"\n}\n" +LatestTransmissionDetails = "{\n \"chainSpecificName\": \"latestTransmissionDetails\",\n \"outputModifications\": [\n {\n \"Fields\": [\n \"LatestTimestamp_\"\n ],\n \"Type\": \"epoch to time\"\n },\n {\n \"Fields\": {\n \"LatestAnswer_\": \"LatestAnswer\",\n \"LatestTimestamp_\": \"LatestTimestamp\"\n },\n \"Type\": \"rename\"\n }\n ]\n}\n" + +[relayConfig.codec] +[relayConfig.codec.configs] +[relayConfig.codec.configs.MedianReport] +typeABI = "[\n {\n \"Name\": \"Timestamp\",\n \"Type\": \"uint32\"\n },\n {\n \"Name\": \"Observers\",\n \"Type\": \"bytes32\"\n },\n {\n \"Name\": \"Observations\",\n \"Type\": \"int192[]\"\n },\n {\n \"Name\": \"JuelsPerFeeCoin\",\n \"Type\": \"int192\"\n }\n]\n" + +[pluginConfig] +juelsPerFeeCoinSource = " // data source 1\n ds1 [type=bridge name=\"%s\"];\n ds1_parse [type=jsonparse path=\"data\"];\n ds1_multiply [type=multiply times=2];\n\n // data source 2\n ds2 [type=http method=GET url=\"%s\"];\n ds2_parse [type=jsonparse path=\"data\"];\n ds2_multiply [type=multiply times=2];\n\n ds1 -> ds1_parse -> ds1_multiply -> answer1;\n ds2 -> ds2_parse -> ds2_multiply -> answer1;\n\n answer1 [type=median index=0];\n" diff --git a/core/services/job/testdata/pretty.toml b/core/services/job/testdata/pretty.toml new file mode 100644 index 00000000000..88bacff7db2 --- /dev/null +++ b/core/services/job/testdata/pretty.toml @@ -0,0 +1,149 @@ +relay = "evm" +pluginType = "median" +contractID = "foo" +ocrKeyBundleID = "bar" +transmitterID = "baz" +contractConfigConfirmations = 1 +contractConfigTrackerPollInterval = "1s" + +[relayConfig] +chainID = 1337 +fromBlock = 42 + +[relayConfig.chainReader.contracts.median] +contractABI = ''' +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "round", + "type": "uint8" + } + ], + "name": "RoundRequested", + "type": "event" + }, + { + "inputs": [], + "name": "latestTransmissionDetails", + "outputs": [ + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "round", + "type": "uint8" + }, + { + "internalType": "int192", + "name": "latestAnswer_", + "type": "int192" + }, + { + "internalType": "uint64", + "name": "latestTimestamp_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] +''' + +[relayConfig.chainReader.contracts.median.configs] +LatestRoundRequested = ''' +{ + "chainSpecificName": "RoundRequested", + "readType": "event" +} +''' +LatestTransmissionDetails = ''' +{ + "chainSpecificName": "latestTransmissionDetails", + "outputModifications": [ + { + "Fields": [ + "LatestTimestamp_" + ], + "Type": "epoch to time" + }, + { + "Fields": { + "LatestAnswer_": "LatestAnswer", + "LatestTimestamp_": "LatestTimestamp" + }, + "Type": "rename" + } + ] +} +''' + +[relayConfig.codec.configs.MedianReport] +typeABI = ''' +[ + { + "Name": "Timestamp", + "Type": "uint32" + }, + { + "Name": "Observers", + "Type": "bytes32" + }, + { + "Name": "Observations", + "Type": "int192[]" + }, + { + "Name": "JuelsPerFeeCoin", + "Type": "int192" + } +] +''' + +[pluginConfig] +juelsPerFeeCoinSource = """ + // data source 1 + ds1 [type=bridge name="%s"]; + ds1_parse [type=jsonparse path="data"]; + ds1_multiply [type=multiply times=2]; + + // data source 2 + ds2 [type=http method=GET url="%s"]; + ds2_parse [type=jsonparse path="data"]; + ds2_multiply [type=multiply times=2]; + + ds1 -> ds1_parse -> ds1_multiply -> answer1; + ds2 -> ds2_parse -> ds2_multiply -> answer1; + + answer1 [type=median index=0]; +""" \ No newline at end of file diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go deleted file mode 100644 index cad2099832d..00000000000 --- a/core/services/ocr2/plugins/median/plugin.go +++ /dev/null @@ -1,68 +0,0 @@ -package median - -import ( - "context" - - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/loop" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types" -) - -type Plugin struct { - loop.Plugin - stop services.StopChan -} - -func NewPlugin(lggr logger.Logger) *Plugin { - return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(services.StopChan)} -} - -func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProvider, dataSource, juelsPerFeeCoin median.DataSource, errorLog loop.ErrorLog) (loop.ReportingPluginFactory, error) { - var ctxVals loop.ContextValues - ctxVals.SetValues(ctx) - lggr := logger.With(p.Logger, ctxVals.Args()...) - - factory := median.NumericalMedianFactory{ - ContractTransmitter: provider.MedianContract(), - DataSource: dataSource, - JuelsPerFeeCoinDataSource: juelsPerFeeCoin, - Logger: logger.NewOCRWrapper(lggr, true, func(msg string) { - ctx, cancelFn := p.stop.NewCtx() - defer cancelFn() - if err := errorLog.SaveError(ctx, msg); err != nil { - lggr.Errorw("Unable to save error", "err", msg) - } - }), - OnchainConfigCodec: provider.OnchainConfigCodec(), - ReportCodec: provider.ReportCodec(), - } - s := &reportingPluginFactoryService{lggr: logger.Named(lggr, "ReportingPluginFactory"), ReportingPluginFactory: factory} - - p.SubService(s) - - return s, nil -} - -type reportingPluginFactoryService struct { - services.StateMachine - lggr logger.Logger - ocrtypes.ReportingPluginFactory -} - -func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() } - -func (r *reportingPluginFactoryService) Start(ctx context.Context) error { - return r.StartOnce("ReportingPluginFactory", func() error { return nil }) -} - -func (r *reportingPluginFactoryService) Close() error { - return r.StopOnce("ReportingPluginFactory", func() error { return nil }) -} - -func (r *reportingPluginFactoryService) HealthReport() map[string]error { - return map[string]error{r.Name(): r.Healthy()} -} diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 4adfc306d64..bdf6feaed47 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -5,14 +5,9 @@ import ( "encoding/json" "errors" "fmt" - "math/big" "time" - "github.com/ethereum/go-ethereum/common" libocr "github.com/smartcontractkit/libocr/offchainreporting2plus" - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - mediantypes "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -57,21 +52,6 @@ func (m *medianConfig) JobPipelineResultWriteQueueDepth() uint64 { return m.jobPipelineResultWriteQueueDepth } -// This wrapper avoids the need to modify the signature of NewMedianFactory in all of the non-evm -// relay repos as well as its primary definition in chainlink-common. Once ChainReader is implemented -// and working on all 4 blockchain families, we can remove the original MedianContract() method from -// MedianProvider and pass medianContract as a separate param to NewMedianFactory -type medianProviderWrapper struct { - types.MedianProvider - contract mediantypes.MedianContract -} - -// Override relay's implementation of MedianContract with product plugin's implementation of -// MedianContract, making use of product-agnostic ChainReader to read the contract instead of relay MedianContract -func (m medianProviderWrapper) MedianContract() mediantypes.MedianContract { - return m.contract -} - func NewMedianServices(ctx context.Context, jb job.Job, isNewlyCreatedJob bool, @@ -145,24 +125,10 @@ func NewMedianServices(ctx context.Context, CreatedAt: time.Now(), }, lggr) - medianPluginCmd := env.MedianPluginCmd.Get() - medianLoopEnabled := medianPluginCmd != "" - - // TODO BCF-2821 handle this properly as this blocks Solana chain reader dev - if !medianLoopEnabled && medianProvider.ChainReader() != nil { - lggr.Info("Chain Reader enabled") - medianProvider = medianProviderWrapper{ - medianProvider, // attach newer MedianContract which uses ChainReader - newMedianContract(provider.ChainReader(), common.HexToAddress(spec.ContractID)), - } - } else { - lggr.Info("Chain Reader disabled") - } - - if medianLoopEnabled { + if cmdName := env.MedianPluginCmd.Get(); cmdName != "" { // use unique logger names so we can use it to register a loop medianLggr := lggr.Named("Median").Named(spec.ContractID).Named(spec.GetID()) - cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), medianPluginCmd) + cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), cmdName) if err2 != nil { err = fmt.Errorf("failed to register loop: %w", err2) abort() @@ -192,49 +158,3 @@ func NewMedianServices(ctx context.Context, } return } - -type medianContract struct { - chainReader types.ChainReader - contract types.BoundContract -} - -type latestTransmissionDetailsResponse struct { - configDigest ocr2types.ConfigDigest - epoch uint32 - round uint8 - latestAnswer *big.Int - latestTimestamp time.Time -} - -type latestRoundRequested struct { - configDigest ocr2types.ConfigDigest - epoch uint32 - round uint8 -} - -func (m *medianContract) LatestTransmissionDetails(ctx context.Context) (configDigest ocr2types.ConfigDigest, epoch uint32, round uint8, latestAnswer *big.Int, latestTimestamp time.Time, err error) { - var resp latestTransmissionDetailsResponse - - err = m.chainReader.GetLatestValue(ctx, m.contract, "LatestTransmissionDetails", nil, &resp) - if err != nil { - return - } - - return resp.configDigest, resp.epoch, resp.round, resp.latestAnswer, resp.latestTimestamp, err -} - -func (m *medianContract) LatestRoundRequested(ctx context.Context, lookback time.Duration) (configDigest ocr2types.ConfigDigest, epoch uint32, round uint8, err error) { - var resp latestRoundRequested - - err = m.chainReader.GetLatestValue(ctx, m.contract, "LatestRoundReported", map[string]string{}, &resp) - if err != nil { - return - } - - return resp.configDigest, resp.epoch, resp.round, err -} - -func newMedianContract(chainReader types.ChainReader, address common.Address) *medianContract { - contract := types.BoundContract{Address: address.String(), Name: "median", Pending: true} - return &medianContract{chainReader, contract} -} diff --git a/core/services/relay/evm/binding.go b/core/services/relay/evm/binding.go new file mode 100644 index 00000000000..e78d9f0a770 --- /dev/null +++ b/core/services/relay/evm/binding.go @@ -0,0 +1,15 @@ +package evm + +import ( + "context" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type readBinding interface { + GetLatestValue(ctx context.Context, params, returnVal any) error + Bind(binding commontypes.BoundContract) error + SetCodec(codec commontypes.RemoteCodec) + Register() error + Unregister() error +} diff --git a/core/services/relay/evm/bindings.go b/core/services/relay/evm/bindings.go new file mode 100644 index 00000000000..1a23128d19f --- /dev/null +++ b/core/services/relay/evm/bindings.go @@ -0,0 +1,61 @@ +package evm + +import ( + "fmt" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +// key is contract name +type contractBindings map[string]readBindings + +// key is read name +type readBindings map[string]readBinding + +func (b contractBindings) GetReadBinding(contractName, readName string) (readBinding, error) { + rb, rbExists := b[contractName] + if !rbExists { + return nil, fmt.Errorf("%w: no contract named %s", commontypes.ErrInvalidType, contractName) + } + + reader, readerExists := rb[readName] + if !readerExists { + return nil, fmt.Errorf("%w: no readName named %s in contract %s", commontypes.ErrInvalidType, readName, contractName) + } + return reader, nil +} + +func (b contractBindings) AddReadBinding(contractName, readName string, reader readBinding) { + rbs, rbsExists := b[contractName] + if !rbsExists { + rbs = readBindings{} + b[contractName] = rbs + } + rbs[readName] = reader +} + +func (b contractBindings) Bind(boundContracts []commontypes.BoundContract) error { + for _, bc := range boundContracts { + rbs, rbsExist := b[bc.Name] + if !rbsExist { + return fmt.Errorf("%w: no contract named %s", commontypes.ErrInvalidConfig, bc.Name) + } + for _, r := range rbs { + if err := r.Bind(bc); err != nil { + return err + } + } + } + return nil +} + +func (b contractBindings) ForEach(fn func(readBinding) error) error { + for _, rbs := range b { + for _, rb := range rbs { + if err := fn(rb); err != nil { + return err + } + } + } + return nil +} diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go index e4da4cc1a49..dba05af7e3c 100644 --- a/core/services/relay/evm/chain_reader.go +++ b/core/services/relay/evm/chain_reader.go @@ -3,14 +3,20 @@ package evm import ( "context" "fmt" - "slices" + "reflect" "strings" "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" + "github.com/smartcontractkit/chainlink-common/pkg/codec" + + commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" @@ -23,132 +29,264 @@ type ChainReaderService interface { } type chainReader struct { - lggr logger.Logger - contractID common.Address - lp logpoller.LogPoller + lggr logger.Logger + lp logpoller.LogPoller + client evmclient.Client + contractBindings contractBindings + parsed *parsedTypes + codec commontypes.RemoteCodec + commonservices.StateMachine } -// NewChainReaderService constructor for ChainReader -func NewChainReaderService(lggr logger.Logger, lp logpoller.LogPoller, contractID common.Address, config types.ChainReaderConfig) (*chainReader, error) { - if err := validateChainReaderConfig(config); err != nil { - return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidConfig, err) +// NewChainReaderService is a constructor for ChainReader, returns nil if there is any error +func NewChainReaderService(lggr logger.Logger, lp logpoller.LogPoller, chain legacyevm.Chain, config types.ChainReaderConfig) (ChainReaderService, error) { + cr := &chainReader{ + lggr: lggr.Named("ChainReader"), + lp: lp, + client: chain.Client(), + contractBindings: contractBindings{}, + parsed: &parsedTypes{encoderDefs: map[string]types.CodecEntry{}, decoderDefs: map[string]types.CodecEntry{}}, } - // TODO BCF-2814 implement initialisation of chain reading definitions and pass them into chainReader - return &chainReader{lggr.Named("ChainReader"), contractID, lp}, nil -} + var err error + if err = cr.init(config.Contracts); err != nil { + return nil, err + } -func (cr *chainReader) Name() string { return cr.lggr.Name() } + if cr.codec, err = cr.parsed.toCodec(); err != nil { + return nil, err + } -func (cr *chainReader) initialize() error { - // Initialize chain reader, start cache polling loop, etc. - return nil -} + err = cr.contractBindings.ForEach(func(b readBinding) error { + b.SetCodec(cr.codec) + return nil + }) -func (cr *chainReader) Start(ctx context.Context) error { - if err := cr.initialize(); err != nil { - return fmt.Errorf("Failed to initialize ChainReader: %w", err) - } - return nil + return cr, err } -func (cr *chainReader) Close() error { return nil } +func (cr *chainReader) Name() string { return cr.lggr.Name() } -func (cr *chainReader) Ready() error { return nil } +var _ commontypes.ContractTypeProvider = &chainReader{} -func (cr *chainReader) HealthReport() map[string]error { - return map[string]error{cr.Name(): nil} -} +func (cr *chainReader) GetLatestValue(ctx context.Context, contractName, method string, params any, returnVal any) error { + b, err := cr.contractBindings.GetReadBinding(contractName, method) + if err != nil { + return err + } -func (cr *chainReader) GetLatestValue(ctx context.Context, bc commontypes.BoundContract, method string, params any, returnVal any) error { - return commontypes.UnimplementedError("Unimplemented method GetLatestValue called") + return b.GetLatestValue(ctx, params, returnVal) } -func validateChainReaderConfig(cfg types.ChainReaderConfig) error { - if len(cfg.ChainContractReaders) == 0 { - return fmt.Errorf("%w: no contract readers defined", commontypes.ErrInvalidConfig) - } +func (cr *chainReader) Bind(_ context.Context, bindings []commontypes.BoundContract) error { + return cr.contractBindings.Bind(bindings) +} - for contractName, chainContractReader := range cfg.ChainContractReaders { - abi, err := abi.JSON(strings.NewReader(chainContractReader.ContractABI)) +func (cr *chainReader) init(chainContractReaders map[string]types.ChainContractReader) error { + for contractName, chainContractReader := range chainContractReaders { + contractAbi, err := abi.JSON(strings.NewReader(chainContractReader.ContractABI)) if err != nil { - return fmt.Errorf("invalid abi: %w", err) + return err } - for chainReadingDefinitionName, chainReaderDefinition := range chainContractReader.ChainReaderDefinitions { + for typeName, chainReaderDefinition := range chainContractReader.Configs { switch chainReaderDefinition.ReadType { case types.Method: - err = validateMethods(abi, chainReaderDefinition) + err = cr.addMethod(contractName, typeName, contractAbi, *chainReaderDefinition) case types.Event: - err = validateEvents(abi, chainReaderDefinition) + err = cr.addEvent(contractName, typeName, contractAbi, *chainReaderDefinition) default: - return fmt.Errorf("%w: invalid chainreading definition read type: %d for contract: %q", commontypes.ErrInvalidConfig, chainReaderDefinition.ReadType, contractName) + return fmt.Errorf( + "%w: invalid chain reader definition read type: %s", + commontypes.ErrInvalidConfig, + chainReaderDefinition.ReadType) } + if err != nil { - return fmt.Errorf("%w: invalid chainreading definition: %q for contract: %q, err: %w", commontypes.ErrInvalidConfig, chainReadingDefinitionName, contractName, err) + return err } } } - return nil } -func validateEvents(contractABI abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { - event, methodExists := contractABI.Events[chainReaderDefinition.ChainSpecificName] +func (cr *chainReader) Start(_ context.Context) error { + return cr.StartOnce("ChainReader", func() error { + return cr.contractBindings.ForEach(readBinding.Register) + }) +} + +func (cr *chainReader) Close() error { + return cr.StopOnce("ChainReader", func() error { + return cr.contractBindings.ForEach(readBinding.Unregister) + }) +} + +func (cr *chainReader) Ready() error { return nil } +func (cr *chainReader) HealthReport() map[string]error { + return map[string]error{cr.Name(): nil} +} + +func (cr *chainReader) CreateContractType(contractName, methodName string, forEncoding bool) (any, error) { + return cr.codec.CreateType(wrapItemType(contractName, methodName, forEncoding), forEncoding) +} + +func wrapItemType(contractName, methodName string, isParams bool) string { + if isParams { + return fmt.Sprintf("params.%s.%s", contractName, methodName) + } + return fmt.Sprintf("return.%s.%s", contractName, methodName) +} + +func (cr *chainReader) addMethod( + contractName, + methodName string, + abi abi.ABI, + chainReaderDefinition types.ChainReaderDefinition) error { + method, methodExists := abi.Methods[chainReaderDefinition.ChainSpecificName] if !methodExists { - return fmt.Errorf("event: %s doesn't exist", chainReaderDefinition.ChainSpecificName) + return fmt.Errorf("%w: method %s doesn't exist", commontypes.ErrInvalidConfig, chainReaderDefinition.ChainSpecificName) } - var abiEventIndexedInputs []abi.Argument - for _, eventInput := range event.Inputs { - if eventInput.Indexed { - abiEventIndexedInputs = append(abiEventIndexedInputs, eventInput) - } + if len(chainReaderDefinition.EventInputFields) != 0 { + return fmt.Errorf( + "%w: method %s has event topic fields defined, but is not an event", + commontypes.ErrInvalidConfig, + chainReaderDefinition.ChainSpecificName) } - var chainReaderEventParams []string - for chainReaderEventParam := range chainReaderDefinition.Params { - chainReaderEventParams = append(chainReaderEventParams, chainReaderEventParam) + cr.contractBindings.AddReadBinding(contractName, methodName, &methodBinding{ + contractName: contractName, + method: methodName, + client: cr.client, + }) + + if err := cr.addEncoderDef(contractName, methodName, method.Inputs, method.ID, chainReaderDefinition); err != nil { + return err } - if !areChainReaderArgumentsValid(abiEventIndexedInputs, chainReaderEventParams) { - var abiEventIndexedInputsNames []string - for _, abiEventIndexedInput := range abiEventIndexedInputs { - abiEventIndexedInputsNames = append(abiEventIndexedInputsNames, abiEventIndexedInput.Name) - } - return fmt.Errorf("params: [%s] don't match abi event indexed inputs: [%s]", strings.Join(chainReaderEventParams, ","), strings.Join(abiEventIndexedInputsNames, ",")) + return cr.addDecoderDef(contractName, methodName, method.Outputs, chainReaderDefinition) +} + +func (cr *chainReader) addEvent(contractName, eventName string, a abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { + event, eventExists := a.Events[chainReaderDefinition.ChainSpecificName] + if !eventExists { + return fmt.Errorf("%w: event %s doesn't exist", commontypes.ErrInvalidConfig, chainReaderDefinition.ChainSpecificName) } - return nil + + filterArgs, topicInfo, indexArgNames := setupEventInput(event, chainReaderDefinition) + if err := verifyEventInputsUsed(chainReaderDefinition, indexArgNames); err != nil { + return err + } + + if err := topicInfo.Init(); err != nil { + return err + } + + // Encoder def's codec won't be used to encode, only for its type as input for GetLatestValue + if err := cr.addEncoderDef(contractName, eventName, filterArgs, nil, chainReaderDefinition); err != nil { + return err + } + + inputInfo, inputModifier, err := cr.getEventInput(chainReaderDefinition, contractName, eventName) + if err != nil { + return err + } + + cr.contractBindings.AddReadBinding(contractName, eventName, &eventBinding{ + contractName: contractName, + eventName: eventName, + lp: cr.lp, + hash: event.ID, + inputInfo: inputInfo, + inputModifier: inputModifier, + topicInfo: topicInfo, + id: wrapItemType(contractName, eventName, false) + uuid.NewString(), + }) + + return cr.addDecoderDef(contractName, eventName, event.Inputs, chainReaderDefinition) } -func validateMethods(abi abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { - method, methodExists := abi.Methods[chainReaderDefinition.ChainSpecificName] - if !methodExists { - return fmt.Errorf("method: %q doesn't exist", chainReaderDefinition.ChainSpecificName) +func (cr *chainReader) getEventInput(def types.ChainReaderDefinition, contractName, eventName string) ( + types.CodecEntry, codec.Modifier, error) { + inputInfo := cr.parsed.encoderDefs[wrapItemType(contractName, eventName, true)] + inMod, err := def.InputModifications.ToModifier(evmDecoderHooks...) + if err != nil { + return nil, nil, err } - var methodNames []string - for methodName := range chainReaderDefinition.Params { - methodNames = append(methodNames, methodName) + // initialize the modification + if _, err = inMod.RetypeToOffChain(reflect.PointerTo(inputInfo.CheckedType()), ""); err != nil { + return nil, nil, err } - if !areChainReaderArgumentsValid(method.Inputs, methodNames) { - var abiMethodInputs []string - for _, input := range method.Inputs { - abiMethodInputs = append(abiMethodInputs, input.Name) + return inputInfo, inMod, nil +} + +func verifyEventInputsUsed(chainReaderDefinition types.ChainReaderDefinition, indexArgNames map[string]bool) error { + for _, value := range chainReaderDefinition.EventInputFields { + if !indexArgNames[abi.ToCamelCase(value)] { + return fmt.Errorf("%w: %s is not an indexed argument of event %s", commontypes.ErrInvalidConfig, value, chainReaderDefinition.ChainSpecificName) } - return fmt.Errorf("params: [%s] don't match abi method inputs: [%s]", strings.Join(methodNames, ","), strings.Join(abiMethodInputs, ",")) } + return nil +} +func (cr *chainReader) addEncoderDef(contractName, methodName string, args abi.Arguments, prefix []byte, chainReaderDefinition types.ChainReaderDefinition) error { + // ABI.Pack prepends the method.ID to the encodings, we'll need the encoder to do the same. + inputMod, err := chainReaderDefinition.InputModifications.ToModifier(evmDecoderHooks...) + if err != nil { + return err + } + input := types.NewCodecEntry(args, prefix, inputMod) + + if err := input.Init(); err != nil { + return err + } + + cr.parsed.encoderDefs[wrapItemType(contractName, methodName, true)] = input return nil } -func areChainReaderArgumentsValid(contractArgs []abi.Argument, chainReaderArgs []string) bool { - for _, contractArg := range contractArgs { - if !slices.Contains(chainReaderArgs, contractArg.Name) { - return false +func (cr *chainReader) addDecoderDef(contractName, methodName string, outputs abi.Arguments, def types.ChainReaderDefinition) error { + mod, err := def.OutputModifications.ToModifier(evmDecoderHooks...) + if err != nil { + return err + } + output := types.NewCodecEntry(outputs, nil, mod) + cr.parsed.decoderDefs[wrapItemType(contractName, methodName, false)] = output + return output.Init() +} + +func setupEventInput(event abi.Event, def types.ChainReaderDefinition) ([]abi.Argument, types.CodecEntry, map[string]bool) { + topicFieldDefs := map[string]bool{} + for _, value := range def.EventInputFields { + capFirstValue := abi.ToCamelCase(value) + topicFieldDefs[capFirstValue] = true + } + + filterArgs := make([]abi.Argument, 0, types.MaxTopicFields) + inputArgs := make([]abi.Argument, 0, len(event.Inputs)) + indexArgNames := map[string]bool{} + + for _, input := range event.Inputs { + if !input.Indexed { + continue + } + + filterWith := topicFieldDefs[abi.ToCamelCase(input.Name)] + if filterWith { + // When presenting the filter off-chain, + // the user will provide the unhashed version of the input + // The reader will hash topics if needed. + inputUnindexed := input + inputUnindexed.Indexed = false + filterArgs = append(filterArgs, inputUnindexed) } + + inputArgs = append(inputArgs, input) + indexArgNames[abi.ToCamelCase(input.Name)] = true } - return true + return filterArgs, types.NewCodecEntry(inputArgs, nil, nil), indexArgNames } diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index ece2234ab55..4981dc3696a 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -1,272 +1,423 @@ -package evm +package evm_test import ( - "encoding/json" + "crypto/ecdsa" "fmt" - "strings" + "math" + "math/big" + "os" + "reflect" + "strconv" "testing" + "time" + "github.com/ethereum/go-ethereum/accounts/abi" + "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/common/hexutil" + "github.com/ethereum/go-ethereum/core" + evmtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/libocr/commontypes" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/codec" - mocklogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . + + commontestutils "github.com/smartcontractkit/chainlink-common/pkg/loop/testutils" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_reader_example" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +const ( + commonGasLimitOnEvms = uint64(4712388) + triggerWithDynamicTopic = "TriggeredEventWithDynamicTopic" + triggerWithAllTopics = "TriggeredWithFourTopics" ) -type chainReaderTestHelper struct { +func TestChainReader(t *testing.T) { + t.Parallel() + it := &chainReaderInterfaceTester{} + RunChainReaderInterfaceTests(t, it) + RunChainReaderInterfaceTests(t, commontestutils.WrapChainReaderTesterForLoop(it)) + t.Run("Dynamically typed topics can be used to filter and have type correct in return", func(t *testing.T) { + it.Setup(t) + + anyString := "foo" + tx, err := it.evmTest.LatestValueHolderTransactor.TriggerEventWithDynamicTopic(it.auth, anyString) + require.NoError(t, err) + it.sim.Commit() + it.incNonce() + it.awaitTx(t, tx) + ctx := testutils.Context(t) + + cr := it.GetChainReader(t) + require.NoError(t, cr.Bind(ctx, it.GetBindings(t))) + + input := struct{ Field string }{Field: anyString} + tp := cr.(clcommontypes.ContractTypeProvider) + output, err := tp.CreateContractType(AnyContractName, triggerWithDynamicTopic, false) + require.NoError(t, err) + rOutput := reflect.Indirect(reflect.ValueOf(output)) + + require.Eventually(t, func() bool { + return cr.GetLatestValue(ctx, AnyContractName, triggerWithDynamicTopic, input, output) == nil + }, it.MaxWaitTimeForEvents(), time.Millisecond*10) + + assert.Equal(t, anyString, rOutput.FieldByName("Field").Interface()) + topic, err := abi.MakeTopics([]any{anyString}) + require.NoError(t, err) + assert.Equal(t, topic[0][0], rOutput.FieldByName("FieldHash").Interface()) + }) + + t.Run("Multiple topics can filter together", func(t *testing.T) { + it.Setup(t) + triggerFourTopics(t, it, int32(1), int32(2), int32(3)) + triggerFourTopics(t, it, int32(2), int32(2), int32(3)) + triggerFourTopics(t, it, int32(1), int32(3), int32(3)) + triggerFourTopics(t, it, int32(1), int32(2), int32(4)) + + ctx := testutils.Context(t) + cr := it.GetChainReader(t) + require.NoError(t, cr.Bind(ctx, it.GetBindings(t))) + var latest struct{ Field1, Field2, Field3 int32 } + params := struct{ Field1, Field2, Field3 int32 }{Field1: 1, Field2: 2, Field3: 3} + + time.Sleep(it.MaxWaitTimeForEvents()) + + require.NoError(t, cr.GetLatestValue(ctx, AnyContractName, triggerWithAllTopics, params, &latest)) + assert.Equal(t, int32(1), latest.Field1) + assert.Equal(t, int32(2), latest.Field2) + assert.Equal(t, int32(3), latest.Field3) + }) +} + +func triggerFourTopics(t *testing.T, it *chainReaderInterfaceTester, i1, i2, i3 int32) { + tx, err := it.evmTest.LatestValueHolderTransactor.TriggerWithFourTopics(it.auth, i1, i2, i3) + require.NoError(t, err) + require.NoError(t, err) + it.sim.Commit() + it.incNonce() + it.awaitTx(t, tx) } -func (crTestHelper chainReaderTestHelper) makeChainReaderConfig(abi string, params map[string]any) evmtypes.ChainReaderConfig { - return evmtypes.ChainReaderConfig{ - ChainContractReaders: map[string]evmtypes.ChainContractReader{ - "MyContract": { - ContractABI: abi, - ChainReaderDefinitions: map[string]evmtypes.ChainReaderDefinition{ - "MyGenericMethod": { - ChainSpecificName: "name", - Params: params, - CacheEnabled: false, - ReadType: evmtypes.Method, +type chainReaderInterfaceTester struct { + chain *mocks.Chain + address string + address2 string + chainConfig types.ChainReaderConfig + auth *bind.TransactOpts + sim *backends.SimulatedBackend + pk *ecdsa.PrivateKey + evmTest *chain_reader_example.LatestValueHolder + cr evm.ChainReaderService +} + +func (it *chainReaderInterfaceTester) MaxWaitTimeForEvents() time.Duration { + // From trial and error, when running on CI, sometimes the boxes get slow + maxWaitTime := time.Second * 20 + maxWaitTimeStr, ok := os.LookupEnv("MAX_WAIT_TIME_FOR_EVENTS_S") + if ok { + wiatS, err := strconv.ParseInt(maxWaitTimeStr, 10, 64) + if err != nil { + fmt.Printf("Error parsing MAX_WAIT_TIME_FOR_EVENTS_S: %v, defaulting to %v\n", err, maxWaitTime) + } + maxWaitTime = time.Second * time.Duration(wiatS) + } + + return maxWaitTime +} + +func (it *chainReaderInterfaceTester) Setup(t *testing.T) { + t.Cleanup(func() { + // DB may be closed by the test already, ignore errors + _ = it.cr.Close() + it.cr = nil + it.evmTest = nil + }) + + // can re-use the same chain for tests, just make new contract for each test + if it.chain != nil { + it.deployNewContracts(t) + return + } + + it.chain = &mocks.Chain{} + it.setupChainNoClient(t) + + testStruct := CreateTestStruct(0, it) + + it.chainConfig = types.ChainReaderConfig{ + Contracts: map[string]types.ChainContractReader{ + AnyContractName: { + ContractABI: chain_reader_example.LatestValueHolderMetaData.ABI, + Configs: map[string]*types.ChainReaderDefinition{ + MethodTakingLatestParamsReturningTestStruct: { + ChainSpecificName: "getElementAtIndex", + OutputModifications: codec.ModifiersConfig{ + &codec.RenameModifierConfig{Fields: map[string]string{"NestedStruct.Inner.IntVal": "I"}}, + }, + }, + MethodReturningUint64: { + ChainSpecificName: "getPrimitiveValue", + }, + DifferentMethodReturningUint64: { + ChainSpecificName: "getDifferentPrimitiveValue", + }, + MethodReturningUint64Slice: { + ChainSpecificName: "getSliceValue", + }, + EventName: { + ChainSpecificName: "Triggered", + ReadType: types.Event, + OutputModifications: codec.ModifiersConfig{ + &codec.RenameModifierConfig{Fields: map[string]string{"NestedStruct.Inner.IntVal": "I"}}, + }, + }, + EventWithFilterName: { + ChainSpecificName: "Triggered", + ReadType: types.Event, + EventInputFields: []string{"Field"}, + }, + triggerWithDynamicTopic: { + ChainSpecificName: triggerWithDynamicTopic, + ReadType: types.Event, + EventInputFields: []string{"fieldHash"}, + InputModifications: codec.ModifiersConfig{ + &codec.RenameModifierConfig{Fields: map[string]string{"FieldHash": "Field"}}, + }, + }, + triggerWithAllTopics: { + ChainSpecificName: triggerWithAllTopics, + ReadType: types.Event, + EventInputFields: []string{"Field1", "Field2", "Field3"}, + }, + MethodReturningSeenStruct: { + ChainSpecificName: "returnSeen", + InputModifications: codec.ModifiersConfig{ + &codec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "BigField": testStruct.BigField.String(), + "Account": hexutil.Encode(testStruct.Account), + }, + }, + }, + OutputModifications: codec.ModifiersConfig{ + &codec.HardCodeModifierConfig{OffChainValues: map[string]any{"ExtraField": anyExtraValue}}, + &codec.RenameModifierConfig{Fields: map[string]string{"NestedStruct.Inner.IntVal": "I"}}, + }, + }, + }, + }, + AnySecondContractName: { + ContractABI: chain_reader_example.LatestValueHolderMetaData.ABI, + Configs: map[string]*types.ChainReaderDefinition{ + MethodReturningUint64: { + ChainSpecificName: "getDifferentPrimitiveValue", }, }, }, }, } + it.chain.On("Client").Return(client.NewSimulatedBackendClient(t, it.sim, big.NewInt(1337))) + it.deployNewContracts(t) } -func (crTestHelper chainReaderTestHelper) makeChainReaderConfigFromStrings(abi string, chainReadingDefinitions string) (evmtypes.ChainReaderConfig, error) { - chainReaderConfigTemplate := `{ - "chainContractReaders": { - "testContract": { - "contractName": "testContract", - "contractABI": "[%s]", - "chainReaderDefinitions": { - %s - } - } - } - }` - - abi = strings.Replace(abi, `"`, `\"`, -1) - formattedCfgJsonString := fmt.Sprintf(chainReaderConfigTemplate, abi, chainReadingDefinitions) - var chainReaderConfig evmtypes.ChainReaderConfig - err := json.Unmarshal([]byte(formattedCfgJsonString), &chainReaderConfig) - return chainReaderConfig, err +func (it *chainReaderInterfaceTester) Name() string { + return "EVM" } -func TestNewChainReader(t *testing.T) { - lggr := logger.TestLogger(t) - lp := mocklogpoller.NewLogPoller(t) - chain := mocks.NewChain(t) - contractID := testutils.NewAddress() - contractABI := `[{"inputs":[{"internalType":"string","name":"param","type":"string"}],"name":"name","stateMutability":"view","type":"function"}]` - - t.Run("happy path", func(t *testing.T) { - params := make(map[string]any) - params["param"] = "" - chainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, params) - chain.On("LogPoller").Return(lp) - _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, chainReaderConfig) - assert.NoError(t, err) - }) +func (it *chainReaderInterfaceTester) GetAccountBytes(i int) []byte { + account := [20]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + account[i%20] += byte(i) + account[(i+3)%20] += byte(i + 3) + return account[:] +} - t.Run("invalid config", func(t *testing.T) { - invalidChainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, map[string]any{}) // missing param - _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, invalidChainReaderConfig) - assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) - }) +func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes.ChainReader { + ctx := testutils.Context(t) + if it.cr != nil { + return it.cr + } - t.Run("ChainReader config is empty", func(t *testing.T) { - emptyChainReaderConfig := evmtypes.ChainReaderConfig{} - _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, emptyChainReaderConfig) - assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) - assert.ErrorContains(t, err, "no contract readers defined") - }) + lggr := logger.NullLogger + db := pgtest.NewSqlxDB(t) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), it.chain.Client(), lggr, time.Millisecond, false, 0, 1, 1, 10000) + require.NoError(t, lp.Start(ctx)) + it.chain.On("LogPoller").Return(lp) + cr, err := evm.NewChainReaderService(lggr, lp, it.chain, it.chainConfig) + require.NoError(t, err) + require.NoError(t, cr.Start(ctx)) + it.cr = cr + return cr } -func TestChainReaderStartClose(t *testing.T) { - lggr := logger.TestLogger(t) - lp := mocklogpoller.NewLogPoller(t) - cr := chainReader{ - lggr: lggr, - lp: lp, +func (it *chainReaderInterfaceTester) SetLatestValue(t *testing.T, testStruct *TestStruct) { + it.sendTxWithTestStruct(t, testStruct, (*chain_reader_example.LatestValueHolderTransactor).AddTestStruct) +} + +func (it *chainReaderInterfaceTester) TriggerEvent(t *testing.T, testStruct *TestStruct) { + it.sendTxWithTestStruct(t, testStruct, (*chain_reader_example.LatestValueHolderTransactor).TriggerEvent) +} + +func (it *chainReaderInterfaceTester) GetBindings(t *testing.T) []clcommontypes.BoundContract { + return []clcommontypes.BoundContract{ + {Name: AnyContractName, Address: it.address, Pending: true}, + {Name: AnySecondContractName, Address: it.address2, Pending: true}, } - err := cr.Start(testutils.Context(t)) - assert.NoError(t, err) - err = cr.Close() - assert.NoError(t, err) } -// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 -func TestValidateChainReaderConfig_HappyPath(t *testing.T) { - type testCase struct { - name string - abiInput string - chainReadingDefinitions string +type testStructFn = func(*chain_reader_example.LatestValueHolderTransactor, *bind.TransactOpts, int32, string, uint8, [32]uint8, common.Address, []common.Address, *big.Int, chain_reader_example.MidLevelTestStruct) (*evmtypes.Transaction, error) + +func (it *chainReaderInterfaceTester) sendTxWithTestStruct(t *testing.T, testStruct *TestStruct, fn testStructFn) { + tx, err := fn( + &it.evmTest.LatestValueHolderTransactor, + it.auth, + testStruct.Field, + testStruct.DifferentField, + uint8(testStruct.OracleID), + convertOracleIDs(testStruct.OracleIDs), + common.Address(testStruct.Account), + convertAccounts(testStruct.Accounts), + testStruct.BigField, + midToInternalType(testStruct.NestedStruct), + ) + require.NoError(t, err) + it.sim.Commit() + it.incNonce() + it.awaitTx(t, tx) +} + +func convertOracleIDs(oracleIDs [32]commontypes.OracleID) [32]byte { + convertedIds := [32]byte{} + for i, id := range oracleIDs { + convertedIds[i] = byte(id) } + return convertedIds +} - var testCases []testCase - testCases = append(testCases, - testCase{ - name: "eventWithMultipleIndexedTopics", - abiInput: `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"}`, - chainReadingDefinitions: `"Swap":{ - "chainSpecificName": "Swap", - "params":{ - "sender": "0x0", - "to": "0x0" - }, - "readType": 1 - }`, - }) - - testCases = append(testCases, - testCase{ - name: "methodWithOneParamAndMultipleResponses", - abiInput: `{"constant":true,"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserAccountData","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"getUserAccountData":{ - "chainSpecificName": "getUserAccountData", - "params":{ - "_user": "0x0" - }, - "readType": 0 - }`, - }) - - testCases = append(testCases, - testCase{ - name: "methodWithMultipleParamsAndOneResult", - abiInput: `{"inputs":[{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"},{"internalType":"uint256","name":"_inputQuantity","type":"uint256"}],"name":"getSwapOutput","stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"getSwapOutput":{ - "chainSpecificName": "getSwapOutput", - "params":{ - "_input":"0x0", - "_output":"0x0", - "_inputQuantity":"0x0" - }, - "readType": 0 - }`, - }) - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) - assert.NoError(t, err) - assert.NoError(t, validateChainReaderConfig(cfg)) - }) +func convertAccounts(accounts [][]byte) []common.Address { + convertedAccounts := make([]common.Address, len(accounts)) + for i, a := range accounts { + convertedAccounts[i] = common.Address(a) } + return convertedAccounts +} - t.Run("large config with all test cases", func(t *testing.T) { - var largeABI string - var manyChainReadingDefinitions string - for _, tc := range testCases { - largeABI += tc.abiInput + "," - manyChainReadingDefinitions += tc.chainReadingDefinitions + "," - } +func (it *chainReaderInterfaceTester) setupChainNoClient(t require.TestingT) { + privateKey, err := crypto.GenerateKey() + require.NoError(t, err) + it.pk = privateKey - largeABI = largeABI[:len(largeABI)-1] - manyChainReadingDefinitions = manyChainReadingDefinitions[:len(manyChainReadingDefinitions)-1] - cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(largeABI, manyChainReadingDefinitions) - assert.NoError(t, err) - assert.NoError(t, validateChainReaderConfig(cfg)) - }) + it.auth, err = bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337)) + require.NoError(t, err) + + it.sim = backends.NewSimulatedBackend(core.GenesisAlloc{it.auth.From: {Balance: big.NewInt(math.MaxInt64)}}, commonGasLimitOnEvms*5000) + it.sim.Commit() } -// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 -func TestValidateChainReaderConfig_BadPath(t *testing.T) { - type testCase struct { - name string - abiInput string - chainReadingDefinitions string - expected error +func (it *chainReaderInterfaceTester) deployNewContracts(t *testing.T) { + it.address = it.deployNewContract(t) + it.address2 = it.deployNewContract(t) +} + +func (it *chainReaderInterfaceTester) deployNewContract(t *testing.T) string { + ctx := testutils.Context(t) + gasPrice, err := it.sim.SuggestGasPrice(ctx) + require.NoError(t, err) + it.auth.GasPrice = gasPrice + + // 105528 was in the error: gas too low: have 0, want 105528 + // Not sure if there's a better way to get it. + it.auth.GasLimit = 10552800 + + address, tx, ts, err := chain_reader_example.DeployLatestValueHolder(it.auth, it.sim) + + require.NoError(t, err) + it.sim.Commit() + if it.evmTest == nil { + it.evmTest = ts } + it.incNonce() + it.awaitTx(t, tx) + return address.String() +} - var testCases []testCase - mismatchedEventArgumentsTestABI := `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Swap","type":"event"}` - testCases = append(testCases, - testCase{ - name: "mismatched abi and event chain reading param values", - abiInput: mismatchedEventArgumentsTestABI, - chainReadingDefinitions: `"Swap":{ - "chainSpecificName": "Swap", - "params":{ - "malformedParam": "0x0" - }, - "readType": 1 - }`, - expected: fmt.Errorf("invalid chainreading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi event indexed inputs: [sender]"), - }) - - mismatchedFunctionArgumentsTestABI := `{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"Swap","payable":false,"stateMutability":"view","type":"function"}` - testCases = append(testCases, - testCase{ - name: "mismatched abi and method chain reading param values", - abiInput: mismatchedFunctionArgumentsTestABI, - chainReadingDefinitions: `"Swap":{ - "chainSpecificName": "Swap", - "params":{ - "malformedParam": "0x0" - }, - "readType": 0 - }`, - expected: fmt.Errorf("invalid chainreading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi method inputs: [from]"), - }, - ) +func (it *chainReaderInterfaceTester) awaitTx(t *testing.T, tx *evmtypes.Transaction) { + ctx := testutils.Context(t) + receipt, err := it.sim.TransactionReceipt(ctx, tx.Hash()) + require.NoError(t, err) + require.Equal(t, evmtypes.ReceiptStatusSuccessful, receipt.Status) +} - testCases = append(testCases, - testCase{ - name: "event doesn't exist", - abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"TestMethod":{ - "chainSpecificName": "Swap", - "readType": 1 - }`, - expected: fmt.Errorf("invalid chainreading definition: \"TestMethod\" for contract: \"testContract\", err: event: Swap doesn't exist"), - }, - ) +func (it *chainReaderInterfaceTester) incNonce() { + if it.auth.Nonce == nil { + it.auth.Nonce = big.NewInt(1) + } else { + it.auth.Nonce = it.auth.Nonce.Add(it.auth.Nonce, big.NewInt(1)) + } +} - testCases = append(testCases, - testCase{ - name: "method doesn't exist", - abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"TestMethod":{ - "chainSpecificName": "Swap", - "readType": 0 - }`, - expected: fmt.Errorf("invalid chainreading definition: \"TestMethod\" for contract: \"testContract\", err: method: \"Swap\" doesn't exist"), - }, - ) +func getAccounts(first TestStruct) []common.Address { + accountBytes := make([]common.Address, len(first.Accounts)) + for i, account := range first.Accounts { + accountBytes[i] = common.Address(account) + } + return accountBytes +} - testCases = append(testCases, testCase{ - name: "invalid abi", - abiInput: `broken abi`, - chainReadingDefinitions: `"TestMethod":{ - "chainSpecificName": "Swap", - "readType": 0 - }`, - expected: fmt.Errorf("invalid abi"), - }) +func argsFromTestStruct(ts TestStruct) []any { + return []any{ + ts.Field, + ts.DifferentField, + uint8(ts.OracleID), + getOracleIDs(ts), + common.Address(ts.Account), + getAccounts(ts), + ts.BigField, + midToInternalType(ts.NestedStruct), + } +} - testCases = append(testCases, testCase{ - name: "invalid read type", - abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"TestMethod":{"readType": 59}`, - expected: fmt.Errorf("invalid chainreading definition read type: 59"), - }) +func getOracleIDs(first TestStruct) [32]byte { + oracleIDs := [32]byte{} + for i, oracleID := range first.OracleIDs { + oracleIDs[i] = byte(oracleID) + } + return oracleIDs +} - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) - assert.NoError(t, err) - if tc.expected == nil { - assert.NoError(t, validateChainReaderConfig(cfg)) - } else { - assert.ErrorContains(t, validateChainReaderConfig(cfg), tc.expected.Error()) - } - }) +func toInternalType(testStruct TestStruct) chain_reader_example.TestStruct { + return chain_reader_example.TestStruct{ + Field: testStruct.Field, + DifferentField: testStruct.DifferentField, + OracleId: byte(testStruct.OracleID), + OracleIds: convertOracleIDs(testStruct.OracleIDs), + Account: common.Address(testStruct.Account), + Accounts: convertAccounts(testStruct.Accounts), + BigField: testStruct.BigField, + NestedStruct: midToInternalType(testStruct.NestedStruct), + } +} + +func midToInternalType(m MidLevelTestStruct) chain_reader_example.MidLevelTestStruct { + return chain_reader_example.MidLevelTestStruct{ + FixedBytes: m.FixedBytes, + Inner: chain_reader_example.InnerTestStruct{ + IntVal: int64(m.Inner.I), + S: m.Inner.S, + }, } } diff --git a/core/services/relay/evm/codec.go b/core/services/relay/evm/codec.go new file mode 100644 index 00000000000..090240c666c --- /dev/null +++ b/core/services/relay/evm/codec.go @@ -0,0 +1,130 @@ +package evm + +import ( + "encoding/json" + "fmt" + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/mitchellh/mapstructure" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +// decodeAccountHook allows strings to be converted to [32]byte allowing config to represent them as 0x... +// BigIntHook allows *big.Int to be represented as any integer type or a string and to go back to them. +// Useful for config, or if when a model may use a go type that isn't a *big.Int when Pack expects one. +// Eg: int32 in a go struct from a plugin could require a *big.Int in Pack for int24, if it fits, we shouldn't care. +// SliceToArrayVerifySizeHook verifies that slices have the correct size when converting to an array +// sizeVerifyBigIntHook allows our custom types that verify the number fits in the on-chain type to be converted as-if +// it was a *big.Int +var evmDecoderHooks = []mapstructure.DecodeHookFunc{decodeAccountHook, codec.BigIntHook, codec.SliceToArrayVerifySizeHook, sizeVerifyBigIntHook} + +// NewCodec creates a new [commontypes.RemoteCodec] for EVM. +// Note that names in the ABI are converted to Go names using [abi.ToCamelCase], +// this is per convention in [abi.MakeTopics], [abi.Arguments.Pack] etc. +// This allows names on-chain to be in go convention when generated. +// It means that if you need to use a [codec.Modifier] to reference a field +// you need to use the Go name instead of the name on-chain. +// eg: rename FooBar -> Bar, not foo_bar_ to Bar if the name on-chain is foo_bar_ +func NewCodec(conf types.CodecConfig) (commontypes.RemoteCodec, error) { + parsed := &parsedTypes{ + encoderDefs: map[string]types.CodecEntry{}, + decoderDefs: map[string]types.CodecEntry{}, + } + + for k, v := range conf.Configs { + args := abi.Arguments{} + if err := json.Unmarshal(([]byte)(v.TypeABI), &args); err != nil { + return nil, err + } + + mod, err := v.ModifierConfigs.ToModifier(evmDecoderHooks...) + if err != nil { + return nil, err + } + + item := types.NewCodecEntry(args, nil, mod) + if err = item.Init(); err != nil { + return nil, err + } + + parsed.encoderDefs[k] = item + parsed.decoderDefs[k] = item + } + + return parsed.toCodec() +} + +type evmCodec struct { + *encoder + *decoder + *parsedTypes +} + +func (c *evmCodec) CreateType(itemType string, forEncoding bool) (any, error) { + var itemTypes map[string]types.CodecEntry + if forEncoding { + itemTypes = c.encoderDefs + } else { + itemTypes = c.decoderDefs + } + + def, ok := itemTypes[itemType] + if !ok { + return nil, fmt.Errorf("%w: cannot find type name %s", commontypes.ErrInvalidType, itemType) + } + + return reflect.New(def.CheckedType()).Interface(), nil +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)) + +func sizeVerifyBigIntHook(from, to reflect.Type, data any) (any, error) { + if from.Implements(types.SizedBigIntType()) && + !to.Implements(types.SizedBigIntType()) && + !reflect.PointerTo(to).Implements(types.SizedBigIntType()) { + return codec.BigIntHook(from, bigIntType, reflect.ValueOf(data).Convert(bigIntType).Interface()) + } + + if !to.Implements(types.SizedBigIntType()) { + return data, nil + } + + var err error + data, err = codec.BigIntHook(from, bigIntType, data) + if err != nil { + return nil, err + } + + bi, ok := data.(*big.Int) + if !ok { + return data, nil + } + + converted := reflect.ValueOf(bi).Convert(to).Interface().(types.SizedBigInt) + return converted, converted.Verify() +} + +func decodeAccountHook(from, to reflect.Type, data any) (any, error) { + if from.Kind() == reflect.String && to == reflect.TypeOf(common.Address{}) { + decoded, err := hexutil.Decode(data.(string)) + if err != nil { + return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) + } else if len(decoded) != common.AddressLength { + return nil, fmt.Errorf( + "%w: wrong number size for address expected %v got %v", + commontypes.ErrSliceWrongLen, + common.AddressLength, len(decoded)) + } + return common.Address(decoded), nil + } + return data, nil +} diff --git a/core/services/relay/evm/codec_fuzz_test.go b/core/services/relay/evm/codec_fuzz_test.go new file mode 100644 index 00000000000..5870e9d77ad --- /dev/null +++ b/core/services/relay/evm/codec_fuzz_test.go @@ -0,0 +1,12 @@ +package evm_test + +import ( + "testing" + + "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" +) + +func FuzzCodec(f *testing.F) { + tester := &codecInterfaceTester{} + interfacetests.RunCodecInterfaceFuzzTests(f, tester) +} diff --git a/core/services/relay/evm/codec_test.go b/core/services/relay/evm/codec_test.go new file mode 100644 index 00000000000..b13051cb010 --- /dev/null +++ b/core/services/relay/evm/codec_test.go @@ -0,0 +1,210 @@ +package evm_test + +import ( + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common/hexutil" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + + looptestutils "github.com/smartcontractkit/chainlink-common/pkg/loop/testutils" //nolint common practice to import test mods with . + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_reader_example" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +const anyExtraValue = 3 + +func TestCodec(t *testing.T) { + tester := &codecInterfaceTester{} + RunCodecInterfaceTests(t, tester) + RunCodecInterfaceTests(t, looptestutils.WrapCodecTesterForLoop(tester)) + + anyN := 10 + c := tester.GetCodec(t) + t.Run("GetMaxEncodingSize delegates to GetMaxSize", func(t *testing.T) { + actual, err := c.GetMaxEncodingSize(testutils.Context(t), anyN, sizeItemType) + assert.NoError(t, err) + + expected, err := types.GetMaxSize(anyN, parseDefs(t)[sizeItemType]) + require.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("GetMaxDecodingSize delegates to GetMaxSize", func(t *testing.T) { + actual, err := c.GetMaxDecodingSize(testutils.Context(t), anyN, sizeItemType) + assert.NoError(t, err) + + expected, err := types.GetMaxSize(anyN, parseDefs(t)[sizeItemType]) + require.NoError(t, err) + assert.Equal(t, expected, actual) + }) +} + +type codecInterfaceTester struct{} + +func (it *codecInterfaceTester) Setup(_ *testing.T) {} + +func (it *codecInterfaceTester) GetAccountBytes(i int) []byte { + account := [20]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + // fuzz tests can make -ve numbers + if i < 0 { + i = -i + } + account[i%20] += byte(i) + account[(i+3)%20] += byte(i + 3) + return account[:] +} + +func (it *codecInterfaceTester) EncodeFields(t *testing.T, request *EncodeRequest) []byte { + if request.TestOn == TestItemType { + return encodeFieldsOnItem(t, request) + } + + return encodeFieldsOnSliceOrArray(t, request) +} + +func (it *codecInterfaceTester) GetCodec(t *testing.T) commontypes.Codec { + codecConfig := types.CodecConfig{Configs: map[string]types.ChainCodecConfig{}} + testStruct := CreateTestStruct(0, it) + for k, v := range codecDefs { + defBytes, err := json.Marshal(v) + require.NoError(t, err) + entry := codecConfig.Configs[k] + entry.TypeABI = string(defBytes) + + if k != sizeItemType && k != NilType { + entry.ModifierConfigs = codec.ModifiersConfig{ + &codec.RenameModifierConfig{Fields: map[string]string{"NestedStruct.Inner.IntVal": "I"}}, + } + } + + if k == TestItemWithConfigExtra { + hardCode := &codec.HardCodeModifierConfig{ + OnChainValues: map[string]any{ + "BigField": testStruct.BigField.String(), + "Account": hexutil.Encode(testStruct.Account), + }, + OffChainValues: map[string]any{"ExtraField": anyExtraValue}, + } + entry.ModifierConfigs = append(entry.ModifierConfigs, hardCode) + } + codecConfig.Configs[k] = entry + } + + c, err := evm.NewCodec(codecConfig) + require.NoError(t, err) + return c +} + +func (it *codecInterfaceTester) IncludeArrayEncodingSizeEnforcement() bool { + return true +} +func (it *codecInterfaceTester) Name() string { + return "EVM" +} + +func encodeFieldsOnItem(t *testing.T, request *EncodeRequest) ocr2types.Report { + return packArgs(t, argsFromTestStruct(request.TestStructs[0]), parseDefs(t)[TestItemType], request) +} + +func encodeFieldsOnSliceOrArray(t *testing.T, request *EncodeRequest) []byte { + oargs := parseDefs(t)[request.TestOn] + args := make([]any, 1) + + switch request.TestOn { + case TestItemArray1Type: + args[0] = [1]chain_reader_example.TestStruct{toInternalType(request.TestStructs[0])} + case TestItemArray2Type: + args[0] = [2]chain_reader_example.TestStruct{toInternalType(request.TestStructs[0]), toInternalType(request.TestStructs[1])} + default: + tmp := make([]chain_reader_example.TestStruct, len(request.TestStructs)) + for i, ts := range request.TestStructs { + tmp[i] = toInternalType(ts) + } + args[0] = tmp + } + + return packArgs(t, args, oargs, request) +} + +func packArgs(t *testing.T, allArgs []any, oargs abi.Arguments, request *EncodeRequest) []byte { + // extra capacity in case we add an argument + args := make(abi.Arguments, len(oargs), len(oargs)+1) + copy(args, oargs) + // decoding has extra field to decode + if request.ExtraField { + fakeType, err := abi.NewType("int32", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + args = append(args, abi.Argument{Name: "FakeField", Type: fakeType}) + allArgs = append(allArgs, 11) + } + + if request.MissingField { + args = args[1:] //nolint we know it's non-zero len + allArgs = allArgs[1:] //nolint we know it's non-zero len + } + + bytes, err := args.Pack(allArgs...) + require.NoError(t, err) + return bytes +} + +var inner = []abi.ArgumentMarshaling{ + {Name: "IntVal", Type: "int64"}, + {Name: "S", Type: "string"}, +} + +var nested = []abi.ArgumentMarshaling{ + {Name: "FixedBytes", Type: "bytes2"}, + {Name: "Inner", Type: "tuple", Components: inner}, +} + +var ts = []abi.ArgumentMarshaling{ + {Name: "Field", Type: "int32"}, + {Name: "DifferentField", Type: "string"}, + {Name: "OracleId", Type: "uint8"}, + {Name: "OracleIds", Type: "uint8[32]"}, + {Name: "Account", Type: "address"}, + {Name: "Accounts", Type: "address[]"}, + {Name: "BigField", Type: "int192"}, + {Name: "NestedStruct", Type: "tuple", Components: nested}, +} + +const sizeItemType = "item for size" + +var codecDefs = map[string][]abi.ArgumentMarshaling{ + TestItemType: ts, + TestItemSliceType: { + {Name: "", Type: "tuple[]", Components: ts}, + }, + TestItemArray1Type: { + {Name: "", Type: "tuple[1]", Components: ts}, + }, + TestItemArray2Type: { + {Name: "", Type: "tuple[2]", Components: ts}, + }, + sizeItemType: { + {Name: "Stuff", Type: "int256[]"}, + {Name: "OtherStuff", Type: "int256"}, + }, + TestItemWithConfigExtra: ts, + NilType: {}, +} + +func parseDefs(t *testing.T) map[string]abi.Arguments { + bytes, err := json.Marshal(codecDefs) + require.NoError(t, err) + var results map[string]abi.Arguments + require.NoError(t, json.Unmarshal(bytes, &results)) + return results +} diff --git a/core/services/relay/evm/decoder.go b/core/services/relay/evm/decoder.go new file mode 100644 index 00000000000..f16875d80f6 --- /dev/null +++ b/core/services/relay/evm/decoder.go @@ -0,0 +1,102 @@ +package evm + +import ( + "context" + "fmt" + "reflect" + + "github.com/mitchellh/mapstructure" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type decoder struct { + Definitions map[string]types.CodecEntry +} + +var _ commontypes.Decoder = &decoder{} + +func (m *decoder) Decode(_ context.Context, raw []byte, into any, itemType string) error { + info, ok := m.Definitions[itemType] + if !ok { + return fmt.Errorf("%w: cannot find definition for %s", commontypes.ErrInvalidType, itemType) + } + + decode, err := extractDecoding(info, raw) + if err != nil { + return err + } + + rDecode := reflect.ValueOf(decode) + switch rDecode.Kind() { + case reflect.Array: + return m.decodeArray(into, rDecode) + case reflect.Slice: + iInto := reflect.Indirect(reflect.ValueOf(into)) + length := rDecode.Len() + iInto.Set(reflect.MakeSlice(iInto.Type(), length, length)) + return setElements(length, rDecode, iInto) + default: + return mapstructureDecode(decode, into) + } +} + +func (m *decoder) decodeArray(into any, rDecode reflect.Value) error { + iInto := reflect.Indirect(reflect.ValueOf(into)) + length := rDecode.Len() + if length != iInto.Len() { + return commontypes.ErrSliceWrongLen + } + iInto.Set(reflect.New(iInto.Type()).Elem()) + return setElements(length, rDecode, iInto) +} + +func (m *decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) { + entry, ok := m.Definitions[itemType] + if !ok { + return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) + } + return entry.GetMaxSize(n) +} + +func extractDecoding(info types.CodecEntry, raw []byte) (any, error) { + unpacked := map[string]any{} + args := info.Args() + if err := args.UnpackIntoMap(unpacked, raw); err != nil { + return nil, fmt.Errorf("%w: %w: for args %#v", commontypes.ErrInvalidEncoding, err, args) + } + var decode any = unpacked + + if noName, ok := unpacked[""]; ok { + decode = noName + } + return decode, nil +} + +func setElements(length int, rDecode reflect.Value, iInto reflect.Value) error { + for i := 0; i < length; i++ { + if err := mapstructureDecode(rDecode.Index(i).Interface(), iInto.Index(i).Addr().Interface()); err != nil { + return err + } + } + + return nil +} + +func mapstructureDecode(src, dest any) error { + mDecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.ComposeDecodeHookFunc(evmDecoderHooks...), + Result: dest, + Squash: true, + }) + if err != nil { + return fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) + } + + if err = mDecoder.Decode(src); err != nil { + return fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) + } + return nil +} diff --git a/core/services/relay/evm/encoder.go b/core/services/relay/evm/encoder.go new file mode 100644 index 00000000000..ae60e4ab35a --- /dev/null +++ b/core/services/relay/evm/encoder.go @@ -0,0 +1,145 @@ +package evm + +import ( + "context" + "fmt" + "reflect" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type encoder struct { + Definitions map[string]types.CodecEntry +} + +var _ commontypes.Encoder = &encoder{} + +func (e *encoder) Encode(_ context.Context, item any, itemType string) (res []byte, err error) { + // nil values can cause abi.Arguments.Pack to panic. + defer func() { + if r := recover(); r != nil { + res = nil + err = fmt.Errorf("%w: cannot encode type", commontypes.ErrInvalidType) + } + }() + info, ok := e.Definitions[itemType] + if !ok { + return nil, fmt.Errorf("%w: cannot find definition for %s", commontypes.ErrInvalidType, itemType) + } + + if len(info.Args()) == 0 { + return info.EncodingPrefix(), nil + } else if item == nil { + return nil, fmt.Errorf("%w: cannot encode nil value for %s", commontypes.ErrInvalidType, itemType) + } + + return encode(reflect.ValueOf(item), info) +} + +func (e *encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) { + entry, ok := e.Definitions[itemType] + if !ok { + return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) + } + return entry.GetMaxSize(n) +} + +func encode(item reflect.Value, info types.CodecEntry) ([]byte, error) { + for item.Kind() == reflect.Pointer { + item = reflect.Indirect(item) + } + switch item.Kind() { + case reflect.Array, reflect.Slice: + native, err := representArray(item, info) + if err != nil { + return nil, err + } + return pack(info, native) + case reflect.Struct, reflect.Map: + values, err := unrollItem(item, info) + if err != nil { + return nil, err + } + return pack(info, values...) + default: + return nil, fmt.Errorf("%w: cannot encode kind %v", commontypes.ErrInvalidType, item.Kind()) + } +} + +func representArray(item reflect.Value, info types.CodecEntry) (any, error) { + length := item.Len() + checkedType := info.CheckedType() + checked := reflect.New(checkedType) + iChecked := reflect.Indirect(checked) + switch checkedType.Kind() { + case reflect.Array: + if checkedType.Len() != length { + return nil, commontypes.ErrSliceWrongLen + } + case reflect.Slice: + iChecked.Set(reflect.MakeSlice(checkedType, length, length)) + default: + return nil, fmt.Errorf("%w: cannot encode %v as array", commontypes.ErrInvalidType, checkedType.Kind()) + } + + checkedElm := checkedType.Elem() + for i := 0; i < length; i++ { + tmp := reflect.New(checkedElm) + if err := mapstructureDecode(item.Index(i).Interface(), tmp.Interface()); err != nil { + return nil, err + } + iChecked.Index(i).Set(tmp.Elem()) + } + native, err := info.ToNative(checked) + if err != nil { + return nil, err + } + + return native.Elem().Interface(), nil +} + +func unrollItem(item reflect.Value, info types.CodecEntry) ([]any, error) { + checkedType := info.CheckedType() + if item.CanAddr() { + item = item.Addr() + } + + if item.Type() == reflect.PointerTo(checkedType) { + var err error + if item, err = info.ToNative(item); err != nil { + return nil, err + } + } else if !info.IsNativePointer(item.Type()) { + var err error + checked := reflect.New(checkedType) + if err = mapstructureDecode(item.Interface(), checked.Interface()); err != nil { + return nil, err + } + if item, err = info.ToNative(checked); err != nil { + return nil, err + } + } + + item = reflect.Indirect(item) + length := item.NumField() + values := make([]any, length) + iType := item.Type() + for i := 0; i < length; i++ { + if iType.Field(i).IsExported() { + values[i] = item.Field(i).Interface() + } + } + return values, nil +} + +func pack(info types.CodecEntry, values ...any) ([]byte, error) { + bytes, err := info.Args().Pack(values...) + if err != nil { + return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) + } + + withPrefix := info.EncodingPrefix() + return append(withPrefix, bytes...), nil +} diff --git a/core/services/relay/evm/event_binding.go b/core/services/relay/evm/event_binding.go new file mode 100644 index 00000000000..85028853c79 --- /dev/null +++ b/core/services/relay/evm/event_binding.go @@ -0,0 +1,272 @@ +package evm + +import ( + "context" + "fmt" + "reflect" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type eventBinding struct { + address common.Address + contractName string + eventName string + lp logpoller.LogPoller + hash common.Hash + codec commontypes.RemoteCodec + pending bool + bound bool + registerCalled bool + lock sync.Mutex + inputInfo types.CodecEntry + inputModifier codec.Modifier + topicInfo types.CodecEntry + // used to allow Register and Unregister to be unique in case two bindings have the same event. + // otherwise, if one unregisters, it'll unregister both with the LogPoller. + id string +} + +var _ readBinding = &eventBinding{} + +func (e *eventBinding) SetCodec(codec commontypes.RemoteCodec) { + e.codec = codec +} + +func (e *eventBinding) Register() error { + e.lock.Lock() + defer e.lock.Unlock() + + e.registerCalled = true + if !e.bound || e.lp.HasFilter(e.id) { + return nil + } + + if err := e.lp.RegisterFilter(logpoller.Filter{ + Name: e.id, + EventSigs: evmtypes.HashArray{e.hash}, + Addresses: evmtypes.AddressArray{e.address}, + }); err != nil { + return fmt.Errorf("%w: %w", commontypes.ErrInternal, err) + } + return nil +} + +func (e *eventBinding) Unregister() error { + e.lock.Lock() + defer e.lock.Unlock() + + if !e.lp.HasFilter(e.id) { + return nil + } + + if err := e.lp.UnregisterFilter(e.id); err != nil { + return fmt.Errorf("%w: %w", commontypes.ErrInternal, err) + } + return nil +} + +func (e *eventBinding) GetLatestValue(ctx context.Context, params, into any) error { + if !e.bound { + return fmt.Errorf("%w: event not bound", commontypes.ErrInvalidType) + } + + confs := logpoller.Finalized + if e.pending { + confs = logpoller.Unconfirmed + } + + if len(e.inputInfo.Args()) == 0 { + return e.getLatestValueWithoutFilters(ctx, confs, into) + } + + return e.getLatestValueWithFilters(ctx, confs, params, into) +} + +func (e *eventBinding) Bind(binding commontypes.BoundContract) error { + if err := e.Unregister(); err != nil { + return err + } + + e.address = common.HexToAddress(binding.Address) + e.pending = binding.Pending + e.bound = true + + if e.registerCalled { + return e.Register() + } + return nil +} + +func (e *eventBinding) getLatestValueWithoutFilters(ctx context.Context, confs logpoller.Confirmations, into any) error { + log, err := e.lp.LatestLogByEventSigWithConfs(e.hash, e.address, confs) + if err = wrapInternalErr(err); err != nil { + return err + } + + return e.decodeLog(ctx, log, into) +} + +func (e *eventBinding) getLatestValueWithFilters( + ctx context.Context, confs logpoller.Confirmations, params, into any) error { + offChain, err := e.convertToOffChainType(params) + if err != nil { + return err + } + + checkedParams, err := e.inputModifier.TransformToOnChain(offChain, "" /* unused */) + if err != nil { + return err + } + + nativeParams, err := e.inputInfo.ToNative(reflect.ValueOf(checkedParams)) + if err != nil { + return err + } + + filtersAndIndices, err := e.encodeParams(nativeParams) + if err != nil { + return err + } + + fai := filtersAndIndices[0] + remainingFilters := filtersAndIndices[1:] + + logs, err := e.lp.IndexedLogs(e.hash, e.address, 1, []common.Hash{fai}, confs) + if err != nil { + return wrapInternalErr(err) + } + + // TODO: there should be a better way to ask log poller to filter these + // First, you should be able to ask for as many topics to match + // Second, you should be able to get the latest only + var logToUse *logpoller.Log + for _, log := range logs { + tmp := log + if compareLogs(&tmp, logToUse) > 0 && matchesRemainingFilters(&tmp, remainingFilters) { + // copy so that it's not pointing to the changing variable + logToUse = &tmp + } + } + + if logToUse == nil { + return fmt.Errorf("%w: no events found", commontypes.ErrNotFound) + } + + return e.decodeLog(ctx, logToUse, into) +} + +func (e *eventBinding) convertToOffChainType(params any) (any, error) { + itemType := wrapItemType(e.contractName, e.eventName, true) + offChain, err := e.codec.CreateType(itemType, true) + if err != nil { + return nil, err + } + + if err = mapstructureDecode(params, offChain); err != nil { + return nil, err + } + + return offChain, nil +} + +func compareLogs(log, use *logpoller.Log) int64 { + if use == nil { + return 1 + } + + if log.BlockNumber != use.BlockNumber { + return log.BlockNumber - use.BlockNumber + } + + return log.LogIndex - use.LogIndex +} + +func matchesRemainingFilters(log *logpoller.Log, filters []common.Hash) bool { + for i, rfai := range filters { + if !reflect.DeepEqual(rfai[:], log.Topics[i+2]) { + return false + } + } + + return true +} + +func (e *eventBinding) encodeParams(item reflect.Value) ([]common.Hash, error) { + for item.Kind() == reflect.Pointer { + item = reflect.Indirect(item) + } + + var topics []any + switch item.Kind() { + case reflect.Array, reflect.Slice: + native, err := representArray(item, e.inputInfo) + if err != nil { + return nil, err + } + topics = []any{native} + case reflect.Struct, reflect.Map: + var err error + if topics, err = unrollItem(item, e.inputInfo); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("%w: cannot encode kind %v", commontypes.ErrInvalidType, item.Kind()) + } + + hashes, err := abi.MakeTopics(topics) + if err != nil { + return nil, wrapInternalErr(err) + } + + if len(hashes) != 1 { + return nil, fmt.Errorf("%w: expected 1 filter set, got %d", commontypes.ErrInternal, len(hashes)) + } + + return hashes[0], nil +} + +func (e *eventBinding) decodeLog(ctx context.Context, log *logpoller.Log, into any) error { + dataType := wrapItemType(e.contractName, e.eventName, false) + if err := e.codec.Decode(ctx, log.Data, into, dataType); err != nil { + return err + } + + topics := make([]common.Hash, len(e.topicInfo.Args())) + if len(log.Topics) < len(topics)+1 { + return fmt.Errorf("%w: not enough topics to decode", commontypes.ErrInvalidType) + } + + for i := 0; i < len(topics); i++ { + topics[i] = common.Hash(log.Topics[i+1]) + } + + topicsInto := map[string]any{} + if err := abi.ParseTopicsIntoMap(topicsInto, e.topicInfo.Args(), topics); err != nil { + return fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) + } + + return mapstructureDecode(topicsInto, into) +} + +func wrapInternalErr(err error) error { + if err == nil { + return nil + } + + errStr := err.Error() + if strings.Contains(errStr, "not found") || strings.Contains(errStr, "no rows") { + return fmt.Errorf("%w: %w", commontypes.ErrNotFound, err) + } + return fmt.Errorf("%w: %w", commontypes.ErrInternal, err) +} diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 633ca69ea67..34d353c48d4 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -52,6 +52,7 @@ type Relayer struct { mercuryPool wsrpc.Pool pgCfg pg.QConfig chainReader commontypes.ChainReader + codec commontypes.Codec } type CSAETHKeystore interface { @@ -183,7 +184,7 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty } transmitter := mercury.NewTransmitter(lggr, cw.ContractConfigTracker(), client, privKey.PublicKey, rargs.JobID, *relayConfig.FeedID, r.db, r.pgCfg, transmitterCodec) - return NewMercuryProvider(cw, r.chainReader, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil + return NewMercuryProvider(cw, r.chainReader, r.codec, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil } func (r *Relayer) NewFunctionsProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { @@ -483,9 +484,14 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } // allow fallback until chain reader is default and median contract is removed, but still log just in case - var chainReaderService commontypes.ChainReader + var chainReaderService ChainReaderService if relayConfig.ChainReader != nil { - if chainReaderService, err = NewChainReaderService(lggr, r.chain.LogPoller(), contractID, *relayConfig.ChainReader); err != nil { + if chainReaderService, err = NewChainReaderService(lggr, r.chain.LogPoller(), r.chain, *relayConfig.ChainReader); err != nil { + return nil, err + } + + boundContracts := []commontypes.BoundContract{{Name: "median", Pending: true, Address: contractID.String()}} + if err = chainReaderService.Bind(context.Background(), boundContracts); err != nil { return nil, err } } else { @@ -493,6 +499,15 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } medianProvider.chainReader = chainReaderService + if relayConfig.Codec != nil { + medianProvider.codec, err = NewCodec(*relayConfig.Codec) + if err != nil { + return nil, err + } + } else { + lggr.Info("Codec missing from RelayConfig; falling back to internal MedianContract") + } + return &medianProvider, nil } @@ -511,14 +526,20 @@ type medianProvider struct { contractTransmitter ContractTransmitter reportCodec median.ReportCodec medianContract *medianContract - chainReader commontypes.ChainReader + chainReader ChainReaderService + codec commontypes.Codec ms services.MultiStart } func (p *medianProvider) Name() string { return p.lggr.Name() } func (p *medianProvider) Start(ctx context.Context) error { - return p.ms.Start(ctx, p.configWatcher, p.contractTransmitter, p.medianContract) + srvcs := []services.StartClose{p.configWatcher, p.contractTransmitter, p.medianContract} + if p.chainReader != nil { + srvcs = append(srvcs, p.chainReader) + } + + return p.ms.Start(ctx, srvcs...) } func (p *medianProvider) Close() error { return p.ms.Close() } @@ -560,3 +581,7 @@ func (p *medianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker func (p *medianProvider) ChainReader() commontypes.ChainReader { return p.chainReader } + +func (p *medianProvider) Codec() commontypes.Codec { + return p.codec +} diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index f1d652fd6f8..b957ab56f3b 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -90,6 +90,10 @@ func (p *functionsProvider) ChainReader() commontypes.ChainReader { return nil } +func (p *functionsProvider) Codec() commontypes.Codec { + return nil +} + func NewFunctionsProvider(chain legacyevm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { relayOpts := evmRelayTypes.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index e02efd8d9a4..e0769fe5b64 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -432,7 +432,6 @@ func TestMercury_SetLatestBlocks(t *testing.T) { headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) // This can happen in some cases e.g. RPC node is offline headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.mercuryChainReader = evm.NewChainReader(headTracker) obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index 2ff882efa6b..d9858ac64c3 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" @@ -24,6 +25,7 @@ var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { configWatcher *configWatcher chainReader commontypes.ChainReader + codec commontypes.Codec transmitter evmmercury.Transmitter reportCodecV1 v1.ReportCodec reportCodecV2 v2.ReportCodec @@ -36,6 +38,7 @@ type mercuryProvider struct { func NewMercuryProvider( configWatcher *configWatcher, chainReader commontypes.ChainReader, + codec commontypes.Codec, mercuryChainReader mercurytypes.ChainReader, transmitter evmmercury.Transmitter, reportCodecV1 v1.ReportCodec, @@ -46,6 +49,7 @@ func NewMercuryProvider( return &mercuryProvider{ configWatcher, chainReader, + codec, transmitter, reportCodecV1, reportCodecV2, @@ -83,6 +87,10 @@ func (p *mercuryProvider) MercuryChainReader() mercurytypes.ChainReader { return p.mercuryChainReader } +func (p *mercuryProvider) Codec() commontypes.Codec { + return p.codec +} + func (p *mercuryProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return p.configWatcher.ContractConfigTracker() } diff --git a/core/services/relay/evm/method_binding.go b/core/services/relay/evm/method_binding.go new file mode 100644 index 00000000000..c5e10cce1c1 --- /dev/null +++ b/core/services/relay/evm/method_binding.go @@ -0,0 +1,66 @@ +package evm + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" +) + +type methodBinding struct { + address common.Address + contractName string + method string + client evmclient.Client + codec commontypes.Codec + bound bool +} + +var _ readBinding = &methodBinding{} + +func (m *methodBinding) SetCodec(codec commontypes.RemoteCodec) { + m.codec = codec +} + +func (m *methodBinding) Register() error { + return nil +} + +func (m *methodBinding) Unregister() error { + return nil +} + +func (m *methodBinding) GetLatestValue(ctx context.Context, params, returnValue any) error { + if !m.bound { + return fmt.Errorf("%w: method not bound", commontypes.ErrInvalidType) + } + + data, err := m.codec.Encode(ctx, params, wrapItemType(m.contractName, m.method, true)) + if err != nil { + return err + } + + callMsg := ethereum.CallMsg{ + To: &m.address, + From: m.address, + Data: data, + } + + bytes, err := m.client.CallContract(ctx, callMsg, nil) + if err != nil { + return fmt.Errorf("%w: %w", commontypes.ErrInternal, err) + } + + return m.codec.Decode(ctx, bytes, returnValue, wrapItemType(m.contractName, m.method, false)) +} + +func (m *methodBinding) Bind(binding commontypes.BoundContract) error { + m.address = common.HexToAddress(binding.Address) + m.bound = true + return nil +} diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 34daf55b835..fd1fdb70480 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -206,6 +206,10 @@ func (c *ocr2keeperProvider) ChainReader() commontypes.ChainReader { return nil } +func (c *ocr2keeperProvider) Codec() commontypes.Codec { + return nil +} + func newOCR2KeeperConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index 1e05f89d9de..d300c71fbef 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -114,6 +114,10 @@ func (c *dkgProvider) ChainReader() commontypes.ChainReader { return nil } +func (c *dkgProvider) Codec() commontypes.Codec { + return nil +} + type ocr2vrfProvider struct { *configWatcher contractTransmitter ContractTransmitter @@ -127,6 +131,10 @@ func (c *ocr2vrfProvider) ChainReader() commontypes.ChainReader { return nil } +func (c *ocr2vrfProvider) Codec() commontypes.Codec { + return nil +} + func newOCR2VRFConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/parsed_types.go b/core/services/relay/evm/parsed_types.go new file mode 100644 index 00000000000..168057e998d --- /dev/null +++ b/core/services/relay/evm/parsed_types.go @@ -0,0 +1,50 @@ +package evm + +import ( + "fmt" + "reflect" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type parsedTypes struct { + encoderDefs map[string]types.CodecEntry + decoderDefs map[string]types.CodecEntry +} + +func (parsed *parsedTypes) toCodec() (commontypes.RemoteCodec, error) { + modByTypeName := map[string]codec.Modifier{} + if err := addEntries(parsed.encoderDefs, modByTypeName); err != nil { + return nil, err + } + if err := addEntries(parsed.decoderDefs, modByTypeName); err != nil { + return nil, err + } + + mod, err := codec.NewByItemTypeModifier(modByTypeName) + if err != nil { + return nil, err + } + underlying := &evmCodec{ + encoder: &encoder{Definitions: parsed.encoderDefs}, + decoder: &decoder{Definitions: parsed.decoderDefs}, + parsedTypes: parsed, + } + return codec.NewModifierCodec(underlying, mod, evmDecoderHooks...) +} + +// addEntries extracts the mods from codecEntry and adds them to modByTypeName use with codec.NewByItemTypeModifier +// Since each input/output can have its own modifications, we need to keep track of them by type name +func addEntries(defs map[string]types.CodecEntry, modByTypeName map[string]codec.Modifier) error { + for k, def := range defs { + modByTypeName[k] = def.Modifier() + _, err := def.Modifier().RetypeToOffChain(reflect.PointerTo(def.CheckedType()), k) + if err != nil { + return fmt.Errorf("%w: cannot retype %v: %w", commontypes.ErrInvalidConfig, k, err) + } + } + return nil +} diff --git a/core/services/relay/evm/types/abi_types.go b/core/services/relay/evm/types/abi_types.go new file mode 100644 index 00000000000..34b12d885b4 --- /dev/null +++ b/core/services/relay/evm/types/abi_types.go @@ -0,0 +1,66 @@ +package types + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/common" +) + +//go:generate go run ./gen/main.go + +var typeMap = map[string]*ABIEncodingType{ + "bool": { + native: reflect.TypeOf(true), + checked: reflect.TypeOf(true), + }, + "int8": { + native: reflect.TypeOf(int8(0)), + checked: reflect.TypeOf(int8(0)), + }, + "int16": { + native: reflect.TypeOf(int16(0)), + checked: reflect.TypeOf(int16(0)), + }, + "int32": { + native: reflect.TypeOf(int32(0)), + checked: reflect.TypeOf(int32(0)), + }, + "int64": { + native: reflect.TypeOf(int64(0)), + checked: reflect.TypeOf(int64(0)), + }, + "uint8": { + native: reflect.TypeOf(uint8(0)), + checked: reflect.TypeOf(uint8(0)), + }, + "uint16": { + native: reflect.TypeOf(uint16(0)), + checked: reflect.TypeOf(uint16(0)), + }, + "uint32": { + native: reflect.TypeOf(uint32(0)), + checked: reflect.TypeOf(uint32(0)), + }, + "uint64": { + native: reflect.TypeOf(uint64(0)), + checked: reflect.TypeOf(uint64(0)), + }, + "string": { + native: reflect.TypeOf(""), + checked: reflect.TypeOf(""), + }, + "address": { + native: reflect.TypeOf(common.Address{}), + checked: reflect.TypeOf(common.Address{}), + }, +} + +type ABIEncodingType struct { + native reflect.Type + checked reflect.Type +} + +func GetAbiEncodingType(name string) (*ABIEncodingType, bool) { + abiType, ok := typeMap[name] + return abiType, ok +} diff --git a/core/services/relay/evm/types/byte_types_gen.go b/core/services/relay/evm/types/byte_types_gen.go new file mode 100644 index 00000000000..cf8d15ccabc --- /dev/null +++ b/core/services/relay/evm/types/byte_types_gen.go @@ -0,0 +1,300 @@ +package types + +import "reflect" + +type bytes1 [1]byte + +func init() { + typeMap["bytes1"] = &ABIEncodingType{ + native: reflect.TypeOf([1]byte{}), + checked: reflect.TypeOf(bytes1{}), + } +} + +type bytes2 [2]byte + +func init() { + typeMap["bytes2"] = &ABIEncodingType{ + native: reflect.TypeOf([2]byte{}), + checked: reflect.TypeOf(bytes2{}), + } +} + +type bytes3 [3]byte + +func init() { + typeMap["bytes3"] = &ABIEncodingType{ + native: reflect.TypeOf([3]byte{}), + checked: reflect.TypeOf(bytes3{}), + } +} + +type bytes4 [4]byte + +func init() { + typeMap["bytes4"] = &ABIEncodingType{ + native: reflect.TypeOf([4]byte{}), + checked: reflect.TypeOf(bytes4{}), + } +} + +type bytes5 [5]byte + +func init() { + typeMap["bytes5"] = &ABIEncodingType{ + native: reflect.TypeOf([5]byte{}), + checked: reflect.TypeOf(bytes5{}), + } +} + +type bytes6 [6]byte + +func init() { + typeMap["bytes6"] = &ABIEncodingType{ + native: reflect.TypeOf([6]byte{}), + checked: reflect.TypeOf(bytes6{}), + } +} + +type bytes7 [7]byte + +func init() { + typeMap["bytes7"] = &ABIEncodingType{ + native: reflect.TypeOf([7]byte{}), + checked: reflect.TypeOf(bytes7{}), + } +} + +type bytes8 [8]byte + +func init() { + typeMap["bytes8"] = &ABIEncodingType{ + native: reflect.TypeOf([8]byte{}), + checked: reflect.TypeOf(bytes8{}), + } +} + +type bytes9 [9]byte + +func init() { + typeMap["bytes9"] = &ABIEncodingType{ + native: reflect.TypeOf([9]byte{}), + checked: reflect.TypeOf(bytes9{}), + } +} + +type bytes10 [10]byte + +func init() { + typeMap["bytes10"] = &ABIEncodingType{ + native: reflect.TypeOf([10]byte{}), + checked: reflect.TypeOf(bytes10{}), + } +} + +type bytes11 [11]byte + +func init() { + typeMap["bytes11"] = &ABIEncodingType{ + native: reflect.TypeOf([11]byte{}), + checked: reflect.TypeOf(bytes11{}), + } +} + +type bytes12 [12]byte + +func init() { + typeMap["bytes12"] = &ABIEncodingType{ + native: reflect.TypeOf([12]byte{}), + checked: reflect.TypeOf(bytes12{}), + } +} + +type bytes13 [13]byte + +func init() { + typeMap["bytes13"] = &ABIEncodingType{ + native: reflect.TypeOf([13]byte{}), + checked: reflect.TypeOf(bytes13{}), + } +} + +type bytes14 [14]byte + +func init() { + typeMap["bytes14"] = &ABIEncodingType{ + native: reflect.TypeOf([14]byte{}), + checked: reflect.TypeOf(bytes14{}), + } +} + +type bytes15 [15]byte + +func init() { + typeMap["bytes15"] = &ABIEncodingType{ + native: reflect.TypeOf([15]byte{}), + checked: reflect.TypeOf(bytes15{}), + } +} + +type bytes16 [16]byte + +func init() { + typeMap["bytes16"] = &ABIEncodingType{ + native: reflect.TypeOf([16]byte{}), + checked: reflect.TypeOf(bytes16{}), + } +} + +type bytes17 [17]byte + +func init() { + typeMap["bytes17"] = &ABIEncodingType{ + native: reflect.TypeOf([17]byte{}), + checked: reflect.TypeOf(bytes17{}), + } +} + +type bytes18 [18]byte + +func init() { + typeMap["bytes18"] = &ABIEncodingType{ + native: reflect.TypeOf([18]byte{}), + checked: reflect.TypeOf(bytes18{}), + } +} + +type bytes19 [19]byte + +func init() { + typeMap["bytes19"] = &ABIEncodingType{ + native: reflect.TypeOf([19]byte{}), + checked: reflect.TypeOf(bytes19{}), + } +} + +type bytes20 [20]byte + +func init() { + typeMap["bytes20"] = &ABIEncodingType{ + native: reflect.TypeOf([20]byte{}), + checked: reflect.TypeOf(bytes20{}), + } +} + +type bytes21 [21]byte + +func init() { + typeMap["bytes21"] = &ABIEncodingType{ + native: reflect.TypeOf([21]byte{}), + checked: reflect.TypeOf(bytes21{}), + } +} + +type bytes22 [22]byte + +func init() { + typeMap["bytes22"] = &ABIEncodingType{ + native: reflect.TypeOf([22]byte{}), + checked: reflect.TypeOf(bytes22{}), + } +} + +type bytes23 [23]byte + +func init() { + typeMap["bytes23"] = &ABIEncodingType{ + native: reflect.TypeOf([23]byte{}), + checked: reflect.TypeOf(bytes23{}), + } +} + +type bytes24 [24]byte + +func init() { + typeMap["bytes24"] = &ABIEncodingType{ + native: reflect.TypeOf([24]byte{}), + checked: reflect.TypeOf(bytes24{}), + } +} + +type bytes25 [25]byte + +func init() { + typeMap["bytes25"] = &ABIEncodingType{ + native: reflect.TypeOf([25]byte{}), + checked: reflect.TypeOf(bytes25{}), + } +} + +type bytes26 [26]byte + +func init() { + typeMap["bytes26"] = &ABIEncodingType{ + native: reflect.TypeOf([26]byte{}), + checked: reflect.TypeOf(bytes26{}), + } +} + +type bytes27 [27]byte + +func init() { + typeMap["bytes27"] = &ABIEncodingType{ + native: reflect.TypeOf([27]byte{}), + checked: reflect.TypeOf(bytes27{}), + } +} + +type bytes28 [28]byte + +func init() { + typeMap["bytes28"] = &ABIEncodingType{ + native: reflect.TypeOf([28]byte{}), + checked: reflect.TypeOf(bytes28{}), + } +} + +type bytes29 [29]byte + +func init() { + typeMap["bytes29"] = &ABIEncodingType{ + native: reflect.TypeOf([29]byte{}), + checked: reflect.TypeOf(bytes29{}), + } +} + +type bytes30 [30]byte + +func init() { + typeMap["bytes30"] = &ABIEncodingType{ + native: reflect.TypeOf([30]byte{}), + checked: reflect.TypeOf(bytes30{}), + } +} + +type bytes31 [31]byte + +func init() { + typeMap["bytes31"] = &ABIEncodingType{ + native: reflect.TypeOf([31]byte{}), + checked: reflect.TypeOf(bytes31{}), + } +} + +type bytes32 [32]byte + +func init() { + typeMap["bytes32"] = &ABIEncodingType{ + native: reflect.TypeOf([32]byte{}), + checked: reflect.TypeOf(bytes32{}), + } +} + +type bytes0 [0]byte + +func init() { + typeMap["bytes0"] = &ABIEncodingType{ + native: reflect.TypeOf([0]byte{}), + checked: reflect.TypeOf(bytes0{}), + } +} diff --git a/core/services/relay/evm/types/codec_entry.go b/core/services/relay/evm/types/codec_entry.go new file mode 100644 index 00000000000..70948ecd6b3 --- /dev/null +++ b/core/services/relay/evm/types/codec_entry.go @@ -0,0 +1,251 @@ +package types + +import ( + "fmt" + "reflect" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +// MaxTopicFields is three because the EVM has a max of four topics, but the first topic is always the event signature. +const MaxTopicFields = 3 + +type CodecEntry interface { + Init() error + Args() abi.Arguments + EncodingPrefix() []byte + GetMaxSize(n int) (int, error) + Modifier() codec.Modifier + + // CheckedType provides a type that can be used to decode into with type-safety around sizes of integers etc. + CheckedType() reflect.Type + + // ToNative converts a pointer to checked value into a pointer of a type to use with the go-ethereum ABI encoder + // Note that modification of the returned value will modify the original checked value and vice versa. + ToNative(checked reflect.Value) (reflect.Value, error) + + // IsNativePointer returns if the type is a pointer to the native type + IsNativePointer(item reflect.Type) bool +} + +func NewCodecEntry(args abi.Arguments, encodingPrefix []byte, mod codec.Modifier) CodecEntry { + if mod == nil { + mod = codec.MultiModifier{} + } + return &codecEntry{args: args, encodingPrefix: encodingPrefix, mod: mod} +} + +type codecEntry struct { + args abi.Arguments + encodingPrefix []byte + checkedType reflect.Type + nativeType reflect.Type + mod codec.Modifier +} + +func (entry *codecEntry) CheckedType() reflect.Type { + return entry.checkedType +} + +func (entry *codecEntry) NativeType() reflect.Type { + return entry.nativeType +} + +func (entry *codecEntry) ToNative(checked reflect.Value) (reflect.Value, error) { + if checked.Type() != reflect.PointerTo(entry.checkedType) { + return reflect.Value{}, fmt.Errorf("%w: checked type %v does not match expected type %v", commontypes.ErrInvalidType, reflect.TypeOf(checked), entry.checkedType) + } + + return reflect.NewAt(entry.nativeType, checked.UnsafePointer()), nil +} + +func (entry *codecEntry) IsNativePointer(item reflect.Type) bool { + return item == reflect.PointerTo(entry.nativeType) +} + +func (entry *codecEntry) Modifier() codec.Modifier { + return entry.mod +} + +func (entry *codecEntry) Args() abi.Arguments { + tmp := make(abi.Arguments, len(entry.args)) + copy(tmp, entry.args) + return tmp +} + +func (entry *codecEntry) EncodingPrefix() []byte { + tmp := make([]byte, len(entry.encodingPrefix)) + copy(tmp, entry.encodingPrefix) + return tmp +} + +func (entry *codecEntry) Init() error { + if entry.checkedType != nil { + return nil + } + + args := unwrapArgs(entry.args) + argLen := len(args) + native := make([]reflect.StructField, argLen) + checked := make([]reflect.StructField, argLen) + + // Single returns that aren't named will return that type + // whereas named parameters will return a struct with the fields + // Eg: function foo() returns (int256) ... will return a *big.Int for the native type + // function foo() returns (int256 i) ... will return a struct { I *big.Int } for the native type + // function foo() returns (int256 i1, int256 i2) ... will return a struct { I1 *big.Int, I2 *big.Int } for the native type + if len(args) == 1 && args[0].Name == "" { + nativeArg, checkedArg, err := getNativeAndCheckedTypesForArg(&args[0]) + if err != nil { + return err + } + entry.nativeType = nativeArg + entry.checkedType = checkedArg + return nil + } + + numIndices := 0 + seenNames := map[string]bool{} + for i, arg := range args { + if arg.Indexed { + if numIndices == MaxTopicFields { + return fmt.Errorf("%w: too many indexed arguments", commontypes.ErrInvalidConfig) + } + numIndices++ + } + + tmp := arg + nativeArg, checkedArg, err := getNativeAndCheckedTypesForArg(&tmp) + if err != nil { + return err + } + if len(arg.Name) == 0 { + return fmt.Errorf("%w: empty field names are not supported for multiple returns", commontypes.ErrInvalidType) + } + + name := strings.ToUpper(arg.Name[:1]) + arg.Name[1:] + if seenNames[name] { + return fmt.Errorf("%w: duplicate field name %s, after ToCamelCase", commontypes.ErrInvalidConfig, name) + } + seenNames[name] = true + native[i] = reflect.StructField{Name: name, Type: nativeArg} + checked[i] = reflect.StructField{Name: name, Type: checkedArg} + } + + entry.nativeType = reflect.StructOf(native) + entry.checkedType = reflect.StructOf(checked) + return nil +} + +func (entry *codecEntry) GetMaxSize(n int) (int, error) { + return GetMaxSize(n, entry.args) +} + +func unwrapArgs(args abi.Arguments) abi.Arguments { + // Unwrap an unnamed tuple so that callers don't need to wrap it + // Eg: If you have struct Foo { ... } and return an unnamed Foo, you should be able ot decode to a go Foo{} directly + if len(args) != 1 || args[0].Name != "" { + return args + } + + elms := args[0].Type.TupleElems + if len(elms) != 0 { + names := args[0].Type.TupleRawNames + args = make(abi.Arguments, len(elms)) + for i, elm := range elms { + args[i] = abi.Argument{ + Name: names[i], + Type: *elm, + } + } + } + return args +} + +func getNativeAndCheckedTypesForArg(arg *abi.Argument) (reflect.Type, reflect.Type, error) { + tmp := arg.Type + if arg.Indexed { + switch arg.Type.T { + case abi.StringTy: + return reflect.TypeOf(common.Hash{}), reflect.TypeOf(common.Hash{}), nil + case abi.ArrayTy: + u8, _ := GetAbiEncodingType("uint8") + if arg.Type.Elem.GetType() == u8.native { + return reflect.TypeOf(common.Hash{}), reflect.TypeOf(common.Hash{}), nil + } + fallthrough + case abi.SliceTy, abi.TupleTy, abi.FixedBytesTy, abi.FixedPointTy, abi.FunctionTy: + // https://github.com/ethereum/go-ethereum/blob/release/1.12/accounts/abi/topics.go#L78 + return nil, nil, fmt.Errorf("%w: unsupported indexed type: %v", commontypes.ErrInvalidConfig, arg.Type) + default: + } + } + + return getNativeAndCheckedTypes(&tmp) +} + +func getNativeAndCheckedTypes(curType *abi.Type) (reflect.Type, reflect.Type, error) { + converter := func(t reflect.Type) reflect.Type { return t } + for curType.Elem != nil { + prior := converter + switch curType.GetType().Kind() { + case reflect.Slice: + converter = func(t reflect.Type) reflect.Type { + return prior(reflect.SliceOf(t)) + } + curType = curType.Elem + case reflect.Array: + tmp := curType + converter = func(t reflect.Type) reflect.Type { + return prior(reflect.ArrayOf(tmp.Size, t)) + } + curType = curType.Elem + default: + return nil, nil, fmt.Errorf( + "%w: cannot create type for kind %v", commontypes.ErrInvalidType, curType.GetType().Kind()) + } + } + base, ok := GetAbiEncodingType(curType.String()) + if ok { + return converter(base.native), converter(base.checked), nil + } + + return createTupleType(curType, converter) +} + +func createTupleType(curType *abi.Type, converter func(reflect.Type) reflect.Type) (reflect.Type, reflect.Type, error) { + if len(curType.TupleElems) == 0 { + if curType.TupleType == nil { + return nil, nil, fmt.Errorf("%w: unsupported solitidy type: %v", commontypes.ErrInvalidType, curType.String()) + } + return curType.TupleType, curType.TupleType, nil + } + + // Create native type ourselves to assure that it'll always have the exact memory layout of checked types + // Otherwise, the "unsafe" casting that will be done to convert from checked to native won't be safe. + // At the time of writing, the way the TupleType is built it will be the same, but I don't want to rely on that + // If they ever add private fields for internal tracking + // or anything it would break us if we don't build the native type. + // As an example of how it could possibly change in the future, I've seen struct{} + // added with tags to the top of generated structs to allow metadata exploration. + nativeFields := make([]reflect.StructField, len(curType.TupleElems)) + checkedFields := make([]reflect.StructField, len(curType.TupleElems)) + for i, elm := range curType.TupleElems { + name := curType.TupleRawNames[i] + nativeFields[i].Name = name + checkedFields[i].Name = name + nativeArgType, checkedArgType, err := getNativeAndCheckedTypes(elm) + if err != nil { + return nil, nil, err + } + nativeFields[i].Type = nativeArgType + checkedFields[i].Type = checkedArgType + } + return converter(reflect.StructOf(nativeFields)), converter(reflect.StructOf(checkedFields)), nil +} diff --git a/core/services/relay/evm/types/codec_entry_test.go b/core/services/relay/evm/types/codec_entry_test.go new file mode 100644 index 00000000000..8cd5e661c9f --- /dev/null +++ b/core/services/relay/evm/types/codec_entry_test.go @@ -0,0 +1,284 @@ +package types + +import ( + "errors" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +func TestCodecEntry(t *testing.T) { + t.Run("basic types", func(t *testing.T) { + type1, err := abi.NewType("uint16", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + type2, err := abi.NewType("string", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + type3, err := abi.NewType("uint24", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + type4, err := abi.NewType("int24", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + args := abi.Arguments{ + {Name: "Field1", Type: type1}, + {Name: "Field2", Type: type2}, + {Name: "Field3", Type: type3}, + {Name: "Field4", Type: type4}, + } + entry := NewCodecEntry(args, nil, nil) + require.NoError(t, entry.Init()) + checked := reflect.New(entry.CheckedType()) + iChecked := reflect.Indirect(checked) + iChecked.FieldByName("Field1").Set(reflect.ValueOf(uint16(2))) + iChecked.FieldByName("Field2").Set(reflect.ValueOf("any string")) + + f3 := big.NewInt( /*2^24 - 1*/ 16777215) + setAndVerifyLimit(t, (*uint24)(f3), f3, iChecked.FieldByName("Field3")) + + f4 := big.NewInt( /*2^23 - 1*/ 8388607) + setAndVerifyLimit(t, (*int24)(f4), f4, iChecked.FieldByName("Field4")) + + rNative, err := entry.ToNative(checked) + require.NoError(t, err) + iNative := reflect.Indirect(rNative) + assert.Equal(t, iNative.Field(0).Interface(), iChecked.Field(0).Interface()) + assert.Equal(t, iNative.Field(1).Interface(), iChecked.Field(1).Interface()) + assert.Equal(t, iNative.Field(2).Interface(), f3) + assert.Equal(t, iNative.Field(3).Interface(), f4) + assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) + }) + + t.Run("tuples", func(t *testing.T) { + type1, err := abi.NewType("uint16", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + tupleType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "Field3", Type: "uint24"}, + {Name: "Field4", Type: "int24"}, + }) + require.NoError(t, err) + args := abi.Arguments{ + {Name: "Field1", Type: type1}, + {Name: "Field2", Type: tupleType}, + } + entry := NewCodecEntry(args, nil, nil) + require.NoError(t, entry.Init()) + + checked := reflect.New(entry.CheckedType()) + iChecked := reflect.Indirect(checked) + iChecked.FieldByName("Field1").Set(reflect.ValueOf(uint16(2))) + f2 := iChecked.FieldByName("Field2") + f3 := big.NewInt( /*2^24 - 1*/ 16777215) + setAndVerifyLimit(t, (*uint24)(f3), f3, f2.FieldByName("Field3")) + f4 := big.NewInt( /*2^23 - 1*/ 8388607) + setAndVerifyLimit(t, (*int24)(f4), f4, f2.FieldByName("Field4")) + + native, err := entry.ToNative(checked) + require.NoError(t, err) + iNative := reflect.Indirect(native) + require.Equal(t, iNative.Field(0).Interface(), iChecked.Field(0).Interface()) + nF2 := iNative.Field(1) + assert.Equal(t, nF2.Field(0).Interface(), f3) + assert.Equal(t, nF2.Field(1).Interface(), f4) + assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) + }) + + t.Run("unwrapped types", func(t *testing.T) { + // This exists to allow you to decode single returned values without naming the parameter + wrappedTuple, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "Field1", Type: "int16"}, + }) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "", Type: wrappedTuple}}, nil, nil) + require.NoError(t, entry.Init()) + checked := reflect.New(entry.CheckedType()) + iChecked := reflect.Indirect(checked) + anyValue := int16(2) + iChecked.FieldByName("Field1").Set(reflect.ValueOf(anyValue)) + native, err := entry.ToNative(checked) + require.NoError(t, err) + iNative := reflect.Indirect(native) + assert.Equal(t, anyValue, iNative.FieldByName("Field1").Interface()) + assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) + }) + + t.Run("slice types", func(t *testing.T) { + type1, err := abi.NewType("int16[]", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "Field1", Type: type1}}, nil, nil) + + require.NoError(t, entry.Init()) + checked := reflect.New(entry.CheckedType()) + iChecked := reflect.Indirect(checked) + anySliceValue := []int16{2, 3} + iChecked.FieldByName("Field1").Set(reflect.ValueOf(anySliceValue)) + native, err := entry.ToNative(checked) + require.NoError(t, err) + iNative := reflect.Indirect(native) + assert.Equal(t, anySliceValue, iNative.FieldByName("Field1").Interface()) + assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) + }) + + t.Run("array types", func(t *testing.T) { + type1, err := abi.NewType("int16[3]", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "Field1", Type: type1}}, nil, nil) + require.NoError(t, entry.Init()) + checked := reflect.New(entry.CheckedType()) + iChecked := reflect.Indirect(checked) + anySliceValue := [3]int16{2, 3, 30} + iChecked.FieldByName("Field1").Set(reflect.ValueOf(anySliceValue)) + native, err := entry.ToNative(checked) + require.NoError(t, err) + iNative := reflect.Indirect(native) + assert.Equal(t, anySliceValue, iNative.FieldByName("Field1").Interface()) + }) + + t.Run("Not return values makes struct{}", func(t *testing.T) { + entry := NewCodecEntry(abi.Arguments{}, nil, nil) + require.NoError(t, entry.Init()) + assert.Equal(t, reflect.TypeOf(struct{}{}), entry.CheckedType()) + native, err := entry.ToNative(reflect.ValueOf(&struct{}{})) + require.NoError(t, err) + assert.Equal(t, &struct{}{}, native.Interface()) + }) + + t.Run("Address works", func(t *testing.T) { + address, err := abi.NewType("address", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "foo", Type: address}}, nil, nil) + require.NoError(t, entry.Init()) + + checked := reflect.New(entry.CheckedType()) + iChecked := reflect.Indirect(checked) + anyAddr := common.Address{1, 2, 3} + iChecked.FieldByName("Foo").Set(reflect.ValueOf(anyAddr)) + + native, err := entry.ToNative(checked) + require.NoError(t, err) + iNative := reflect.Indirect(native) + assert.Equal(t, anyAddr, iNative.FieldByName("Foo").Interface()) + assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) + }) + + t.Run("Multiple unnamed parameters are not supported", func(t *testing.T) { + anyType, err := abi.NewType("int16[3]", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "", Type: anyType}, {Name: "", Type: anyType}}, nil, nil) + assert.True(t, errors.Is(entry.Init(), commontypes.ErrInvalidType)) + }) + + t.Run("Multiple abi arguments with the same name returns an error", func(t *testing.T) { + anyType, err := abi.NewType("int16[3]", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType}, {Name: "Name", Type: anyType}}, nil, nil) + assert.True(t, errors.Is(entry.Init(), commontypes.ErrInvalidConfig)) + }) + + t.Run("Indexed basic types leave their native and checked types as-is", func(t *testing.T) { + anyType, err := abi.NewType("int16", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType, Indexed: true}}, nil, nil) + require.NoError(t, entry.Init()) + checkedField, ok := entry.CheckedType().FieldByName("Name") + require.True(t, ok) + assert.Equal(t, reflect.TypeOf(int16(0)), checkedField.Type) + native, err := entry.ToNative(reflect.New(entry.CheckedType())) + require.NoError(t, err) + iNative := reflect.Indirect(native) + assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) + }) + + t.Run("Indexed non basic types change to hash", func(t *testing.T) { + anyType, err := abi.NewType("string", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType, Indexed: true}}, nil, nil) + require.NoError(t, entry.Init()) + nativeField, ok := entry.CheckedType().FieldByName("Name") + require.True(t, ok) + assert.Equal(t, reflect.TypeOf(common.Hash{}), nativeField.Type) + native, err := entry.ToNative(reflect.New(entry.CheckedType())) + require.NoError(t, err) + assertHaveSameStructureAndNames(t, native.Type().Elem(), entry.CheckedType()) + }) + + t.Run("Too many indexed items returns an error", func(t *testing.T) { + anyType, err := abi.NewType("int16", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry( + abi.Arguments{ + {Name: "Name1", Type: anyType, Indexed: true}, + {Name: "Name2", Type: anyType, Indexed: true}, + {Name: "Name3", Type: anyType, Indexed: true}, + {Name: "Name4", Type: anyType, Indexed: true}, + }, nil, nil) + require.True(t, errors.Is(entry.Init(), commontypes.ErrInvalidConfig)) + }) + + // TODO: when the TODO on + // https://github.com/ethereum/go-ethereum/blob/release/1.12/accounts/abi/topics.go#L78 + // is removed, remove this test. + t.Run("Using unsupported types by go-ethereum returns an error", func(t *testing.T) { + anyType, err := abi.NewType("int256[2]", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType, Indexed: true}}, nil, nil) + assert.True(t, errors.Is(entry.Init(), commontypes.ErrInvalidConfig)) + }) + + t.Run("Modifier returns provided modifier", func(t *testing.T) { + anyType, err := abi.NewType("int16", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + mod := codec.NewRenamer(map[string]string{"Name": "RenamedName"}) + entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType, Indexed: true}}, nil, mod) + assert.Equal(t, mod, entry.Modifier()) + }) + + t.Run("EncodingPrefix returns provided prefix", func(t *testing.T) { + anyType, err := abi.NewType("int16", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + prefix := []byte{1, 2, 3} + entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType, Indexed: true}}, prefix, nil) + assert.Equal(t, prefix, entry.EncodingPrefix()) + }) +} + +// sized and bi must be the same pointer. +func setAndVerifyLimit(t *testing.T, sbi SizedBigInt, bi *big.Int, field reflect.Value) { + require.Same(t, reflect.NewAt(reflect.TypeOf(big.Int{}), reflect.ValueOf(sbi).UnsafePointer()).Interface(), bi) + field.Set(reflect.ValueOf(sbi)) + assert.NoError(t, sbi.Verify()) + bi.Add(bi, big.NewInt(1)) + assert.IsType(t, commontypes.ErrInvalidType, sbi.Verify()) +} + +// verifying the same structure allows us to use unsafe pointers to cast between them. +// This is done for perf and simplicity in mapping the two structures. +// [reflect.NewAt]'s use is the same as (*native)(unsafe.Pointer(checked)) +// See the safe usecase 1 from [unsafe.Pointer], as this is a subset of that. +// This also verifies field names are the same. +func assertHaveSameStructureAndNames(t *testing.T, t1, t2 reflect.Type) { + require.Equal(t, t1.Kind(), t2.Kind()) + + switch t1.Kind() { + case reflect.Array: + require.Equal(t, t1.Len(), t2.Len()) + assertHaveSameStructureAndNames(t, t1.Elem(), t2.Elem()) + case reflect.Slice, reflect.Pointer: + assertHaveSameStructureAndNames(t, t1.Elem(), t2.Elem()) + case reflect.Struct: + numFields := t1.NumField() + require.Equal(t, numFields, t2.NumField()) + for i := 0; i < numFields; i++ { + require.Equal(t, t1.Field(i).Name, t2.Field(i).Name) + assertHaveSameStructureAndNames(t, t1.Field(i).Type, t2.Field(i).Type) + } + default: + require.Equal(t, t1, t2) + } +} diff --git a/core/services/relay/evm/types/gen/bytes.go.tmpl b/core/services/relay/evm/types/gen/bytes.go.tmpl new file mode 100644 index 00000000000..3c06529b0f3 --- /dev/null +++ b/core/services/relay/evm/types/gen/bytes.go.tmpl @@ -0,0 +1,14 @@ +package types + +import "reflect" + +{{ range . }} +type bytes{{.Size}} [{{.Size}}]byte +func init() { + typeMap["bytes{{.Size}}"] = &ABIEncodingType { + native: reflect.TypeOf([{{.Size}}]byte{}), + checked: reflect.TypeOf(bytes{{.Size}}{}), + } +} + +{{ end }} \ No newline at end of file diff --git a/core/services/relay/evm/types/gen/ints.go.tmpl b/core/services/relay/evm/types/gen/ints.go.tmpl new file mode 100644 index 00000000000..b5f0fa363ca --- /dev/null +++ b/core/services/relay/evm/types/gen/ints.go.tmpl @@ -0,0 +1,74 @@ +package types + +import ( + "math/big" + "reflect" + + "github.com/fxamacker/cbor/v2" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type SizedBigInt interface { + Verify() error + private() +} + +var sizedBigIntType = reflect.TypeOf((*SizedBigInt)(nil)).Elem() +func SizedBigIntType() reflect.Type { + return sizedBigIntType +} + +{{ range . }} +type {{.Prefix}}int{{.Size}} big.Int +func (i *{{.Prefix}}int{{.Size}}) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *{{.Prefix}}int{{.Size}}) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *{{.Prefix}}int{{.Size}}) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *{{.Prefix}}int{{.Size}}) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *{{.Prefix}}int{{.Size}}) Verify() error { + bi := (*big.Int)(i) + {{ if .Signed }} + if !codec.FitsInNBitsSigned({{.Size}}, bi) { + return types.ErrInvalidType + } + {{ else }} + if bi.BitLen() > {{.Size}} || bi.Sign() < 0 { + return types.ErrInvalidType + } + {{ end }} + return nil +} + +func (i *{{.Prefix}}int{{.Size}}) private() {} + +func init() { + typeMap["{{.Prefix}}int{{.Size}}"] = &ABIEncodingType { + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*{{.Prefix}}int{{.Size}})(nil)), + } +} +{{ end }} \ No newline at end of file diff --git a/core/services/relay/evm/types/gen/main.go b/core/services/relay/evm/types/gen/main.go new file mode 100644 index 00000000000..84e7c008ee0 --- /dev/null +++ b/core/services/relay/evm/types/gen/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "bytes" + _ "embed" + "go/format" + "os" + "text/template" +) + +func main() { + genInts() + genBytes() +} + +func genBytes() { + byteTypes := [33]ByteType{} + for i := 1; i < 33; i++ { + byteTypes[i-1].Size = i + } + mustRunTemplate("bytes", bytesTemplate, "byte_types_gen.go", byteTypes) +} + +func genInts() { + var intTypes []*IntType + + // 8, 16, 32, and 64 bits have their own type in go that is used by abi. + // The test use *big.Int + for i := 24; i <= 256; i += 8 { + if i == 32 || i == 64 { + continue + } + + signed := &IntType{Size: i, Signed: true} + unsigned := &IntType{Prefix: "u", Size: i} + intTypes = append(intTypes, signed, unsigned) + } + mustRunTemplate("ints", intsTemplate, "int_types_gen.go", intTypes) +} + +func mustRunTemplate(name, rawTemplate, outputFile string, input any) { + t := template.Must(template.New(name).Parse(rawTemplate)) + + br := bytes.Buffer{} + if err := t.Execute(&br, input); err != nil { + panic(err) + } + + res, err := format.Source(br.Bytes()) + if err != nil { + panic(err) + } + + if err = os.WriteFile(outputFile, res, 0600); err != nil { + panic(err) + } +} + +type IntType struct { + Prefix string + Size int + Signed bool +} + +type ByteType struct { + Size int +} + +//go:embed bytes.go.tmpl +var bytesTemplate string + +//go:embed ints.go.tmpl +var intsTemplate string diff --git a/core/services/relay/evm/types/int_types_gen.go b/core/services/relay/evm/types/int_types_gen.go new file mode 100644 index 00000000000..1ff82d5691d --- /dev/null +++ b/core/services/relay/evm/types/int_types_gen.go @@ -0,0 +1,2710 @@ +package types + +import ( + "math/big" + "reflect" + + "github.com/fxamacker/cbor/v2" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type SizedBigInt interface { + Verify() error + private() +} + +var sizedBigIntType = reflect.TypeOf((*SizedBigInt)(nil)).Elem() + +func SizedBigIntType() reflect.Type { + return sizedBigIntType +} + +type int24 big.Int + +func (i *int24) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int24) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int24) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int24) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int24) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(24, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int24) private() {} + +func init() { + typeMap["int24"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int24)(nil)), + } +} + +type uint24 big.Int + +func (i *uint24) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint24) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint24) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint24) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint24) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 24 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint24) private() {} + +func init() { + typeMap["uint24"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint24)(nil)), + } +} + +type int40 big.Int + +func (i *int40) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int40) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int40) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int40) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int40) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(40, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int40) private() {} + +func init() { + typeMap["int40"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int40)(nil)), + } +} + +type uint40 big.Int + +func (i *uint40) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint40) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint40) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint40) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint40) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 40 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint40) private() {} + +func init() { + typeMap["uint40"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint40)(nil)), + } +} + +type int48 big.Int + +func (i *int48) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int48) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int48) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int48) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int48) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(48, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int48) private() {} + +func init() { + typeMap["int48"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int48)(nil)), + } +} + +type uint48 big.Int + +func (i *uint48) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint48) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint48) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint48) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint48) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 48 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint48) private() {} + +func init() { + typeMap["uint48"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint48)(nil)), + } +} + +type int56 big.Int + +func (i *int56) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int56) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int56) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int56) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int56) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(56, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int56) private() {} + +func init() { + typeMap["int56"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int56)(nil)), + } +} + +type uint56 big.Int + +func (i *uint56) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint56) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint56) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint56) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint56) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 56 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint56) private() {} + +func init() { + typeMap["uint56"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint56)(nil)), + } +} + +type int72 big.Int + +func (i *int72) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int72) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int72) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int72) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int72) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(72, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int72) private() {} + +func init() { + typeMap["int72"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int72)(nil)), + } +} + +type uint72 big.Int + +func (i *uint72) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint72) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint72) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint72) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint72) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 72 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint72) private() {} + +func init() { + typeMap["uint72"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint72)(nil)), + } +} + +type int80 big.Int + +func (i *int80) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int80) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int80) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int80) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int80) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(80, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int80) private() {} + +func init() { + typeMap["int80"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int80)(nil)), + } +} + +type uint80 big.Int + +func (i *uint80) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint80) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint80) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint80) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint80) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 80 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint80) private() {} + +func init() { + typeMap["uint80"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint80)(nil)), + } +} + +type int88 big.Int + +func (i *int88) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int88) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int88) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int88) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int88) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(88, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int88) private() {} + +func init() { + typeMap["int88"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int88)(nil)), + } +} + +type uint88 big.Int + +func (i *uint88) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint88) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint88) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint88) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint88) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 88 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint88) private() {} + +func init() { + typeMap["uint88"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint88)(nil)), + } +} + +type int96 big.Int + +func (i *int96) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int96) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int96) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int96) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int96) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(96, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int96) private() {} + +func init() { + typeMap["int96"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int96)(nil)), + } +} + +type uint96 big.Int + +func (i *uint96) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint96) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint96) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint96) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint96) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 96 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint96) private() {} + +func init() { + typeMap["uint96"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint96)(nil)), + } +} + +type int104 big.Int + +func (i *int104) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int104) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int104) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int104) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int104) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(104, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int104) private() {} + +func init() { + typeMap["int104"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int104)(nil)), + } +} + +type uint104 big.Int + +func (i *uint104) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint104) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint104) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint104) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint104) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 104 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint104) private() {} + +func init() { + typeMap["uint104"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint104)(nil)), + } +} + +type int112 big.Int + +func (i *int112) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int112) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int112) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int112) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int112) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(112, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int112) private() {} + +func init() { + typeMap["int112"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int112)(nil)), + } +} + +type uint112 big.Int + +func (i *uint112) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint112) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint112) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint112) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint112) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 112 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint112) private() {} + +func init() { + typeMap["uint112"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint112)(nil)), + } +} + +type int120 big.Int + +func (i *int120) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int120) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int120) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int120) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int120) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(120, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int120) private() {} + +func init() { + typeMap["int120"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int120)(nil)), + } +} + +type uint120 big.Int + +func (i *uint120) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint120) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint120) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint120) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint120) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 120 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint120) private() {} + +func init() { + typeMap["uint120"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint120)(nil)), + } +} + +type int128 big.Int + +func (i *int128) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int128) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int128) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int128) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int128) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(128, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int128) private() {} + +func init() { + typeMap["int128"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int128)(nil)), + } +} + +type uint128 big.Int + +func (i *uint128) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint128) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint128) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint128) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint128) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 128 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint128) private() {} + +func init() { + typeMap["uint128"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint128)(nil)), + } +} + +type int136 big.Int + +func (i *int136) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int136) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int136) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int136) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int136) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(136, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int136) private() {} + +func init() { + typeMap["int136"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int136)(nil)), + } +} + +type uint136 big.Int + +func (i *uint136) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint136) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint136) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint136) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint136) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 136 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint136) private() {} + +func init() { + typeMap["uint136"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint136)(nil)), + } +} + +type int144 big.Int + +func (i *int144) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int144) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int144) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int144) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int144) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(144, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int144) private() {} + +func init() { + typeMap["int144"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int144)(nil)), + } +} + +type uint144 big.Int + +func (i *uint144) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint144) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint144) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint144) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint144) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 144 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint144) private() {} + +func init() { + typeMap["uint144"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint144)(nil)), + } +} + +type int152 big.Int + +func (i *int152) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int152) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int152) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int152) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int152) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(152, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int152) private() {} + +func init() { + typeMap["int152"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int152)(nil)), + } +} + +type uint152 big.Int + +func (i *uint152) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint152) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint152) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint152) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint152) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 152 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint152) private() {} + +func init() { + typeMap["uint152"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint152)(nil)), + } +} + +type int160 big.Int + +func (i *int160) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int160) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int160) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int160) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int160) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(160, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int160) private() {} + +func init() { + typeMap["int160"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int160)(nil)), + } +} + +type uint160 big.Int + +func (i *uint160) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint160) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint160) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint160) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint160) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 160 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint160) private() {} + +func init() { + typeMap["uint160"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint160)(nil)), + } +} + +type int168 big.Int + +func (i *int168) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int168) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int168) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int168) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int168) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(168, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int168) private() {} + +func init() { + typeMap["int168"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int168)(nil)), + } +} + +type uint168 big.Int + +func (i *uint168) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint168) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint168) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint168) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint168) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 168 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint168) private() {} + +func init() { + typeMap["uint168"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint168)(nil)), + } +} + +type int176 big.Int + +func (i *int176) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int176) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int176) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int176) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int176) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(176, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int176) private() {} + +func init() { + typeMap["int176"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int176)(nil)), + } +} + +type uint176 big.Int + +func (i *uint176) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint176) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint176) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint176) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint176) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 176 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint176) private() {} + +func init() { + typeMap["uint176"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint176)(nil)), + } +} + +type int184 big.Int + +func (i *int184) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int184) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int184) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int184) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int184) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(184, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int184) private() {} + +func init() { + typeMap["int184"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int184)(nil)), + } +} + +type uint184 big.Int + +func (i *uint184) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint184) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint184) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint184) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint184) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 184 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint184) private() {} + +func init() { + typeMap["uint184"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint184)(nil)), + } +} + +type int192 big.Int + +func (i *int192) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int192) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int192) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int192) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int192) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(192, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int192) private() {} + +func init() { + typeMap["int192"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int192)(nil)), + } +} + +type uint192 big.Int + +func (i *uint192) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint192) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint192) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint192) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint192) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 192 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint192) private() {} + +func init() { + typeMap["uint192"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint192)(nil)), + } +} + +type int200 big.Int + +func (i *int200) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int200) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int200) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int200) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int200) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(200, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int200) private() {} + +func init() { + typeMap["int200"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int200)(nil)), + } +} + +type uint200 big.Int + +func (i *uint200) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint200) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint200) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint200) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint200) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 200 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint200) private() {} + +func init() { + typeMap["uint200"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint200)(nil)), + } +} + +type int208 big.Int + +func (i *int208) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int208) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int208) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int208) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int208) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(208, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int208) private() {} + +func init() { + typeMap["int208"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int208)(nil)), + } +} + +type uint208 big.Int + +func (i *uint208) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint208) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint208) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint208) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint208) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 208 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint208) private() {} + +func init() { + typeMap["uint208"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint208)(nil)), + } +} + +type int216 big.Int + +func (i *int216) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int216) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int216) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int216) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int216) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(216, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int216) private() {} + +func init() { + typeMap["int216"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int216)(nil)), + } +} + +type uint216 big.Int + +func (i *uint216) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint216) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint216) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint216) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint216) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 216 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint216) private() {} + +func init() { + typeMap["uint216"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint216)(nil)), + } +} + +type int224 big.Int + +func (i *int224) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int224) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int224) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int224) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int224) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(224, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int224) private() {} + +func init() { + typeMap["int224"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int224)(nil)), + } +} + +type uint224 big.Int + +func (i *uint224) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint224) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint224) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint224) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint224) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 224 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint224) private() {} + +func init() { + typeMap["uint224"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint224)(nil)), + } +} + +type int232 big.Int + +func (i *int232) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int232) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int232) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int232) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int232) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(232, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int232) private() {} + +func init() { + typeMap["int232"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int232)(nil)), + } +} + +type uint232 big.Int + +func (i *uint232) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint232) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint232) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint232) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint232) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 232 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint232) private() {} + +func init() { + typeMap["uint232"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint232)(nil)), + } +} + +type int240 big.Int + +func (i *int240) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int240) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int240) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int240) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int240) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(240, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int240) private() {} + +func init() { + typeMap["int240"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int240)(nil)), + } +} + +type uint240 big.Int + +func (i *uint240) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint240) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint240) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint240) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint240) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 240 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint240) private() {} + +func init() { + typeMap["uint240"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint240)(nil)), + } +} + +type int248 big.Int + +func (i *int248) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int248) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int248) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int248) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int248) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(248, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int248) private() {} + +func init() { + typeMap["int248"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int248)(nil)), + } +} + +type uint248 big.Int + +func (i *uint248) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint248) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint248) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint248) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint248) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 248 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint248) private() {} + +func init() { + typeMap["uint248"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint248)(nil)), + } +} + +type int256 big.Int + +func (i *int256) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *int256) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *int256) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *int256) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *int256) Verify() error { + bi := (*big.Int)(i) + + if !codec.FitsInNBitsSigned(256, bi) { + return types.ErrInvalidType + } + + return nil +} + +func (i *int256) private() {} + +func init() { + typeMap["int256"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*int256)(nil)), + } +} + +type uint256 big.Int + +func (i *uint256) UnmarshalCBOR(input []byte) error { + bi := (*big.Int)(i) + if err := cbor.Unmarshal(input, bi); err != nil { + return err + } + + return i.Verify() +} + +func (i *uint256) MarshalCBOR() ([]byte, error) { + return cbor.Marshal((*big.Int)(i)) +} + +func (i *uint256) UnmarshalText(input []byte) error { + bi := (*big.Int)(i) + if _, ok := bi.SetString(string(input), 10); !ok { + return types.ErrInvalidType + } + + return i.Verify() +} + +func (i *uint256) MarshalText() ([]byte, error) { + bi := (*big.Int)(i) + return []byte(bi.String()), nil +} + +func (i *uint256) Verify() error { + bi := (*big.Int)(i) + + if bi.BitLen() > 256 || bi.Sign() < 0 { + return types.ErrInvalidType + } + + return nil +} + +func (i *uint256) private() {} + +func init() { + typeMap["uint256"] = &ABIEncodingType{ + native: reflect.TypeOf((*big.Int)(nil)), + checked: reflect.TypeOf((*uint256)(nil)), + } +} diff --git a/core/services/relay/evm/types/int_types_test.go b/core/services/relay/evm/types/int_types_test.go new file mode 100644 index 00000000000..6930287c869 --- /dev/null +++ b/core/services/relay/evm/types/int_types_test.go @@ -0,0 +1,54 @@ +package types + +import ( + "errors" + "fmt" + "math/big" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +func TestIntTypes(t *testing.T) { + t.Parallel() + for i := 24; i <= 256; i += 8 { + if i == 64 || i == 32 { + continue + } + t.Run(fmt.Sprintf("int%v", i), func(t *testing.T) { + tpe, ok := GetAbiEncodingType(fmt.Sprintf("int%v", i)) + require.True(t, ok) + minVal := new(big.Int).Neg(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(i-1)), nil)) + maxVal := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(i-1)), nil), big.NewInt(1)) + assertBigIntBounds(t, tpe, minVal, maxVal) + }) + + t.Run(fmt.Sprintf("uint%v", i), func(t *testing.T) { + tep, ok := GetAbiEncodingType(fmt.Sprintf("uint%v", i)) + require.True(t, ok) + minVal := big.NewInt(0) + maxVal := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(i)), nil), big.NewInt(1)) + assertBigIntBounds(t, tep, minVal, maxVal) + }) + } +} + +func assertBigIntBounds(t *testing.T, tpe *ABIEncodingType, min, max *big.Int) { + t.Helper() + assert.Equal(t, reflect.TypeOf(min), tpe.native) + assert.True(t, tpe.checked.ConvertibleTo(reflect.TypeOf(min))) + minMinusOne := new(big.Int).Sub(min, big.NewInt(1)) + maxPlusOne := new(big.Int).Add(max, big.NewInt(1)) + sbi := reflect.ValueOf(min).Convert(tpe.checked).Interface().(SizedBigInt) + assert.NoError(t, sbi.Verify()) + sbi = reflect.ValueOf(max).Convert(tpe.checked).Interface().(SizedBigInt) + assert.NoError(t, sbi.Verify()) + sbi = reflect.ValueOf(minMinusOne).Convert(tpe.checked).Interface().(SizedBigInt) + assert.True(t, errors.Is(types.ErrInvalidType, sbi.Verify())) + sbi = reflect.ValueOf(maxPlusOne).Convert(tpe.checked).Interface().(SizedBigInt) + assert.True(t, errors.Is(types.ErrInvalidType, sbi.Verify())) +} diff --git a/core/services/relay/evm/types/size_helper.go b/core/services/relay/evm/types/size_helper.go new file mode 100644 index 00000000000..921b8a28315 --- /dev/null +++ b/core/services/relay/evm/types/size_helper.go @@ -0,0 +1,69 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +func GetMaxSize(n int, args abi.Arguments) (int, error) { + size := 0 + for _, arg := range args { + tmp := arg.Type + argSize, _, err := getTypeSize(n, &tmp, true, false) + if err != nil { + return 0, err + } + size += argSize + } + + return size, nil +} + +func getTypeSize(n int, t *abi.Type, dynamicTypeAllowed bool, isNested bool) (int, bool, error) { + // See https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding + switch t.T { + case abi.ArrayTy: + elmSize, _, err := getTypeSize(n, t.Elem, false, true) + return t.Size * elmSize, false, err + case abi.SliceTy: + if !dynamicTypeAllowed { + return 0, false, commontypes.ErrInvalidType + } + elmSize, _, err := getTypeSize(n, t.Elem, false, true) + return 32 /*header*/ + 32 /*footer*/ + elmSize*n, true, err + case abi.BytesTy, abi.StringTy: + if !dynamicTypeAllowed { + return 0, false, commontypes.ErrInvalidType + } + totalSize := (n + 31) / 32 * 32 // strings and bytes are padded to 32 bytes + return 32 /*header*/ + 32 /*footer*/ + totalSize, true, nil + case abi.TupleTy: + return getTupleSize(n, t, isNested) + default: + // types are padded to 32 bytes + return 32, false, nil + } +} + +func getTupleSize(n int, t *abi.Type, isNested bool) (int, bool, error) { + // No header or footer, because if the tuple is dynamically sized we would need to know the inner slice sizes + // so it would return error for that element. + size := 0 + dynamic := false + for _, elm := range t.TupleElems { + argSize, dynamicArg, err := getTypeSize(n, elm, !isNested, true) + if err != nil { + return 0, false, err + } + dynamic = dynamic || dynamicArg + size += argSize + } + + if dynamic { + // offset for the element needs to be included there are dynamic elements + size += 32 + } + + return size, dynamic, nil +} diff --git a/core/services/relay/evm/types/size_helper_test.go b/core/services/relay/evm/types/size_helper_test.go new file mode 100644 index 00000000000..202269a4536 --- /dev/null +++ b/core/services/relay/evm/types/size_helper_test.go @@ -0,0 +1,263 @@ +package types_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +const anyNumElements = 10 + +func TestGetMaxSize(t *testing.T) { + t.Run("Basic types all encode to 32 bytes", func(t *testing.T) { + args := abi.Arguments{ + {Name: "I8", Type: mustType(t, "int8")}, + {Name: "I80", Type: mustType(t, "int80")}, + {Name: "I256", Type: mustType(t, "int256")}, + {Name: "B3", Type: mustType(t, "bytes3")}, + {Name: "B32", Type: mustType(t, "bytes32")}, + {Name: "TF", Type: mustType(t, "bool")}, + } + + runSizeTest(t, anyNumElements, args, int8(9), big.NewInt(3), big.NewInt(200), [3]byte{1, 3, 4}, make32Bytes(1), true) + }) + + t.Run("Slices of basic types all encode to 32 bytes each + header and footer", func(t *testing.T) { + args := abi.Arguments{ + {Name: "I8", Type: mustType(t, "int8[]")}, + {Name: "I80", Type: mustType(t, "int80[]")}, + {Name: "I256", Type: mustType(t, "int256[]")}, + {Name: "B3", Type: mustType(t, "bytes3[]")}, + {Name: "B32", Type: mustType(t, "bytes32[]")}, + {Name: "TF", Type: mustType(t, "bool[]")}, + } + + i8 := []int8{9, 2, 1, 3, 5, 6, 2, 1, 2, 3} + i80 := []*big.Int{big.NewInt(9), big.NewInt(2), big.NewInt(1), big.NewInt(3), big.NewInt(5), big.NewInt(6), big.NewInt(2), big.NewInt(1), big.NewInt(2), big.NewInt(3)} + i256 := []*big.Int{big.NewInt(119), big.NewInt(112), big.NewInt(1), big.NewInt(3), big.NewInt(5), big.NewInt(6), big.NewInt(2), big.NewInt(1), big.NewInt(2), big.NewInt(3)} + b3 := [][3]byte{{1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}} + b32 := [][32]byte{make32Bytes(1), make32Bytes(2), make32Bytes(3), make32Bytes(4), make32Bytes(5), make32Bytes(6), make32Bytes(7), make32Bytes(8), make32Bytes(9), make32Bytes(10)} + tf := []bool{true, false, true, false, true, false, true, false, true, false} + runSizeTest(t, anyNumElements, args, i8, i80, i256, b3, b32, tf) + }) + + t.Run("Arrays of basic types all encode to 32 bytes each", func(t *testing.T) { + args := abi.Arguments{ + {Name: "I8", Type: mustType(t, "int8[3]")}, + {Name: "I80", Type: mustType(t, "int80[3]")}, + {Name: "I256", Type: mustType(t, "int256[3]")}, + {Name: "B3", Type: mustType(t, "bytes3[3]")}, + {Name: "B32", Type: mustType(t, "bytes32[3]")}, + {Name: "TF", Type: mustType(t, "bool[3]")}, + } + + i8 := [3]int8{9, 2, 1} + i80 := [3]*big.Int{big.NewInt(9), big.NewInt(2), big.NewInt(1)} + i256 := [3]*big.Int{big.NewInt(119), big.NewInt(112), big.NewInt(1)} + b3 := [3][3]byte{{1, 2, 3}, {1, 2, 3}, {1, 2, 3}} + b32 := [3][32]byte{make32Bytes(1), make32Bytes(2), make32Bytes(3)} + tf := [3]bool{true, false, true} + runSizeTest(t, anyNumElements, args, i8, i80, i256, b3, b32, tf) + }) + + t.Run("Tuples are a sum of their elements", func(t *testing.T) { + tuple1 := []abi.ArgumentMarshaling{ + {Name: "I8", Type: "int8"}, + {Name: "I80", Type: "int80"}, + {Name: "I256", Type: "int256"}, + {Name: "B3", Type: "bytes3"}, + {Name: "B32", Type: "bytes32"}, + {Name: "TF", Type: "bool"}, + } + t1, err := abi.NewType("tuple", "", tuple1) + require.NoError(t, err) + + tuple2 := []abi.ArgumentMarshaling{ + {Name: "I80", Type: "int80"}, + {Name: "TF", Type: "bool"}, + } + t2, err := abi.NewType("tuple", "", tuple2) + require.NoError(t, err) + + args := abi.Arguments{ + {Name: "t1", Type: t1}, + {Name: "t2", Type: t2}, + } + arg1 := struct { + I8 int8 + I80 *big.Int + I256 *big.Int + B3 [3]byte + B32 [32]byte + TF bool + }{ + int8(9), big.NewInt(3), big.NewInt(200), [3]byte{1, 3, 4}, make32Bytes(1), true, + } + + arg2 := struct { + I80 *big.Int + TF bool + }{ + big.NewInt(3), true, + } + runSizeTest(t, anyNumElements, args, arg1, arg2) + }) + + t.Run("Slices of tuples are a sum of their elements with header and footer", func(t *testing.T) { + tuple1 := []abi.ArgumentMarshaling{ + {Name: "I80", Type: "int80"}, + {Name: "TF", Type: "bool"}, + } + t1, err := abi.NewType("tuple[]", "", tuple1) + require.NoError(t, err) + + args := abi.Arguments{ + {Name: "t1", Type: t1}, + } + arg1 := []struct { + I80 *big.Int + TF bool + }{ + {big.NewInt(1), true}, + {big.NewInt(2), true}, + {big.NewInt(3), true}, + {big.NewInt(4), false}, + {big.NewInt(5), true}, + {big.NewInt(6), true}, + {big.NewInt(7), true}, + {big.NewInt(8), false}, + {big.NewInt(9), true}, + {big.NewInt(10), true}, + } + runSizeTest(t, anyNumElements, args, arg1) + }) + + t.Run("Arrays of tuples are a sum of their elements", func(t *testing.T) { + tuple1 := []abi.ArgumentMarshaling{ + {Name: "I80", Type: "int80"}, + {Name: "TF", Type: "bool"}, + } + t1, err := abi.NewType("tuple[3]", "", tuple1) + require.NoError(t, err) + + args := abi.Arguments{ + {Name: "t1", Type: t1}, + } + arg1 := []struct { + I80 *big.Int + TF bool + }{ + {big.NewInt(1), true}, + {big.NewInt(2), true}, + {big.NewInt(3), true}, + } + runSizeTest(t, anyNumElements, args, arg1) + + }) + + t.Run("Bytes pack themselves", func(t *testing.T) { + args := abi.Arguments{{Name: "B", Type: mustType(t, "bytes")}} + t.Run("No padding needed", func(t *testing.T) { + padded := []byte("12345789022345678903234567890412345678905123456789061234") + runSizeTest(t, 64, args, padded) + }) + t.Run("Padding needed", func(t *testing.T) { + needsPadding := []byte("12345789022345678903234567890412345678905123456") + runSizeTest(t, 56, args, needsPadding) + }) + }) + + t.Run("Strings pack themselves", func(t *testing.T) { + args := abi.Arguments{{Name: "B", Type: mustType(t, "string")}} + t.Run("No padding needed", func(t *testing.T) { + padded := "12345789022345678903234567890412345678905123456789061234" + runSizeTest(t, 64, args, padded) + }) + t.Run("Padding needed", func(t *testing.T) { + needsPadding := "12345789022345678903234567890412345678905123456" + runSizeTest(t, 56, args, needsPadding) + }) + }) + + t.Run("Nested dynamic types return errors", func(t *testing.T) { + t.Run("Slice in slice", func(t *testing.T) { + args := abi.Arguments{{Name: "B", Type: mustType(t, "int32[][]")}} + _, err := types.GetMaxSize(anyNumElements, args) + assert.IsType(t, commontypes.ErrInvalidType, err) + }) + t.Run("Slice in array", func(t *testing.T) { + args := abi.Arguments{{Name: "B", Type: mustType(t, "int32[][2]")}} + _, err := types.GetMaxSize(anyNumElements, args) + assert.IsType(t, commontypes.ErrInvalidType, err) + }) + }) + + t.Run("Slices in a top level tuple works as-if they are the sized element", func(t *testing.T) { + tuple1 := []abi.ArgumentMarshaling{ + {Name: "I80", Type: "int80[]"}, + {Name: "TF", Type: "bool[]"}, + } + t1, err := abi.NewType("tuple", "", tuple1) + require.NoError(t, err) + args := abi.Arguments{{Name: "tuple", Type: t1}} + + arg1 := struct { + I80 []*big.Int + TF []bool + }{ + I80: []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3), big.NewInt(4), big.NewInt(5), big.NewInt(6), big.NewInt(7), big.NewInt(8), big.NewInt(9), big.NewInt(10)}, + TF: []bool{true, true, true, false, true, true, true, false, true, true}, + } + + runSizeTest(t, anyNumElements, args, arg1) + }) + + t.Run("Nested dynamic tuples return errors", func(t *testing.T) { + tuple1 := []abi.ArgumentMarshaling{ + {Name: "I8", Type: "int8"}, + {Name: "I80", Type: "int80"}, + {Name: "I256", Type: "int256"}, + {Name: "B3", Type: "bytes3"}, + {Name: "B32", Type: "bytes32"}, + {Name: "TF", Type: "bool[]"}, + } + + tuple2 := []abi.ArgumentMarshaling{ + {Name: "I80", Type: "int80"}, + {Name: "T1", Type: "tuple", Components: tuple1}, + } + t2, err := abi.NewType("tuple", "", tuple2) + require.NoError(t, err) + + args := abi.Arguments{{Name: "t2", Type: t2}} + _, err = types.GetMaxSize(anyNumElements, args) + assert.IsType(t, commontypes.ErrInvalidType, err) + }) +} + +func runSizeTest(t *testing.T, n int, args abi.Arguments, params ...any) { + + actual, err := types.GetMaxSize(n, args) + require.NoError(t, err) + + expected, err := args.Pack(params...) + require.NoError(t, err) + assert.Equal(t, len(expected), actual) +} + +func mustType(t *testing.T, name string) abi.Type { + aType, err := abi.NewType(name, "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + return aType +} + +func make32Bytes(firstByte byte) [32]byte { + return [32]byte{firstByte, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3} +} diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index 129ccb4a5e6..0697605edab 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "context" "encoding/json" "errors" @@ -8,50 +9,108 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" - - "gopkg.in/guregu/null.v2" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "gopkg.in/guregu/null.v2" + "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) type ChainReaderConfig struct { - // ChainContractReaders key is contract name - ChainContractReaders map[string]ChainContractReader `json:"chainContractReaders"` + // Contracts key is contract name + Contracts map[string]ChainContractReader `json:"contracts" toml:"contracts"` +} + +type CodecConfig struct { + // Configs key is the type's name for the codec + Configs map[string]ChainCodecConfig `json:"configs" toml:"configs"` +} + +type ChainCodecConfig struct { + TypeABI string `json:"typeAbi" toml:"typeABI"` + ModifierConfigs codec.ModifiersConfig `toml:"modifierConfigs,omitempty"` } type ChainContractReader struct { - ContractABI string `json:"contractABI"` - // ChainReaderDefinitions key is chainAgnostic read name. - ChainReaderDefinitions map[string]ChainReaderDefinition `json:"chainReaderDefinitions"` + ContractABI string `json:"contractABI" toml:"contractABI"` + // key is genericName from config + Configs map[string]*ChainReaderDefinition `json:"configs" toml:"configs"` +} + +type ChainReaderDefinition chainReaderDefinitionFields + +// chainReaderDefinitionFields has the fields for ChainReaderDefinition but no methods. +// This is necessary because package json recognizes the text encoding methods used for TOML, +// and would infinitely recurse on itself. +type chainReaderDefinitionFields struct { + CacheEnabled bool `json:"cacheEnabled,omitempty"` + // chain specific contract method name or event type. + ChainSpecificName string `json:"chainSpecificName"` + ReadType ReadType `json:"readType,omitempty"` + InputModifications codec.ModifiersConfig `json:"inputModifications,omitempty"` + OutputModifications codec.ModifiersConfig `json:"outputModifications,omitempty"` + + // EventInputFields allows you to choose which indexed fields are expected from the input + EventInputFields []string `json:"eventInputFields,omitempty"` +} + +func (d *ChainReaderDefinition) MarshalText() ([]byte, error) { + var b bytes.Buffer + e := json.NewEncoder(&b) + e.SetIndent("", " ") + if err := e.Encode((*chainReaderDefinitionFields)(d)); err != nil { + return nil, err + } + return b.Bytes(), nil } -type ChainReaderDefinition struct { - ChainSpecificName string `json:"chainSpecificName"` // chain specific contract method name or event type. - Params map[string]any `json:"params"` - ReturnValues []string `json:"returnValues"` - CacheEnabled bool `json:"cacheEnabled"` - ReadType ReadType `json:"readType"` +func (d *ChainReaderDefinition) UnmarshalText(b []byte) error { + return json.Unmarshal(b, (*chainReaderDefinitionFields)(d)) } -type ReadType int64 +type ReadType int const ( - Method ReadType = 0 - Event ReadType = 1 + Method ReadType = iota + Event ) +func (r ReadType) String() string { + switch r { + case Method: + return "method" + case Event: + return "event" + } + return fmt.Sprintf("ReadType(%d)", r) +} + +func (r ReadType) MarshalText() ([]byte, error) { + return []byte(r.String()), nil +} + +func (r *ReadType) UnmarshalText(text []byte) error { + switch string(text) { + case "method": + *r = Method + return nil + case "event": + *r = Event + return nil + } + return fmt.Errorf("unrecognized ReadType: %s", string(text)) +} + type RelayConfig struct { ChainID *big.Big `json:"chainID"` FromBlock uint64 `json:"fromBlock"` EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` ConfigContractAddress *common.Address `json:"configContractAddress"` ChainReader *ChainReaderConfig `json:"chainReader"` + Codec *CodecConfig `json:"codec"` // Contract-specific SendingKeys pq.StringArray `json:"sendingKeys"` @@ -60,15 +119,15 @@ type RelayConfig struct { FeedID *common.Hash `json:"feedID"` } +var ErrBadRelayConfig = errors.New("bad relay config") + type RelayOpts struct { // TODO BCF-2508 -- should anyone ever get the raw config bytes that are embedded in args? if not, // make this private and wrap the arg fields with funcs on RelayOpts - commontypes.RelayArgs + types.RelayArgs c *RelayConfig } -var ErrBadRelayConfig = errors.New("bad relay config") - func NewRelayOpts(args types.RelayArgs) *RelayOpts { return &RelayOpts{ RelayArgs: args, @@ -98,9 +157,9 @@ type ConfigPoller interface { Replay(ctx context.Context, fromBlock int64) error } -// TODO(FUN-668): Migrate this fully into commontypes.FunctionsProvider +// TODO(FUN-668): Migrate this fully into types.FunctionsProvider type FunctionsProvider interface { - commontypes.FunctionsProvider + types.FunctionsProvider LogPollerWrapper() LogPollerWrapper } diff --git a/go.mod b/go.mod index a5aa4eb23dc..b8180998e92 100644 --- a/go.mod +++ b/go.mod @@ -65,12 +65,12 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 - github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 @@ -106,7 +106,6 @@ require ( ) require ( - cloud.google.com/go/compute v1.23.3 // indirect contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect @@ -313,10 +312,9 @@ require ( golang.org/x/arch v0.6.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/appengine v1.6.8 // indirect + google.golang.org/api v0.149.0 // indirect google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect diff --git a/go.sum b/go.sum index 5578bf8ff31..0eccd2bba0b 100644 --- a/go.sum +++ b/go.sum @@ -1153,18 +1153,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 h1:3cWO2/lFVDul5SVTgl4/RX/GXcT8Zq5NGMPeNEz09tY= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a h1:lgM0yPo0KqSntLY4Y42RAH3avdv+Kyne8n+VM7cwlxo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 h1:ziqC+WW/2/UI6w3DShy7HGzJMWWLIYHT5ev2Qaa3h6I= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 h1:1BcjXuviSAKttOX7BZoVHRZZGfxqoA2+AL8tykmkdoc= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8/go.mod h1:vy1L7NybTy2F/Yv7BOh+oZBa1MACD6gzd1+DkcSkfp8= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba h1:6rnQrD8NaLfLOPHszW1hbpviqpU8011gzdZk6wKP1xY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= @@ -1664,7 +1664,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index 37db348815b..829d85a8498 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" @@ -376,6 +377,7 @@ func StartNewOCR2Round( timeout time.Duration, logger zerolog.Logger, ) error { + time.Sleep(5 * time.Second) for i := 0; i < len(ocrInstances); i++ { err := ocrInstances[i].RequestNewRound() if err != nil { @@ -383,9 +385,12 @@ func StartNewOCR2Round( } ocrRound := contracts.NewOffchainAggregatorV2RoundConfirmer(ocrInstances[i], big.NewInt(roundNumber), timeout, logger) client.AddHeaderEventSubscription(ocrInstances[i].Address(), ocrRound) - err = client.WaitForEvents() + err = ocrRound.Wait() // wait for OCR Round to complete if err != nil { - return fmt.Errorf("failed to wait for event subscriptions of OCR instance %d: %w", i+1, err) + return fmt.Errorf("failed to wait for OCR Round %d to complete instance %d", roundNumber, i) + } + if !ocrRound.Complete() { + return fmt.Errorf("failed to complete OCR Round %d for ocr instance %d", roundNumber, i) } } return nil diff --git a/integration-tests/actions/ocr2_helpers_local.go b/integration-tests/actions/ocr2_helpers_local.go index 4a08921b8d1..8a0a02c050f 100644 --- a/integration-tests/actions/ocr2_helpers_local.go +++ b/integration-tests/actions/ocr2_helpers_local.go @@ -18,7 +18,9 @@ import ( "golang.org/x/sync/errgroup" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" @@ -38,6 +40,7 @@ func CreateOCRv2JobsLocal( mockAdapterValue int, // Value to get from the mock server when querying the path chainId uint64, // EVM chain ID forwardingAllowed bool, + enableChainReaderAndCodec bool, ) error { // Collect P2P ID bootstrapP2PIds, err := bootstrapNode.MustReadP2PKeys() @@ -125,6 +128,43 @@ func CreateOCRv2JobsLocal( P2PV2Bootstrappers: pq.StringArray{p2pV2Bootstrapper}, // bootstrap node key and address @bootstrap:6690 }, } + if enableChainReaderAndCodec { + ocrSpec.OCR2OracleSpec.RelayConfig["chainReader"] = evmtypes.ChainReaderConfig{ + Contracts: map[string]evmtypes.ChainContractReader{ + "median": { + ContractABI: `[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"round","type":"uint8"}],"name":"RoundRequested","type":"event"},{"inputs":[],"name":"latestTransmissionDetails","outputs":[{"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"internalType":"uint32","name":"epoch","type":"uint32"},{"internalType":"uint8","name":"round","type":"uint8"},{"internalType":"int192","name":"latestAnswer_","type":"int192"},{"internalType":"uint64","name":"latestTimestamp_","type":"uint64"}],"stateMutability":"view","type":"function"}]`, + Configs: map[string]*evmtypes.ChainReaderDefinition{ + "LatestTransmissionDetails": { + ChainSpecificName: "latestTransmissionDetails", + OutputModifications: codec.ModifiersConfig{ + &codec.EpochToTimeModifierConfig{ + Fields: []string{"LatestTimestamp_"}, + }, + &codec.RenameModifierConfig{ + Fields: map[string]string{ + "LatestAnswer_": "LatestAnswer", + "LatestTimestamp_": "LatestTimestamp", + }, + }, + }, + }, + "LatestRoundRequested": { + ChainSpecificName: "RoundRequested", + ReadType: evmtypes.Event, + }, + }, + }, + }, + } + ocrSpec.OCR2OracleSpec.RelayConfig["codec"] = evmtypes.CodecConfig{ + Configs: map[string]evmtypes.ChainCodecConfig{ + "MedianReport": { + TypeABI: `[{"Name": "Timestamp","Type": "uint32"},{"Name": "Observers","Type": "bytes32"},{"Name": "Observations","Type": "int192[]"},{"Name": "JuelsPerFeeCoin","Type": "int192"}]`, + }, + }, + } + } + _, err = chainlinkNode.MustCreateJob(ocrSpec) if err != nil { return fmt.Errorf("creating OCR task job on OCR node have failed: %w", err) diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index e6e1de25e41..9268968800d 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -6,6 +6,7 @@ import ( "text/template" "time" + "github.com/pelletier/go-toml/v2" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -1027,6 +1028,12 @@ func (o *OCR2TaskJobSpec) String() (string, error) { if o.OCR2OracleSpec.FeedID != nil { feedID = o.OCR2OracleSpec.FeedID.Hex() } + relayConfig, err := toml.Marshal(struct { + RelayConfig job.JSONConfig `toml:"relayConfig"` + }{RelayConfig: o.OCR2OracleSpec.RelayConfig}) + if err != nil { + return "", fmt.Errorf("failed to marshal relay config: %w", err) + } specWrap := struct { Name string JobType string @@ -1036,7 +1043,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { FeedID string Relay string PluginType string - RelayConfig map[string]interface{} + RelayConfig string PluginConfig map[string]interface{} P2PV2Bootstrappers []string OCRKeyBundleID string @@ -1056,7 +1063,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { FeedID: feedID, Relay: string(o.OCR2OracleSpec.Relay), PluginType: string(o.OCR2OracleSpec.PluginType), - RelayConfig: o.OCR2OracleSpec.RelayConfig, + RelayConfig: string(relayConfig), PluginConfig: o.OCR2OracleSpec.PluginConfig, P2PV2Bootstrappers: o.OCR2OracleSpec.P2PV2Bootstrappers, OCRKeyBundleID: o.OCR2OracleSpec.OCRKeyBundleID.String, @@ -1071,37 +1078,37 @@ func (o *OCR2TaskJobSpec) String() (string, error) { type = "{{ .JobType }}" name = "{{.Name}}" forwardingAllowed = {{.ForwardingAllowed}} -{{if .MaxTaskDuration}} +{{- if .MaxTaskDuration}} maxTaskDuration = "{{ .MaxTaskDuration }}" {{end}} -{{if .PluginType}} +{{- if .PluginType}} pluginType = "{{ .PluginType }}" {{end}} relay = "{{.Relay}}" schemaVersion = 1 contractID = "{{.ContractID}}" -{{if .FeedID}} +{{- if .FeedID}} feedID = "{{.FeedID}}" {{end}} -{{if eq .JobType "offchainreporting2" }} +{{- if eq .JobType "offchainreporting2" }} ocrKeyBundleID = "{{.OCRKeyBundleID}}" {{end}} -{{if eq .JobType "offchainreporting2" }} +{{- if eq .JobType "offchainreporting2" }} transmitterID = "{{.TransmitterID}}" {{end}} -{{if .BlockchainTimeout}} +{{- if .BlockchainTimeout}} blockchainTimeout = "{{.BlockchainTimeout}}" {{end}} -{{if .ContractConfirmations}} +{{- if .ContractConfirmations}} contractConfigConfirmations = {{.ContractConfirmations}} {{end}} -{{if .TrackerPollInterval}} +{{- if .TrackerPollInterval}} contractConfigTrackerPollInterval = "{{.TrackerPollInterval}}" {{end}} -{{if .TrackerSubscribeInterval}} +{{- if .TrackerSubscribeInterval}} contractConfigTrackerSubscribeInterval = "{{.TrackerSubscribeInterval}}" {{end}} -{{if .P2PV2Bootstrappers}} +{{- if .P2PV2Bootstrappers}} p2pv2Bootstrappers = [{{range .P2PV2Bootstrappers}}"{{.}}",{{end}}]{{end}} -{{if .MonitoringEndpoint}} +{{- if .MonitoringEndpoint}} monitoringEndpoint = "{{.MonitoringEndpoint}}" {{end}} -{{if .ObservationSource}} +{{- if .ObservationSource}} observationSource = """ {{.ObservationSource}} """{{end}} @@ -1109,8 +1116,7 @@ observationSource = """ [pluginConfig]{{range $key, $value := .PluginConfig}} {{$key}} = {{$value}}{{end}} {{end}} -[relayConfig]{{range $key, $value := .RelayConfig}} -{{$key}} = {{$value}}{{end}} +{{.RelayConfig}} ` return MarshallTemplate(specWrap, "OCR2 Job", ocr2TemplateString) } diff --git a/integration-tests/client/chainlink_models_test.go b/integration-tests/client/chainlink_models_test.go new file mode 100644 index 00000000000..5dbac2c27c4 --- /dev/null +++ b/integration-tests/client/chainlink_models_test.go @@ -0,0 +1,135 @@ +package client + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +func TestOCR2TaskJobSpec_String(t *testing.T) { + for _, tt := range []struct { + name string + spec OCR2TaskJobSpec + exp string + }{ + { + name: "chain-reader-codec", + spec: OCR2TaskJobSpec{ + OCR2OracleSpec: job.OCR2OracleSpec{ + RelayConfig: map[string]interface{}{ + "chainID": 1337, + "fromBlock": 42, + "chainReader": evmtypes.ChainReaderConfig{ + Contracts: map[string]evmtypes.ChainContractReader{ + "median": { + ContractABI: `[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "requester", + "type": "address" + } + ], + "name": "RoundRequested", + "type": "event" + } +] +`, + Configs: map[string]*evmtypes.ChainReaderDefinition{ + "LatestTransmissionDetails": { + ChainSpecificName: "latestTransmissionDetails", + OutputModifications: codec.ModifiersConfig{ + &codec.EpochToTimeModifierConfig{ + Fields: []string{"LatestTimestamp_"}, + }, + &codec.RenameModifierConfig{ + Fields: map[string]string{ + "LatestAnswer_": "LatestAnswer", + "LatestTimestamp_": "LatestTimestamp", + }, + }, + }, + }, + "LatestRoundRequested": { + ChainSpecificName: "RoundRequested", + ReadType: evmtypes.Event, + }, + }, + }, + }, + }, + "codec": evmtypes.CodecConfig{ + Configs: map[string]evmtypes.ChainCodecConfig{ + "MedianReport": { + TypeABI: `[ + { + "Name": "Timestamp", + "Type": "uint32" + } +] +`, + }, + }, + }, + }, + PluginConfig: map[string]interface{}{"juelsPerFeeCoinSource": ` // data source 1 + ds1 [type=bridge name="%s"]; + ds1_parse [type=jsonparse path="data"]; + ds1_multiply [type=multiply times=2]; + + // data source 2 + ds2 [type=http method=GET url="%s"]; + ds2_parse [type=jsonparse path="data"]; + ds2_multiply [type=multiply times=2]; + + ds1 -> ds1_parse -> ds1_multiply -> answer1; + ds2 -> ds2_parse -> ds2_multiply -> answer1; + + answer1 [type=median index=0]; +`, + }, + }, + }, + exp: ` +type = "" +name = "" +forwardingAllowed = false +relay = "" +schemaVersion = 1 +contractID = "" + +[relayConfig] +chainID = 1337 +fromBlock = 42 + +[relayConfig.chainReader] +[relayConfig.chainReader.contracts] +[relayConfig.chainReader.contracts.median] +contractABI = "[\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"requester\",\n \"type\": \"address\"\n }\n ],\n \"name\": \"RoundRequested\",\n \"type\": \"event\"\n }\n]\n" + +[relayConfig.chainReader.contracts.median.configs] +LatestRoundRequested = "{\n \"chainSpecificName\": \"RoundRequested\",\n \"readType\": \"event\"\n}\n" +LatestTransmissionDetails = "{\n \"chainSpecificName\": \"latestTransmissionDetails\",\n \"output_modifications\": [\n {\n \"Fields\": [\n \"LatestTimestamp_\"\n ],\n \"Type\": \"epoch to time\"\n },\n {\n \"Fields\": {\n \"LatestAnswer_\": \"LatestAnswer\",\n \"LatestTimestamp_\": \"LatestTimestamp\"\n },\n \"Type\": \"rename\"\n }\n ]\n}\n" + +[relayConfig.codec] +[relayConfig.codec.configs] +[relayConfig.codec.configs.MedianReport] +typeABI = "[\n {\n \"Name\": \"Timestamp\",\n \"Type\": \"uint32\"\n }\n]\n" + +`, + }, + } { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.spec.String() + require.NoError(t, err) + require.Equal(t, tt.exp, got) + }) + } +} diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index fefc9ca8838..1cf3bc05416 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -440,6 +440,7 @@ func (n *ClNode) getContainerRequest(secrets string) ( AlwaysPullImage: n.AlwaysPullImage, Image: fmt.Sprintf("%s:%s", n.ContainerImage, n.ContainerVersion), ExposedPorts: []string{"6688/tcp"}, + Env: n.ContainerEnvs, Entrypoint: []string{"chainlink", "-c", configPath, "-s", secretsPath, diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 53135cfea7c..f559c517f0b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a github.com/smartcontractkit/chainlink-testing-framework v1.22.6 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -364,11 +364,11 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect - github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 09c9979d578..ab678c43b5b 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1486,18 +1486,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1 h1:3cWO2/lFVDul5SVTgl4/RX/GXcT8Zq5NGMPeNEz09tY= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240118012339-4864e2306bb1/go.mod h1:f+0ei9N4PlTJHu7pbGzEjTnBUr45syPdGFu5+31lS5Q= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5 h1:kBnmjv3fxU7krVIqZFvo1m4F6qBc4vPURQFX/mcChhI= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231218175426-6e0427c661e5/go.mod h1:EoM7wQ81mov7wsUzG4zEnnr0EH0POEo/I0hRDg433TU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a h1:lgM0yPo0KqSntLY4Y42RAH3avdv+Kyne8n+VM7cwlxo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= -github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312 h1:ziqC+WW/2/UI6w3DShy7HGzJMWWLIYHT5ev2Qaa3h6I= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231219140448-151a4725f312/go.mod h1:vqnojBNdzHNI6asWezJlottUiVEXudMEGf2Mz5R+xps= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a h1:atCXqF8e5U2zfEaA87cKJs+K1MAbOVh3V05gEd60fR0= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231219014050-0c4a7831293a/go.mod h1:YWKpf+hO9XMlzIWQT8yGoky3aeFLzMUVsjbs80LD77M= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 h1:1BcjXuviSAKttOX7BZoVHRZZGfxqoA2+AL8tykmkdoc= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8/go.mod h1:vy1L7NybTy2F/Yv7BOh+oZBa1MACD6gzd1+DkcSkfp8= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba h1:6rnQrD8NaLfLOPHszW1hbpviqpU8011gzdZk6wKP1xY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= github.com/smartcontractkit/chainlink-testing-framework v1.22.6 h1:5kWMlo99RY/ys4EWGMPsEg1sfY67f5YQogI8PohdRvw= github.com/smartcontractkit/chainlink-testing-framework v1.22.6/go.mod h1:FBRC6elqaqO8jiMZMfa3UKrvwzZhMGUtYqVTGYmfFrA= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= @@ -2189,8 +2189,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= -google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= +google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 90dc20a3898..f1a01643f5e 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -77,7 +77,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - err = actions.CreateOCRv2JobsLocal(ocrInstances, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), true) + err = actions.CreateOCRv2JobsLocal(ocrInstances, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), true, false) require.NoError(t, err, "Error creating OCRv2 jobs with forwarders") err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 2603b6e9e6a..f45c324faed 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -12,23 +12,29 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/v2/core/config/env" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - "github.com/smartcontractkit/chainlink/v2/core/config/env" ) // Tests a basic OCRv2 median feed func TestOCRv2Basic(t *testing.T) { t.Parallel() + noMedianPlugin := map[string]string{string(env.MedianPluginCmd): ""} + medianPlugin := map[string]string{string(env.MedianPluginCmd): "chainlink-feeds"} for _, test := range []struct { - name string - env map[string]string + name string + env map[string]string + chainReaderAndCodec bool }{ - {"legacy", map[string]string{string(env.MedianPluginCmd): ""}}, - {"plugins", map[string]string{string(env.MedianPluginCmd): "chainlink-feeds"}}, + {"legacy", noMedianPlugin, false}, + {"legacy-chain-reader", noMedianPlugin, true}, + {"plugins", medianPlugin, false}, + {"plugins-chain-reader", medianPlugin, true}, } { test := test t.Run(test.name, func(t *testing.T) { @@ -80,7 +86,7 @@ func TestOCRv2Basic(t *testing.T) { aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, test.chainReaderAndCodec) require.NoError(t, err, "Error creating OCRv2 jobs") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) @@ -161,7 +167,7 @@ func TestOCRv2Request(t *testing.T) { aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, false) require.NoError(t, err, "Error creating OCRv2 jobs") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) @@ -236,7 +242,7 @@ func TestOCRv2JobReplacement(t *testing.T) { aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, false) require.NoError(t, err, "Error creating OCRv2 jobs") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) @@ -272,7 +278,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.DeleteBridges(nodeClients) require.NoError(t, err) - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false, false) require.NoError(t, err, "Error creating OCRv2 jobs") err = actions.WatchNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index ba06d5ea462..d4470eef70a 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -75,6 +76,10 @@ func (p provider) ChainReader() types.ChainReader { return nil } +func (p provider) Codec() types.Codec { + return nil +} + func TestNewPlugin(t *testing.T) { lggr := logger.TestLogger(t) p := NewPlugin(lggr) diff --git a/sonar-project.properties b/sonar-project.properties index c40b5f361e1..b6f3f1567c6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.sources=. sonar.python.version=3.8 # Full exclusions from the static analysis -sonar.exclusions=**/node_modules/**/*,**/mocks/**/*, **/testdata/**/*, **/contracts/typechain/**/*, **/contracts/artifacts/**/*, **/contracts/cache/**/*, **/contracts/scripts/**/*, **/generated/**/*, **/fixtures/**/*, **/docs/**/*, **/tools/**/*, **/*.pb.go, **/*report.xml, **/*.config.ts, **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go +sonar.exclusions=**/node_modules/**/*,**/mocks/**/*, **/testdata/**/*, **/contracts/typechain/**/*, **/contracts/artifacts/**/*, **/contracts/cache/**/*, **/contracts/scripts/**/*, **/generated/**/*, **/fixtures/**/*, **/docs/**/*, **/tools/**/*, **/*.pb.go, **/*report.xml, **/*.config.ts, **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go, core/services/relay/evm/types/*_gen.go, core/services/relay/evm/types/gen/main.go, core/services/relay/evm/testfiles/* # Coverage exclusions sonar.coverage.exclusions=**/*.test.ts, **/*_test.go, **/contracts/test/**/*, **/contracts/**/tests/**/*, **/core/**/testutils/**/*, **/core/**/mocks/**/*, **/core/**/cltest/**/*, **/integration-tests/**/*, **/generated/**/*, **/core/scripts**/* , **/*.pb.go, ./plugins/**/*, **/main.go, **/0195_add_not_null_to_evm_chain_id_in_job_specs.go # Duplication exclusions diff --git a/tools/bin/go_core_fuzz b/tools/bin/go_core_fuzz new file mode 100755 index 00000000000..3ea7d9bb0cb --- /dev/null +++ b/tools/bin/go_core_fuzz @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -o pipefail +set +e + +SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"` +OUTPUT_FILE=${OUTPUT_FILE:-"./output.txt"} +USE_TEE="${USE_TEE:-true}" + +echo "Failed tests and panics: ---------------------" +echo "" +GO_LDFLAGS=$(bash tools/bin/ldflags) +use_tee() { + if [ "$USE_TEE" = "true" ]; then + tee "$@" + else + cat > "$@" + fi +} +go test -json -ldflags "$GO_LDFLAGS" github.com/smartcontractkit/chainlink/v2/core/services/relay/evm -fuzz . -fuzztime 12s | use_tee $OUTPUT_FILE +EXITCODE=${PIPESTATUS[0]} + +# Assert no known sensitive strings present in test logger output +printf "\n----------------------------------------------\n\n" +echo "Beginning check of output logs for sensitive strings" +$SCRIPT_PATH/scrub_logs $OUTPUT_FILE +if [[ $? != 0 ]]; then + exit 1 +fi + +echo "Exit code: $EXITCODE" +if [[ $EXITCODE != 0 ]]; then + echo "Encountered test failures." +else + echo "All tests passed!" +fi +exit $EXITCODE From c45ff8999f44f9dae71194bc39bb8fc7dd446163 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:10:00 -0500 Subject: [PATCH 183/234] Always checkout repo in workflow to support argo sync and rename image tag (#11846) * Always checkout repo in workflow to support argo sync * Add PR number as an image tag suffix. To avoid collisions. * Revert "Add PR number as an image tag suffix." This reverts commit f994e8d2e6adc06ba12a747b28b225dc49f96889. --- .github/workflows/build-publish-pr.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml index fd5533c1b5b..25ffa394551 100644 --- a/.github/workflows/build-publish-pr.yml +++ b/.github/workflows/build-publish-pr.yml @@ -20,6 +20,9 @@ jobs: env: ECR_IMAGE_NAME: crib-chainlink-untrusted steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Git Short SHA shell: bash env: @@ -36,10 +39,6 @@ jobs: AWS_REGION: ${{ secrets.AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.AWS_OIDC_IAM_ROLE_PUBLISH_PR_ARN }} - - name: Checkout repository - if: steps.check-image.outputs.exists == 'false' - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Build and publish chainlink image if: steps.check-image.outputs.exists == 'false' uses: ./.github/actions/build-sign-publish-chainlink From 388e7794dffc9f228e04aec264ff5bcac7908520 Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:06:34 -0800 Subject: [PATCH 184/234] [Functions] Add extra validations for offchain heartbeats (#11783) 1. Add AllowedHeartbeatInitiators list to node's config and validate senders of incoming requests against it (same logic as in Gateway). 2. Validate Sender value in nodes' reponses to make sure it matches the expected node. Extend an integration test to cover this change. 3. Validate age of incoming requests against RequestTimeoutSec from job config to avoid processing ones that already timed out. 4. Disallow null-byte suffixes in message fields to avoid any potential confusion with default padding. --- core/services/functions/connector_handler.go | 83 +++++++++++-------- .../functions/connector_handler_test.go | 38 +++++++-- core/services/gateway/api/message.go | 10 +++ core/services/gateway/api/message_test.go | 18 +++- core/services/gateway/connectionmanager.go | 4 + .../gateway_integration_test.go | 77 +++++++++++++---- .../ocr2/plugins/functions/config/config.go | 1 + .../services/ocr2/plugins/functions/plugin.go | 15 ++-- .../ocr2/plugins/functions/plugin_test.go | 12 ++- 9 files changed, 193 insertions(+), 65 deletions(-) diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 1594dc6eb56..18df644c876 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -6,6 +6,7 @@ import ( "crypto/ecdsa" "encoding/json" "fmt" + "strings" "sync" "time" @@ -25,34 +26,34 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" "github.com/smartcontractkit/chainlink/v2/core/services/s4" ) type functionsConnectorHandler struct { services.StateMachine - connector connector.GatewayConnector - signerKey *ecdsa.PrivateKey - nodeAddress string - storage s4.Storage - allowlist functions.OnchainAllowlist - rateLimiter *hc.RateLimiter - subscriptions functions.OnchainSubscriptions - minimumBalance assets.Link - listener FunctionsListener - offchainTransmitter OffchainTransmitter - heartbeatRequests map[RequestID]*HeartbeatResponse - orderedRequests []RequestID - mu sync.Mutex - chStop services.StopChan - shutdownWaitGroup sync.WaitGroup - lggr logger.Logger + connector connector.GatewayConnector + signerKey *ecdsa.PrivateKey + nodeAddress string + storage s4.Storage + allowlist functions.OnchainAllowlist + rateLimiter *hc.RateLimiter + subscriptions functions.OnchainSubscriptions + minimumBalance assets.Link + listener FunctionsListener + offchainTransmitter OffchainTransmitter + allowedHeartbeatInitiators map[string]struct{} + heartbeatRequests map[RequestID]*HeartbeatResponse + requestTimeoutSec uint32 + orderedRequests []RequestID + mu sync.Mutex + chStop services.StopChan + shutdownWaitGroup sync.WaitGroup + lggr logger.Logger } -const ( - HeartbeatRequestTimeoutSec = 240 - HeartbeatCacheSize = 1000 -) +const HeartbeatCacheSize = 1000 var ( _ connector.Signer = &functionsConnectorHandler{} @@ -71,23 +72,29 @@ func InternalId(sender []byte, requestId []byte) RequestID { return RequestID(crypto.Keccak256Hash(append(sender, requestId...)).Bytes()) } -func NewFunctionsConnectorHandler(nodeAddress string, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions functions.OnchainSubscriptions, listener FunctionsListener, offchainTransmitter OffchainTransmitter, minimumBalance assets.Link, lggr logger.Logger) (*functionsConnectorHandler, error) { +func NewFunctionsConnectorHandler(pluginConfig *config.PluginConfig, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions functions.OnchainSubscriptions, listener FunctionsListener, offchainTransmitter OffchainTransmitter, lggr logger.Logger) (*functionsConnectorHandler, error) { if signerKey == nil || storage == nil || allowlist == nil || rateLimiter == nil || subscriptions == nil || listener == nil || offchainTransmitter == nil { return nil, fmt.Errorf("all dependencies must be non-nil") } + allowedHeartbeatInitiators := make(map[string]struct{}) + for _, initiator := range pluginConfig.AllowedHeartbeatInitiators { + allowedHeartbeatInitiators[strings.ToLower(initiator)] = struct{}{} + } return &functionsConnectorHandler{ - nodeAddress: nodeAddress, - signerKey: signerKey, - storage: storage, - allowlist: allowlist, - rateLimiter: rateLimiter, - subscriptions: subscriptions, - minimumBalance: minimumBalance, - listener: listener, - offchainTransmitter: offchainTransmitter, - heartbeatRequests: make(map[RequestID]*HeartbeatResponse), - chStop: make(services.StopChan), - lggr: lggr.Named("FunctionsConnectorHandler"), + nodeAddress: pluginConfig.GatewayConnectorConfig.NodeAddress, + signerKey: signerKey, + storage: storage, + allowlist: allowlist, + rateLimiter: rateLimiter, + subscriptions: subscriptions, + minimumBalance: pluginConfig.MinimumSubscriptionBalance, + listener: listener, + offchainTransmitter: offchainTransmitter, + allowedHeartbeatInitiators: allowedHeartbeatInitiators, + heartbeatRequests: make(map[RequestID]*HeartbeatResponse), + requestTimeoutSec: pluginConfig.RequestTimeoutSec, + chStop: make(services.StopChan), + lggr: lggr.Named("FunctionsConnectorHandler"), }, nil } @@ -211,6 +218,10 @@ func (h *functionsConnectorHandler) handleHeartbeat(ctx context.Context, gateway h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse(fmt.Sprintf("failed to unmarshal request: %v", err))) return } + if _, ok := h.allowedHeartbeatInitiators[requestBody.Sender]; !ok { + h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse("sender not allowed to send heartbeat requests")) + return + } if !bytes.Equal(request.RequestInitiator, fromAddr.Bytes()) { h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse("RequestInitiator doesn't match sender")) return @@ -219,6 +230,10 @@ func (h *functionsConnectorHandler) handleHeartbeat(ctx context.Context, gateway h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse("SubscriptionOwner doesn't match sender")) return } + if request.Timestamp < uint64(time.Now().Unix())-uint64(h.requestTimeoutSec) { + h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse("Request is too old")) + return + } internalId := InternalId(fromAddr.Bytes(), request.RequestId) request.RequestId = internalId[:] @@ -250,7 +265,7 @@ func internalErrorResponse(internalError string) HeartbeatResponse { func (h *functionsConnectorHandler) handleOffchainRequest(request *OffchainRequest) { defer h.shutdownWaitGroup.Done() stopCtx, _ := h.chStop.NewCtx() - ctx, cancel := context.WithTimeout(stopCtx, time.Duration(HeartbeatRequestTimeoutSec)*time.Second) + ctx, cancel := context.WithTimeout(stopCtx, time.Duration(h.requestTimeoutSec)*time.Second) defer cancel() err := h.listener.HandleOffchainRequest(ctx, request) if err != nil { diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index 409f9cdcc56..9a5a9042693 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -10,6 +10,7 @@ import ( "time" geth_common "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/onsi/gomega" "github.com/smartcontractkit/chainlink-common/pkg/assets" @@ -19,9 +20,11 @@ import ( sfmocks "github.com/smartcontractkit/chainlink/v2/core/services/functions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/common" + gwconnector "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" gcmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector/mocks" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" gfmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" "github.com/smartcontractkit/chainlink/v2/core/services/s4" s4mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" @@ -30,7 +33,7 @@ import ( "github.com/stretchr/testify/require" ) -func newOffchainRequest(t *testing.T, sender []byte) (*api.Message, functions.RequestID) { +func newOffchainRequest(t *testing.T, sender []byte, ageSec uint64) (*api.Message, functions.RequestID) { requestId := make([]byte, 32) _, err := rand.Read(requestId) require.NoError(t, err) @@ -39,6 +42,7 @@ func newOffchainRequest(t *testing.T, sender []byte) (*api.Message, functions.Re RequestInitiator: sender, SubscriptionId: 1, SubscriptionOwner: sender, + Timestamp: uint64(time.Now().Unix()) - ageSec, } internalId := functions.InternalId(request.RequestInitiator, request.RequestId) @@ -74,7 +78,15 @@ func TestFunctionsConnectorHandler(t *testing.T) { allowlist.On("Close", mock.Anything).Return(nil) subscriptions.On("Start", mock.Anything).Return(nil) subscriptions.On("Close", mock.Anything).Return(nil) - handler, err := functions.NewFunctionsConnectorHandler(addr.Hex(), privateKey, storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, *assets.NewLinkFromJuels(100), logger) + config := &config.PluginConfig{ + GatewayConnectorConfig: &gwconnector.ConnectorConfig{ + NodeAddress: addr.Hex(), + }, + MinimumSubscriptionBalance: *assets.NewLinkFromJuels(100), + RequestTimeoutSec: 1_000, + AllowedHeartbeatInitiators: []string{crypto.PubkeyToAddress(privateKey.PublicKey).Hex()}, + } + handler, err := functions.NewFunctionsConnectorHandler(config, privateKey, storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, logger) require.NoError(t, err) handler.SetConnector(connector) @@ -257,7 +269,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { t.Run("heartbeat success", func(t *testing.T) { ctx := testutils.Context(t) - msg, internalId := newOffchainRequest(t, addr.Bytes()) + msg, internalId := newOffchainRequest(t, addr.Bytes(), 0) require.NoError(t, msg.Sign(privateKey)) // first call to trigger the request @@ -292,7 +304,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { t.Run("heartbeat internal error", func(t *testing.T) { ctx := testutils.Context(t) - msg, _ := newOffchainRequest(t, addr.Bytes()) + msg, _ := newOffchainRequest(t, addr.Bytes(), 0) require.NoError(t, msg.Sign(privateKey)) // first call to trigger the request @@ -319,7 +331,23 @@ func TestFunctionsConnectorHandler(t *testing.T) { t.Run("heartbeat sender address doesn't match", func(t *testing.T) { ctx := testutils.Context(t) - msg, _ := newOffchainRequest(t, geth_common.BytesToAddress([]byte("0x1234")).Bytes()) + msg, _ := newOffchainRequest(t, geth_common.BytesToAddress([]byte("0x1234")).Bytes(), 0) + require.NoError(t, msg.Sign(privateKey)) + + var response functions.HeartbeatResponse + allowlist.On("Allow", addr).Return(true).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { + respMsg, ok := args[2].(*api.Message) + require.True(t, ok) + require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) + require.Equal(t, functions.RequestStateInternalError, response.Status) + }).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + }) + + t.Run("heartbeat request too old", func(t *testing.T) { + ctx := testutils.Context(t) + msg, _ := newOffchainRequest(t, addr.Bytes(), 10_000) require.NoError(t, msg.Sign(privateKey)) var response functions.HeartbeatResponse diff --git a/core/services/gateway/api/message.go b/core/services/gateway/api/message.go index c01d3bb9f2e..5e6c8e49247 100644 --- a/core/services/gateway/api/message.go +++ b/core/services/gateway/api/message.go @@ -20,6 +20,7 @@ const ( MessageMethodMaxLen = 64 MessageDonIdMaxLen = 64 MessageReceiverLen = 2 + 2*20 + NullChar = "\x00" ) /* @@ -56,12 +57,21 @@ func (m *Message) Validate() error { if len(m.Body.MessageId) == 0 || len(m.Body.MessageId) > MessageIdMaxLen { return errors.New("invalid message ID length") } + if strings.HasSuffix(m.Body.MessageId, NullChar) { + return errors.New("message ID ending with null bytes") + } if len(m.Body.Method) == 0 || len(m.Body.Method) > MessageMethodMaxLen { return errors.New("invalid method name length") } + if strings.HasSuffix(m.Body.Method, NullChar) { + return errors.New("method name ending with null bytes") + } if len(m.Body.DonId) == 0 || len(m.Body.DonId) > MessageDonIdMaxLen { return errors.New("invalid DON ID length") } + if strings.HasSuffix(m.Body.DonId, NullChar) { + return errors.New("DON ID ending with null bytes") + } if len(m.Body.Receiver) != 0 && len(m.Body.Receiver) != MessageReceiverLen { return errors.New("invalid Receiver length") } diff --git a/core/services/gateway/api/message_test.go b/core/services/gateway/api/message_test.go index a0835ea24bb..1f292db26b9 100644 --- a/core/services/gateway/api/message_test.go +++ b/core/services/gateway/api/message_test.go @@ -31,22 +31,38 @@ func TestMessage_Validate(t *testing.T) { // missing message ID msg.Body.MessageId = "" require.Error(t, msg.Validate()) + // message ID ending with null bytes + msg.Body.MessageId = "myid\x00\x00" + require.Error(t, msg.Validate()) msg.Body.MessageId = "abcd" + require.NoError(t, msg.Validate()) // missing DON ID msg.Body.DonId = "" require.Error(t, msg.Validate()) + // DON ID ending with null bytes + msg.Body.DonId = "mydon\x00\x00" + require.Error(t, msg.Validate()) msg.Body.DonId = "donA" + require.NoError(t, msg.Validate()) - // method too long + // method name too long msg.Body.Method = string(bytes.Repeat([]byte("a"), api.MessageMethodMaxLen+1)) require.Error(t, msg.Validate()) + // empty method name + msg.Body.Method = "" + require.Error(t, msg.Validate()) + // method name ending with null bytes + msg.Body.Method = "method\x00" + require.Error(t, msg.Validate()) msg.Body.Method = "request" + require.NoError(t, msg.Validate()) // incorrect receiver msg.Body.Receiver = "blah" require.Error(t, msg.Validate()) msg.Body.Receiver = "0x0000000000000000000000000000000000000000" + require.NoError(t, msg.Validate()) // invalid signature msg.Signature = "0x00" diff --git a/core/services/gateway/connectionmanager.go b/core/services/gateway/connectionmanager.go index 9f88b51e7b5..e5f7fb13afb 100644 --- a/core/services/gateway/connectionmanager.go +++ b/core/services/gateway/connectionmanager.go @@ -287,6 +287,10 @@ func (m *donConnectionManager) readLoop(nodeAddress string, nodeState *nodeState m.lggr.Errorw("message validation error when reading from node", "nodeAddress", nodeAddress, "err", err) break } + if msg.Body.Sender != nodeAddress { + m.lggr.Errorw("message sender mismatch when reading from node", "nodeAddress", nodeAddress, "sender", msg.Body.Sender) + break + } err = m.handler.HandleNodeMessage(ctx, msg, nodeAddress) if err != nil { m.lggr.Error("error when calling HandleNodeMessage ", err) diff --git a/core/services/gateway/integration_tests/gateway_integration_test.go b/core/services/gateway/integration_tests/gateway_integration_test.go index 9e4900efeee..a2064b7a591 100644 --- a/core/services/gateway/integration_tests/gateway_integration_test.go +++ b/core/services/gateway/integration_tests/gateway_integration_test.go @@ -5,6 +5,7 @@ import ( "context" "crypto/ecdsa" "fmt" + "io" "net/http" "strings" "sync/atomic" @@ -36,18 +37,18 @@ Path = "/node" Port = 0 HandshakeTimeoutMillis = 2_000 MaxRequestBytes = 20_000 -ReadTimeoutMillis = 100 -RequestTimeoutMillis = 100 -WriteTimeoutMillis = 100 +ReadTimeoutMillis = 1000 +RequestTimeoutMillis = 1000 +WriteTimeoutMillis = 1000 [UserServerConfig] Path = "/user" Port = 0 ContentTypeHeader = "application/jsonrpc" MaxRequestBytes = 20_000 -ReadTimeoutMillis = 100 -RequestTimeoutMillis = 100 -WriteTimeoutMillis = 100 +ReadTimeoutMillis = 1000 +RequestTimeoutMillis = 1000 +WriteTimeoutMillis = 1000 [[Dons]] DonId = "test_don" @@ -72,6 +73,13 @@ Id = "test_gateway" URL = "%s" ` +const ( + messageId1 = "123" + messageId2 = "456" + + nodeResponsePayload = `{"response":"correct response"}` +) + func parseGatewayConfig(t *testing.T, tomlConfig string) *config.GatewayConfig { var cfg config.GatewayConfig err := toml.Unmarshal([]byte(tomlConfig), &cfg) @@ -94,6 +102,21 @@ type client struct { func (c *client) HandleGatewayMessage(ctx context.Context, gatewayId string, msg *api.Message) { c.done.Store(true) + // send back user's message without re-signing - should be ignored by the Gateway + _ = c.connector.SendToGateway(ctx, gatewayId, msg) + // send back a correct response + responseMsg := &api.Message{Body: api.MessageBody{ + MessageId: msg.Body.MessageId, + Method: "test", + DonId: "test_don", + Receiver: msg.Body.Sender, + Payload: []byte(nodeResponsePayload), + }} + err := responseMsg.Sign(c.privateKey) + if err != nil { + panic(err) + } + _ = c.connector.SendToGateway(ctx, gatewayId, responseMsg) } func (c *client) Sign(data ...[]byte) ([]byte, error) { @@ -111,7 +134,9 @@ func (*client) Close() error { func TestIntegration_Gateway_NoFullNodes_BasicConnectionAndMessage(t *testing.T) { t.Parallel() - nodeKeys := common.NewTestNodes(t, 1)[0] + testWallets := common.NewTestNodes(t, 2) + nodeKeys := testWallets[0] + userKeys := testWallets[1] // Verify that addresses in config are case-insensitive nodeKeys.Address = strings.ToUpper(nodeKeys.Address) @@ -132,17 +157,39 @@ func TestIntegration_Gateway_NoFullNodes_BasicConnectionAndMessage(t *testing.T) client.connector = connector servicetest.Run(t, connector) - // Send requests until one of them reaches Connector + // Send requests until one of them reaches Connector (i.e. the node) gomega.NewGomegaWithT(t).Eventually(func() bool { - msg := &api.Message{Body: api.MessageBody{MessageId: "123", Method: "test", DonId: "test_don"}} - require.NoError(t, msg.Sign(nodeKeys.PrivateKey)) - codec := api.JsonRPCCodec{} - rawMsg, err := codec.EncodeRequest(msg) - require.NoError(t, err) - req, err := http.NewRequestWithContext(testutils.Context(t), "POST", userUrl, bytes.NewBuffer(rawMsg)) - require.NoError(t, err) + req := newHttpRequestObject(t, messageId1, userUrl, userKeys.PrivateKey) httpClient := &http.Client{} _, _ = httpClient.Do(req) // could initially return error if Gateway is not fully initialized yet return client.done.Load() }, testutils.WaitTimeout(t), testutils.TestInterval).Should(gomega.Equal(true)) + + // Send another request and validate that response has correct content and sender + req := newHttpRequestObject(t, messageId2, userUrl, userKeys.PrivateKey) + httpClient := &http.Client{} + resp, err := httpClient.Do(req) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + + rawResp, err := io.ReadAll(resp.Body) + require.NoError(t, err) + codec := api.JsonRPCCodec{} + respMsg, err := codec.DecodeResponse(rawResp) + require.NoError(t, err) + require.NoError(t, respMsg.Validate()) + require.Equal(t, strings.ToLower(nodeKeys.Address), respMsg.Body.Sender) + require.Equal(t, messageId2, respMsg.Body.MessageId) + require.Equal(t, nodeResponsePayload, string(respMsg.Body.Payload)) +} + +func newHttpRequestObject(t *testing.T, messageId string, userUrl string, signerKey *ecdsa.PrivateKey) *http.Request { + msg := &api.Message{Body: api.MessageBody{MessageId: messageId, Method: "test", DonId: "test_don"}} + require.NoError(t, msg.Sign(signerKey)) + codec := api.JsonRPCCodec{} + rawMsg, err := codec.EncodeRequest(msg) + require.NoError(t, err) + req, err := http.NewRequestWithContext(testutils.Context(t), "POST", userUrl, bytes.NewBuffer(rawMsg)) + require.NoError(t, err) + return req } diff --git a/core/services/ocr2/plugins/functions/config/config.go b/core/services/ocr2/plugins/functions/config/config.go index 13e02042506..38af7b8587f 100644 --- a/core/services/ocr2/plugins/functions/config/config.go +++ b/core/services/ocr2/plugins/functions/config/config.go @@ -41,6 +41,7 @@ type PluginConfig struct { MaxRequestSizesList []uint32 `json:"maxRequestSizesList"` MaxSecretsSizesList []uint32 `json:"maxSecretsSizesList"` MinimumSubscriptionBalance assets.Link `json:"minimumSubscriptionBalance"` + AllowedHeartbeatInitiators []string `json:"allowedHeartbeatInitiators"` GatewayConnectorConfig *connector.ConnectorConfig `json:"gatewayConnectorConfig"` OnchainAllowlist *functions.OnchainAllowlistConfig `json:"onchainAllowlist"` OnchainSubscriptions *functions.OnchainSubscriptionsConfig `json:"onchainSubscriptions"` diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index c6cfa946aba..fd72b6fd38e 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -152,7 +151,7 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs return nil, errors.Wrap(err, "failed to create a OnchainSubscriptions") } connectorLogger := conf.Logger.Named("GatewayConnector").With("jobName", conf.Job.PipelineSpec.JobName) - connector, err2 := NewConnector(pluginConfig.GatewayConnectorConfig, conf.EthKeystore, conf.Chain.ID(), s4Storage, allowlist, rateLimiter, subscriptions, functionsListener, offchainTransmitter, pluginConfig.MinimumSubscriptionBalance, connectorLogger) + connector, err2 := NewConnector(&pluginConfig, conf.EthKeystore, conf.Chain.ID(), s4Storage, allowlist, rateLimiter, subscriptions, functionsListener, offchainTransmitter, connectorLogger) if err2 != nil { return nil, errors.Wrap(err, "failed to create a GatewayConnector") } @@ -179,24 +178,26 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs return allServices, nil } -func NewConnector(gwcCfg *connector.ConnectorConfig, ethKeystore keystore.Eth, chainID *big.Int, s4Storage s4.Storage, allowlist gwFunctions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions gwFunctions.OnchainSubscriptions, listener functions.FunctionsListener, offchainTransmitter functions.OffchainTransmitter, minimumBalance assets.Link, lggr logger.Logger) (connector.GatewayConnector, error) { +func NewConnector(pluginConfig *config.PluginConfig, ethKeystore keystore.Eth, chainID *big.Int, s4Storage s4.Storage, allowlist gwFunctions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions gwFunctions.OnchainSubscriptions, listener functions.FunctionsListener, offchainTransmitter functions.OffchainTransmitter, lggr logger.Logger) (connector.GatewayConnector, error) { enabledKeys, err := ethKeystore.EnabledKeysForChain(chainID) if err != nil { return nil, err } - configuredNodeAddress := common.HexToAddress(gwcCfg.NodeAddress) + configuredNodeAddress := common.HexToAddress(pluginConfig.GatewayConnectorConfig.NodeAddress) idx := slices.IndexFunc(enabledKeys, func(key ethkey.KeyV2) bool { return key.Address == configuredNodeAddress }) if idx == -1 { return nil, errors.New("key for configured node address not found") } signerKey := enabledKeys[idx].ToEcdsaPrivKey() - nodeAddress := enabledKeys[idx].ID() + if enabledKeys[idx].ID() != pluginConfig.GatewayConnectorConfig.NodeAddress { + return nil, errors.New("node address mismatch") + } - handler, err := functions.NewFunctionsConnectorHandler(nodeAddress, signerKey, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, minimumBalance, lggr) + handler, err := functions.NewFunctionsConnectorHandler(pluginConfig, signerKey, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, lggr) if err != nil { return nil, err } - connector, err := connector.NewGatewayConnector(gwcCfg, handler, handler, utils.NewRealClock(), lggr) + connector, err := connector.NewGatewayConnector(pluginConfig.GatewayConnectorConfig, handler, handler, utils.NewRealClock(), lggr) if err != nil { return nil, err } diff --git a/core/services/ocr2/plugins/functions/plugin_test.go b/core/services/ocr2/plugins/functions/plugin_test.go index d77fabcc437..6d3f57b086c 100644 --- a/core/services/ocr2/plugins/functions/plugin_test.go +++ b/core/services/ocr2/plugins/functions/plugin_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/logger" sfmocks "github.com/smartcontractkit/chainlink/v2/core/services/functions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" @@ -17,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" s4mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" ) @@ -39,7 +39,10 @@ func TestNewConnector_Success(t *testing.T) { listener := sfmocks.NewFunctionsListener(t) offchainTransmitter := sfmocks.NewOffchainTransmitter(t) ethKeystore.On("EnabledKeysForChain", mock.Anything).Return([]ethkey.KeyV2{keyV2}, nil) - _, err = functions.NewConnector(gwcCfg, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, *assets.NewLinkFromJuels(0), logger.TestLogger(t)) + config := &config.PluginConfig{ + GatewayConnectorConfig: gwcCfg, + } + _, err = functions.NewConnector(config, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, logger.TestLogger(t)) require.NoError(t, err) } @@ -64,6 +67,9 @@ func TestNewConnector_NoKeyForConfiguredAddress(t *testing.T) { listener := sfmocks.NewFunctionsListener(t) offchainTransmitter := sfmocks.NewOffchainTransmitter(t) ethKeystore.On("EnabledKeysForChain", mock.Anything).Return([]ethkey.KeyV2{{Address: common.HexToAddress(addresses[1])}}, nil) - _, err = functions.NewConnector(gwcCfg, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, *assets.NewLinkFromJuels(0), logger.TestLogger(t)) + config := &config.PluginConfig{ + GatewayConnectorConfig: gwcCfg, + } + _, err = functions.NewConnector(config, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, logger.TestLogger(t)) require.Error(t, err) } From 91c799ca3aebbdbbab43ebe4a9b0c5529cf5cf3f Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 23 Jan 2024 09:47:42 -0500 Subject: [PATCH 185/234] Adds Grafana Logging for Live Testnets (#11798) * Adds Grafana Logging for Live Testnets * Bartek's comments * Updates with Summary Action --- .github/workflows/integration-tests.yml | 22 ++++----- .github/workflows/live-testnet-tests.yml | 59 ++++++++++++++++++------ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8ff6c3d4cd6..8338a097c6c 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -45,7 +45,7 @@ jobs: echo "should-enforce=$SHOULD_ENFORCE" >> $GITHUB_OUTPUT - name: Enforce CTF Version if: steps.condition-check.outputs.should-enforce == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: go-project-path: ./integration-tests module-name: github.com/smartcontractkit/chainlink-testing-framework @@ -87,7 +87,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Go - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download go_mod_path: ./integration-tests/go.mod @@ -275,7 +275,7 @@ jobs: ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} @@ -305,8 +305,6 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 - with: - test_directory: ./integration-tests/smoke/ eth-smoke-tests-matrix: if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} @@ -454,7 +452,7 @@ jobs: ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} @@ -485,7 +483,7 @@ jobs: ## Run this step when changes that do not need the test to run are made - name: Run Setup if: needs.changes.outputs.src == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download go_mod_path: ./integration-tests/go.mod @@ -578,7 +576,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Run Setup - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_download_vendor_packages_command: | cd ./integration-tests @@ -626,7 +624,7 @@ jobs: run: | echo "Running migration tests from version '${{ steps.get_latest_version.outputs.latest_version }}' to: '${{ github.sha }}'" - name: Run Migration Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -738,7 +736,7 @@ jobs: steps: - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: repository: chainlink-solana-tests tag: ${{ needs.get_solana_sha.outputs.sha }} @@ -876,7 +874,7 @@ jobs: ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Run Setup if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: go_mod_path: ./integration-tests/go.mod cache_restore_only: true @@ -905,7 +903,7 @@ jobs: docker rm "$CONTAINER_ID" - name: Run Tests if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: export ENV_JOB_IMAGE=${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-solana-tests:${{ needs.get_solana_sha.outputs.sha }} && make test_smoke cl_repo: ${{ env.CHAINLINK_IMAGE }} diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index c970751779d..a4c0c244f03 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -123,7 +123,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download token: ${{ secrets.GITHUB_TOKEN }} @@ -258,7 +258,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia @@ -278,6 +278,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 bsc-testnet-tests: # TODO: BSC RPCs are all in a bad state right now, so we're skipping these tests until they're fixed @@ -310,7 +313,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-bsc-testnet @@ -330,6 +333,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 optimism-sepolia-smoke-tests: environment: integration @@ -360,7 +366,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-sepolia @@ -380,6 +386,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 arbitrum-sepolia-smoke-tests: environment: integration @@ -410,7 +419,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-sepolia @@ -430,6 +439,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 base-sepolia-smoke-tests: environment: integration @@ -456,7 +468,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-base-sepolia @@ -476,6 +488,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 polygon-mumbai-smoke-tests: environment: integration @@ -506,7 +521,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-polygon-mumbai @@ -526,6 +541,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 avalanche-fuji-smoke-tests: environment: integration @@ -556,7 +574,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-avalanche-fuji @@ -576,6 +594,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 fantom-testnet-smoke-tests: environment: integration @@ -606,7 +627,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-fantom-testnet @@ -626,6 +647,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 celo-alfajores-smoke-tests: environment: integration @@ -652,7 +676,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-celo-alfajores @@ -672,6 +696,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 scroll-sepolia-smoke-tests: # TODO: Disabled until bug TT-767 is fixed @@ -700,7 +727,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-scroll-sepolia @@ -720,6 +747,9 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 linea-goerli-smoke-tests: environment: integration @@ -746,7 +776,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-linea-goerli @@ -765,4 +795,7 @@ jobs: cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} \ No newline at end of file + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Print failed test summary + if: always() + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 \ No newline at end of file From bc0120588f32b9ff9d4745c48bd16ad972a6f818 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Tue, 23 Jan 2024 17:05:05 +0200 Subject: [PATCH 186/234] VRF-780: updating VRF Owner contract; VRF-847: add posibility to deploy test coordinator, better customisation options for VRF job spec (#11769) * VRF-780: updating VRF Owner contract VRF-847: add posibility to deploy test coordinator, better customisation options for VRF job spec * VRF-847: add missing go binding * VRF-847: fixing solidity lint * VRF-847: fixing compile error * VRF-847: fixing solidity lint * VRF-847: fixing go lint issue * VRF-847: fixing go lint issue * VRF-847: update * VRF-847: minor updates * fixing go lint * fixing VRF v2 Plus load test * reverting pnpm-lock.yaml changes * adding billing type to load tests; finishing CTF test for VRF Owner * fixing VRF V2 Plus test * decreasing max gas price for VRF V2 Plus test * fixing typo --- .../on-demand-vrfv2-performance-test.yml | 1 + .../on-demand-vrfv2plus-performance-test.yml | 4 +- contracts/scripts/native_solc_compile_all_vrf | 2 + .../vrf/testhelpers/VRFCoordinatorTestV2.sol | 837 +++++ .../testhelpers/VRFMockETHLINKAggregator.sol | 53 + .../testhelpers/VRFV2LoadTestWithMetrics.sol | 92 +- .../testhelpers/VRFV2OwnerTestConsumer.sol | 25 +- .../vrf_coordinator_test_v2.go | 3115 +++++++++++++++++ .../vrf_load_test_with_metrics.go | 317 +- .../vrf_mock_ethlink_aggregator.go | 377 ++ .../vrf_owner_test_consumer.go | 191 +- ...rapper-dependency-versions-do-not-edit.txt | 6 +- core/gethwrappers/go_generate.go | 2 + core/scripts/common/vrf/jobs/jobs.go | 48 +- core/scripts/common/vrf/model/model.go | 9 + core/scripts/common/vrf/setup-envs/README.md | 46 +- core/scripts/common/vrf/setup-envs/main.go | 30 +- core/scripts/vrfv2/testnet/main.go | 148 +- .../vrfv2/testnet/v2scripts/super_scripts.go | 70 +- core/scripts/vrfv2/testnet/v2scripts/util.go | 19 +- core/scripts/vrfv2plus/testnet/main.go | 3 +- .../testnet/v2plusscripts/super_scripts.go | 65 +- .../vrfv2plus/testnet/v2plusscripts/util.go | 3 +- .../vrfv2_actions/vrfv2_config/config.go | 2 +- .../actions/vrfv2_actions/vrfv2_models.go | 1 + .../actions/vrfv2_actions/vrfv2_steps.go | 351 +- .../vrfv2plus/vrfv2plus_config/config.go | 10 +- .../actions/vrfv2plus/vrfv2plus_steps.go | 118 +- integration-tests/client/chainlink_models.go | 79 +- .../contracts/contract_deployer.go | 21 + .../contracts/contract_vrf_models.go | 34 + .../contracts/ethereum_vrfv2_contracts.go | 337 ++ integration-tests/load/vrfv2/vrfv2_test.go | 12 +- integration-tests/load/vrfv2plus/config.go | 1 + integration-tests/load/vrfv2plus/config.toml | 25 +- integration-tests/load/vrfv2plus/gun.go | 23 +- .../load/vrfv2plus/vrfv2plus_test.go | 7 +- integration-tests/smoke/vrfv2_test.go | 142 +- integration-tests/smoke/vrfv2plus_test.go | 20 +- 39 files changed, 6130 insertions(+), 516 deletions(-) create mode 100644 contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol create mode 100644 contracts/src/v0.8/vrf/testhelpers/VRFMockETHLINKAggregator.sol create mode 100644 core/gethwrappers/generated/vrf_coordinator_test_v2/vrf_coordinator_test_v2.go create mode 100644 core/gethwrappers/generated/vrf_mock_ethlink_aggregator/vrf_mock_ethlink_aggregator.go diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml index 56a5c8eee84..3c505ed24ff 100644 --- a/.github/workflows/on-demand-vrfv2-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -20,6 +20,7 @@ on: - "POLYGON_MUMBAI" - "AVALANCHE_FUJI" - "AVALANCHE_MAINNET" + - "NEXON_DEV" fundingPrivateKey: description: Private funding key (Skip for Simulated) required: false diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 238f40de882..86c35d8e076 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -20,6 +20,7 @@ on: - "POLYGON_MUMBAI" - "AVALANCHE_FUJI" - "AVALANCHE_MAINNET" + - "NEXON_DEV" fundingPrivateKey: description: Private funding key (Skip for Simulated) required: false @@ -71,7 +72,8 @@ jobs: contents: read env: LOKI_URL: ${{ secrets.LOKI_URL }} - LOKI_TOKEN: ${{ secrets.LOKI_TOKEN }} + LOKI_TENANT_ID: ${{ secrets.LOKI_TENANT_ID }} + LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} SELECTED_NETWORKS: ${{ inputs.network }} TEST_TYPE: ${{ inputs.performanceTestType }} VRFV2PLUS_TEST_DURATION: ${{ inputs.testDuration }} diff --git a/contracts/scripts/native_solc_compile_all_vrf b/contracts/scripts/native_solc_compile_all_vrf index 828ba790510..3b6b96d2f50 100755 --- a/contracts/scripts/native_solc_compile_all_vrf +++ b/contracts/scripts/native_solc_compile_all_vrf @@ -101,6 +101,8 @@ compileContract vrf/testhelpers/VRFLoadTestOwnerlessConsumer.sol compileContract vrf/testhelpers/VRFLoadTestExternalSubOwner.sol compileContract vrf/testhelpers/VRFV2LoadTestWithMetrics.sol compileContract vrf/testhelpers/VRFV2OwnerTestConsumer.sol +compileContractAltOpts vrf/testhelpers/VRFCoordinatorTestV2.sol 10000 +compileContract vrf/testhelpers/VRFMockETHLINKAggregator.sol # Helper contracts compileContract vrf/interfaces/IAuthorizedReceiver.sol diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol b/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol new file mode 100644 index 00000000000..04d09e2b9c4 --- /dev/null +++ b/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorTestV2.sol @@ -0,0 +1,837 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; +import {BlockhashStoreInterface} from "../interfaces/BlockhashStoreInterface.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; +import {VRFCoordinatorV2Interface} from "../interfaces/VRFCoordinatorV2Interface.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol"; +import {VRF} from "../VRF.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {VRFConsumerBaseV2} from "../VRFConsumerBaseV2.sol"; + +contract VRFCoordinatorTestV2 is + VRF, + ConfirmedOwner, + TypeAndVersionInterface, + VRFCoordinatorV2Interface, + IERC677Receiver +{ + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + BlockhashStoreInterface public immutable BLOCKHASH_STORE; + + // We need to maintain a list of consuming addresses. + // This bound ensures we are able to loop over them as needed. + // Should a user require more consumers, they can use multiple subscriptions. + uint16 public constant MAX_CONSUMERS = 100; + + error TooManyConsumers(); + error InsufficientBalance(); + error InvalidConsumer(uint64 subId, address consumer); + error InvalidSubscription(); + error OnlyCallableFromLink(); + error InvalidCalldata(); + error MustBeSubOwner(address owner); + error PendingRequestExists(); + error MustBeRequestedOwner(address proposedOwner); + error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen + event FundsRecovered(address to, uint256 amount); + // We use the subscription struct (1 word) + // at fulfillment time. + struct Subscription { + // There are only 1e9*1e18 = 1e27 juels in existence, so the balance can fit in uint96 (2^96 ~ 7e28) + uint96 balance; // Common link balance used for all consumer requests. + uint64 reqCount; // For fee tiers + } + // We use the config for the mgmt APIs + struct SubscriptionConfig { + address owner; // Owner can fund/withdraw/cancel the sub. + address requestedOwner; // For safely transferring sub ownership. + // Maintains the list of keys in s_consumers. + // We do this for 2 reasons: + // 1. To be able to clean up all keys from s_consumers when canceling a subscription. + // 2. To be able to return the list of all consumers in getSubscription. + // Note that we need the s_consumers map to be able to directly check if a + // consumer is valid without reading all the consumers from storage. + address[] consumers; + } + // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. + mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subId */ /* nonce */ private s_consumers; + mapping(uint64 => SubscriptionConfig) /* subId */ /* subscriptionConfig */ private s_subscriptionConfigs; + mapping(uint64 => Subscription) /* subId */ /* subscription */ private s_subscriptions; + // We make the sub count public so that its possible to + // get all the current subscriptions via getSubscription. + uint64 private s_currentSubId; + // s_totalBalance tracks the total link sent to/from + // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. + // A discrepancy with this contract's link balance indicates someone + // sent tokens using transfer and so we may need to use recoverFunds. + uint96 private s_totalBalance; + event SubscriptionCreated(uint64 indexed subId, address owner); + event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); + event SubscriptionConsumerAdded(uint64 indexed subId, address consumer); + event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer); + event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); + event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to); + event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to); + + // Set this maximum to 200 to give us a 56 block window to fulfill + // the request before requiring the block hash feeder. + uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; + uint32 public constant MAX_NUM_WORDS = 500; + // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) + // and some arithmetic operations. + uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; + error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); + error GasLimitTooBig(uint32 have, uint32 want); + error NumWordsTooBig(uint32 have, uint32 want); + error ProvingKeyAlreadyRegistered(bytes32 keyHash); + error NoSuchProvingKey(bytes32 keyHash); + error InvalidLinkWeiPrice(int256 linkWei); + error InsufficientGasForConsumer(uint256 have, uint256 want); + error NoCorrespondingRequest(); + error IncorrectCommitment(); + error BlockhashNotInStore(uint256 blockNum); + error PaymentTooLarge(); + error Reentrant(); + + struct RequestCommitment { + uint64 blockNum; + uint64 subId; + uint32 callbackGasLimit; + uint32 numWords; + address sender; + } + mapping(bytes32 => address) /* keyHash */ /* oracle */ private s_provingKeys; + bytes32[] private s_provingKeyHashes; + mapping(address => uint96) /* oracle */ /* LINK balance */ private s_withdrawableTokens; + mapping(uint256 => bytes32) /* requestID */ /* commitment */ private s_requestCommitments; + event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); + event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle); + event RandomWordsRequested( + bytes32 indexed keyHash, + uint256 requestId, + uint256 preSeed, + uint64 indexed subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + address indexed sender + ); + event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); + + struct Config { + uint16 minimumRequestConfirmations; + uint32 maxGasLimit; + // Reentrancy protection. + bool reentrancyLock; + // stalenessSeconds is how long before we consider the feed price to be stale + // and fallback to fallbackWeiPerUnitLink. + uint32 stalenessSeconds; + // Gas to cover oracle payment after we calculate the payment. + // We make it configurable in case those operations are repriced. + uint32 gasAfterPaymentCalculation; + } + int256 private s_fallbackWeiPerUnitLink; + Config private s_config; + FeeConfig private s_feeConfig; + struct FeeConfig { + // Flat fee charged per fulfillment in millionths of link + // So fee range is [0, 2^32/10^6]. + uint32 fulfillmentFlatFeeLinkPPMTier1; + uint32 fulfillmentFlatFeeLinkPPMTier2; + uint32 fulfillmentFlatFeeLinkPPMTier3; + uint32 fulfillmentFlatFeeLinkPPMTier4; + uint32 fulfillmentFlatFeeLinkPPMTier5; + uint24 reqsForTier2; + uint24 reqsForTier3; + uint24 reqsForTier4; + uint24 reqsForTier5; + } + event ConfigSet( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + FeeConfig feeConfig + ); + + constructor(address link, address blockhashStore, address linkEthFeed) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + BLOCKHASH_STORE = BlockhashStoreInterface(blockhashStore); + } + + /** + * @notice Registers a proving key to an oracle. + * @param oracle address of the oracle + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + if (s_provingKeys[kh] != address(0)) { + revert ProvingKeyAlreadyRegistered(kh); + } + s_provingKeys[kh] = oracle; + s_provingKeyHashes.push(kh); + emit ProvingKeyRegistered(kh, oracle); + } + + /** + * @notice Deregisters a proving key to an oracle. + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + address oracle = s_provingKeys[kh]; + if (oracle == address(0)) { + revert NoSuchProvingKey(kh); + } + delete s_provingKeys[kh]; + for (uint256 i = 0; i < s_provingKeyHashes.length; i++) { + if (s_provingKeyHashes[i] == kh) { + bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1]; + // Copy last element and overwrite kh to be deleted with it + s_provingKeyHashes[i] = last; + s_provingKeyHashes.pop(); + } + } + emit ProvingKeyDeregistered(kh, oracle); + } + + /** + * @notice Returns the proving key hash key associated with this public key + * @param publicKey the key to return the hash of + */ + function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { + return keccak256(abi.encode(publicKey)); + } + + /** + * @notice Sets the configuration of the vrfv2 coordinator + * @param minimumRequestConfirmations global min for request confirmations + * @param maxGasLimit global max for request gas limit + * @param stalenessSeconds if the eth/link feed is more stale then this, use the fallback price + * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement + * @param fallbackWeiPerUnitLink fallback eth/link price in the case of a stale feed + * @param feeConfig fee tier configuration + */ + function setConfig( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + FeeConfig memory feeConfig + ) external onlyOwner { + if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { + revert InvalidRequestConfirmations( + minimumRequestConfirmations, + minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + if (fallbackWeiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink); + } + s_config = Config({ + minimumRequestConfirmations: minimumRequestConfirmations, + maxGasLimit: maxGasLimit, + stalenessSeconds: stalenessSeconds, + gasAfterPaymentCalculation: gasAfterPaymentCalculation, + reentrancyLock: false + }); + s_feeConfig = feeConfig; + s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; + emit ConfigSet( + minimumRequestConfirmations, + maxGasLimit, + stalenessSeconds, + gasAfterPaymentCalculation, + fallbackWeiPerUnitLink, + s_feeConfig + ); + } + + function getConfig() + external + view + returns ( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation + ) + { + return ( + s_config.minimumRequestConfirmations, + s_config.maxGasLimit, + s_config.stalenessSeconds, + s_config.gasAfterPaymentCalculation + ); + } + + function getFeeConfig() + external + view + returns ( + uint32 fulfillmentFlatFeeLinkPPMTier1, + uint32 fulfillmentFlatFeeLinkPPMTier2, + uint32 fulfillmentFlatFeeLinkPPMTier3, + uint32 fulfillmentFlatFeeLinkPPMTier4, + uint32 fulfillmentFlatFeeLinkPPMTier5, + uint24 reqsForTier2, + uint24 reqsForTier3, + uint24 reqsForTier4, + uint24 reqsForTier5 + ) + { + return ( + s_feeConfig.fulfillmentFlatFeeLinkPPMTier1, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier2, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier3, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier4, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier5, + s_feeConfig.reqsForTier2, + s_feeConfig.reqsForTier3, + s_feeConfig.reqsForTier4, + s_feeConfig.reqsForTier5 + ); + } + + function getTotalBalance() external view returns (uint256) { + return s_totalBalance; + } + + function getFallbackWeiPerUnitLink() external view returns (int256) { + return s_fallbackWeiPerUnitLink; + } + + /** + * @notice Owner cancel subscription, sends remaining link directly to the subscription owner. + * @param subId subscription id + * @dev notably can be called even if there are pending requests, outstanding ones may fail onchain + */ + function ownerCancelSubscription(uint64 subId) external onlyOwner { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner); + } + + /** + * @notice Recover link sent with transfer instead of transferAndCall. + * @param to address to send link to + */ + function recoverFunds(address to) external onlyOwner { + uint256 externalBalance = LINK.balanceOf(address(this)); + uint256 internalBalance = uint256(s_totalBalance); + if (internalBalance > externalBalance) { + revert BalanceInvariantViolated(internalBalance, externalBalance); + } + if (internalBalance < externalBalance) { + uint256 amount = externalBalance - internalBalance; + LINK.transfer(to, amount); + emit FundsRecovered(to, amount); + } + // If the balances are equal, nothing to be done. + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function getRequestConfig() external view override returns (uint16, uint32, bytes32[] memory) { + return (s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_provingKeyHashes); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function requestRandomWords( + bytes32 keyHash, + uint64 subId, + uint16 requestConfirmations, + uint32 callbackGasLimit, + uint32 numWords + ) external override nonReentrant returns (uint256) { + // Input validation using the subscription storage. + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // Its important to ensure that the consumer is in fact who they say they + // are, otherwise they could use someone else's subscription balance. + // A nonce of 0 indicates consumer is not allocated to the sub. + uint64 currentNonce = s_consumers[msg.sender][subId]; + if (currentNonce == 0) { + revert InvalidConsumer(subId, msg.sender); + } + // Input validation using the config storage word. + if ( + requestConfirmations < s_config.minimumRequestConfirmations || requestConfirmations > MAX_REQUEST_CONFIRMATIONS + ) { + revert InvalidRequestConfirmations( + requestConfirmations, + s_config.minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + // No lower bound on the requested gas limit. A user could request 0 + // and they would simply be billed for the proof verification and wouldn't be + // able to do anything with the random value. + if (callbackGasLimit > s_config.maxGasLimit) { + revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit); + } + if (numWords > MAX_NUM_WORDS) { + revert NumWordsTooBig(numWords, MAX_NUM_WORDS); + } + // Note we do not check whether the keyHash is valid to save gas. + // The consequence for users is that they can send requests + // for invalid keyHashes which will simply not be fulfilled. + uint64 nonce = currentNonce + 1; + (uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, subId, nonce); + + s_requestCommitments[requestId] = keccak256( + abi.encode(requestId, block.number, subId, callbackGasLimit, numWords, msg.sender) + ); + emit RandomWordsRequested( + keyHash, + requestId, + preSeed, + subId, + requestConfirmations, + callbackGasLimit, + numWords, + msg.sender + ); + s_consumers[msg.sender][subId] = nonce; + + return requestId; + } + + /** + * @notice Get request commitment + * @param requestId id of request + * @dev used to determine if a request is fulfilled or not + */ + function getCommitment(uint256 requestId) external view returns (bytes32) { + return s_requestCommitments[requestId]; + } + + function computeRequestId( + bytes32 keyHash, + address sender, + uint64 subId, + uint64 nonce + ) private pure returns (uint256, uint256) { + uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); + return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available. + */ + function callWithExactGas(uint256 gasAmount, address target, bytes memory data) private returns (bool success) { + // solhint-disable-next-line no-inline-assembly + assembly { + let g := gas() + // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow + // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, GAS_FOR_CALL_EXACT_CHECK) { + revert(0, 0) + } + g := sub(g, GAS_FOR_CALL_EXACT_CHECK) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + function getRandomnessFromProof( + Proof memory proof, + RequestCommitment memory rc + ) private view returns (bytes32 keyHash, uint256 requestId, uint256 randomness) { + keyHash = hashOfKey(proof.pk); + // Only registered proving keys are permitted. + address oracle = s_provingKeys[keyHash]; + if (oracle == address(0)) { + revert NoSuchProvingKey(keyHash); + } + requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); + bytes32 commitment = s_requestCommitments[requestId]; + if (commitment == 0) { + revert NoCorrespondingRequest(); + } + if ( + commitment != keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender)) + ) { + revert IncorrectCommitment(); + } + + bytes32 blockHash = blockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + blockHash = BLOCKHASH_STORE.getBlockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + revert BlockhashNotInStore(rc.blockNum); + } + } + + // The seed actually used by the VRF machinery, mixing in the blockhash + uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); + randomness = VRF._randomValueFromVRFProof(proof, actualSeed); // Reverts on failure + } + + /* + * @notice Compute fee based on the request count + * @param reqCount number of requests + * @return feePPM fee in LINK PPM + */ + function getFeeTier(uint64 reqCount) public view returns (uint32) { + FeeConfig memory fc = s_feeConfig; + if (0 <= reqCount && reqCount <= fc.reqsForTier2) { + return fc.fulfillmentFlatFeeLinkPPMTier1; + } + if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { + return fc.fulfillmentFlatFeeLinkPPMTier2; + } + if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { + return fc.fulfillmentFlatFeeLinkPPMTier3; + } + if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { + return fc.fulfillmentFlatFeeLinkPPMTier4; + } + return fc.fulfillmentFlatFeeLinkPPMTier5; + } + + /* + * @notice Fulfill a randomness request + * @param proof contains the proof and randomness + * @param rc request commitment pre-image, committed to at request time + * @return payment amount billed to the subscription + * @dev simulated offchain to determine if sufficient balance is present to fulfill the request + */ + function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint96) { + uint256 startGas = gasleft(); + (bytes32 keyHash, uint256 requestId, uint256 randomness) = getRandomnessFromProof(proof, rc); + + uint256[] memory randomWords = new uint256[](rc.numWords); + for (uint256 i = 0; i < rc.numWords; i++) { + randomWords[i] = uint256(keccak256(abi.encode(randomness, i))); + } + + delete s_requestCommitments[requestId]; + VRFConsumerBaseV2 v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, requestId, randomWords); + // Call with explicitly the amount of callback gas requested + // Important to not let them exhaust the gas budget and avoid oracle payment. + // Do not allow any non-view/non-pure coordinator functions to be called + // during the consumers callback code via reentrancyLock. + // Note that callWithExactGas will revert if we do not have sufficient gas + // to give the callee their requested amount. + s_config.reentrancyLock = true; + bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp); + s_config.reentrancyLock = false; + + // Increment the req count for fee tier selection. + uint64 reqCount = s_subscriptions[rc.subId].reqCount; + s_subscriptions[rc.subId].reqCount += 1; + + // We want to charge users exactly for how much gas they use in their callback. + // The gasAfterPaymentCalculation is meant to cover these additional operations where we + // decrement the subscription balance and increment the oracles withdrawable balance. + // We also add the flat link fee to the payment amount. + // Its specified in millionths of link, if s_config.fulfillmentFlatFeeLinkPPM = 1 + // 1 link / 1e6 = 1e18 juels / 1e6 = 1e12 juels. + uint96 payment = calculatePaymentAmount( + startGas, + s_config.gasAfterPaymentCalculation, + getFeeTier(reqCount), + tx.gasprice + ); + if (s_subscriptions[rc.subId].balance < payment) { + revert InsufficientBalance(); + } + s_subscriptions[rc.subId].balance -= payment; + s_withdrawableTokens[s_provingKeys[keyHash]] += payment; + // Include payment in the event for tracking costs. + emit RandomWordsFulfilled(requestId, randomness, payment, success); + return payment; + } + + // Get the amount of gas used for fulfillment + function calculatePaymentAmount( + uint256 startGas, + uint256 gasAfterPaymentCalculation, + uint32 fulfillmentFlatFeeLinkPPM, + uint256 weiPerUnitGas + ) internal view returns (uint96) { + int256 weiPerUnitLink; + weiPerUnitLink = getFeedData(); + if (weiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(weiPerUnitLink); + } + // (1e18 juels/link) (wei/gas * gas) / (wei/link) = juels + uint256 paymentNoFee = (1e18 * weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft())) / + uint256(weiPerUnitLink); + uint256 fee = 1e12 * uint256(fulfillmentFlatFeeLinkPPM); + if (paymentNoFee > (1e27 - fee)) { + revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. + } + return uint96(paymentNoFee + fee); + } + + function getFeedData() private view returns (int256) { + uint32 stalenessSeconds = s_config.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 weiPerUnitLink; + (, weiPerUnitLink, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + // solhint-disable-next-line not-rely-on-time + if (staleFallback && stalenessSeconds < block.timestamp - timestamp) { + weiPerUnitLink = s_fallbackWeiPerUnitLink; + } + return weiPerUnitLink; + } + + /* + * @notice Oracle withdraw LINK earned through fulfilling requests + * @param recipient where to send the funds + * @param amount amount to withdraw + */ + function oracleWithdraw(address recipient, uint96 amount) external nonReentrant { + if (s_withdrawableTokens[msg.sender] < amount) { + revert InsufficientBalance(); + } + s_withdrawableTokens[msg.sender] -= amount; + s_totalBalance -= amount; + if (!LINK.transfer(recipient, amount)) { + revert InsufficientBalance(); + } + } + + function onTokenTransfer(address /* sender */, uint256 amount, bytes calldata data) external override nonReentrant { + if (msg.sender != address(LINK)) { + revert OnlyCallableFromLink(); + } + if (data.length != 32) { + revert InvalidCalldata(); + } + uint64 subId = abi.decode(data, (uint64)); + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // We do not check that the msg.sender is the subscription owner, + // anyone can fund a subscription. + uint256 oldBalance = s_subscriptions[subId].balance; + s_subscriptions[subId].balance += uint96(amount); + s_totalBalance += uint96(amount); + emit SubscriptionFunded(subId, oldBalance, oldBalance + amount); + } + + function getCurrentSubId() external view returns (uint64) { + return s_currentSubId; + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function getSubscription( + uint64 subId + ) external view override returns (uint96 balance, uint64 reqCount, address owner, address[] memory consumers) { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + return ( + s_subscriptions[subId].balance, + s_subscriptions[subId].reqCount, + s_subscriptionConfigs[subId].owner, + s_subscriptionConfigs[subId].consumers + ); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function createSubscription() external override nonReentrant returns (uint64) { + s_currentSubId++; + uint64 currentSubId = s_currentSubId; + address[] memory consumers = new address[](0); + s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0}); + s_subscriptionConfigs[currentSubId] = SubscriptionConfig({ + owner: msg.sender, + requestedOwner: address(0), + consumers: consumers + }); + + emit SubscriptionCreated(currentSubId, msg.sender); + return currentSubId; + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function requestSubscriptionOwnerTransfer( + uint64 subId, + address newOwner + ) external override onlySubOwner(subId) nonReentrant { + // Proposing to address(0) would never be claimable so don't need to check. + if (s_subscriptionConfigs[subId].requestedOwner != newOwner) { + s_subscriptionConfigs[subId].requestedOwner = newOwner; + emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); + } + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function acceptSubscriptionOwnerTransfer(uint64 subId) external override nonReentrant { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) { + revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner); + } + address oldOwner = s_subscriptionConfigs[subId].owner; + s_subscriptionConfigs[subId].owner = msg.sender; + s_subscriptionConfigs[subId].requestedOwner = address(0); + emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function removeConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + if (s_consumers[consumer][subId] == 0) { + revert InvalidConsumer(subId, consumer); + } + // Note bounded by MAX_CONSUMERS + address[] memory consumers = s_subscriptionConfigs[subId].consumers; + uint256 lastConsumerIndex = consumers.length - 1; + for (uint256 i = 0; i < consumers.length; i++) { + if (consumers[i] == consumer) { + address last = consumers[lastConsumerIndex]; + // Storage write to preserve last element + s_subscriptionConfigs[subId].consumers[i] = last; + // Storage remove last element + s_subscriptionConfigs[subId].consumers.pop(); + break; + } + } + delete s_consumers[consumer][subId]; + emit SubscriptionConsumerRemoved(subId, consumer); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + // Already maxed, cannot add any more consumers. + if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) { + revert TooManyConsumers(); + } + if (s_consumers[consumer][subId] != 0) { + // Idempotence - do nothing if already added. + // Ensures uniqueness in s_subscriptions[subId].consumers. + return; + } + // Initialize the nonce to 1, indicating the consumer is allocated. + s_consumers[consumer][subId] = 1; + s_subscriptionConfigs[subId].consumers.push(consumer); + + emit SubscriptionConsumerAdded(subId, consumer); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function cancelSubscription(uint64 subId, address to) external override onlySubOwner(subId) nonReentrant { + if (pendingRequestExists(subId)) { + revert PendingRequestExists(); + } + cancelSubscriptionHelper(subId, to); + } + + function cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + Subscription memory sub = s_subscriptions[subId]; + uint96 balance = sub.balance; + // Note bounded by MAX_CONSUMERS; + // If no consumers, does nothing. + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + delete s_consumers[subConfig.consumers[i]][subId]; + } + delete s_subscriptionConfigs[subId]; + delete s_subscriptions[subId]; + s_totalBalance -= balance; + if (!LINK.transfer(to, uint256(balance))) { + revert InsufficientBalance(); + } + emit SubscriptionCanceled(subId, to, balance); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + * @dev Looping is bounded to MAX_CONSUMERS*(number of keyhashes). + * @dev Used to disable subscription canceling while outstanding request are present. + */ + function pendingRequestExists(uint64 subId) public view override returns (bool) { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + for (uint256 j = 0; j < s_provingKeyHashes.length; j++) { + (uint256 reqId, ) = computeRequestId( + s_provingKeyHashes[j], + subConfig.consumers[i], + subId, + s_consumers[subConfig.consumers[i]][subId] + ); + if (s_requestCommitments[reqId] != 0) { + return true; + } + } + } + return false; + } + + modifier onlySubOwner(uint64 subId) { + address owner = s_subscriptionConfigs[subId].owner; + if (owner == address(0)) { + revert InvalidSubscription(); + } + if (msg.sender != owner) { + revert MustBeSubOwner(owner); + } + _; + } + + modifier nonReentrant() { + if (s_config.reentrancyLock) { + revert Reentrant(); + } + _; + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "VRFCoordinatorV2 1.0.0"; + } +} diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFMockETHLINKAggregator.sol b/contracts/src/v0.8/vrf/testhelpers/VRFMockETHLINKAggregator.sol new file mode 100644 index 00000000000..86c77202434 --- /dev/null +++ b/contracts/src/v0.8/vrf/testhelpers/VRFMockETHLINKAggregator.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../../shared/interfaces/AggregatorV3Interface.sol"; + +contract VRFMockETHLINKAggregator is AggregatorV3Interface { + int256 public answer; + uint256 private blockTimestampDeduction = 0; + + constructor(int256 _answer) public { + answer = _answer; + } + + function decimals() external view override returns (uint8) { + return 18; + } + + function description() external view override returns (string memory) { + return "VRFMockETHLINKAggregator"; + } + + function version() external view override returns (uint256) { + return 1; + } + + function getRoundData( + uint80 _roundId + ) + external + view + override + returns (uint80 roundId, int256 ans, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + return (1, answer, getDeductedBlockTimestamp(), getDeductedBlockTimestamp(), 1); + } + + function latestRoundData() + external + view + override + returns (uint80 roundId, int256 ans, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + return (1, answer, getDeductedBlockTimestamp(), getDeductedBlockTimestamp(), 1); + } + + function getDeductedBlockTimestamp() internal view returns (uint256) { + return block.timestamp - blockTimestampDeduction; + } + + function setBlockTimestampDeduction(uint256 _blockTimestampDeduction) external { + blockTimestampDeduction = _blockTimestampDeduction; + } +} diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol b/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol index d364e5002b4..fa44b3eee30 100644 --- a/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol +++ b/contracts/src/v0.8/vrf/testhelpers/VRFV2LoadTestWithMetrics.sol @@ -3,16 +3,16 @@ pragma solidity ^0.8.0; import {VRFCoordinatorV2Interface} from "../interfaces/VRFCoordinatorV2Interface.sol"; import {VRFConsumerBaseV2} from "../VRFConsumerBaseV2.sol"; -import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol"; +import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; /** * @title The VRFLoadTestExternalSubOwner contract. * @notice Allows making many VRF V2 randomness requests in a single transaction for load testing. */ -contract VRFV2LoadTestWithMetrics is VRFConsumerBaseV2, ConfirmedOwner { +contract VRFV2LoadTestWithMetrics is VRFConsumerBaseV2 { VRFCoordinatorV2Interface public immutable COORDINATOR; - + LinkTokenInterface public LINKTOKEN; uint256 public s_responseCount; uint256 public s_requestCount; uint256 public s_averageFulfillmentInMillions = 0; // in millions for better precision @@ -21,6 +21,8 @@ contract VRFV2LoadTestWithMetrics is VRFConsumerBaseV2, ConfirmedOwner { uint256 public s_lastRequestId; mapping(uint256 => uint256) internal requestHeights; // requestIds to block number when rand request was made + event SubscriptionCreatedFundedAndConsumerAdded(uint64 subId, address consumer, uint256 amount); + struct RequestStatus { bool fulfilled; uint256[] randomWords; @@ -32,7 +34,7 @@ contract VRFV2LoadTestWithMetrics is VRFConsumerBaseV2, ConfirmedOwner { mapping(uint256 => RequestStatus) /* requestId */ /* requestStatus */ public s_requests; - constructor(address _vrfCoordinator) VRFConsumerBaseV2(_vrfCoordinator) ConfirmedOwner(msg.sender) { + constructor(address _vrfCoordinator) VRFConsumerBaseV2(_vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator); } @@ -64,28 +66,30 @@ contract VRFV2LoadTestWithMetrics is VRFConsumerBaseV2, ConfirmedOwner { uint32 _callbackGasLimit, uint32 _numWords, uint16 _requestCount - ) external onlyOwner { - for (uint16 i = 0; i < _requestCount; i++) { - uint256 requestId = COORDINATOR.requestRandomWords( - _keyHash, - _subId, - _requestConfirmations, - _callbackGasLimit, - _numWords - ); - s_lastRequestId = requestId; - uint256 requestBlockNumber = ChainSpecificUtil._getBlockNumber(); - s_requests[requestId] = RequestStatus({ - randomWords: new uint256[](0), - fulfilled: false, - requestTimestamp: block.timestamp, - fulfilmentTimestamp: 0, - requestBlockNumber: requestBlockNumber, - fulfilmentBlockNumber: 0 - }); - s_requestCount++; - requestHeights[requestId] = requestBlockNumber; - } + ) external { + _makeLoadTestRequests(_subId, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount); + } + + function requestRandomWordsWithForceFulfill( + uint16 _requestConfirmations, + bytes32 _keyHash, + uint32 _callbackGasLimit, + uint32 _numWords, + uint16 _requestCount, + uint256 _subTopUpAmount, + address _link + ) external { + // create a subscription, address(this) will be the owner + uint64 _subId = COORDINATOR.createSubscription(); + // add address(this) as a consumer on the subscription + COORDINATOR.addConsumer(_subId, address(this)); + topUpSubscription(_subId, _subTopUpAmount, _link); + emit SubscriptionCreatedFundedAndConsumerAdded(_subId, address(this), _subTopUpAmount); + + _makeLoadTestRequests(_subId, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount); + + COORDINATOR.removeConsumer(_subId, address(this)); + COORDINATOR.cancelSubscription(_subId, msg.sender); } function reset() external { @@ -120,4 +124,40 @@ contract VRFV2LoadTestWithMetrics is VRFConsumerBaseV2, ConfirmedOwner { request.fulfilmentBlockNumber ); } + + function _makeLoadTestRequests( + uint64 _subId, + uint16 _requestConfirmations, + bytes32 _keyHash, + uint32 _callbackGasLimit, + uint32 _numWords, + uint16 _requestCount + ) internal { + for (uint16 i = 0; i < _requestCount; i++) { + uint256 requestId = COORDINATOR.requestRandomWords( + _keyHash, + _subId, + _requestConfirmations, + _callbackGasLimit, + _numWords + ); + s_lastRequestId = requestId; + uint256 requestBlockNumber = ChainSpecificUtil._getBlockNumber(); + s_requests[requestId] = RequestStatus({ + randomWords: new uint256[](0), + fulfilled: false, + requestTimestamp: block.timestamp, + fulfilmentTimestamp: 0, + requestBlockNumber: requestBlockNumber, + fulfilmentBlockNumber: 0 + }); + s_requestCount++; + requestHeights[requestId] = requestBlockNumber; + } + } + + function topUpSubscription(uint64 _subId, uint256 _amount, address _link) public { + LINKTOKEN = LinkTokenInterface(_link); + LINKTOKEN.transferAndCall(address(COORDINATOR), _amount, abi.encode(_subId)); + } } diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol b/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol index dd1af80a92d..5961f4e5d49 100644 --- a/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol +++ b/contracts/src/v0.8/vrf/testhelpers/VRFV2OwnerTestConsumer.sol @@ -5,9 +5,11 @@ import {VRFCoordinatorV2Interface} from "../interfaces/VRFCoordinatorV2Interface import {VRFConsumerBaseV2} from "../VRFConsumerBaseV2.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol"; +import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; contract VRFV2OwnerTestConsumer is VRFConsumerBaseV2, ConfirmedOwner { VRFCoordinatorV2Interface public COORDINATOR; + LinkTokenInterface public LINKTOKEN; uint64 public subId; uint256 public s_responseCount; uint256 public s_requestCount; @@ -17,6 +19,8 @@ contract VRFV2OwnerTestConsumer is VRFConsumerBaseV2, ConfirmedOwner { uint256 public s_lastRequestId; mapping(uint256 => uint256) internal requestHeights; // requestIds to block number when rand request was made + event SubscriptionCreatedFundedAndConsumerAdded(uint64 subId, address consumer, uint256 amount); + struct RequestStatus { bool fulfilled; uint256[] randomWords; @@ -28,12 +32,9 @@ contract VRFV2OwnerTestConsumer is VRFConsumerBaseV2, ConfirmedOwner { mapping(uint256 => RequestStatus) /* requestId */ /* requestStatus */ public s_requests; - constructor(address _vrfCoordinator) VRFConsumerBaseV2(_vrfCoordinator) ConfirmedOwner(msg.sender) { + constructor(address _vrfCoordinator, address _link) VRFConsumerBaseV2(_vrfCoordinator) ConfirmedOwner(msg.sender) { COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator); - // create a subscription, address(this) will be the owner - subId = COORDINATOR.createSubscription(); - // add address(this) as a consumer on the subscription - COORDINATOR.addConsumer(subId, address(this)); + LINKTOKEN = LinkTokenInterface(_link); } function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override { @@ -62,8 +63,16 @@ contract VRFV2OwnerTestConsumer is VRFConsumerBaseV2, ConfirmedOwner { bytes32 _keyHash, uint32 _callbackGasLimit, uint32 _numWords, - uint16 _requestCount + uint16 _requestCount, + uint256 _subTopUpAmount ) external onlyOwner { + // create a subscription, address(this) will be the owner + subId = COORDINATOR.createSubscription(); + // add address(this) as a consumer on the subscription + COORDINATOR.addConsumer(subId, address(this)); + topUpSubscription(_subTopUpAmount); + emit SubscriptionCreatedFundedAndConsumerAdded(subId, address(this), _subTopUpAmount); + for (uint16 i = 0; i < _requestCount; i++) { uint256 requestId = COORDINATOR.requestRandomWords( _keyHash, @@ -122,4 +131,8 @@ contract VRFV2OwnerTestConsumer is VRFConsumerBaseV2, ConfirmedOwner { request.fulfilmentBlockNumber ); } + + function topUpSubscription(uint256 amount) public onlyOwner { + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(subId)); + } } diff --git a/core/gethwrappers/generated/vrf_coordinator_test_v2/vrf_coordinator_test_v2.go b/core/gethwrappers/generated/vrf_coordinator_test_v2/vrf_coordinator_test_v2.go new file mode 100644 index 00000000000..cb4e2cea016 --- /dev/null +++ b/core/gethwrappers/generated/vrf_coordinator_test_v2/vrf_coordinator_test_v2.go @@ -0,0 +1,3115 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package vrf_coordinator_test_v2 + +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 VRFCoordinatorTestV2FeeConfig struct { + FulfillmentFlatFeeLinkPPMTier1 uint32 + FulfillmentFlatFeeLinkPPMTier2 uint32 + FulfillmentFlatFeeLinkPPMTier3 uint32 + FulfillmentFlatFeeLinkPPMTier4 uint32 + FulfillmentFlatFeeLinkPPMTier5 uint32 + ReqsForTier2 *big.Int + ReqsForTier3 *big.Int + ReqsForTier4 *big.Int + ReqsForTier5 *big.Int +} + +type VRFCoordinatorTestV2RequestCommitment struct { + BlockNum uint64 + SubId uint64 + CallbackGasLimit uint32 + NumWords uint32 + Sender common.Address +} + +type VRFProof struct { + Pk [2]*big.Int + Gamma [2]*big.Int + C *big.Int + S *big.Int + Seed *big.Int + UWitness common.Address + CGammaWitness [2]*big.Int + SHashWitness [2]*big.Int + ZInv *big.Int +} + +var VRFCoordinatorTestV2MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkEthFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"internalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"externalBalance\",\"type\":\"uint256\"}],\"name\":\"BalanceInvariantViolated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"}],\"name\":\"BlockhashNotInStore\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"have\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"want\",\"type\":\"uint256\"}],\"name\":\"InsufficientGasForConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"have\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"min\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"max\",\"type\":\"uint16\"}],\"name\":\"InvalidRequestConfirmations\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeRequestedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCorrespondingRequest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"NoSuchProvingKey\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"NumWordsTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingRequestExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Reentrant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier1\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier2\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier3\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier4\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier5\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier2\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier3\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier4\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier5\",\"type\":\"uint24\"}],\"indexed\":false,\"internalType\":\"structVRFCoordinatorTestV2.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"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\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_ETH_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"deregisterProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"internalType\":\"structVRFCoordinatorTestV2.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"}],\"name\":\"getCommitment\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentSubId\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFallbackWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFeeConfig\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier1\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier2\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier3\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier4\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier5\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier2\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier3\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier4\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier5\",\"type\":\"uint24\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"}],\"name\":\"getFeeTier\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRequestConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTotalBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier1\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier2\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier3\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier4\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier5\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier2\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier3\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier4\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier5\",\"type\":\"uint24\"}],\"internalType\":\"structVRFCoordinatorTestV2.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"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\"}]", + Bin: "0x60e06040523480156200001157600080fd5b5060405162005755380380620057558339810160408190526200003491620001b1565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000e8565b5050506001600160601b0319606093841b811660805290831b811660a052911b1660c052620001fb565b6001600160a01b038116331415620001435760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001ac57600080fd5b919050565b600080600060608486031215620001c757600080fd5b620001d28462000194565b9250620001e26020850162000194565b9150620001f26040850162000194565b90509250925092565b60805160601c60a05160601c60c05160601c6154f0620002656000396000818161051901526138180152600081816106030152613c0401526000818161036d015281816114df0152818161233c01528181612d7301528181612eaf01526134d401526154f06000f3fe608060405234801561001057600080fd5b506004361061025b5760003560e01c80636f64f03f11610145578063ad178361116100bd578063d2f9f9a71161008c578063e72f6e3011610071578063e72f6e30146106e0578063e82ad7d4146106f3578063f2fde38b1461071657600080fd5b8063d2f9f9a7146106ba578063d7ae1d30146106cd57600080fd5b8063ad178361146105fe578063af198b9714610625578063c3f909d414610655578063caf70c4a146106a757600080fd5b80638da5cb5b11610114578063a21a23e4116100f9578063a21a23e4146105c0578063a47c7696146105c8578063a4c0ed36146105eb57600080fd5b80638da5cb5b1461059c5780639f87fad7146105ad57600080fd5b80636f64f03f1461055b5780637341c10c1461056e57806379ba509714610581578063823597401461058957600080fd5b8063356dac71116101d85780635fbbc0d2116101a757806366316d8d1161018c57806366316d8d14610501578063689c45171461051457806369bcdb7d1461053b57600080fd5b80635fbbc0d2146103f357806364d51a2a146104f957600080fd5b8063356dac71146103a757806340d6bb82146103af5780634cb48a54146103cd5780635d3b1d30146103e057600080fd5b806308821d581161022f57806315c48b841161021457806315c48b841461030e578063181f5a77146103295780631b6b6d231461036857600080fd5b806308821d58146102cf57806312b58349146102e257600080fd5b80620122911461026057806302bcc5b61461028057806304c357cb1461029557806306bfa637146102a8575b600080fd5b610268610729565b6040516102779392919061503a565b60405180910390f35b61029361028e366004614e86565b6107a5565b005b6102936102a3366004614ea1565b610837565b60055467ffffffffffffffff165b60405167ffffffffffffffff9091168152602001610277565b6102936102dd366004614b97565b6109eb565b6005546801000000000000000090046bffffffffffffffffffffffff165b604051908152602001610277565b61031660c881565b60405161ffff9091168152602001610277565b604080518082018252601681527f565246436f6f7264696e61746f72563220312e302e3000000000000000000000602082015290516102779190614fe5565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610277565b600a54610300565b6103b86101f481565b60405163ffffffff9091168152602001610277565b6102936103db366004614d30565b610bb0565b6103006103ee366004614c0a565b610fa7565b600c546040805163ffffffff80841682526401000000008404811660208301526801000000000000000084048116928201929092526c010000000000000000000000008304821660608201527001000000000000000000000000000000008304909116608082015262ffffff740100000000000000000000000000000000000000008304811660a0830152770100000000000000000000000000000000000000000000008304811660c08301527a0100000000000000000000000000000000000000000000000000008304811660e08301527d01000000000000000000000000000000000000000000000000000000000090920490911661010082015261012001610277565b610316606481565b61029361050f366004614b4f565b61138a565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b610300610549366004614e6d565b60009081526009602052604090205490565b610293610569366004614a94565b6115d9565b61029361057c366004614ea1565b611709565b610293611956565b610293610597366004614e86565b611a1f565b6000546001600160a01b031661038f565b6102936105bb366004614ea1565b611be5565b6102b6611fe4565b6105db6105d6366004614e86565b6121c7565b60405161027794939291906151d8565b6102936105f9366004614ac8565b6122ea565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b610638610633366004614c68565b612541565b6040516bffffffffffffffffffffffff9091168152602001610277565b600b546040805161ffff8316815263ffffffff6201000084048116602083015267010000000000000084048116928201929092526b010000000000000000000000909204166060820152608001610277565b6103006106b5366004614bb3565b6129db565b6103b86106c8366004614e86565b612a0b565b6102936106db366004614ea1565b612c00565b6102936106ee366004614a79565b612d3a565b610706610701366004614e86565b612f77565b6040519015158152602001610277565b610293610724366004614a79565b61319a565b600b546007805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff1693919283919083018282801561079357602002820191906000526020600020905b81548152602001906001019080831161077f575b50505050509050925092509250909192565b6107ad6131ab565b67ffffffffffffffff81166000908152600360205260409020546001600160a01b0316610806576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600360205260409020546108349082906001600160a01b0316613207565b50565b67ffffffffffffffff821660009081526003602052604090205482906001600160a01b031680610893576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b038216146108e5576040517fd8a3fb520000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b600b546601000000000000900460ff161561092c576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600360205260409020600101546001600160a01b038481169116146109e55767ffffffffffffffff841660008181526003602090815260409182902060010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0388169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b6109f36131ab565b604080518082018252600091610a229190849060029083908390808284376000920191909152506129db915050565b6000818152600660205260409020549091506001600160a01b031680610a77576040517f77f5b84c000000000000000000000000000000000000000000000000000000008152600481018390526024016108dc565b600082815260066020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b600754811015610b67578260078281548110610aca57610aca615485565b90600052602060002001541415610b55576007805460009190610aef9060019061533f565b81548110610aff57610aff615485565b906000526020600020015490508060078381548110610b2057610b20615485565b6000918252602090912001556007805480610b3d57610b3d615456565b60019003818190600052602060002001600090559055505b80610b5f81615383565b915050610aac565b50806001600160a01b03167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610ba391815260200190565b60405180910390a2505050565b610bb86131ab565b60c861ffff87161115610c0b576040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff871660048201819052602482015260c860448201526064016108dc565b60008213610c48576040517f43d4cf66000000000000000000000000000000000000000000000000000000008152600481018390526024016108dc565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600b80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001690971762010000909502949094177fffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff166701000000000000009092027fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff16919091176b010000000000000000000000909302929092179093558651600c80549489015189890151938a0151978a0151968a015160c08b015160e08c01516101008d01519588167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009099169890981764010000000093881693909302929092177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1668010000000000000000958716959095027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff16949094176c0100000000000000000000000098861698909802979097177fffffffffffffffffff00000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000096909416959095027fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff16929092177401000000000000000000000000000000000000000062ffffff92831602177fffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffff1677010000000000000000000000000000000000000000000000958216959095027fffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16949094177a01000000000000000000000000000000000000000000000000000092851692909202919091177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167d0100000000000000000000000000000000000000000000000000000000009390911692909202919091178155600a84905590517fc21e3bd2e0b339d2848f0dd956947a88966c242c0c0c582a33137a5c1ceb5cb291610f97918991899189918991899190615099565b60405180910390a1505050505050565b600b546000906601000000000000900460ff1615610ff1576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff85166000908152600360205260409020546001600160a01b031661104a576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260026020908152604080832067ffffffffffffffff808a16855292529091205416806110ba576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff871660048201523360248201526044016108dc565b600b5461ffff90811690861610806110d6575060c861ffff8616115b1561112657600b546040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff8088166004830152909116602482015260c860448201526064016108dc565b600b5463ffffffff620100009091048116908516111561118d57600b546040517ff5d7e01e00000000000000000000000000000000000000000000000000000000815263ffffffff80871660048301526201000090920490911660248201526044016108dc565b6101f463ffffffff841611156111df576040517f47386bec00000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526101f460248201526044016108dc565b60006111ec82600161529b565b6040805160208082018c9052338284015267ffffffffffffffff808c16606084015284166080808401919091528351808403909101815260a08301845280519082012060c083018d905260e080840182905284518085039091018152610100909301909352815191012091925060009182916040805160208101849052439181019190915267ffffffffffffffff8c16606082015263ffffffff808b166080830152891660a08201523360c0820152919350915060e00160408051808303601f19018152828252805160209182012060008681526009835283902055848352820183905261ffff8a169082015263ffffffff808916606083015287166080820152339067ffffffffffffffff8b16908c907f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a97729060a00160405180910390a45033600090815260026020908152604080832067ffffffffffffffff808d16855292529091208054919093167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790915591505095945050505050565b600b546601000000000000900460ff16156113d1576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600860205260409020546bffffffffffffffffffffffff8083169116101561142b576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260086020526040812080548392906114589084906bffffffffffffffffffffffff16615356565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080600560088282829054906101000a90046bffffffffffffffffffffffff166114af9190615356565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040161154d9291906001600160a01b039290921682526bffffffffffffffffffffffff16602082015260400190565b602060405180830381600087803b15801561156757600080fd5b505af115801561157b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061159f9190614bcf565b6115d5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6115e16131ab565b6040805180820182526000916116109190849060029083908390808284376000920191909152506129db915050565b6000818152600660205260409020549091506001600160a01b031615611665576040517f4a0b8fa7000000000000000000000000000000000000000000000000000000008152600481018290526024016108dc565b600081815260066020908152604080832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0388169081179091556007805460018101825594527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610ba3565b67ffffffffffffffff821660009081526003602052604090205482906001600160a01b031680611765576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b038216146117b2576040517fd8a3fb520000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016108dc565b600b546601000000000000900460ff16156117f9576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206002015460641415611850576040517f05a48e0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038316600090815260026020908152604080832067ffffffffffffffff8089168552925290912054161561188a576109e5565b6001600160a01b038316600081815260026020818152604080842067ffffffffffffffff8a1680865290835281852080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001908117909155600384528286209094018054948501815585529382902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001685179055905192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e091016109dc565b6001546001600160a01b031633146119b05760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016108dc565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600b546601000000000000900460ff1615611a66576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600360205260409020546001600160a01b0316611abf576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600360205260409020600101546001600160a01b03163314611b475767ffffffffffffffff8116600090815260036020526040908190206001015490517fd084e9750000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016108dc565b67ffffffffffffffff81166000818152600360209081526040918290208054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001909301805490931690925583516001600160a01b03909116808252928101919091529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f0910160405180910390a25050565b67ffffffffffffffff821660009081526003602052604090205482906001600160a01b031680611c41576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03821614611c8e576040517fd8a3fb520000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016108dc565b600b546601000000000000900460ff1615611cd5576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038316600090815260026020908152604080832067ffffffffffffffff808916855292529091205416611d56576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526001600160a01b03841660248201526044016108dc565b67ffffffffffffffff8416600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611dc457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611da6575b50505050509050600060018251611ddb919061533f565b905060005b8251811015611f5357856001600160a01b0316838281518110611e0557611e05615485565b60200260200101516001600160a01b03161415611f41576000838381518110611e3057611e30615485565b6020026020010151905080600360008a67ffffffffffffffff1667ffffffffffffffff1681526020019081526020016000206002018381548110611e7657611e76615485565b600091825260208083209190910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03949094169390931790925567ffffffffffffffff8a168152600390915260409020600201805480611ee357611ee3615456565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611f53565b80611f4b81615383565b915050611de0565b506001600160a01b038516600081815260026020908152604080832067ffffffffffffffff8b168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b91015b60405180910390a2505050505050565b600b546000906601000000000000900460ff161561202e576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005805467ffffffffffffffff16906000612048836153bc565b82546101009290920a67ffffffffffffffff81810219909316918316021790915560055416905060008060405190808252806020026020018201604052801561209b578160200160208202803683370190505b506040805180820182526000808252602080830182815267ffffffffffffffff888116808552600484528685209551865493516bffffffffffffffffffffffff9091167fffffffffffffffffffffffff0000000000000000000000000000000000000000948516176c01000000000000000000000000919093160291909117909455845160608101865233815280830184815281870188815295855260038452959093208351815483166001600160a01b039182161782559551600182018054909316961695909517905591518051949550909361217f92600285019201906147d3565b505060405133815267ffffffffffffffff841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b67ffffffffffffffff8116600090815260036020526040812054819081906060906001600160a01b0316612227576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff80861660009081526004602090815260408083205460038352928190208054600290910180548351818602810186019094528084526bffffffffffffffffffffffff8616966c01000000000000000000000000909604909516946001600160a01b039092169390929183918301828280156122d457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116122b6575b5050505050905093509350935093509193509193565b600b546601000000000000900460ff1615612331576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612393576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081146123cd576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006123db82840184614e86565b67ffffffffffffffff81166000908152600360205260409020549091506001600160a01b0316612437576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260046020526040812080546bffffffffffffffffffffffff169186919061246e83856152c7565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555084600560088282829054906101000a90046bffffffffffffffffffffffff166124c591906152c7565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508167ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f882878461252c9190615283565b60408051928352602083019190915201611fd4565b600b546000906601000000000000900460ff161561258b576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a9050600080600061259f878761362c565b9250925092506000866060015163ffffffff1667ffffffffffffffff8111156125ca576125ca6154b4565b6040519080825280602002602001820160405280156125f3578160200160208202803683370190505b50905060005b876060015163ffffffff168110156126675760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c82828151811061264a5761264a615485565b60209081029190910101528061265f81615383565b9150506125f9565b506000838152600960205260408082208290555181907f1fe543e300000000000000000000000000000000000000000000000000000000906126af908790869060240161518a565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff166601000000000000179055908a015160808b015191925060009161275f9163ffffffff169084613936565b600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff1690556020808c01805167ffffffffffffffff9081166000908152600490935260408084205492518216845290922080549394506c01000000000000000000000000918290048316936001939192600c926127e392869290041661529b565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550600061283a8a600b600001600b9054906101000a900463ffffffff1663ffffffff1661283485612a0b565b3a613984565b6020808e015167ffffffffffffffff166000908152600490915260409020549091506bffffffffffffffffffffffff808316911610156128a6576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808d015167ffffffffffffffff16600090815260049091526040812080548392906128e29084906bffffffffffffffffffffffff16615356565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b8152600660209081526040808320546001600160a01b03168352600890915281208054859450909261293e918591166152c7565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550877f7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e48883866040516129c1939291909283526bffffffffffffffffffffffff9190911660208301521515604082015260600190565b60405180910390a299505050505050505050505b92915050565b6000816040516020016129ee9190614fd7565b604051602081830303815290604052805190602001209050919050565b6040805161012081018252600c5463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c010000000000000000000000008104831660608301527001000000000000000000000000000000008104909216608082015262ffffff740100000000000000000000000000000000000000008304811660a08301819052770100000000000000000000000000000000000000000000008404821660c08401527a0100000000000000000000000000000000000000000000000000008404821660e08401527d0100000000000000000000000000000000000000000000000000000000009093041661010082015260009167ffffffffffffffff841611612b29575192915050565b8267ffffffffffffffff168160a0015162ffffff16108015612b5e57508060c0015162ffffff168367ffffffffffffffff1611155b15612b6d576020015192915050565b8267ffffffffffffffff168160c0015162ffffff16108015612ba257508060e0015162ffffff168367ffffffffffffffff1611155b15612bb1576040015192915050565b8267ffffffffffffffff168160e0015162ffffff16108015612be7575080610100015162ffffff168367ffffffffffffffff1611155b15612bf6576060015192915050565b6080015192915050565b67ffffffffffffffff821660009081526003602052604090205482906001600160a01b031680612c5c576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03821614612ca9576040517fd8a3fb520000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016108dc565b600b546601000000000000900460ff1615612cf0576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612cf984612f77565b15612d30576040517fb42f66e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109e58484613207565b612d426131ab565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015612dbd57600080fd5b505afa158015612dd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df59190614bf1565b6005549091506801000000000000000090046bffffffffffffffffffffffff1681811115612e59576040517fa99da30200000000000000000000000000000000000000000000000000000000815260048101829052602481018390526044016108dc565b81811015612f72576000612e6d828461533f565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038681166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401602060405180830381600087803b158015612ef557600080fd5b505af1158015612f09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f2d9190614bcf565b50604080516001600160a01b0386168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a1505b505050565b67ffffffffffffffff81166000908152600360209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561300c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612fee575b505050505081525050905060005b8160400151518110156131905760005b60075481101561317d5760006131466007838154811061304c5761304c615485565b90600052602060002001548560400151858151811061306d5761306d615485565b602002602001015188600260008960400151898151811061309057613090615485565b6020908102919091018101516001600160a01b03168252818101929092526040908101600090812067ffffffffffffffff808f16835293522054166040805160208082018790526001600160a01b03959095168183015267ffffffffffffffff9384166060820152919092166080808301919091528251808303909101815260a08201835280519084012060c082019490945260e080820185905282518083039091018152610100909101909152805191012091565b506000818152600960205260409020549091501561316a5750600195945050505050565b508061317581615383565b91505061302a565b508061318881615383565b91505061301a565b5060009392505050565b6131a26131ab565b61083481613a8c565b6000546001600160a01b031633146132055760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016108dc565b565b600b546601000000000000900460ff161561324e576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff82166000908152600360209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152929593948601938301828280156132df57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116132c1575b5050509190925250505067ffffffffffffffff80851660009081526004602090815260408083208151808301909252546bffffffffffffffffffffffff81168083526c01000000000000000000000000909104909416918101919091529293505b8360400151518110156133d957600260008560400151838151811061336757613367615485565b6020908102919091018101516001600160a01b03168252818101929092526040908101600090812067ffffffffffffffff8a168252909252902080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000169055806133d181615383565b915050613340565b5067ffffffffffffffff8516600090815260036020526040812080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811682556001820180549091169055906134346002830182614850565b505067ffffffffffffffff8516600090815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600580548291906008906134a49084906801000000000000000090046bffffffffffffffffffffffff16615356565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb85836bffffffffffffffffffffffff166040518363ffffffff1660e01b81526004016135429291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b15801561355c57600080fd5b505af1158015613570573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135949190614bcf565b6135ca576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001600160a01b03861681526bffffffffffffffffffffffff8316602082015267ffffffffffffffff8716917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050565b600080600061363e85600001516129db565b6000818152600660205260409020549093506001600160a01b031680613693576040517f77f5b84c000000000000000000000000000000000000000000000000000000008152600481018590526024016108dc565b60808601516040516136b2918691602001918252602082015260400190565b60408051601f1981840301815291815281516020928301206000818152600990935291205490935080613711576040517f3688124a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516020808801516040808a015160608b015160808c0151925161377d968b96909594910195865267ffffffffffffffff948516602087015292909316604085015263ffffffff90811660608501529190911660808301526001600160a01b031660a082015260c00190565b6040516020818303038152906040528051906020012081146137cb576040517fd529142c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855167ffffffffffffffff1640806138e25786516040517fe9413d3800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e9413d389060240160206040518083038186803b15801561386257600080fd5b505afa158015613876573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061389a9190614bf1565b9050806138e25786516040517f175dadad00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108dc565b6000886080015182604051602001613904929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c90506139298982613b4e565b9450505050509250925092565b60005a61138881101561394857600080fd5b61138881039050846040820482031161396057600080fd5b50823b61396c57600080fd5b60008083516020850160008789f190505b9392505050565b60008061398f613bb9565b9050600081136139ce576040517f43d4cf66000000000000000000000000000000000000000000000000000000008152600481018290526024016108dc565b6000815a6139dc8989615283565b6139e6919061533f565b6139f886670de0b6b3a7640000615302565b613a029190615302565b613a0c91906152ee565b90506000613a2563ffffffff871664e8d4a51000615302565b9050613a3d816b033b2e3c9fd0803ce800000061533f565b821115613a76576040517fe80fa38100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613a808183615283565b98975050505050505050565b6001600160a01b038116331415613ae55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016108dc565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000613b828360000151846020015185604001518660600151868860a001518960c001518a60e001518b6101000151613cc0565b60038360200151604051602001613b9a929190615176565b60408051601f1981840301815291905280516020909101209392505050565b600b54604080517ffeaf968c0000000000000000000000000000000000000000000000000000000081529051600092670100000000000000900463ffffffff169182151591849182917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169163feaf968c9160048083019260a0929190829003018186803b158015613c5257600080fd5b505afa158015613c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8a9190614ecb565b509450909250849150508015613cae5750613ca5824261533f565b8463ffffffff16105b15613cb85750600a545b949350505050565b613cc989613efb565b613d155760405162461bcd60e51b815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e20637572766500000000000060448201526064016108dc565b613d1e88613efb565b613d6a5760405162461bcd60e51b815260206004820152601560248201527f67616d6d61206973206e6f74206f6e206375727665000000000000000000000060448201526064016108dc565b613d7383613efb565b613dbf5760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e20637572766500000060448201526064016108dc565b613dc882613efb565b613e145760405162461bcd60e51b815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e2063757276650000000060448201526064016108dc565b613e20878a8887613fd4565b613e6c5760405162461bcd60e51b815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e6573730000000000000060448201526064016108dc565b6000613e788a87614125565b90506000613e8b898b878b868989614189565b90506000613e9c838d8d8a866142a9565b9050808a14613eed5760405162461bcd60e51b815260206004820152600d60248201527f696e76616c69642070726f6f660000000000000000000000000000000000000060448201526064016108dc565b505050505050505050505050565b80516000906401000003d01911613f545760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420782d6f7264696e617465000000000000000000000000000060448201526064016108dc565b60208201516401000003d01911613fad5760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420792d6f7264696e617465000000000000000000000000000060448201526064016108dc565b60208201516401000003d019908009613fcd8360005b60200201516142e9565b1492915050565b60006001600160a01b03821661402c5760405162461bcd60e51b815260206004820152600b60248201527f626164207769746e65737300000000000000000000000000000000000000000060448201526064016108dc565b60208401516000906001161561404357601c614046565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418587600060200201510986517ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa1580156140fd573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b61412d61486e565b61415a6001848460405160200161414693929190614fb6565b60405160208183030381529060405261430d565b90505b61416681613efb565b6129d55780516040805160208101929092526141829101614146565b905061415d565b61419161486e565b825186516401000003d01990819006910614156141f05760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e6374000060448201526064016108dc565b6141fb87898861435c565b6142475760405162461bcd60e51b815260206004820152601660248201527f4669727374206d756c20636865636b206661696c65640000000000000000000060448201526064016108dc565b61425284868561435c565b61429e5760405162461bcd60e51b815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c656400000000000000000060448201526064016108dc565b613a808684846144a4565b6000600286868685876040516020016142c796959493929190614f44565b60408051601f1981840301815291905280516020909101209695505050505050565b6000806401000003d01980848509840990506401000003d019600782089392505050565b61431561486e565b61431e8261456b565b815261433361432e826000613fc3565b6145a6565b602082018190526002900660011415614357576020810180516401000003d0190390525b919050565b6000826143ab5760405162461bcd60e51b815260206004820152600b60248201527f7a65726f207363616c617200000000000000000000000000000000000000000060448201526064016108dc565b835160208501516000906143c1906002906153e4565b156143cd57601c6143d0565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614450573d6000803e3d6000fd5b50505060206040510351905060008660405160200161446f9190614f32565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b6144ac61486e565b8351602080860151855191860151600093849384936144cd939091906145c6565b919450925090506401000003d01985820960011461452d5760405162461bcd60e51b815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a0000000000000060448201526064016108dc565b60405180604001604052806401000003d0198061454c5761454c615427565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d019811061435757604080516020808201939093528151808203840181529082019091528051910120614573565b60006129d58260026145bf6401000003d0196001615283565b901c6146a6565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a089050600061460683838585614766565b909850905061461788828e8861478a565b909850905061462888828c8761478a565b9098509050600061463b8d878b8561478a565b909850905061464c88828686614766565b909850905061465d88828e8961478a565b9098509050818114614692576401000003d019818a0998506401000003d01982890997506401000003d0198183099650614696565b8196505b5050505050509450945094915050565b6000806146b161488c565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a08201526146e36148aa565b60208160c08460057ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa92508261475c5760405162461bcd60e51b815260206004820152601260248201527f6269674d6f64457870206661696c75726521000000000000000000000000000060448201526064016108dc565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215614840579160200282015b8281111561484057825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039091161782556020909201916001909101906147f3565b5061484c9291506148c8565b5090565b508054600082559060005260206000209081019061083491906148c8565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b8082111561484c57600081556001016148c9565b80356001600160a01b038116811461435757600080fd5b80604081018310156129d557600080fd5b600082601f83011261491657600080fd5b6040516040810181811067ffffffffffffffff82111715614939576149396154b4565b806040525080838560408601111561495057600080fd5b60005b6002811015614972578135835260209283019290910190600101614953565b509195945050505050565b600060a0828403121561498f57600080fd5b60405160a0810181811067ffffffffffffffff821117156149b2576149b26154b4565b6040529050806149c183614a47565b81526149cf60208401614a47565b60208201526149e060408401614a33565b60408201526149f160608401614a33565b6060820152614a02608084016148dd565b60808201525092915050565b803561ffff8116811461435757600080fd5b803562ffffff8116811461435757600080fd5b803563ffffffff8116811461435757600080fd5b803567ffffffffffffffff8116811461435757600080fd5b805169ffffffffffffffffffff8116811461435757600080fd5b600060208284031215614a8b57600080fd5b61397d826148dd565b60008060608385031215614aa757600080fd5b614ab0836148dd565b9150614abf84602085016148f4565b90509250929050565b60008060008060608587031215614ade57600080fd5b614ae7856148dd565b935060208501359250604085013567ffffffffffffffff80821115614b0b57600080fd5b818701915087601f830112614b1f57600080fd5b813581811115614b2e57600080fd5b886020828501011115614b4057600080fd5b95989497505060200194505050565b60008060408385031215614b6257600080fd5b614b6b836148dd565b915060208301356bffffffffffffffffffffffff81168114614b8c57600080fd5b809150509250929050565b600060408284031215614ba957600080fd5b61397d83836148f4565b600060408284031215614bc557600080fd5b61397d8383614905565b600060208284031215614be157600080fd5b8151801515811461397d57600080fd5b600060208284031215614c0357600080fd5b5051919050565b600080600080600060a08688031215614c2257600080fd5b85359450614c3260208701614a47565b9350614c4060408701614a0e565b9250614c4e60608701614a33565b9150614c5c60808701614a33565b90509295509295909350565b600080828403610240811215614c7d57600080fd5b6101a080821215614c8d57600080fd5b614c95615259565b9150614ca18686614905565b8252614cb08660408701614905565b60208301526080850135604083015260a0850135606083015260c08501356080830152614cdf60e086016148dd565b60a0830152610100614cf387828801614905565b60c0840152614d06876101408801614905565b60e08401526101808601358184015250819350614d258682870161497d565b925050509250929050565b6000806000806000808688036101c0811215614d4b57600080fd5b614d5488614a0e565b9650614d6260208901614a33565b9550614d7060408901614a33565b9450614d7e60608901614a33565b935060808801359250610120807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6083011215614db957600080fd5b614dc1615259565b9150614dcf60a08a01614a33565b8252614ddd60c08a01614a33565b6020830152614dee60e08a01614a33565b6040830152610100614e01818b01614a33565b6060840152614e11828b01614a33565b6080840152614e236101408b01614a20565b60a0840152614e356101608b01614a20565b60c0840152614e476101808b01614a20565b60e0840152614e596101a08b01614a20565b818401525050809150509295509295509295565b600060208284031215614e7f57600080fd5b5035919050565b600060208284031215614e9857600080fd5b61397d82614a47565b60008060408385031215614eb457600080fd5b614ebd83614a47565b9150614abf602084016148dd565b600080600080600060a08688031215614ee357600080fd5b614eec86614a5f565b9450602086015193506040860151925060608601519150614c5c60808701614a5f565b8060005b60028110156109e5578151845260209384019390910190600101614f13565b614f3c8183614f0f565b604001919050565b868152614f546020820187614f0f565b614f616060820186614f0f565b614f6e60a0820185614f0f565b614f7b60e0820184614f0f565b60609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166101208201526101340195945050505050565b838152614fc66020820184614f0f565b606081019190915260800192915050565b604081016129d58284614f0f565b600060208083528351808285015260005b8181101561501257858101830151858201604001528201614ff6565b81811115615024576000604083870101525b50601f01601f1916929092016040019392505050565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b8181101561508b5784518352938301939183019160010161506f565b509098975050505050505050565b60006101c08201905061ffff8816825263ffffffff808816602084015280871660408401528086166060840152846080840152835481811660a08501526150ed60c08501838360201c1663ffffffff169052565b61510460e08501838360401c1663ffffffff169052565b61511c6101008501838360601c1663ffffffff169052565b6151346101208501838360801c1663ffffffff169052565b62ffffff60a082901c811661014086015260b882901c811661016086015260d082901c1661018085015260e81c6101a090930192909252979650505050505050565b8281526060810161397d6020830184614f0f565b6000604082018483526020604081850152818551808452606086019150828701935060005b818110156151cb578451835293830193918301916001016151af565b5090979650505050505050565b6000608082016bffffffffffffffffffffffff87168352602067ffffffffffffffff8716818501526001600160a01b0380871660408601526080606086015282865180855260a087019150838801945060005b8181101561524957855184168352948401949184019160010161522b565b50909a9950505050505050505050565b604051610120810167ffffffffffffffff8111828210171561527d5761527d6154b4565b60405290565b60008219821115615296576152966153f8565b500190565b600067ffffffffffffffff8083168185168083038211156152be576152be6153f8565b01949350505050565b60006bffffffffffffffffffffffff8083168185168083038211156152be576152be6153f8565b6000826152fd576152fd615427565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561533a5761533a6153f8565b500290565b600082821015615351576153516153f8565b500390565b60006bffffffffffffffffffffffff8381169083168181101561537b5761537b6153f8565b039392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156153b5576153b56153f8565b5060010190565b600067ffffffffffffffff808316818114156153da576153da6153f8565b6001019392505050565b6000826153f3576153f3615427565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", +} + +var VRFCoordinatorTestV2ABI = VRFCoordinatorTestV2MetaData.ABI + +var VRFCoordinatorTestV2Bin = VRFCoordinatorTestV2MetaData.Bin + +func DeployVRFCoordinatorTestV2(auth *bind.TransactOpts, backend bind.ContractBackend, link common.Address, blockhashStore common.Address, linkEthFeed common.Address) (common.Address, *types.Transaction, *VRFCoordinatorTestV2, error) { + parsed, err := VRFCoordinatorTestV2MetaData.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(VRFCoordinatorTestV2Bin), backend, link, blockhashStore, linkEthFeed) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &VRFCoordinatorTestV2{address: address, abi: *parsed, VRFCoordinatorTestV2Caller: VRFCoordinatorTestV2Caller{contract: contract}, VRFCoordinatorTestV2Transactor: VRFCoordinatorTestV2Transactor{contract: contract}, VRFCoordinatorTestV2Filterer: VRFCoordinatorTestV2Filterer{contract: contract}}, nil +} + +type VRFCoordinatorTestV2 struct { + address common.Address + abi abi.ABI + VRFCoordinatorTestV2Caller + VRFCoordinatorTestV2Transactor + VRFCoordinatorTestV2Filterer +} + +type VRFCoordinatorTestV2Caller struct { + contract *bind.BoundContract +} + +type VRFCoordinatorTestV2Transactor struct { + contract *bind.BoundContract +} + +type VRFCoordinatorTestV2Filterer struct { + contract *bind.BoundContract +} + +type VRFCoordinatorTestV2Session struct { + Contract *VRFCoordinatorTestV2 + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type VRFCoordinatorTestV2CallerSession struct { + Contract *VRFCoordinatorTestV2Caller + CallOpts bind.CallOpts +} + +type VRFCoordinatorTestV2TransactorSession struct { + Contract *VRFCoordinatorTestV2Transactor + TransactOpts bind.TransactOpts +} + +type VRFCoordinatorTestV2Raw struct { + Contract *VRFCoordinatorTestV2 +} + +type VRFCoordinatorTestV2CallerRaw struct { + Contract *VRFCoordinatorTestV2Caller +} + +type VRFCoordinatorTestV2TransactorRaw struct { + Contract *VRFCoordinatorTestV2Transactor +} + +func NewVRFCoordinatorTestV2(address common.Address, backend bind.ContractBackend) (*VRFCoordinatorTestV2, error) { + abi, err := abi.JSON(strings.NewReader(VRFCoordinatorTestV2ABI)) + if err != nil { + return nil, err + } + contract, err := bindVRFCoordinatorTestV2(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2{address: address, abi: abi, VRFCoordinatorTestV2Caller: VRFCoordinatorTestV2Caller{contract: contract}, VRFCoordinatorTestV2Transactor: VRFCoordinatorTestV2Transactor{contract: contract}, VRFCoordinatorTestV2Filterer: VRFCoordinatorTestV2Filterer{contract: contract}}, nil +} + +func NewVRFCoordinatorTestV2Caller(address common.Address, caller bind.ContractCaller) (*VRFCoordinatorTestV2Caller, error) { + contract, err := bindVRFCoordinatorTestV2(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2Caller{contract: contract}, nil +} + +func NewVRFCoordinatorTestV2Transactor(address common.Address, transactor bind.ContractTransactor) (*VRFCoordinatorTestV2Transactor, error) { + contract, err := bindVRFCoordinatorTestV2(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2Transactor{contract: contract}, nil +} + +func NewVRFCoordinatorTestV2Filterer(address common.Address, filterer bind.ContractFilterer) (*VRFCoordinatorTestV2Filterer, error) { + contract, err := bindVRFCoordinatorTestV2(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2Filterer{contract: contract}, nil +} + +func bindVRFCoordinatorTestV2(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := VRFCoordinatorTestV2MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFCoordinatorTestV2.Contract.VRFCoordinatorTestV2Caller.contract.Call(opts, result, method, params...) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.VRFCoordinatorTestV2Transactor.contract.Transfer(opts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.VRFCoordinatorTestV2Transactor.contract.Transact(opts, method, params...) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFCoordinatorTestV2.Contract.contract.Call(opts, result, method, params...) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.contract.Transfer(opts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.contract.Transact(opts, method, params...) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) BLOCKHASHSTORE(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "BLOCKHASH_STORE") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) BLOCKHASHSTORE() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.BLOCKHASHSTORE(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) BLOCKHASHSTORE() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.BLOCKHASHSTORE(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) LINK(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "LINK") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) LINK() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.LINK(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) LINK() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.LINK(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) LINKETHFEED(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "LINK_ETH_FEED") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) LINKETHFEED() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.LINKETHFEED(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) LINKETHFEED() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.LINKETHFEED(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) MAXCONSUMERS(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "MAX_CONSUMERS") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) MAXCONSUMERS() (uint16, error) { + return _VRFCoordinatorTestV2.Contract.MAXCONSUMERS(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) MAXCONSUMERS() (uint16, error) { + return _VRFCoordinatorTestV2.Contract.MAXCONSUMERS(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) MAXNUMWORDS(opts *bind.CallOpts) (uint32, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "MAX_NUM_WORDS") + + if err != nil { + return *new(uint32), err + } + + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) MAXNUMWORDS() (uint32, error) { + return _VRFCoordinatorTestV2.Contract.MAXNUMWORDS(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) MAXNUMWORDS() (uint32, error) { + return _VRFCoordinatorTestV2.Contract.MAXNUMWORDS(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "MAX_REQUEST_CONFIRMATIONS") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) MAXREQUESTCONFIRMATIONS() (uint16, error) { + return _VRFCoordinatorTestV2.Contract.MAXREQUESTCONFIRMATIONS(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) MAXREQUESTCONFIRMATIONS() (uint16, error) { + return _VRFCoordinatorTestV2.Contract.MAXREQUESTCONFIRMATIONS(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetCommitment(opts *bind.CallOpts, requestId *big.Int) ([32]byte, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getCommitment", requestId) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetCommitment(requestId *big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV2.Contract.GetCommitment(&_VRFCoordinatorTestV2.CallOpts, requestId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetCommitment(requestId *big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV2.Contract.GetCommitment(&_VRFCoordinatorTestV2.CallOpts, requestId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetConfig(opts *bind.CallOpts) (GetConfig, + + error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getConfig") + + outstruct := new(GetConfig) + if err != nil { + return *outstruct, err + } + + outstruct.MinimumRequestConfirmations = *abi.ConvertType(out[0], new(uint16)).(*uint16) + outstruct.MaxGasLimit = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.StalenessSeconds = *abi.ConvertType(out[2], new(uint32)).(*uint32) + outstruct.GasAfterPaymentCalculation = *abi.ConvertType(out[3], new(uint32)).(*uint32) + + return *outstruct, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetConfig() (GetConfig, + + error) { + return _VRFCoordinatorTestV2.Contract.GetConfig(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetConfig() (GetConfig, + + error) { + return _VRFCoordinatorTestV2.Contract.GetConfig(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetCurrentSubId(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getCurrentSubId") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetCurrentSubId() (uint64, error) { + return _VRFCoordinatorTestV2.Contract.GetCurrentSubId(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetCurrentSubId() (uint64, error) { + return _VRFCoordinatorTestV2.Contract.GetCurrentSubId(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetFallbackWeiPerUnitLink(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getFallbackWeiPerUnitLink") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetFallbackWeiPerUnitLink() (*big.Int, error) { + return _VRFCoordinatorTestV2.Contract.GetFallbackWeiPerUnitLink(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetFallbackWeiPerUnitLink() (*big.Int, error) { + return _VRFCoordinatorTestV2.Contract.GetFallbackWeiPerUnitLink(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetFeeConfig(opts *bind.CallOpts) (GetFeeConfig, + + error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getFeeConfig") + + outstruct := new(GetFeeConfig) + if err != nil { + return *outstruct, err + } + + outstruct.FulfillmentFlatFeeLinkPPMTier1 = *abi.ConvertType(out[0], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier2 = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier3 = *abi.ConvertType(out[2], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier4 = *abi.ConvertType(out[3], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier5 = *abi.ConvertType(out[4], new(uint32)).(*uint32) + outstruct.ReqsForTier2 = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) + outstruct.ReqsForTier3 = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) + outstruct.ReqsForTier4 = *abi.ConvertType(out[7], new(*big.Int)).(**big.Int) + outstruct.ReqsForTier5 = *abi.ConvertType(out[8], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetFeeConfig() (GetFeeConfig, + + error) { + return _VRFCoordinatorTestV2.Contract.GetFeeConfig(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetFeeConfig() (GetFeeConfig, + + error) { + return _VRFCoordinatorTestV2.Contract.GetFeeConfig(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetFeeTier(opts *bind.CallOpts, reqCount uint64) (uint32, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getFeeTier", reqCount) + + if err != nil { + return *new(uint32), err + } + + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetFeeTier(reqCount uint64) (uint32, error) { + return _VRFCoordinatorTestV2.Contract.GetFeeTier(&_VRFCoordinatorTestV2.CallOpts, reqCount) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetFeeTier(reqCount uint64) (uint32, error) { + return _VRFCoordinatorTestV2.Contract.GetFeeTier(&_VRFCoordinatorTestV2.CallOpts, reqCount) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetRequestConfig(opts *bind.CallOpts) (uint16, uint32, [][32]byte, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getRequestConfig") + + if err != nil { + return *new(uint16), *new(uint32), *new([][32]byte), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + out1 := *abi.ConvertType(out[1], new(uint32)).(*uint32) + out2 := *abi.ConvertType(out[2], new([][32]byte)).(*[][32]byte) + + return out0, out1, out2, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetRequestConfig() (uint16, uint32, [][32]byte, error) { + return _VRFCoordinatorTestV2.Contract.GetRequestConfig(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetRequestConfig() (uint16, uint32, [][32]byte, error) { + return _VRFCoordinatorTestV2.Contract.GetRequestConfig(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetSubscription(opts *bind.CallOpts, subId uint64) (GetSubscription, + + error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getSubscription", subId) + + outstruct := new(GetSubscription) + if err != nil { + return *outstruct, err + } + + outstruct.Balance = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ReqCount = *abi.ConvertType(out[1], new(uint64)).(*uint64) + outstruct.Owner = *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + outstruct.Consumers = *abi.ConvertType(out[3], new([]common.Address)).(*[]common.Address) + + return *outstruct, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetSubscription(subId uint64) (GetSubscription, + + error) { + return _VRFCoordinatorTestV2.Contract.GetSubscription(&_VRFCoordinatorTestV2.CallOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetSubscription(subId uint64) (GetSubscription, + + error) { + return _VRFCoordinatorTestV2.Contract.GetSubscription(&_VRFCoordinatorTestV2.CallOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) GetTotalBalance(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "getTotalBalance") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) GetTotalBalance() (*big.Int, error) { + return _VRFCoordinatorTestV2.Contract.GetTotalBalance(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) GetTotalBalance() (*big.Int, error) { + return _VRFCoordinatorTestV2.Contract.GetTotalBalance(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) HashOfKey(opts *bind.CallOpts, publicKey [2]*big.Int) ([32]byte, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "hashOfKey", publicKey) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) HashOfKey(publicKey [2]*big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV2.Contract.HashOfKey(&_VRFCoordinatorTestV2.CallOpts, publicKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) HashOfKey(publicKey [2]*big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV2.Contract.HashOfKey(&_VRFCoordinatorTestV2.CallOpts, publicKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) Owner() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.Owner(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) Owner() (common.Address, error) { + return _VRFCoordinatorTestV2.Contract.Owner(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) PendingRequestExists(opts *bind.CallOpts, subId uint64) (bool, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "pendingRequestExists", subId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) PendingRequestExists(subId uint64) (bool, error) { + return _VRFCoordinatorTestV2.Contract.PendingRequestExists(&_VRFCoordinatorTestV2.CallOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) PendingRequestExists(subId uint64) (bool, error) { + return _VRFCoordinatorTestV2.Contract.PendingRequestExists(&_VRFCoordinatorTestV2.CallOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Caller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _VRFCoordinatorTestV2.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) TypeAndVersion() (string, error) { + return _VRFCoordinatorTestV2.Contract.TypeAndVersion(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2CallerSession) TypeAndVersion() (string, error) { + return _VRFCoordinatorTestV2.Contract.TypeAndVersion(&_VRFCoordinatorTestV2.CallOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "acceptOwnership") +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) AcceptOwnership() (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.AcceptOwnership(&_VRFCoordinatorTestV2.TransactOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.AcceptOwnership(&_VRFCoordinatorTestV2.TransactOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) AcceptSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "acceptSubscriptionOwnerTransfer", subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) AcceptSubscriptionOwnerTransfer(subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.AcceptSubscriptionOwnerTransfer(&_VRFCoordinatorTestV2.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) AcceptSubscriptionOwnerTransfer(subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.AcceptSubscriptionOwnerTransfer(&_VRFCoordinatorTestV2.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) AddConsumer(opts *bind.TransactOpts, subId uint64, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "addConsumer", subId, consumer) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) AddConsumer(subId uint64, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.AddConsumer(&_VRFCoordinatorTestV2.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) AddConsumer(subId uint64, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.AddConsumer(&_VRFCoordinatorTestV2.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) CancelSubscription(opts *bind.TransactOpts, subId uint64, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "cancelSubscription", subId, to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) CancelSubscription(subId uint64, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.CancelSubscription(&_VRFCoordinatorTestV2.TransactOpts, subId, to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) CancelSubscription(subId uint64, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.CancelSubscription(&_VRFCoordinatorTestV2.TransactOpts, subId, to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "createSubscription") +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) CreateSubscription() (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.CreateSubscription(&_VRFCoordinatorTestV2.TransactOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) CreateSubscription() (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.CreateSubscription(&_VRFCoordinatorTestV2.TransactOpts) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "deregisterProvingKey", publicProvingKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) DeregisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.DeregisterProvingKey(&_VRFCoordinatorTestV2.TransactOpts, publicProvingKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) DeregisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.DeregisterProvingKey(&_VRFCoordinatorTestV2.TransactOpts, publicProvingKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) FulfillRandomWords(opts *bind.TransactOpts, proof VRFProof, rc VRFCoordinatorTestV2RequestCommitment) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "fulfillRandomWords", proof, rc) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) FulfillRandomWords(proof VRFProof, rc VRFCoordinatorTestV2RequestCommitment) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.FulfillRandomWords(&_VRFCoordinatorTestV2.TransactOpts, proof, rc) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) FulfillRandomWords(proof VRFProof, rc VRFCoordinatorTestV2RequestCommitment) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.FulfillRandomWords(&_VRFCoordinatorTestV2.TransactOpts, proof, rc) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "onTokenTransfer", arg0, amount, data) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) OnTokenTransfer(arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.OnTokenTransfer(&_VRFCoordinatorTestV2.TransactOpts, arg0, amount, data) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) OnTokenTransfer(arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.OnTokenTransfer(&_VRFCoordinatorTestV2.TransactOpts, arg0, amount, data) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "oracleWithdraw", recipient, amount) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) OracleWithdraw(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.OracleWithdraw(&_VRFCoordinatorTestV2.TransactOpts, recipient, amount) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) OracleWithdraw(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.OracleWithdraw(&_VRFCoordinatorTestV2.TransactOpts, recipient, amount) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) OwnerCancelSubscription(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "ownerCancelSubscription", subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) OwnerCancelSubscription(subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.OwnerCancelSubscription(&_VRFCoordinatorTestV2.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) OwnerCancelSubscription(subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.OwnerCancelSubscription(&_VRFCoordinatorTestV2.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "recoverFunds", to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) RecoverFunds(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RecoverFunds(&_VRFCoordinatorTestV2.TransactOpts, to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) RecoverFunds(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RecoverFunds(&_VRFCoordinatorTestV2.TransactOpts, to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "registerProvingKey", oracle, publicProvingKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) RegisterProvingKey(oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RegisterProvingKey(&_VRFCoordinatorTestV2.TransactOpts, oracle, publicProvingKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) RegisterProvingKey(oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RegisterProvingKey(&_VRFCoordinatorTestV2.TransactOpts, oracle, publicProvingKey) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) RemoveConsumer(opts *bind.TransactOpts, subId uint64, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "removeConsumer", subId, consumer) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) RemoveConsumer(subId uint64, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RemoveConsumer(&_VRFCoordinatorTestV2.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) RemoveConsumer(subId uint64, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RemoveConsumer(&_VRFCoordinatorTestV2.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) RequestRandomWords(opts *bind.TransactOpts, keyHash [32]byte, subId uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "requestRandomWords", keyHash, subId, requestConfirmations, callbackGasLimit, numWords) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) RequestRandomWords(keyHash [32]byte, subId uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RequestRandomWords(&_VRFCoordinatorTestV2.TransactOpts, keyHash, subId, requestConfirmations, callbackGasLimit, numWords) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) RequestRandomWords(keyHash [32]byte, subId uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RequestRandomWords(&_VRFCoordinatorTestV2.TransactOpts, keyHash, subId, requestConfirmations, callbackGasLimit, numWords) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64, newOwner common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "requestSubscriptionOwnerTransfer", subId, newOwner) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) RequestSubscriptionOwnerTransfer(subId uint64, newOwner common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RequestSubscriptionOwnerTransfer(&_VRFCoordinatorTestV2.TransactOpts, subId, newOwner) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) RequestSubscriptionOwnerTransfer(subId uint64, newOwner common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.RequestSubscriptionOwnerTransfer(&_VRFCoordinatorTestV2.TransactOpts, subId, newOwner) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorTestV2FeeConfig) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "setConfig", minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorTestV2FeeConfig) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.SetConfig(&_VRFCoordinatorTestV2.TransactOpts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorTestV2FeeConfig) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.SetConfig(&_VRFCoordinatorTestV2.TransactOpts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Transactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.contract.Transact(opts, "transferOwnership", to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Session) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.TransferOwnership(&_VRFCoordinatorTestV2.TransactOpts, to) +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2TransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV2.Contract.TransferOwnership(&_VRFCoordinatorTestV2.TransactOpts, to) +} + +type VRFCoordinatorTestV2ConfigSetIterator struct { + Event *VRFCoordinatorTestV2ConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2ConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2ConfigSet) + 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(VRFCoordinatorTestV2ConfigSet) + 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 *VRFCoordinatorTestV2ConfigSetIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2ConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2ConfigSet struct { + MinimumRequestConfirmations uint16 + MaxGasLimit uint32 + StalenessSeconds uint32 + GasAfterPaymentCalculation uint32 + FallbackWeiPerUnitLink *big.Int + FeeConfig VRFCoordinatorTestV2FeeConfig + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterConfigSet(opts *bind.FilterOpts) (*VRFCoordinatorTestV2ConfigSetIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2ConfigSetIterator{contract: _VRFCoordinatorTestV2.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2ConfigSet) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2ConfigSet) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "ConfigSet", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseConfigSet(log types.Log) (*VRFCoordinatorTestV2ConfigSet, error) { + event := new(VRFCoordinatorTestV2ConfigSet) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2FundsRecoveredIterator struct { + Event *VRFCoordinatorTestV2FundsRecovered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2FundsRecoveredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2FundsRecovered) + 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(VRFCoordinatorTestV2FundsRecovered) + 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 *VRFCoordinatorTestV2FundsRecoveredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2FundsRecoveredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2FundsRecovered struct { + To common.Address + Amount *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterFundsRecovered(opts *bind.FilterOpts) (*VRFCoordinatorTestV2FundsRecoveredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "FundsRecovered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2FundsRecoveredIterator{contract: _VRFCoordinatorTestV2.contract, event: "FundsRecovered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchFundsRecovered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2FundsRecovered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "FundsRecovered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2FundsRecovered) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "FundsRecovered", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseFundsRecovered(log types.Log) (*VRFCoordinatorTestV2FundsRecovered, error) { + event := new(VRFCoordinatorTestV2FundsRecovered) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "FundsRecovered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2OwnershipTransferRequestedIterator struct { + Event *VRFCoordinatorTestV2OwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2OwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2OwnershipTransferRequested) + 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(VRFCoordinatorTestV2OwnershipTransferRequested) + 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 *VRFCoordinatorTestV2OwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2OwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2OwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV2OwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2OwnershipTransferRequestedIterator{contract: _VRFCoordinatorTestV2.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2OwnershipTransferRequested) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseOwnershipTransferRequested(log types.Log) (*VRFCoordinatorTestV2OwnershipTransferRequested, error) { + event := new(VRFCoordinatorTestV2OwnershipTransferRequested) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2OwnershipTransferredIterator struct { + Event *VRFCoordinatorTestV2OwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2OwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2OwnershipTransferred) + 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(VRFCoordinatorTestV2OwnershipTransferred) + 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 *VRFCoordinatorTestV2OwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2OwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2OwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV2OwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2OwnershipTransferredIterator{contract: _VRFCoordinatorTestV2.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2OwnershipTransferred) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "OwnershipTransferred", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseOwnershipTransferred(log types.Log) (*VRFCoordinatorTestV2OwnershipTransferred, error) { + event := new(VRFCoordinatorTestV2OwnershipTransferred) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2ProvingKeyDeregisteredIterator struct { + Event *VRFCoordinatorTestV2ProvingKeyDeregistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2ProvingKeyDeregisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2ProvingKeyDeregistered) + 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(VRFCoordinatorTestV2ProvingKeyDeregistered) + 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 *VRFCoordinatorTestV2ProvingKeyDeregisteredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2ProvingKeyDeregisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2ProvingKeyDeregistered struct { + KeyHash [32]byte + Oracle common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterProvingKeyDeregistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorTestV2ProvingKeyDeregisteredIterator, error) { + + var oracleRule []interface{} + for _, oracleItem := range oracle { + oracleRule = append(oracleRule, oracleItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "ProvingKeyDeregistered", oracleRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2ProvingKeyDeregisteredIterator{contract: _VRFCoordinatorTestV2.contract, event: "ProvingKeyDeregistered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2ProvingKeyDeregistered, oracle []common.Address) (event.Subscription, error) { + + var oracleRule []interface{} + for _, oracleItem := range oracle { + oracleRule = append(oracleRule, oracleItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "ProvingKeyDeregistered", oracleRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2ProvingKeyDeregistered) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "ProvingKeyDeregistered", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseProvingKeyDeregistered(log types.Log) (*VRFCoordinatorTestV2ProvingKeyDeregistered, error) { + event := new(VRFCoordinatorTestV2ProvingKeyDeregistered) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "ProvingKeyDeregistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2ProvingKeyRegisteredIterator struct { + Event *VRFCoordinatorTestV2ProvingKeyRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2ProvingKeyRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2ProvingKeyRegistered) + 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(VRFCoordinatorTestV2ProvingKeyRegistered) + 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 *VRFCoordinatorTestV2ProvingKeyRegisteredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2ProvingKeyRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2ProvingKeyRegistered struct { + KeyHash [32]byte + Oracle common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorTestV2ProvingKeyRegisteredIterator, error) { + + var oracleRule []interface{} + for _, oracleItem := range oracle { + oracleRule = append(oracleRule, oracleItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "ProvingKeyRegistered", oracleRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2ProvingKeyRegisteredIterator{contract: _VRFCoordinatorTestV2.contract, event: "ProvingKeyRegistered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2ProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) { + + var oracleRule []interface{} + for _, oracleItem := range oracle { + oracleRule = append(oracleRule, oracleItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "ProvingKeyRegistered", oracleRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2ProvingKeyRegistered) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "ProvingKeyRegistered", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseProvingKeyRegistered(log types.Log) (*VRFCoordinatorTestV2ProvingKeyRegistered, error) { + event := new(VRFCoordinatorTestV2ProvingKeyRegistered) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "ProvingKeyRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2RandomWordsFulfilledIterator struct { + Event *VRFCoordinatorTestV2RandomWordsFulfilled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2RandomWordsFulfilledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2RandomWordsFulfilled) + 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(VRFCoordinatorTestV2RandomWordsFulfilled) + 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 *VRFCoordinatorTestV2RandomWordsFulfilledIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2RandomWordsFulfilledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2RandomWordsFulfilled struct { + RequestId *big.Int + OutputSeed *big.Int + Payment *big.Int + Success bool + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int) (*VRFCoordinatorTestV2RandomWordsFulfilledIterator, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "RandomWordsFulfilled", requestIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2RandomWordsFulfilledIterator{contract: _VRFCoordinatorTestV2.contract, event: "RandomWordsFulfilled", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2RandomWordsFulfilled, requestId []*big.Int) (event.Subscription, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "RandomWordsFulfilled", requestIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2RandomWordsFulfilled) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "RandomWordsFulfilled", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseRandomWordsFulfilled(log types.Log) (*VRFCoordinatorTestV2RandomWordsFulfilled, error) { + event := new(VRFCoordinatorTestV2RandomWordsFulfilled) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "RandomWordsFulfilled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2RandomWordsRequestedIterator struct { + Event *VRFCoordinatorTestV2RandomWordsRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2RandomWordsRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2RandomWordsRequested) + 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(VRFCoordinatorTestV2RandomWordsRequested) + 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 *VRFCoordinatorTestV2RandomWordsRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2RandomWordsRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2RandomWordsRequested struct { + KeyHash [32]byte + RequestId *big.Int + PreSeed *big.Int + SubId uint64 + MinimumRequestConfirmations uint16 + CallbackGasLimit uint32 + NumWords uint32 + Sender common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []uint64, sender []common.Address) (*VRFCoordinatorTestV2RandomWordsRequestedIterator, error) { + + var keyHashRule []interface{} + for _, keyHashItem := range keyHash { + keyHashRule = append(keyHashRule, keyHashItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "RandomWordsRequested", keyHashRule, subIdRule, senderRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2RandomWordsRequestedIterator{contract: _VRFCoordinatorTestV2.contract, event: "RandomWordsRequested", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2RandomWordsRequested, keyHash [][32]byte, subId []uint64, sender []common.Address) (event.Subscription, error) { + + var keyHashRule []interface{} + for _, keyHashItem := range keyHash { + keyHashRule = append(keyHashRule, keyHashItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "RandomWordsRequested", keyHashRule, subIdRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2RandomWordsRequested) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "RandomWordsRequested", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseRandomWordsRequested(log types.Log) (*VRFCoordinatorTestV2RandomWordsRequested, error) { + event := new(VRFCoordinatorTestV2RandomWordsRequested) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "RandomWordsRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionCanceledIterator struct { + Event *VRFCoordinatorTestV2SubscriptionCanceled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionCanceledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionCanceled) + 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(VRFCoordinatorTestV2SubscriptionCanceled) + 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 *VRFCoordinatorTestV2SubscriptionCanceledIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionCanceled struct { + SubId uint64 + To common.Address + Amount *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionCanceled(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionCanceledIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionCanceled", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionCanceledIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionCanceled", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionCanceled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionCanceled, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionCanceled", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionCanceled) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionCanceled", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionCanceled(log types.Log) (*VRFCoordinatorTestV2SubscriptionCanceled, error) { + event := new(VRFCoordinatorTestV2SubscriptionCanceled) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionConsumerAddedIterator struct { + Event *VRFCoordinatorTestV2SubscriptionConsumerAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionConsumerAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionConsumerAdded) + 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(VRFCoordinatorTestV2SubscriptionConsumerAdded) + 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 *VRFCoordinatorTestV2SubscriptionConsumerAddedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionConsumerAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionConsumerAdded struct { + SubId uint64 + Consumer common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionConsumerAdded(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionConsumerAddedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionConsumerAdded", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionConsumerAddedIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionConsumerAdded", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionConsumerAdded, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionConsumerAdded", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionConsumerAdded) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionConsumerAdded", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionConsumerAdded(log types.Log) (*VRFCoordinatorTestV2SubscriptionConsumerAdded, error) { + event := new(VRFCoordinatorTestV2SubscriptionConsumerAdded) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionConsumerAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator struct { + Event *VRFCoordinatorTestV2SubscriptionConsumerRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionConsumerRemoved) + 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(VRFCoordinatorTestV2SubscriptionConsumerRemoved) + 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 *VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionConsumerRemoved struct { + SubId uint64 + Consumer common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionConsumerRemoved(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionConsumerRemoved", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionConsumerRemoved", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionConsumerRemoved(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionConsumerRemoved, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionConsumerRemoved", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionConsumerRemoved) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionConsumerRemoved", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionConsumerRemoved(log types.Log) (*VRFCoordinatorTestV2SubscriptionConsumerRemoved, error) { + event := new(VRFCoordinatorTestV2SubscriptionConsumerRemoved) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionConsumerRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionCreatedIterator struct { + Event *VRFCoordinatorTestV2SubscriptionCreated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionCreatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionCreated) + 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(VRFCoordinatorTestV2SubscriptionCreated) + 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 *VRFCoordinatorTestV2SubscriptionCreatedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionCreatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionCreated struct { + SubId uint64 + Owner common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionCreated(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionCreatedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionCreated", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionCreatedIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionCreated", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionCreated(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionCreated, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionCreated", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionCreated) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionCreated", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionCreated(log types.Log) (*VRFCoordinatorTestV2SubscriptionCreated, error) { + event := new(VRFCoordinatorTestV2SubscriptionCreated) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionCreated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionFundedIterator struct { + Event *VRFCoordinatorTestV2SubscriptionFunded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionFundedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionFunded) + 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(VRFCoordinatorTestV2SubscriptionFunded) + 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 *VRFCoordinatorTestV2SubscriptionFundedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionFundedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionFunded struct { + SubId uint64 + OldBalance *big.Int + NewBalance *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionFunded(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionFundedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionFunded", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionFundedIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionFunded", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionFunded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionFunded, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionFunded", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionFunded) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionFunded", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionFunded(log types.Log) (*VRFCoordinatorTestV2SubscriptionFunded, error) { + event := new(VRFCoordinatorTestV2SubscriptionFunded) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionFunded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator struct { + Event *VRFCoordinatorTestV2SubscriptionOwnerTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionOwnerTransferRequested) + 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(VRFCoordinatorTestV2SubscriptionOwnerTransferRequested) + 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 *VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionOwnerTransferRequested struct { + SubId uint64 + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionOwnerTransferRequested(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionOwnerTransferRequested", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionOwnerTransferRequested", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionOwnerTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionOwnerTransferRequested, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionOwnerTransferRequested", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionOwnerTransferRequested) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionOwnerTransferRequested", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionOwnerTransferRequested(log types.Log) (*VRFCoordinatorTestV2SubscriptionOwnerTransferRequested, error) { + event := new(VRFCoordinatorTestV2SubscriptionOwnerTransferRequested) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionOwnerTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator struct { + Event *VRFCoordinatorTestV2SubscriptionOwnerTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV2SubscriptionOwnerTransferred) + 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(VRFCoordinatorTestV2SubscriptionOwnerTransferred) + 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 *VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV2SubscriptionOwnerTransferred struct { + SubId uint64 + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) FilterSubscriptionOwnerTransferred(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.FilterLogs(opts, "SubscriptionOwnerTransferred", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator{contract: _VRFCoordinatorTestV2.contract, event: "SubscriptionOwnerTransferred", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) WatchSubscriptionOwnerTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionOwnerTransferred, subId []uint64) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV2.contract.WatchLogs(opts, "SubscriptionOwnerTransferred", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV2SubscriptionOwnerTransferred) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionOwnerTransferred", 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 (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2Filterer) ParseSubscriptionOwnerTransferred(log types.Log) (*VRFCoordinatorTestV2SubscriptionOwnerTransferred, error) { + event := new(VRFCoordinatorTestV2SubscriptionOwnerTransferred) + if err := _VRFCoordinatorTestV2.contract.UnpackLog(event, "SubscriptionOwnerTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetConfig struct { + MinimumRequestConfirmations uint16 + MaxGasLimit uint32 + StalenessSeconds uint32 + GasAfterPaymentCalculation uint32 +} +type GetFeeConfig struct { + FulfillmentFlatFeeLinkPPMTier1 uint32 + FulfillmentFlatFeeLinkPPMTier2 uint32 + FulfillmentFlatFeeLinkPPMTier3 uint32 + FulfillmentFlatFeeLinkPPMTier4 uint32 + FulfillmentFlatFeeLinkPPMTier5 uint32 + ReqsForTier2 *big.Int + ReqsForTier3 *big.Int + ReqsForTier4 *big.Int + ReqsForTier5 *big.Int +} +type GetSubscription struct { + Balance *big.Int + ReqCount uint64 + Owner common.Address + Consumers []common.Address +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _VRFCoordinatorTestV2.abi.Events["ConfigSet"].ID: + return _VRFCoordinatorTestV2.ParseConfigSet(log) + case _VRFCoordinatorTestV2.abi.Events["FundsRecovered"].ID: + return _VRFCoordinatorTestV2.ParseFundsRecovered(log) + case _VRFCoordinatorTestV2.abi.Events["OwnershipTransferRequested"].ID: + return _VRFCoordinatorTestV2.ParseOwnershipTransferRequested(log) + case _VRFCoordinatorTestV2.abi.Events["OwnershipTransferred"].ID: + return _VRFCoordinatorTestV2.ParseOwnershipTransferred(log) + case _VRFCoordinatorTestV2.abi.Events["ProvingKeyDeregistered"].ID: + return _VRFCoordinatorTestV2.ParseProvingKeyDeregistered(log) + case _VRFCoordinatorTestV2.abi.Events["ProvingKeyRegistered"].ID: + return _VRFCoordinatorTestV2.ParseProvingKeyRegistered(log) + case _VRFCoordinatorTestV2.abi.Events["RandomWordsFulfilled"].ID: + return _VRFCoordinatorTestV2.ParseRandomWordsFulfilled(log) + case _VRFCoordinatorTestV2.abi.Events["RandomWordsRequested"].ID: + return _VRFCoordinatorTestV2.ParseRandomWordsRequested(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionCanceled"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionCanceled(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionConsumerAdded"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionConsumerAdded(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionConsumerRemoved"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionConsumerRemoved(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionCreated"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionCreated(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionFunded"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionFunded(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionOwnerTransferRequested"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionOwnerTransferRequested(log) + case _VRFCoordinatorTestV2.abi.Events["SubscriptionOwnerTransferred"].ID: + return _VRFCoordinatorTestV2.ParseSubscriptionOwnerTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (VRFCoordinatorTestV2ConfigSet) Topic() common.Hash { + return common.HexToHash("0xc21e3bd2e0b339d2848f0dd956947a88966c242c0c0c582a33137a5c1ceb5cb2") +} + +func (VRFCoordinatorTestV2FundsRecovered) Topic() common.Hash { + return common.HexToHash("0x59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600") +} + +func (VRFCoordinatorTestV2OwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (VRFCoordinatorTestV2OwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (VRFCoordinatorTestV2ProvingKeyDeregistered) Topic() common.Hash { + return common.HexToHash("0x72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d") +} + +func (VRFCoordinatorTestV2ProvingKeyRegistered) Topic() common.Hash { + return common.HexToHash("0xe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b8") +} + +func (VRFCoordinatorTestV2RandomWordsFulfilled) Topic() common.Hash { + return common.HexToHash("0x7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4") +} + +func (VRFCoordinatorTestV2RandomWordsRequested) Topic() common.Hash { + return common.HexToHash("0x63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772") +} + +func (VRFCoordinatorTestV2SubscriptionCanceled) Topic() common.Hash { + return common.HexToHash("0xe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815") +} + +func (VRFCoordinatorTestV2SubscriptionConsumerAdded) Topic() common.Hash { + return common.HexToHash("0x43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e0") +} + +func (VRFCoordinatorTestV2SubscriptionConsumerRemoved) Topic() common.Hash { + return common.HexToHash("0x182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b") +} + +func (VRFCoordinatorTestV2SubscriptionCreated) Topic() common.Hash { + return common.HexToHash("0x464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf") +} + +func (VRFCoordinatorTestV2SubscriptionFunded) Topic() common.Hash { + return common.HexToHash("0xd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f8") +} + +func (VRFCoordinatorTestV2SubscriptionOwnerTransferRequested) Topic() common.Hash { + return common.HexToHash("0x69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be") +} + +func (VRFCoordinatorTestV2SubscriptionOwnerTransferred) Topic() common.Hash { + return common.HexToHash("0x6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f0") +} + +func (_VRFCoordinatorTestV2 *VRFCoordinatorTestV2) Address() common.Address { + return _VRFCoordinatorTestV2.address +} + +type VRFCoordinatorTestV2Interface interface { + BLOCKHASHSTORE(opts *bind.CallOpts) (common.Address, error) + + LINK(opts *bind.CallOpts) (common.Address, error) + + LINKETHFEED(opts *bind.CallOpts) (common.Address, error) + + MAXCONSUMERS(opts *bind.CallOpts) (uint16, error) + + MAXNUMWORDS(opts *bind.CallOpts) (uint32, error) + + MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts) (uint16, error) + + GetCommitment(opts *bind.CallOpts, requestId *big.Int) ([32]byte, error) + + GetConfig(opts *bind.CallOpts) (GetConfig, + + error) + + GetCurrentSubId(opts *bind.CallOpts) (uint64, error) + + GetFallbackWeiPerUnitLink(opts *bind.CallOpts) (*big.Int, error) + + GetFeeConfig(opts *bind.CallOpts) (GetFeeConfig, + + error) + + GetFeeTier(opts *bind.CallOpts, reqCount uint64) (uint32, error) + + GetRequestConfig(opts *bind.CallOpts) (uint16, uint32, [][32]byte, error) + + GetSubscription(opts *bind.CallOpts, subId uint64) (GetSubscription, + + error) + + GetTotalBalance(opts *bind.CallOpts) (*big.Int, error) + + HashOfKey(opts *bind.CallOpts, publicKey [2]*big.Int) ([32]byte, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + PendingRequestExists(opts *bind.CallOpts, subId uint64) (bool, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AcceptSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) + + AddConsumer(opts *bind.TransactOpts, subId uint64, consumer common.Address) (*types.Transaction, error) + + CancelSubscription(opts *bind.TransactOpts, subId uint64, to common.Address) (*types.Transaction, error) + + CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) + + DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) + + FulfillRandomWords(opts *bind.TransactOpts, proof VRFProof, rc VRFCoordinatorTestV2RequestCommitment) (*types.Transaction, error) + + OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) + + OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) + + OwnerCancelSubscription(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) + + RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) + + RemoveConsumer(opts *bind.TransactOpts, subId uint64, consumer common.Address) (*types.Transaction, error) + + RequestRandomWords(opts *bind.TransactOpts, keyHash [32]byte, subId uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32) (*types.Transaction, error) + + RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64, newOwner common.Address) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorTestV2FeeConfig) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + FilterConfigSet(opts *bind.FilterOpts) (*VRFCoordinatorTestV2ConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2ConfigSet) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*VRFCoordinatorTestV2ConfigSet, error) + + FilterFundsRecovered(opts *bind.FilterOpts) (*VRFCoordinatorTestV2FundsRecoveredIterator, error) + + WatchFundsRecovered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2FundsRecovered) (event.Subscription, error) + + ParseFundsRecovered(log types.Log) (*VRFCoordinatorTestV2FundsRecovered, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV2OwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*VRFCoordinatorTestV2OwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV2OwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*VRFCoordinatorTestV2OwnershipTransferred, error) + + FilterProvingKeyDeregistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorTestV2ProvingKeyDeregisteredIterator, error) + + WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2ProvingKeyDeregistered, oracle []common.Address) (event.Subscription, error) + + ParseProvingKeyDeregistered(log types.Log) (*VRFCoordinatorTestV2ProvingKeyDeregistered, error) + + FilterProvingKeyRegistered(opts *bind.FilterOpts, oracle []common.Address) (*VRFCoordinatorTestV2ProvingKeyRegisteredIterator, error) + + WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2ProvingKeyRegistered, oracle []common.Address) (event.Subscription, error) + + ParseProvingKeyRegistered(log types.Log) (*VRFCoordinatorTestV2ProvingKeyRegistered, error) + + FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int) (*VRFCoordinatorTestV2RandomWordsFulfilledIterator, error) + + WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2RandomWordsFulfilled, requestId []*big.Int) (event.Subscription, error) + + ParseRandomWordsFulfilled(log types.Log) (*VRFCoordinatorTestV2RandomWordsFulfilled, error) + + FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []uint64, sender []common.Address) (*VRFCoordinatorTestV2RandomWordsRequestedIterator, error) + + WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2RandomWordsRequested, keyHash [][32]byte, subId []uint64, sender []common.Address) (event.Subscription, error) + + ParseRandomWordsRequested(log types.Log) (*VRFCoordinatorTestV2RandomWordsRequested, error) + + FilterSubscriptionCanceled(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionCanceledIterator, error) + + WatchSubscriptionCanceled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionCanceled, subId []uint64) (event.Subscription, error) + + ParseSubscriptionCanceled(log types.Log) (*VRFCoordinatorTestV2SubscriptionCanceled, error) + + FilterSubscriptionConsumerAdded(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionConsumerAddedIterator, error) + + WatchSubscriptionConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionConsumerAdded, subId []uint64) (event.Subscription, error) + + ParseSubscriptionConsumerAdded(log types.Log) (*VRFCoordinatorTestV2SubscriptionConsumerAdded, error) + + FilterSubscriptionConsumerRemoved(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionConsumerRemovedIterator, error) + + WatchSubscriptionConsumerRemoved(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionConsumerRemoved, subId []uint64) (event.Subscription, error) + + ParseSubscriptionConsumerRemoved(log types.Log) (*VRFCoordinatorTestV2SubscriptionConsumerRemoved, error) + + FilterSubscriptionCreated(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionCreatedIterator, error) + + WatchSubscriptionCreated(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionCreated, subId []uint64) (event.Subscription, error) + + ParseSubscriptionCreated(log types.Log) (*VRFCoordinatorTestV2SubscriptionCreated, error) + + FilterSubscriptionFunded(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionFundedIterator, error) + + WatchSubscriptionFunded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionFunded, subId []uint64) (event.Subscription, error) + + ParseSubscriptionFunded(log types.Log) (*VRFCoordinatorTestV2SubscriptionFunded, error) + + FilterSubscriptionOwnerTransferRequested(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionOwnerTransferRequestedIterator, error) + + WatchSubscriptionOwnerTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionOwnerTransferRequested, subId []uint64) (event.Subscription, error) + + ParseSubscriptionOwnerTransferRequested(log types.Log) (*VRFCoordinatorTestV2SubscriptionOwnerTransferRequested, error) + + FilterSubscriptionOwnerTransferred(opts *bind.FilterOpts, subId []uint64) (*VRFCoordinatorTestV2SubscriptionOwnerTransferredIterator, error) + + WatchSubscriptionOwnerTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV2SubscriptionOwnerTransferred, subId []uint64) (event.Subscription, error) + + ParseSubscriptionOwnerTransferred(log types.Log) (*VRFCoordinatorTestV2SubscriptionOwnerTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go b/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go index 76b5e267d3b..cbd84c86a63 100644 --- a/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go +++ b/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go @@ -31,8 +31,8 @@ var ( ) var VRFV2LoadTestWithMetricsMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vrfCoordinator\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"have\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"want\",\"type\":\"address\"}],\"name\":\"OnlyCoordinatorCanFulfill\",\"type\":\"error\"},{\"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\":\"COORDINATOR\",\"outputs\":[{\"internalType\":\"contractVRFCoordinatorV2Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"}],\"name\":\"getRequestStatus\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"_requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"_keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"_callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_numWords\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestCount\",\"type\":\"uint16\"}],\"name\":\"requestRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_averageFulfillmentInMillions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fastestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_lastRequestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requests\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_responseCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_slowestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60c0604052600060045560006005556103e760065534801561002057600080fd5b5060405161111138038061111183398101604081905261003f91610199565b6001600160601b0319606082901b1660805233806000816100a75760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03848116919091179091558116156100d7576100d7816100ef565b50505060601b6001600160601b03191660a0526101c9565b6001600160a01b0381163314156101485760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161009e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156101ab57600080fd5b81516001600160a01b03811681146101c257600080fd5b9392505050565b60805160601c60a05160601c610f0f6102026000396000818161014301526103da0152600081816102b7015261031f0152610f0f6000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c806379ba509711610097578063d826f88f11610066578063d826f88f1461023f578063d8a4676f1461025e578063dc1670db14610283578063f2fde38b1461028c57600080fd5b806379ba5097146101a55780638da5cb5b146101ad578063a168fa89146101cb578063b1e217491461023657600080fd5b80633b2bcbf1116100d35780633b2bcbf11461013e578063557d2e921461018a578063737144bc1461019357806374dba1241461019c57600080fd5b80631757f11c146100fa5780631fe543e314610116578063271095ef1461012b575b600080fd5b61010360055481565b6040519081526020015b60405180910390f35b610129610124366004610bcb565b61029f565b005b610129610139366004610cba565b61035f565b6101657f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161010d565b61010360035481565b61010360045481565b61010360065481565b610129610577565b60005473ffffffffffffffffffffffffffffffffffffffff16610165565b61020c6101d9366004610b99565b6009602052600090815260409020805460028201546003830154600484015460059094015460ff90931693919290919085565b6040805195151586526020860194909452928401919091526060830152608082015260a00161010d565b61010360075481565b6101296000600481905560058190556103e76006556003819055600255565b61027161026c366004610b99565b610674565b60405161010d96959493929190610d36565b61010360025481565b61012961029a366004610b5c565b610759565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610351576040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b61035b828261076d565b5050565b610367610894565b60005b8161ffff168161ffff16101561056e576040517f5d3b1d300000000000000000000000000000000000000000000000000000000081526004810186905267ffffffffffffffff8816602482015261ffff8716604482015263ffffffff8086166064830152841660848201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690635d3b1d309060a401602060405180830381600087803b15801561043357600080fd5b505af1158015610447573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046b9190610bb2565b60078190559050600061047c610917565b6040805160c08101825260008082528251818152602080820185528084019182524284860152606084018390526080840186905260a084018390528783526009815293909120825181547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016901515178155905180519495509193909261050a926001850192910190610ad1565b506040820151600282015560608201516003808301919091556080830151600483015560a090920151600590910155805490600061054783610e6b565b9091555050600091825260086020526040909120558061056681610e49565b91505061036a565b50505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146105f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610348565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000818152600960209081526040808320815160c081018352815460ff16151581526001820180548451818702810187019095528085526060958795869586958695869591949293858401939092908301828280156106f257602002820191906000526020600020905b8154815260200190600101908083116106de575b505050505081526020016002820154815260200160038201548152602001600482015481526020016005820154815250509050806000015181602001518260400151836060015184608001518560a001519650965096509650965096505091939550919395565b610761610894565b61076a816109b4565b50565b6000610777610917565b600084815260086020526040812054919250906107949083610e32565b905060006107a582620f4240610df5565b90506005548211156107b75760058290555b60065482106107c8576006546107ca565b815b6006556002546107da578061080d565b6002546107e8906001610da2565b816002546004546107f99190610df5565b6108039190610da2565b61080d9190610dba565b600455600085815260096020908152604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081178255865161085f939290910191870190610ad1565b506000858152600960205260408120426003820155600501849055600280549161088883610e6b565b91905055505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610915576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610348565b565b60004661092381610aaa565b156109ad57606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561096f57600080fd5b505afa158015610983573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a79190610bb2565b91505090565b4391505090565b73ffffffffffffffffffffffffffffffffffffffff8116331415610a34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610348565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600061a4b1821480610abe575062066eed82145b80610acb575062066eee82145b92915050565b828054828255906000526020600020908101928215610b0c579160200282015b82811115610b0c578251825591602001919060010190610af1565b50610b18929150610b1c565b5090565b5b80821115610b185760008155600101610b1d565b803561ffff81168114610b4357600080fd5b919050565b803563ffffffff81168114610b4357600080fd5b600060208284031215610b6e57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610b9257600080fd5b9392505050565b600060208284031215610bab57600080fd5b5035919050565b600060208284031215610bc457600080fd5b5051919050565b60008060408385031215610bde57600080fd5b8235915060208084013567ffffffffffffffff80821115610bfe57600080fd5b818601915086601f830112610c1257600080fd5b813581811115610c2457610c24610ed3565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f83011681018181108582111715610c6757610c67610ed3565b604052828152858101935084860182860187018b1015610c8657600080fd5b600095505b83861015610ca9578035855260019590950194938601938601610c8b565b508096505050505050509250929050565b60008060008060008060c08789031215610cd357600080fd5b863567ffffffffffffffff81168114610ceb57600080fd5b9550610cf960208801610b31565b945060408701359350610d0e60608801610b48565b9250610d1c60808801610b48565b9150610d2a60a08801610b31565b90509295509295509295565b600060c082018815158352602060c08185015281895180845260e086019150828b01935060005b81811015610d7957845183529383019391830191600101610d5d565b505060408501989098525050506060810193909352608083019190915260a09091015292915050565b60008219821115610db557610db5610ea4565b500190565b600082610df0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610e2d57610e2d610ea4565b500290565b600082821015610e4457610e44610ea4565b500390565b600061ffff80831681811415610e6157610e61610ea4565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610e9d57610e9d610ea4565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vrfCoordinator\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"have\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"want\",\"type\":\"address\"}],\"name\":\"OnlyCoordinatorCanFulfill\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCreatedFundedAndConsumerAdded\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"COORDINATOR\",\"outputs\":[{\"internalType\":\"contractVRFCoordinatorV2Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINKTOKEN\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"}],\"name\":\"getRequestStatus\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"_requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"_keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"_callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_numWords\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestCount\",\"type\":\"uint16\"}],\"name\":\"requestRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"_requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"_keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"_callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_numWords\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestCount\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"_subTopUpAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_link\",\"type\":\"address\"}],\"name\":\"requestRandomWordsWithForceFulfill\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_averageFulfillmentInMillions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fastestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_lastRequestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requests\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_responseCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_slowestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_subId\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_link\",\"type\":\"address\"}],\"name\":\"topUpSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60c0604052600060035560006004556103e760055534801561002057600080fd5b5060405161133038038061133083398101604081905261003f91610059565b60601b6001600160601b031916608081905260a052610089565b60006020828403121561006b57600080fd5b81516001600160a01b038116811461008257600080fd5b9392505050565b60805160601c60a05160601c61124b6100e560003960008181610156015281816103e00152818161048c0152818161056b0152818161067d0152818161072a0152610a250152600081816102c4015261032c015261124b6000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c806362cce24411610097578063b1e2174911610066578063b1e2174914610256578063d826f88f1461025f578063d8a4676f1461027e578063dc1670db146102a357600080fd5b806362cce244146101c6578063737144bc146101d957806374dba124146101e2578063a168fa89146101eb57600080fd5b80632be555da116100d35780632be555da1461013e5780633b2bcbf11461015157806355380dfb1461019d578063557d2e92146101bd57600080fd5b80631757f11c146100fa5780631fe543e314610116578063271095ef1461012b575b600080fd5b61010360045481565b6040519081526020015b60405180910390f35b610129610124366004610e08565b6102ac565b005b610129610139366004610f14565b61036b565b61012961014c366004610f83565b610381565b6101787f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161010d565b6000546101789073ffffffffffffffffffffffffffffffffffffffff1681565b61010360025481565b6101296101d4366004610d5e565b610488565b61010360035481565b61010360055481565b61022c6101f9366004610dd6565b6008602052600090815260409020805460028201546003830154600484015460059094015460ff90931693919290919085565b6040805195151586526020860194909452928401919091526060830152608082015260a00161010d565b61010360065481565b6101296000600381905560048190556103e76005556002819055600155565b61029161028c366004610dd6565b6107a7565b60405161010d96959493929190611059565b61010360015481565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461035d576040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602482015260440160405180910390fd5b610367828261088c565b5050565b6103798686868686866109b2565b505050505050565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040805167ffffffffffffffff86166020820152634000aea0917f0000000000000000000000000000000000000000000000000000000000000000918691016040516020818303038152906040526040518463ffffffff1660e01b815260040161043093929190610fc1565b602060405180830381600087803b15801561044a57600080fd5b505af115801561045e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104829190610d35565b50505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a21a23e46040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156104f257600080fd5b505af1158015610506573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061052a9190610ef7565b6040517f7341c10c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201523060248201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690637341c10c90604401600060405180830381600087803b1580156105c457600080fd5b505af11580156105d8573d6000803e3d6000fd5b505050506105e7818484610381565b6040805167ffffffffffffffff831681523060208201529081018490527f56c142509574e8340ca0190b029c74464b84037d2876278ea0ade3ffb1f0042c9060600160405180910390a161063f8189898989896109b2565b6040517f9f87fad700000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201523060248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639f87fad790604401600060405180830381600087803b1580156106d657600080fd5b505af11580156106ea573d6000803e3d6000fd5b50506040517fd7ae1d3000000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16925063d7ae1d309150604401600060405180830381600087803b15801561078557600080fd5b505af1158015610799573d6000803e3d6000fd5b505050505050505050505050565b6000818152600860209081526040808320815160c081018352815460ff161515815260018201805484518187028101870190955280855260609587958695869586958695919492938584019390929083018282801561082557602002820191906000526020600020905b815481526020019060010190808311610811575b505050505081526020016002820154815260200160038201548152602001600482015481526020016005820154815250509050806000015181602001518260400151836060015184608001518560a001519650965096509650965096505091939550919395565b6000610896610bc2565b600084815260076020526040812054919250906108b39083611155565b905060006108c482620f4240611118565b90506004548211156108d65760048290555b60055482106108e7576005546108e9565b815b6005556001546108f9578061092b565b60018054610906916110c5565b816001546003546109179190611118565b61092191906110c5565b61092b91906110dd565b600355600085815260086020908152604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081178255865161097d939290910191870190610c86565b50600085815260086020526040812042600382015560050184905560018054916109a68361118e565b91905055505050505050565b60005b8161ffff168161ffff161015610bb9576040517f5d3b1d300000000000000000000000000000000000000000000000000000000081526004810186905267ffffffffffffffff8816602482015261ffff8716604482015263ffffffff8086166064830152841660848201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690635d3b1d309060a401602060405180830381600087803b158015610a7e57600080fd5b505af1158015610a92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab69190610def565b600681905590506000610ac7610bc2565b6040805160c08101825260008082528251818152602080820185528084019182524284860152606084018390526080840186905260a084018390528783526008815293909120825181547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169015151781559051805194955091939092610b55926001850192910190610c86565b506040820151600280830191909155606083015160038301556080830151600483015560a0909201516005909101558054906000610b928361118e565b90915550506000918252600760205260409091205580610bb18161116c565b9150506109b5565b50505050505050565b600046610bce81610c5f565b15610c5857606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c1a57600080fd5b505afa158015610c2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c529190610def565b91505090565b4391505090565b600061a4b1821480610c73575062066eed82145b80610c80575062066eee82145b92915050565b828054828255906000526020600020908101928215610cc1579160200282015b82811115610cc1578251825591602001919060010190610ca6565b50610ccd929150610cd1565b5090565b5b80821115610ccd5760008155600101610cd2565b803573ffffffffffffffffffffffffffffffffffffffff81168114610d0a57600080fd5b919050565b803561ffff81168114610d0a57600080fd5b803563ffffffff81168114610d0a57600080fd5b600060208284031215610d4757600080fd5b81518015158114610d5757600080fd5b9392505050565b600080600080600080600060e0888a031215610d7957600080fd5b610d8288610d0f565b965060208801359550610d9760408901610d21565b9450610da560608901610d21565b9350610db360808901610d0f565b925060a08801359150610dc860c08901610ce6565b905092959891949750929550565b600060208284031215610de857600080fd5b5035919050565b600060208284031215610e0157600080fd5b5051919050565b60008060408385031215610e1b57600080fd5b8235915060208084013567ffffffffffffffff80821115610e3b57600080fd5b818601915086601f830112610e4f57600080fd5b813581811115610e6157610e616111f6565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f83011681018181108582111715610ea457610ea46111f6565b604052828152858101935084860182860187018b1015610ec357600080fd5b600095505b83861015610ee6578035855260019590950194938601938601610ec8565b508096505050505050509250929050565b600060208284031215610f0957600080fd5b8151610d5781611225565b60008060008060008060c08789031215610f2d57600080fd5b8635610f3881611225565b9550610f4660208801610d0f565b945060408701359350610f5b60608801610d21565b9250610f6960808801610d21565b9150610f7760a08801610d0f565b90509295509295509295565b600080600060608486031215610f9857600080fd5b8335610fa381611225565b925060208401359150610fb860408501610ce6565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff8416815260006020848184015260606040840152835180606085015260005b8181101561101157858101830151858201608001528201610ff5565b81811115611023576000608083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160800195945050505050565b600060c082018815158352602060c08185015281895180845260e086019150828b01935060005b8181101561109c57845183529383019391830191600101611080565b505060408501989098525050506060810193909352608083019190915260a09091015292915050565b600082198211156110d8576110d86111c7565b500190565b600082611113577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611150576111506111c7565b500290565b600082821015611167576111676111c7565b500390565b600061ffff80831681811415611184576111846111c7565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156111c0576111c06111c7565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8116811461123b57600080fd5b5056fea164736f6c6343000806000a", } var VRFV2LoadTestWithMetricsABI = VRFV2LoadTestWithMetricsMetaData.ABI @@ -193,6 +193,28 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCallerSession) COORDINA return _VRFV2LoadTestWithMetrics.Contract.COORDINATOR(&_VRFV2LoadTestWithMetrics.CallOpts) } +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCaller) LINKTOKEN(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFV2LoadTestWithMetrics.contract.Call(opts, &out, "LINKTOKEN") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) LINKTOKEN() (common.Address, error) { + return _VRFV2LoadTestWithMetrics.Contract.LINKTOKEN(&_VRFV2LoadTestWithMetrics.CallOpts) +} + +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCallerSession) LINKTOKEN() (common.Address, error) { + return _VRFV2LoadTestWithMetrics.Contract.LINKTOKEN(&_VRFV2LoadTestWithMetrics.CallOpts) +} + func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCaller) GetRequestStatus(opts *bind.CallOpts, _requestId *big.Int) (GetRequestStatus, error) { @@ -227,28 +249,6 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCallerSession) GetReque return _VRFV2LoadTestWithMetrics.Contract.GetRequestStatus(&_VRFV2LoadTestWithMetrics.CallOpts, _requestId) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCaller) Owner(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _VRFV2LoadTestWithMetrics.contract.Call(opts, &out, "owner") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) Owner() (common.Address, error) { - return _VRFV2LoadTestWithMetrics.Contract.Owner(&_VRFV2LoadTestWithMetrics.CallOpts) -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCallerSession) Owner() (common.Address, error) { - return _VRFV2LoadTestWithMetrics.Contract.Owner(&_VRFV2LoadTestWithMetrics.CallOpts) -} - func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCaller) SAverageFulfillmentInMillions(opts *bind.CallOpts) (*big.Int, error) { var out []interface{} err := _VRFV2LoadTestWithMetrics.contract.Call(opts, &out, "s_averageFulfillmentInMillions") @@ -414,18 +414,6 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsCallerSession) SSlowest return _VRFV2LoadTestWithMetrics.Contract.SSlowestFulfillment(&_VRFV2LoadTestWithMetrics.CallOpts) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "acceptOwnership") -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) AcceptOwnership() (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.Contract.AcceptOwnership(&_VRFV2LoadTestWithMetrics.TransactOpts) -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) AcceptOwnership() (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.Contract.AcceptOwnership(&_VRFV2LoadTestWithMetrics.TransactOpts) -} - func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) RawFulfillRandomWords(opts *bind.TransactOpts, requestId *big.Int, randomWords []*big.Int) (*types.Transaction, error) { return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "rawFulfillRandomWords", requestId, randomWords) } @@ -450,168 +438,44 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) Requ return _VRFV2LoadTestWithMetrics.Contract.RequestRandomWords(&_VRFV2LoadTestWithMetrics.TransactOpts, _subId, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) Reset(opts *bind.TransactOpts) (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "reset") +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) RequestRandomWordsWithForceFulfill(opts *bind.TransactOpts, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int, _link common.Address) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "requestRandomWordsWithForceFulfill", _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount, _subTopUpAmount, _link) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) Reset() (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.Contract.Reset(&_VRFV2LoadTestWithMetrics.TransactOpts) -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) Reset() (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.Contract.Reset(&_VRFV2LoadTestWithMetrics.TransactOpts) -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "transferOwnership", to) -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) TransferOwnership(to common.Address) (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.Contract.TransferOwnership(&_VRFV2LoadTestWithMetrics.TransactOpts, to) -} - -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { - return _VRFV2LoadTestWithMetrics.Contract.TransferOwnership(&_VRFV2LoadTestWithMetrics.TransactOpts, to) -} - -type VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator struct { - Event *VRFV2LoadTestWithMetricsOwnershipTransferRequested - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) RequestRandomWordsWithForceFulfill(_requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int, _link common.Address) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.Contract.RequestRandomWordsWithForceFulfill(&_VRFV2LoadTestWithMetrics.TransactOpts, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount, _subTopUpAmount, _link) } -func (it *VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(VRFV2LoadTestWithMetricsOwnershipTransferRequested) - 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(VRFV2LoadTestWithMetricsOwnershipTransferRequested) - 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 (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) RequestRandomWordsWithForceFulfill(_requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int, _link common.Address) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.Contract.RequestRandomWordsWithForceFulfill(&_VRFV2LoadTestWithMetrics.TransactOpts, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount, _subTopUpAmount, _link) } -func (it *VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator) Error() error { - return it.fail +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) Reset(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "reset") } -func (it *VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator) Close() error { - it.sub.Unsubscribe() - return nil +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) Reset() (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.Contract.Reset(&_VRFV2LoadTestWithMetrics.TransactOpts) } -type VRFV2LoadTestWithMetricsOwnershipTransferRequested struct { - From common.Address - To common.Address - Raw types.Log +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) Reset() (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.Contract.Reset(&_VRFV2LoadTestWithMetrics.TransactOpts) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator, error) { - - var fromRule []interface{} - for _, fromItem := range from { - fromRule = append(fromRule, fromItem) - } - var toRule []interface{} - for _, toItem := range to { - toRule = append(toRule, toItem) - } - - logs, sub, err := _VRFV2LoadTestWithMetrics.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) - if err != nil { - return nil, err - } - return &VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator{contract: _VRFV2LoadTestWithMetrics.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactor) TopUpSubscription(opts *bind.TransactOpts, _subId uint64, _amount *big.Int, _link common.Address) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.contract.Transact(opts, "topUpSubscription", _subId, _amount, _link) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFV2LoadTestWithMetricsOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { - - var fromRule []interface{} - for _, fromItem := range from { - fromRule = append(fromRule, fromItem) - } - var toRule []interface{} - for _, toItem := range to { - toRule = append(toRule, toItem) - } - - logs, sub, err := _VRFV2LoadTestWithMetrics.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(VRFV2LoadTestWithMetricsOwnershipTransferRequested) - if err := _VRFV2LoadTestWithMetrics.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsSession) TopUpSubscription(_subId uint64, _amount *big.Int, _link common.Address) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.Contract.TopUpSubscription(&_VRFV2LoadTestWithMetrics.TransactOpts, _subId, _amount, _link) } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) ParseOwnershipTransferRequested(log types.Log) (*VRFV2LoadTestWithMetricsOwnershipTransferRequested, error) { - event := new(VRFV2LoadTestWithMetricsOwnershipTransferRequested) - if err := _VRFV2LoadTestWithMetrics.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsTransactorSession) TopUpSubscription(_subId uint64, _amount *big.Int, _link common.Address) (*types.Transaction, error) { + return _VRFV2LoadTestWithMetrics.Contract.TopUpSubscription(&_VRFV2LoadTestWithMetrics.TransactOpts, _subId, _amount, _link) } -type VRFV2LoadTestWithMetricsOwnershipTransferredIterator struct { - Event *VRFV2LoadTestWithMetricsOwnershipTransferred +type VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator struct { + Event *VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded contract *bind.BoundContract event string @@ -622,7 +486,7 @@ type VRFV2LoadTestWithMetricsOwnershipTransferredIterator struct { fail error } -func (it *VRFV2LoadTestWithMetricsOwnershipTransferredIterator) Next() bool { +func (it *VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator) Next() bool { if it.fail != nil { return false @@ -631,7 +495,7 @@ func (it *VRFV2LoadTestWithMetricsOwnershipTransferredIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(VRFV2LoadTestWithMetricsOwnershipTransferred) + it.Event = new(VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -646,7 +510,7 @@ func (it *VRFV2LoadTestWithMetricsOwnershipTransferredIterator) Next() bool { select { case log := <-it.logs: - it.Event = new(VRFV2LoadTestWithMetricsOwnershipTransferred) + it.Event = new(VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -661,51 +525,34 @@ func (it *VRFV2LoadTestWithMetricsOwnershipTransferredIterator) Next() bool { } } -func (it *VRFV2LoadTestWithMetricsOwnershipTransferredIterator) Error() error { +func (it *VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator) Error() error { return it.fail } -func (it *VRFV2LoadTestWithMetricsOwnershipTransferredIterator) Close() error { +func (it *VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator) Close() error { it.sub.Unsubscribe() return nil } -type VRFV2LoadTestWithMetricsOwnershipTransferred struct { - From common.Address - To common.Address - Raw types.Log +type VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded struct { + SubId uint64 + Consumer common.Address + Amount *big.Int + Raw types.Log } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2LoadTestWithMetricsOwnershipTransferredIterator, error) { +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) FilterSubscriptionCreatedFundedAndConsumerAdded(opts *bind.FilterOpts) (*VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator, error) { - var fromRule []interface{} - for _, fromItem := range from { - fromRule = append(fromRule, fromItem) - } - var toRule []interface{} - for _, toItem := range to { - toRule = append(toRule, toItem) - } - - logs, sub, err := _VRFV2LoadTestWithMetrics.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + logs, sub, err := _VRFV2LoadTestWithMetrics.contract.FilterLogs(opts, "SubscriptionCreatedFundedAndConsumerAdded") if err != nil { return nil, err } - return &VRFV2LoadTestWithMetricsOwnershipTransferredIterator{contract: _VRFV2LoadTestWithMetrics.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil + return &VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator{contract: _VRFV2LoadTestWithMetrics.contract, event: "SubscriptionCreatedFundedAndConsumerAdded", logs: logs, sub: sub}, nil } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFV2LoadTestWithMetricsOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { - - var fromRule []interface{} - for _, fromItem := range from { - fromRule = append(fromRule, fromItem) - } - var toRule []interface{} - for _, toItem := range to { - toRule = append(toRule, toItem) - } +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) WatchSubscriptionCreatedFundedAndConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) (event.Subscription, error) { - logs, sub, err := _VRFV2LoadTestWithMetrics.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + logs, sub, err := _VRFV2LoadTestWithMetrics.contract.WatchLogs(opts, "SubscriptionCreatedFundedAndConsumerAdded") if err != nil { return nil, err } @@ -715,8 +562,8 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) WatchOwnershi select { case log := <-logs: - event := new(VRFV2LoadTestWithMetricsOwnershipTransferred) - if err := _VRFV2LoadTestWithMetrics.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + event := new(VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) + if err := _VRFV2LoadTestWithMetrics.contract.UnpackLog(event, "SubscriptionCreatedFundedAndConsumerAdded", log); err != nil { return err } event.Raw = log @@ -737,9 +584,9 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) WatchOwnershi }), nil } -func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) ParseOwnershipTransferred(log types.Log) (*VRFV2LoadTestWithMetricsOwnershipTransferred, error) { - event := new(VRFV2LoadTestWithMetricsOwnershipTransferred) - if err := _VRFV2LoadTestWithMetrics.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { +func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetricsFilterer) ParseSubscriptionCreatedFundedAndConsumerAdded(log types.Log) (*VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded, error) { + event := new(VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) + if err := _VRFV2LoadTestWithMetrics.contract.UnpackLog(event, "SubscriptionCreatedFundedAndConsumerAdded", log); err != nil { return nil, err } event.Raw = log @@ -764,22 +611,16 @@ type SRequests struct { func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetrics) ParseLog(log types.Log) (generated.AbigenLog, error) { switch log.Topics[0] { - case _VRFV2LoadTestWithMetrics.abi.Events["OwnershipTransferRequested"].ID: - return _VRFV2LoadTestWithMetrics.ParseOwnershipTransferRequested(log) - case _VRFV2LoadTestWithMetrics.abi.Events["OwnershipTransferred"].ID: - return _VRFV2LoadTestWithMetrics.ParseOwnershipTransferred(log) + case _VRFV2LoadTestWithMetrics.abi.Events["SubscriptionCreatedFundedAndConsumerAdded"].ID: + return _VRFV2LoadTestWithMetrics.ParseSubscriptionCreatedFundedAndConsumerAdded(log) default: return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) } } -func (VRFV2LoadTestWithMetricsOwnershipTransferRequested) Topic() common.Hash { - return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") -} - -func (VRFV2LoadTestWithMetricsOwnershipTransferred) Topic() common.Hash { - return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +func (VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) Topic() common.Hash { + return common.HexToHash("0x56c142509574e8340ca0190b029c74464b84037d2876278ea0ade3ffb1f0042c") } func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetrics) Address() common.Address { @@ -789,12 +630,12 @@ func (_VRFV2LoadTestWithMetrics *VRFV2LoadTestWithMetrics) Address() common.Addr type VRFV2LoadTestWithMetricsInterface interface { COORDINATOR(opts *bind.CallOpts) (common.Address, error) + LINKTOKEN(opts *bind.CallOpts) (common.Address, error) + GetRequestStatus(opts *bind.CallOpts, _requestId *big.Int) (GetRequestStatus, error) - Owner(opts *bind.CallOpts) (common.Address, error) - SAverageFulfillmentInMillions(opts *bind.CallOpts) (*big.Int, error) SFastestFulfillment(opts *bind.CallOpts) (*big.Int, error) @@ -811,27 +652,21 @@ type VRFV2LoadTestWithMetricsInterface interface { SSlowestFulfillment(opts *bind.CallOpts) (*big.Int, error) - AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - RawFulfillRandomWords(opts *bind.TransactOpts, requestId *big.Int, randomWords []*big.Int) (*types.Transaction, error) RequestRandomWords(opts *bind.TransactOpts, _subId uint64, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16) (*types.Transaction, error) - Reset(opts *bind.TransactOpts) (*types.Transaction, error) - - TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + RequestRandomWordsWithForceFulfill(opts *bind.TransactOpts, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int, _link common.Address) (*types.Transaction, error) - FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2LoadTestWithMetricsOwnershipTransferRequestedIterator, error) - - WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFV2LoadTestWithMetricsOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + Reset(opts *bind.TransactOpts) (*types.Transaction, error) - ParseOwnershipTransferRequested(log types.Log) (*VRFV2LoadTestWithMetricsOwnershipTransferRequested, error) + TopUpSubscription(opts *bind.TransactOpts, _subId uint64, _amount *big.Int, _link common.Address) (*types.Transaction, error) - FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2LoadTestWithMetricsOwnershipTransferredIterator, error) + FilterSubscriptionCreatedFundedAndConsumerAdded(opts *bind.FilterOpts) (*VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAddedIterator, error) - WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFV2LoadTestWithMetricsOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + WatchSubscriptionCreatedFundedAndConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded) (event.Subscription, error) - ParseOwnershipTransferred(log types.Log) (*VRFV2LoadTestWithMetricsOwnershipTransferred, error) + ParseSubscriptionCreatedFundedAndConsumerAdded(log types.Log) (*VRFV2LoadTestWithMetricsSubscriptionCreatedFundedAndConsumerAdded, error) ParseLog(log types.Log) (generated.AbigenLog, error) diff --git a/core/gethwrappers/generated/vrf_mock_ethlink_aggregator/vrf_mock_ethlink_aggregator.go b/core/gethwrappers/generated/vrf_mock_ethlink_aggregator/vrf_mock_ethlink_aggregator.go new file mode 100644 index 00000000000..d6736acfb9b --- /dev/null +++ b/core/gethwrappers/generated/vrf_mock_ethlink_aggregator/vrf_mock_ethlink_aggregator.go @@ -0,0 +1,377 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package vrf_mock_ethlink_aggregator + +import ( + "errors" + "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" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +var VRFMockETHLINKAggregatorMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"_answer\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"answer\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"description\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint80\",\"name\":\"_roundId\",\"type\":\"uint80\"}],\"name\":\"getRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"ans\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"ans\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_blockTimestampDeduction\",\"type\":\"uint256\"}],\"name\":\"setBlockTimestampDeduction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6080604052600060015534801561001557600080fd5b506040516103383803806103388339810160408190526100349161003c565b600055610055565b60006020828403121561004e57600080fd5b5051919050565b6102d4806100646000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806385bb7d691161005b57806385bb7d69146100e65780639a6fc8f5146100ef578063f0ad37df14610139578063feaf968c1461014e57600080fd5b8063313ce5671461008257806354fd4d50146100965780637284e416146100a7575b600080fd5b604051601281526020015b60405180910390f35b60015b60405190815260200161008d565b604080518082018252601881527f5652464d6f636b4554484c494e4b41676772656761746f7200000000000000006020820152905161008d9190610216565b61009960005481565b6101026100fd3660046101e3565b610156565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a00161008d565b61014c6101473660046101ca565b600155565b005b610102610186565b6000806000806000600160005461016b6101b5565b6101736101b5565b9299919850965090945060019350915050565b6000806000806000600160005461019b6101b5565b6101a36101b5565b92989197509550909350600192509050565b6000600154426101c59190610289565b905090565b6000602082840312156101dc57600080fd5b5035919050565b6000602082840312156101f557600080fd5b813569ffffffffffffffffffff8116811461020f57600080fd5b9392505050565b600060208083528351808285015260005b8181101561024357858101830151858201604001528201610227565b81811115610255576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6000828210156102c2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b50039056fea164736f6c6343000806000a", +} + +var VRFMockETHLINKAggregatorABI = VRFMockETHLINKAggregatorMetaData.ABI + +var VRFMockETHLINKAggregatorBin = VRFMockETHLINKAggregatorMetaData.Bin + +func DeployVRFMockETHLINKAggregator(auth *bind.TransactOpts, backend bind.ContractBackend, _answer *big.Int) (common.Address, *types.Transaction, *VRFMockETHLINKAggregator, error) { + parsed, err := VRFMockETHLINKAggregatorMetaData.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(VRFMockETHLINKAggregatorBin), backend, _answer) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &VRFMockETHLINKAggregator{address: address, abi: *parsed, VRFMockETHLINKAggregatorCaller: VRFMockETHLINKAggregatorCaller{contract: contract}, VRFMockETHLINKAggregatorTransactor: VRFMockETHLINKAggregatorTransactor{contract: contract}, VRFMockETHLINKAggregatorFilterer: VRFMockETHLINKAggregatorFilterer{contract: contract}}, nil +} + +type VRFMockETHLINKAggregator struct { + address common.Address + abi abi.ABI + VRFMockETHLINKAggregatorCaller + VRFMockETHLINKAggregatorTransactor + VRFMockETHLINKAggregatorFilterer +} + +type VRFMockETHLINKAggregatorCaller struct { + contract *bind.BoundContract +} + +type VRFMockETHLINKAggregatorTransactor struct { + contract *bind.BoundContract +} + +type VRFMockETHLINKAggregatorFilterer struct { + contract *bind.BoundContract +} + +type VRFMockETHLINKAggregatorSession struct { + Contract *VRFMockETHLINKAggregator + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type VRFMockETHLINKAggregatorCallerSession struct { + Contract *VRFMockETHLINKAggregatorCaller + CallOpts bind.CallOpts +} + +type VRFMockETHLINKAggregatorTransactorSession struct { + Contract *VRFMockETHLINKAggregatorTransactor + TransactOpts bind.TransactOpts +} + +type VRFMockETHLINKAggregatorRaw struct { + Contract *VRFMockETHLINKAggregator +} + +type VRFMockETHLINKAggregatorCallerRaw struct { + Contract *VRFMockETHLINKAggregatorCaller +} + +type VRFMockETHLINKAggregatorTransactorRaw struct { + Contract *VRFMockETHLINKAggregatorTransactor +} + +func NewVRFMockETHLINKAggregator(address common.Address, backend bind.ContractBackend) (*VRFMockETHLINKAggregator, error) { + abi, err := abi.JSON(strings.NewReader(VRFMockETHLINKAggregatorABI)) + if err != nil { + return nil, err + } + contract, err := bindVRFMockETHLINKAggregator(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &VRFMockETHLINKAggregator{address: address, abi: abi, VRFMockETHLINKAggregatorCaller: VRFMockETHLINKAggregatorCaller{contract: contract}, VRFMockETHLINKAggregatorTransactor: VRFMockETHLINKAggregatorTransactor{contract: contract}, VRFMockETHLINKAggregatorFilterer: VRFMockETHLINKAggregatorFilterer{contract: contract}}, nil +} + +func NewVRFMockETHLINKAggregatorCaller(address common.Address, caller bind.ContractCaller) (*VRFMockETHLINKAggregatorCaller, error) { + contract, err := bindVRFMockETHLINKAggregator(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &VRFMockETHLINKAggregatorCaller{contract: contract}, nil +} + +func NewVRFMockETHLINKAggregatorTransactor(address common.Address, transactor bind.ContractTransactor) (*VRFMockETHLINKAggregatorTransactor, error) { + contract, err := bindVRFMockETHLINKAggregator(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &VRFMockETHLINKAggregatorTransactor{contract: contract}, nil +} + +func NewVRFMockETHLINKAggregatorFilterer(address common.Address, filterer bind.ContractFilterer) (*VRFMockETHLINKAggregatorFilterer, error) { + contract, err := bindVRFMockETHLINKAggregator(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &VRFMockETHLINKAggregatorFilterer{contract: contract}, nil +} + +func bindVRFMockETHLINKAggregator(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := VRFMockETHLINKAggregatorMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFMockETHLINKAggregator.Contract.VRFMockETHLINKAggregatorCaller.contract.Call(opts, result, method, params...) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.Contract.VRFMockETHLINKAggregatorTransactor.contract.Transfer(opts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.Contract.VRFMockETHLINKAggregatorTransactor.contract.Transact(opts, method, params...) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFMockETHLINKAggregator.Contract.contract.Call(opts, result, method, params...) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.Contract.contract.Transfer(opts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.Contract.contract.Transact(opts, method, params...) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCaller) Answer(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFMockETHLINKAggregator.contract.Call(opts, &out, "answer") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) Answer() (*big.Int, error) { + return _VRFMockETHLINKAggregator.Contract.Answer(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerSession) Answer() (*big.Int, error) { + return _VRFMockETHLINKAggregator.Contract.Answer(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCaller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _VRFMockETHLINKAggregator.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) Decimals() (uint8, error) { + return _VRFMockETHLINKAggregator.Contract.Decimals(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerSession) Decimals() (uint8, error) { + return _VRFMockETHLINKAggregator.Contract.Decimals(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCaller) Description(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _VRFMockETHLINKAggregator.contract.Call(opts, &out, "description") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) Description() (string, error) { + return _VRFMockETHLINKAggregator.Contract.Description(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerSession) Description() (string, error) { + return _VRFMockETHLINKAggregator.Contract.Description(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCaller) GetRoundData(opts *bind.CallOpts, _roundId *big.Int) (GetRoundData, + + error) { + var out []interface{} + err := _VRFMockETHLINKAggregator.contract.Call(opts, &out, "getRoundData", _roundId) + + outstruct := new(GetRoundData) + if err != nil { + return *outstruct, err + } + + outstruct.RoundId = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.Ans = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.StartedAt = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.UpdatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.AnsweredInRound = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) GetRoundData(_roundId *big.Int) (GetRoundData, + + error) { + return _VRFMockETHLINKAggregator.Contract.GetRoundData(&_VRFMockETHLINKAggregator.CallOpts, _roundId) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerSession) GetRoundData(_roundId *big.Int) (GetRoundData, + + error) { + return _VRFMockETHLINKAggregator.Contract.GetRoundData(&_VRFMockETHLINKAggregator.CallOpts, _roundId) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCaller) LatestRoundData(opts *bind.CallOpts) (LatestRoundData, + + error) { + var out []interface{} + err := _VRFMockETHLINKAggregator.contract.Call(opts, &out, "latestRoundData") + + outstruct := new(LatestRoundData) + if err != nil { + return *outstruct, err + } + + outstruct.RoundId = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.Ans = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.StartedAt = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.UpdatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.AnsweredInRound = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) LatestRoundData() (LatestRoundData, + + error) { + return _VRFMockETHLINKAggregator.Contract.LatestRoundData(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerSession) LatestRoundData() (LatestRoundData, + + error) { + return _VRFMockETHLINKAggregator.Contract.LatestRoundData(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCaller) Version(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFMockETHLINKAggregator.contract.Call(opts, &out, "version") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) Version() (*big.Int, error) { + return _VRFMockETHLINKAggregator.Contract.Version(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorCallerSession) Version() (*big.Int, error) { + return _VRFMockETHLINKAggregator.Contract.Version(&_VRFMockETHLINKAggregator.CallOpts) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorTransactor) SetBlockTimestampDeduction(opts *bind.TransactOpts, _blockTimestampDeduction *big.Int) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.contract.Transact(opts, "setBlockTimestampDeduction", _blockTimestampDeduction) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorSession) SetBlockTimestampDeduction(_blockTimestampDeduction *big.Int) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.Contract.SetBlockTimestampDeduction(&_VRFMockETHLINKAggregator.TransactOpts, _blockTimestampDeduction) +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregatorTransactorSession) SetBlockTimestampDeduction(_blockTimestampDeduction *big.Int) (*types.Transaction, error) { + return _VRFMockETHLINKAggregator.Contract.SetBlockTimestampDeduction(&_VRFMockETHLINKAggregator.TransactOpts, _blockTimestampDeduction) +} + +type GetRoundData struct { + RoundId *big.Int + Ans *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +} +type LatestRoundData struct { + RoundId *big.Int + Ans *big.Int + StartedAt *big.Int + UpdatedAt *big.Int + AnsweredInRound *big.Int +} + +func (_VRFMockETHLINKAggregator *VRFMockETHLINKAggregator) Address() common.Address { + return _VRFMockETHLINKAggregator.address +} + +type VRFMockETHLINKAggregatorInterface interface { + Answer(opts *bind.CallOpts) (*big.Int, error) + + Decimals(opts *bind.CallOpts) (uint8, error) + + Description(opts *bind.CallOpts) (string, error) + + GetRoundData(opts *bind.CallOpts, _roundId *big.Int) (GetRoundData, + + error) + + LatestRoundData(opts *bind.CallOpts) (LatestRoundData, + + error) + + Version(opts *bind.CallOpts) (*big.Int, error) + + SetBlockTimestampDeduction(opts *bind.TransactOpts, _blockTimestampDeduction *big.Int) (*types.Transaction, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go b/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go index 8a17b64378e..333bb11562d 100644 --- a/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go +++ b/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go @@ -31,15 +31,15 @@ var ( ) var VRFV2OwnerTestConsumerMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vrfCoordinator\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"have\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"want\",\"type\":\"address\"}],\"name\":\"OnlyCoordinatorCanFulfill\",\"type\":\"error\"},{\"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\":\"COORDINATOR\",\"outputs\":[{\"internalType\":\"contractVRFCoordinatorV2Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"}],\"name\":\"getRequestStatus\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"_requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"_keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"_callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_numWords\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestCount\",\"type\":\"uint16\"}],\"name\":\"requestRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_averageFulfillmentInMillions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fastestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_lastRequestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requests\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_responseCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_slowestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"subId\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a0604052600060055560006006556103e76007553480156200002157600080fd5b50604051620013cc380380620013cc8339810160408190526200004491620002bf565b6001600160601b0319606082901b166080523380600081620000ad5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000e057620000e08162000213565b5050600280546001600160a01b0319166001600160a01b0384169081179091556040805163288688f960e21b8152905191925063a21a23e49160048083019260209291908290030181600087803b1580156200013b57600080fd5b505af115801562000150573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001769190620002f1565b600280546001600160401b03928316600160a01b908102600160a01b600160e01b03198316811793849055604051631cd0704360e21b81529190930490931660048401523060248401526001600160a01b0391821691161790637341c10c90604401600060405180830381600087803b158015620001f357600080fd5b505af115801562000208573d6000803e3d6000fd5b50505050506200031c565b6001600160a01b0381163314156200026e5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620000a4565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060208284031215620002d257600080fd5b81516001600160a01b0381168114620002ea57600080fd5b9392505050565b6000602082840312156200030457600080fd5b81516001600160401b0381168114620002ea57600080fd5b60805160601c61108a62000342600039600081816103000152610368015261108a6000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c8063a168fa8911610097578063d8a4676f11610066578063d8a4676f14610262578063dc1670db14610287578063eb1d28bb14610290578063f2fde38b146102d557600080fd5b8063a168fa89146101bc578063ad603ea214610227578063b1e217491461023a578063d826f88f1461024357600080fd5b8063737144bc116100d3578063737144bc1461018457806374dba1241461018d57806379ba5097146101965780638da5cb5b1461019e57600080fd5b80631757f11c146101055780631fe543e3146101215780633b2bcbf114610136578063557d2e921461017b575b600080fd5b61010e60065481565b6040519081526020015b60405180910390f35b61013461012f366004610dc2565b6102e8565b005b6002546101569073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610118565b61010e60045481565b61010e60055481565b61010e60075481565b6101346103a8565b60005473ffffffffffffffffffffffffffffffffffffffff16610156565b6101fd6101ca366004610d90565b600a602052600090815260409020805460028201546003830154600484015460059094015460ff90931693919290919085565b6040805195151586526020860194909452928401919091526060830152608082015260a001610118565b610134610235366004610d32565b6104a5565b61010e60085481565b6101346000600581905560068190556103e76007556004819055600355565b610275610270366004610d90565b610809565b60405161011896959493929190610eb1565b61010e60035481565b6002546102bc9074010000000000000000000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff9091168152602001610118565b6101346102e3366004610cf5565b6108ee565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461039a576040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b6103a48282610902565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610429576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610391565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6104ad610a2d565b60005b8161ffff168161ffff1610156106ad576002546040517f5d3b1d300000000000000000000000000000000000000000000000000000000081526004810187905274010000000000000000000000000000000000000000820467ffffffffffffffff16602482015261ffff8816604482015263ffffffff80871660648301528516608482015260009173ffffffffffffffffffffffffffffffffffffffff1690635d3b1d309060a401602060405180830381600087803b15801561057257600080fd5b505af1158015610586573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105aa9190610da9565b6008819055905060006105bb610ab0565b6040805160c08101825260008082528251818152602080820185528084019182524284860152606084018390526080840186905260a08401839052878352600a815293909120825181547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169015151781559051805194955091939092610649926001850192910190610c6a565b506040820151600282015560608201516003820155608082015160048083019190915560a090920151600590910155805490600061068683610fe6565b909155505060009182526009602052604090912055806106a581610fc4565b9150506104b0565b506002546040517f9f87fad700000000000000000000000000000000000000000000000000000000815274010000000000000000000000000000000000000000820467ffffffffffffffff16600482015230602482015273ffffffffffffffffffffffffffffffffffffffff90911690639f87fad790604401600060405180830381600087803b15801561074057600080fd5b505af1158015610754573d6000803e3d6000fd5b50506002546040517fd7ae1d3000000000000000000000000000000000000000000000000000000000815274010000000000000000000000000000000000000000820467ffffffffffffffff16600482015233602482015273ffffffffffffffffffffffffffffffffffffffff909116925063d7ae1d309150604401600060405180830381600087803b1580156107ea57600080fd5b505af11580156107fe573d6000803e3d6000fd5b505050505050505050565b6000818152600a60209081526040808320815160c081018352815460ff161515815260018201805484518187028101870190955280855260609587958695869586958695919492938584019390929083018282801561088757602002820191906000526020600020905b815481526020019060010190808311610873575b505050505081526020016002820154815260200160038201548152602001600482015481526020016005820154815250509050806000015181602001518260400151836060015184608001518560a001519650965096509650965096505091939550919395565b6108f6610a2d565b6108ff81610b4d565b50565b600061090c610ab0565b600084815260096020526040812054919250906109299083610fad565b9050600061093a82620f4240610f70565b905060065482111561094c5760068290555b600754821061095d5760075461095f565b815b60075560035461096f57806109a2565b60035461097d906001610f1d565b8160035460055461098e9190610f70565b6109989190610f1d565b6109a29190610f35565b6005556000858152600a6020908152604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117825586516109f4939290910191870190610c6a565b506000858152600a60205260408120426003808301919091556005909101859055805491610a2183610fe6565b91905055505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610aae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610391565b565b600046610abc81610c43565b15610b4657606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b0857600080fd5b505afa158015610b1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b409190610da9565b91505090565b4391505090565b73ffffffffffffffffffffffffffffffffffffffff8116331415610bcd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610391565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600061a4b1821480610c57575062066eed82145b80610c64575062066eee82145b92915050565b828054828255906000526020600020908101928215610ca5579160200282015b82811115610ca5578251825591602001919060010190610c8a565b50610cb1929150610cb5565b5090565b5b80821115610cb15760008155600101610cb6565b803561ffff81168114610cdc57600080fd5b919050565b803563ffffffff81168114610cdc57600080fd5b600060208284031215610d0757600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610d2b57600080fd5b9392505050565b600080600080600060a08688031215610d4a57600080fd5b610d5386610cca565b945060208601359350610d6860408701610ce1565b9250610d7660608701610ce1565b9150610d8460808701610cca565b90509295509295909350565b600060208284031215610da257600080fd5b5035919050565b600060208284031215610dbb57600080fd5b5051919050565b60008060408385031215610dd557600080fd5b8235915060208084013567ffffffffffffffff80821115610df557600080fd5b818601915086601f830112610e0957600080fd5b813581811115610e1b57610e1b61104e565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f83011681018181108582111715610e5e57610e5e61104e565b604052828152858101935084860182860187018b1015610e7d57600080fd5b600095505b83861015610ea0578035855260019590950194938601938601610e82565b508096505050505050509250929050565b600060c082018815158352602060c08185015281895180845260e086019150828b01935060005b81811015610ef457845183529383019391830191600101610ed8565b505060408501989098525050506060810193909352608083019190915260a09091015292915050565b60008219821115610f3057610f3061101f565b500190565b600082610f6b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610fa857610fa861101f565b500290565b600082821015610fbf57610fbf61101f565b500390565b600061ffff80831681811415610fdc57610fdc61101f565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156110185761101861101f565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vrfCoordinator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_link\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"have\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"want\",\"type\":\"address\"}],\"name\":\"OnlyCoordinatorCanFulfill\",\"type\":\"error\"},{\"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\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCreatedFundedAndConsumerAdded\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"COORDINATOR\",\"outputs\":[{\"internalType\":\"contractVRFCoordinatorV2Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINKTOKEN\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"}],\"name\":\"getRequestStatus\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"_requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"_keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"_callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_numWords\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_requestCount\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"_subTopUpAmount\",\"type\":\"uint256\"}],\"name\":\"requestRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_averageFulfillmentInMillions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fastestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_lastRequestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requests\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"fulfilled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"requestTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requestBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fulfilmentBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_responseCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_slowestFulfillment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"subId\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"topUpSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a0604052600060065560006007556103e76008553480156200002157600080fd5b50604051620016ee380380620016ee8339810160408190526200004491620001e2565b6001600160601b0319606083901b166080523380600081620000ad5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000e057620000e08162000119565b5050600280546001600160a01b039485166001600160a01b0319918216179091556003805493909416921691909117909155506200021a565b6001600160a01b038116331415620001745760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620000a4565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001dd57600080fd5b919050565b60008060408385031215620001f657600080fd5b6200020183620001c5565b91506200021160208401620001c5565b90509250929050565b60805160601c6114ae620002406000396000818161036901526103d101526114ae6000f3fe608060405234801561001057600080fd5b50600436106101365760003560e01c80638da5cb5b116100b2578063d8a4676f11610081578063eb1d28bb11610066578063eb1d28bb146102e6578063f2fde38b1461032b578063f82d24381461033e57600080fd5b8063d8a4676f146102b8578063dc1670db146102dd57600080fd5b80638da5cb5b14610207578063a168fa8914610225578063b1e2174914610290578063d826f88f1461029957600080fd5b8063557d2e921161010957806374dba124116100ee57806374dba124146101e357806379ba5097146101ec57806386850e93146101f457600080fd5b8063557d2e92146101d1578063737144bc146101da57600080fd5b80631757f11c1461013b5780631fe543e3146101575780633b2bcbf11461016c57806355380dfb146101b1575b600080fd5b61014460075481565b6040519081526020015b60405180910390f35b61016a610165366004611124565b610351565b005b60025461018c9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161014e565b60035461018c9073ffffffffffffffffffffffffffffffffffffffff1681565b61014460055481565b61014460065481565b61014460085481565b61016a610411565b61016a6102023660046110f2565b61050e565b60005473ffffffffffffffffffffffffffffffffffffffff1661018c565b6102666102333660046110f2565b600b602052600090815260409020805460028201546003830154600484015460059094015460ff90931693919290919085565b6040805195151586526020860194909452928401919091526060830152608082015260a00161014e565b61014460095481565b61016a6000600681905560078190556103e76008556005819055600455565b6102cb6102c63660046110f2565b6105ea565b60405161014e969594939291906112d5565b61014460045481565b6003546103129074010000000000000000000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff909116815260200161014e565b61016a61033936600461102d565b6106cf565b61016a61034c36600461108c565b6106e3565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610403576040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b61040d8282610c3e565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610492576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016103fa565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610516610d65565b6003546002546040805174010000000000000000000000000000000000000000840467ffffffffffffffff16602082015273ffffffffffffffffffffffffffffffffffffffff93841693634000aea09316918591016040516020818303038152906040526040518463ffffffff1660e01b81526004016105989392919061123d565b602060405180830381600087803b1580156105b257600080fd5b505af11580156105c6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040d919061106a565b6000818152600b60209081526040808320815160c081018352815460ff161515815260018201805484518187028101870190955280855260609587958695869586958695919492938584019390929083018282801561066857602002820191906000526020600020905b815481526020019060010190808311610654575b505050505081526020016002820154815260200160038201548152602001600482015481526020016005820154815250509050806000015181602001518260400151836060015184608001518560a001519650965096509650965096505091939550919395565b6106d7610d65565b6106e081610de8565b50565b6106eb610d65565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a21a23e46040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561075557600080fd5b505af1158015610769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078d9190611213565b600380547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000067ffffffffffffffff938416810291909117918290556002546040517f7341c10c00000000000000000000000000000000000000000000000000000000815291909204909216600483015230602483015273ffffffffffffffffffffffffffffffffffffffff1690637341c10c90604401600060405180830381600087803b15801561085457600080fd5b505af1158015610868573d6000803e3d6000fd5b505050506108758161050e565b600354604080517401000000000000000000000000000000000000000090920467ffffffffffffffff16825230602083015281018290527f56c142509574e8340ca0190b029c74464b84037d2876278ea0ade3ffb1f0042c9060600160405180910390a160005b8261ffff168161ffff161015610ad9576002546003546040517f5d3b1d30000000000000000000000000000000000000000000000000000000008152600481018990527401000000000000000000000000000000000000000090910467ffffffffffffffff16602482015261ffff8916604482015263ffffffff80881660648301528616608482015260009173ffffffffffffffffffffffffffffffffffffffff1690635d3b1d309060a401602060405180830381600087803b1580156109a257600080fd5b505af11580156109b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109da919061110b565b6009819055905060006109eb610ede565b6040805160c08101825260008082528251818152602080820185528084019182524284860152606084018390526080840186905260a08401839052878352600b815293909120825181547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169015151781559051805194955091939092610a79926001850192910190610fa2565b5060408201516002820155606082015160038201556080820151600482015560a0909101516005918201558054906000610ab28361140a565b90915550506000918252600a60205260409091205580610ad1816113e8565b9150506108dc565b506002546003546040517f9f87fad70000000000000000000000000000000000000000000000000000000081527401000000000000000000000000000000000000000090910467ffffffffffffffff16600482015230602482015273ffffffffffffffffffffffffffffffffffffffff90911690639f87fad790604401600060405180830381600087803b158015610b7057600080fd5b505af1158015610b84573d6000803e3d6000fd5b50506002546003546040517fd7ae1d300000000000000000000000000000000000000000000000000000000081527401000000000000000000000000000000000000000090910467ffffffffffffffff16600482015233602482015273ffffffffffffffffffffffffffffffffffffffff909116925063d7ae1d309150604401600060405180830381600087803b158015610c1e57600080fd5b505af1158015610c32573d6000803e3d6000fd5b50505050505050505050565b6000610c48610ede565b6000848152600a602052604081205491925090610c6590836113d1565b90506000610c7682620f4240611394565b9050600754821115610c885760078290555b6008548210610c9957600854610c9b565b815b600855600454610cab5780610cde565b600454610cb9906001611341565b81600454600654610cca9190611394565b610cd49190611341565b610cde9190611359565b6006556000858152600b6020908152604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811782558651610d30939290910191870190610fa2565b506000858152600b602052604081204260038201556005018490556004805491610d598361140a565b91905055505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610de6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016103fa565b565b73ffffffffffffffffffffffffffffffffffffffff8116331415610e68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016103fa565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600046610eea81610f7b565b15610f7457606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f3657600080fd5b505afa158015610f4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f6e919061110b565b91505090565b4391505090565b600061a4b1821480610f8f575062066eed82145b80610f9c575062066eee82145b92915050565b828054828255906000526020600020908101928215610fdd579160200282015b82811115610fdd578251825591602001919060010190610fc2565b50610fe9929150610fed565b5090565b5b80821115610fe95760008155600101610fee565b803561ffff8116811461101457600080fd5b919050565b803563ffffffff8116811461101457600080fd5b60006020828403121561103f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461106357600080fd5b9392505050565b60006020828403121561107c57600080fd5b8151801515811461106357600080fd5b60008060008060008060c087890312156110a557600080fd5b6110ae87611002565b9550602087013594506110c360408801611019565b93506110d160608801611019565b92506110df60808801611002565b915060a087013590509295509295509295565b60006020828403121561110457600080fd5b5035919050565b60006020828403121561111d57600080fd5b5051919050565b6000806040838503121561113757600080fd5b8235915060208084013567ffffffffffffffff8082111561115757600080fd5b818601915086601f83011261116b57600080fd5b81358181111561117d5761117d611472565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811085821117156111c0576111c0611472565b604052828152858101935084860182860187018b10156111df57600080fd5b600095505b838610156112025780358552600195909501949386019386016111e4565b508096505050505050509250929050565b60006020828403121561122557600080fd5b815167ffffffffffffffff8116811461106357600080fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260006020848184015260606040840152835180606085015260005b8181101561128d57858101830151858201608001528201611271565b8181111561129f576000608083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160800195945050505050565b600060c082018815158352602060c08185015281895180845260e086019150828b01935060005b81811015611318578451835293830193918301916001016112fc565b505060408501989098525050506060810193909352608083019190915260a09091015292915050565b6000821982111561135457611354611443565b500190565b60008261138f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156113cc576113cc611443565b500290565b6000828210156113e3576113e3611443565b500390565b600061ffff8083168181141561140057611400611443565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561143c5761143c611443565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", } var VRFV2OwnerTestConsumerABI = VRFV2OwnerTestConsumerMetaData.ABI var VRFV2OwnerTestConsumerBin = VRFV2OwnerTestConsumerMetaData.Bin -func DeployVRFV2OwnerTestConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, _vrfCoordinator common.Address) (common.Address, *types.Transaction, *VRFV2OwnerTestConsumer, error) { +func DeployVRFV2OwnerTestConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, _vrfCoordinator common.Address, _link common.Address) (common.Address, *types.Transaction, *VRFV2OwnerTestConsumer, error) { parsed, err := VRFV2OwnerTestConsumerMetaData.GetAbi() if err != nil { return common.Address{}, nil, nil, err @@ -48,7 +48,7 @@ func DeployVRFV2OwnerTestConsumer(auth *bind.TransactOpts, backend bind.Contract return common.Address{}, nil, nil, errors.New("GetABI returned nil") } - address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(VRFV2OwnerTestConsumerBin), backend, _vrfCoordinator) + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(VRFV2OwnerTestConsumerBin), backend, _vrfCoordinator, _link) if err != nil { return common.Address{}, nil, nil, err } @@ -193,6 +193,28 @@ func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerCallerSession) COORDINATOR( return _VRFV2OwnerTestConsumer.Contract.COORDINATOR(&_VRFV2OwnerTestConsumer.CallOpts) } +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerCaller) LINKTOKEN(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFV2OwnerTestConsumer.contract.Call(opts, &out, "LINKTOKEN") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerSession) LINKTOKEN() (common.Address, error) { + return _VRFV2OwnerTestConsumer.Contract.LINKTOKEN(&_VRFV2OwnerTestConsumer.CallOpts) +} + +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerCallerSession) LINKTOKEN() (common.Address, error) { + return _VRFV2OwnerTestConsumer.Contract.LINKTOKEN(&_VRFV2OwnerTestConsumer.CallOpts) +} + func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerCaller) GetRequestStatus(opts *bind.CallOpts, _requestId *big.Int) (GetRequestStatus, error) { @@ -460,16 +482,16 @@ func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactorSession) RawFulfi return _VRFV2OwnerTestConsumer.Contract.RawFulfillRandomWords(&_VRFV2OwnerTestConsumer.TransactOpts, requestId, randomWords) } -func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactor) RequestRandomWords(opts *bind.TransactOpts, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16) (*types.Transaction, error) { - return _VRFV2OwnerTestConsumer.contract.Transact(opts, "requestRandomWords", _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount) +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactor) RequestRandomWords(opts *bind.TransactOpts, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int) (*types.Transaction, error) { + return _VRFV2OwnerTestConsumer.contract.Transact(opts, "requestRandomWords", _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount, _subTopUpAmount) } -func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerSession) RequestRandomWords(_requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16) (*types.Transaction, error) { - return _VRFV2OwnerTestConsumer.Contract.RequestRandomWords(&_VRFV2OwnerTestConsumer.TransactOpts, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount) +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerSession) RequestRandomWords(_requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int) (*types.Transaction, error) { + return _VRFV2OwnerTestConsumer.Contract.RequestRandomWords(&_VRFV2OwnerTestConsumer.TransactOpts, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount, _subTopUpAmount) } -func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactorSession) RequestRandomWords(_requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16) (*types.Transaction, error) { - return _VRFV2OwnerTestConsumer.Contract.RequestRandomWords(&_VRFV2OwnerTestConsumer.TransactOpts, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount) +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactorSession) RequestRandomWords(_requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int) (*types.Transaction, error) { + return _VRFV2OwnerTestConsumer.Contract.RequestRandomWords(&_VRFV2OwnerTestConsumer.TransactOpts, _requestConfirmations, _keyHash, _callbackGasLimit, _numWords, _requestCount, _subTopUpAmount) } func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactor) Reset(opts *bind.TransactOpts) (*types.Transaction, error) { @@ -484,6 +506,18 @@ func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactorSession) Reset() return _VRFV2OwnerTestConsumer.Contract.Reset(&_VRFV2OwnerTestConsumer.TransactOpts) } +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactor) TopUpSubscription(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) { + return _VRFV2OwnerTestConsumer.contract.Transact(opts, "topUpSubscription", amount) +} + +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerSession) TopUpSubscription(amount *big.Int) (*types.Transaction, error) { + return _VRFV2OwnerTestConsumer.Contract.TopUpSubscription(&_VRFV2OwnerTestConsumer.TransactOpts, amount) +} + +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactorSession) TopUpSubscription(amount *big.Int) (*types.Transaction, error) { + return _VRFV2OwnerTestConsumer.Contract.TopUpSubscription(&_VRFV2OwnerTestConsumer.TransactOpts, amount) +} + func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { return _VRFV2OwnerTestConsumer.contract.Transact(opts, "transferOwnership", to) } @@ -768,6 +802,125 @@ func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerFilterer) ParseOwnershipTra return event, nil } +type VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator struct { + Event *VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) + 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(VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) + 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 *VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator) Error() error { + return it.fail +} + +func (it *VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded struct { + SubId uint64 + Consumer common.Address + Amount *big.Int + Raw types.Log +} + +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerFilterer) FilterSubscriptionCreatedFundedAndConsumerAdded(opts *bind.FilterOpts) (*VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator, error) { + + logs, sub, err := _VRFV2OwnerTestConsumer.contract.FilterLogs(opts, "SubscriptionCreatedFundedAndConsumerAdded") + if err != nil { + return nil, err + } + return &VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator{contract: _VRFV2OwnerTestConsumer.contract, event: "SubscriptionCreatedFundedAndConsumerAdded", logs: logs, sub: sub}, nil +} + +func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerFilterer) WatchSubscriptionCreatedFundedAndConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) (event.Subscription, error) { + + logs, sub, err := _VRFV2OwnerTestConsumer.contract.WatchLogs(opts, "SubscriptionCreatedFundedAndConsumerAdded") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) + if err := _VRFV2OwnerTestConsumer.contract.UnpackLog(event, "SubscriptionCreatedFundedAndConsumerAdded", 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 (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumerFilterer) ParseSubscriptionCreatedFundedAndConsumerAdded(log types.Log) (*VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded, error) { + event := new(VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) + if err := _VRFV2OwnerTestConsumer.contract.UnpackLog(event, "SubscriptionCreatedFundedAndConsumerAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type GetRequestStatus struct { Fulfilled bool RandomWords []*big.Int @@ -790,6 +943,8 @@ func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumer) ParseLog(log types.Log) ( return _VRFV2OwnerTestConsumer.ParseOwnershipTransferRequested(log) case _VRFV2OwnerTestConsumer.abi.Events["OwnershipTransferred"].ID: return _VRFV2OwnerTestConsumer.ParseOwnershipTransferred(log) + case _VRFV2OwnerTestConsumer.abi.Events["SubscriptionCreatedFundedAndConsumerAdded"].ID: + return _VRFV2OwnerTestConsumer.ParseSubscriptionCreatedFundedAndConsumerAdded(log) default: return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) @@ -804,6 +959,10 @@ func (VRFV2OwnerTestConsumerOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) Topic() common.Hash { + return common.HexToHash("0x56c142509574e8340ca0190b029c74464b84037d2876278ea0ade3ffb1f0042c") +} + func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumer) Address() common.Address { return _VRFV2OwnerTestConsumer.address } @@ -811,6 +970,8 @@ func (_VRFV2OwnerTestConsumer *VRFV2OwnerTestConsumer) Address() common.Address type VRFV2OwnerTestConsumerInterface interface { COORDINATOR(opts *bind.CallOpts) (common.Address, error) + LINKTOKEN(opts *bind.CallOpts) (common.Address, error) + GetRequestStatus(opts *bind.CallOpts, _requestId *big.Int) (GetRequestStatus, error) @@ -839,10 +1000,12 @@ type VRFV2OwnerTestConsumerInterface interface { RawFulfillRandomWords(opts *bind.TransactOpts, requestId *big.Int, randomWords []*big.Int) (*types.Transaction, error) - RequestRandomWords(opts *bind.TransactOpts, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16) (*types.Transaction, error) + RequestRandomWords(opts *bind.TransactOpts, _requestConfirmations uint16, _keyHash [32]byte, _callbackGasLimit uint32, _numWords uint32, _requestCount uint16, _subTopUpAmount *big.Int) (*types.Transaction, error) Reset(opts *bind.TransactOpts) (*types.Transaction, error) + TopUpSubscription(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFV2OwnerTestConsumerOwnershipTransferRequestedIterator, error) @@ -857,6 +1020,12 @@ type VRFV2OwnerTestConsumerInterface interface { ParseOwnershipTransferred(log types.Log) (*VRFV2OwnerTestConsumerOwnershipTransferred, error) + FilterSubscriptionCreatedFundedAndConsumerAdded(opts *bind.FilterOpts) (*VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAddedIterator, error) + + WatchSubscriptionCreatedFundedAndConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded) (event.Subscription, error) + + ParseSubscriptionCreatedFundedAndConsumerAdded(log types.Log) (*VRFV2OwnerTestConsumerSubscriptionCreatedFundedAndConsumerAdded, error) + ParseLog(log types.Log) (generated.AbigenLog, error) Address() common.Address diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index ea8d1a020e2..394f4636667 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -75,6 +75,7 @@ vrf_consumer_v2: ../../contracts/solc/v0.8.6/VRFConsumerV2/VRFConsumerV2.abi ../ vrf_consumer_v2_plus_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample/VRFConsumerV2PlusUpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample/VRFConsumerV2PlusUpgradeableExample.bin 3155c611e4d6882e9324b6e975033b31356776ea8b031ca63d63da37589d583b vrf_consumer_v2_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.bin f1790a9a2f2a04c730593e483459709cb89e897f8a19d7a3ac0cfe6a97265e6e vrf_coordinator_mock: ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.bin 5c495cf8df1f46d8736b9150cdf174cce358cb8352f60f0d5bb9581e23920501 +vrf_coordinator_test_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorTestV2/VRFCoordinatorTestV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorTestV2/VRFCoordinatorTestV2.bin eaefd785c38bac67fb11a7fc2737ab2da68c988ca170e7db8ff235c80893e01c vrf_coordinator_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.bin 295f35ce282060317dfd01f45959f5a2b05ba26913e422fbd4fb6bf90b107006 vrf_coordinator_v2_5: ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin bcad64e1b41278998c94867370fd9c4809b1376ccc004955bc7ed33fe716408c vrf_coordinator_v2_plus_v2_example: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.bin 4a5b86701983b1b65f0a8dfa116b3f6d75f8f706fa274004b57bdf5992e4cec3 @@ -82,12 +83,13 @@ vrf_coordinator_v2plus_interface: ../../contracts/solc/v0.8.6/IVRFCoordinatorV2P vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.bin 14f888eb313930b50233a6f01ea31eba0206b7f41a41f6311670da8bb8a26963 vrf_load_test_external_sub_owner: ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.bin 2097faa70265e420036cc8a3efb1f1e0836ad2d7323b295b9a26a125dbbe6c7d vrf_load_test_ownerless_consumer: ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.bin 74f914843cbc70b9c3079c3e1c709382ce415225e8bb40113e7ac018bfcb0f5c -vrf_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.bin 8ab9de5816fbdf93a2865e2711b85a39a6fc9c413a4b336578c485be1158d430 +vrf_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.bin c9621c52d216a090ff6bbe942f1b75d2bce8658a27323c3789e5e14b523277ee vrf_log_emitter: ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.abi ../../contracts/solc/v0.8.19/VRFLogEmitter/VRFLogEmitter.bin 15f491d445ac4d0c712d1cbe4e5054c759b080bf20de7d54bfe2a82cde4dcf06 vrf_malicious_consumer_v2: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.bin 9755fa8ffc7f5f0b337d5d413d77b0c9f6cd6f68c31727d49acdf9d4a51bc522 vrf_malicious_consumer_v2_plus: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.bin e2a72638e11da807b6533d037e7e5aaeed695efd5035777b8e20d2f8973a574c +vrf_mock_ethlink_aggregator: ../../contracts/solc/v0.8.6/VRFMockETHLINKAggregator/VRFMockETHLINKAggregator.abi ../../contracts/solc/v0.8.6/VRFMockETHLINKAggregator/VRFMockETHLINKAggregator.bin a6e753984eeec8107e205ae517f74d4616bf23cffda50a25538ffc16ac4b036f vrf_owner: ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.abi ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.bin eccfae5ee295b5850e22f61240c469f79752b8d9a3bac5d64aec7ac8def2f6cb -vrf_owner_test_consumer: ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.bin 0537bbe96c5a8bbd44d0a65fbb7e51f6a9f9e75f4673225845ac1ba33f4e7974 +vrf_owner_test_consumer: ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.bin 6969de242efe8f366ae4097fc279d9375c8e2d0307aaa322e31f2ce6b8c1909a vrf_ownerless_consumer_example: ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample/VRFOwnerlessConsumerExample.abi ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample/VRFOwnerlessConsumerExample.bin 9893b3805863273917fb282eed32274e32aa3d5c2a67a911510133e1218132be vrf_single_consumer_example: ../../contracts/solc/v0.8.6/VRFSingleConsumerExample/VRFSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFSingleConsumerExample/VRFSingleConsumerExample.bin 892a5ed35da2e933f7fd7835cd6f7f70ef3aa63a9c03a22c5b1fd026711b0ece vrf_v2_consumer_wrapper: ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.bin 12368b3b5e06392440143a13b94c0ea2f79c4c897becc3b060982559e10ace40 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 9bd3f2fd41a..d4bbbcab11c 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -103,6 +103,8 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.abi ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.bin VRFV2TransparentUpgradeableProxy vrfv2_transparent_upgradeable_proxy //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.abi ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.bin VRFV2ProxyAdmin vrfv2_proxy_admin //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.bin ChainSpecificUtilHelper chain_specific_util_helper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorTestV2/VRFCoordinatorTestV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorTestV2/VRFCoordinatorTestV2.bin VRFCoordinatorTestV2 vrf_coordinator_test_v2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFMockETHLINKAggregator/VRFMockETHLINKAggregator.abi ../../contracts/solc/v0.8.6/VRFMockETHLINKAggregator/VRFMockETHLINKAggregator.bin VRFMockETHLINKAggregator vrf_mock_ethlink_aggregator // VRF V2 Wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin VRFV2Wrapper vrfv2_wrapper diff --git a/core/scripts/common/vrf/jobs/jobs.go b/core/scripts/common/vrf/jobs/jobs.go index 674cca175c8..7e304f431be 100644 --- a/core/scripts/common/vrf/jobs/jobs.go +++ b/core/scripts/common/vrf/jobs/jobs.go @@ -7,13 +7,14 @@ schemaVersion = 1 coordinatorAddress = "%s" batchCoordinatorAddress = "%s" batchFulfillmentEnabled = %t -batchFulfillmentGasMultiplier = 1.1 +batchFulfillmentGasMultiplier = %f +customRevertsPipelineEnabled = %t publicKey = "%s" minIncomingConfirmations = %d evmChainID = "%d" fromAddresses = ["%s"] -pollPeriod = "300ms" -requestTimeout = "30m0s" +pollPeriod = "%s" +requestTimeout = "%s" observationSource = """decode_log [type=ethabidecodelog abi="RandomWordsRequested(bytes32 indexed keyHash,uint256 requestId,uint256 preSeed,uint64 indexed subId,uint16 minimumRequestConfirmations,uint32 callbackGasLimit,uint32 numWords,address indexed sender)" data="$(jobRun.logData)" @@ -25,7 +26,7 @@ vrf [type=vrfv2 topics="$(jobRun.logTopics)"] estimate_gas [type=estimategaslimit to="%s" - multiplier="1.1" + multiplier="%f" data="$(vrf.output)"] simulate [type=ethcall from="%s" @@ -45,13 +46,13 @@ schemaVersion = 1 coordinatorAddress = "%s" batchCoordinatorAddress = "%s" batchFulfillmentEnabled = %t -batchFulfillmentGasMultiplier = 1.1 +batchFulfillmentGasMultiplier = %f publicKey = "%s" minIncomingConfirmations = %d evmChainID = "%d" fromAddresses = ["%s"] -pollPeriod = "300ms" -requestTimeout = "30m0s" +pollPeriod = "%s" +requestTimeout = "%s" observationSource = """ decode_log [type=ethabidecodelog abi="RandomWordsRequested(bytes32 indexed keyHash,uint256 requestId,uint256 preSeed,uint256 indexed subId,uint16 minimumRequestConfirmations,uint32 callbackGasLimit,uint32 numWords,bytes extraArgs,address indexed sender)" @@ -64,7 +65,7 @@ generate_proof [type=vrfv2plus topics="$(jobRun.logTopics)"] estimate_gas [type=estimategaslimit to="%s" - multiplier="1.1" + multiplier="%f" data="$(generate_proof.output)"] simulate_fulfillment [type=ethcall from="%s" @@ -91,6 +92,20 @@ runTimeout = "1m0s" evmChainID = "%d" fromAddresses = ["%s"] ` + BHSPlusJobFormatted = `type = "blockhashstore" +schemaVersion = 1 +name = "blockhashstore" +forwardingAllowed = false +coordinatorV2PlusAddress = "%s" +waitBlocks = %d +lookbackBlocks = %d +blockhashStoreAddress = "%s" +pollPeriod = "30s" +runTimeout = "1m0s" +evmChainID = "%d" +fromAddresses = ["%s"] +` + BHFJobFormatted = `type = "blockheaderfeeder" schemaVersion = 1 name = "blockheaderfeeder" @@ -106,5 +121,22 @@ evmChainID = "%d" fromAddresses = ["%s"] getBlockhashesBatchSize = 50 storeBlockhashesBatchSize = 10 +` + + BHFPlusJobFormatted = `type = "blockheaderfeeder" +schemaVersion = 1 +name = "blockheaderfeeder" +forwardingAllowed = false +coordinatorV2PlusAddress = "%s" +waitBlocks = 256 +lookbackBlocks = 1_000 +blockhashStoreAddress = "%s" +batchBlockhashStoreAddress = "%s" +pollPeriod = "10s" +runTimeout = "30s" +evmChainID = "%d" +fromAddresses = ["%s"] +getBlockhashesBatchSize = 50 +storeBlockhashesBatchSize = 10 ` ) diff --git a/core/scripts/common/vrf/model/model.go b/core/scripts/common/vrf/model/model.go index 1b4e59fe4f3..ba919eb3c4b 100644 --- a/core/scripts/common/vrf/model/model.go +++ b/core/scripts/common/vrf/model/model.go @@ -49,3 +49,12 @@ type VRFKeyRegistrationConfig struct { VRFKeyUncompressedPubKey string RegisterAgainstAddress string } + +type CoordinatorJobSpecConfig struct { + BatchFulfillmentEnabled bool + BatchFulfillmentGasMultiplier float64 + EstimateGasMultiplier float64 + PollPeriod string + RequestTimeout string + RevertsPipelineEnabled bool +} diff --git a/core/scripts/common/vrf/setup-envs/README.md b/core/scripts/common/vrf/setup-envs/README.md index 5aa6aa4754f..372e7358441 100644 --- a/core/scripts/common/vrf/setup-envs/README.md +++ b/core/scripts/common/vrf/setup-envs/README.md @@ -15,10 +15,12 @@ export ETH_CHAIN_ID= export ACCOUNT_KEY= ``` 5. execute from `core/scripts/common/vrf/setup-envs` folder - * `--vrf-version` - "v2" or "v2plus" + * `--vrf-version` - "v2" or "v2plus" + +#### VRF V2 ``` go run . \ ---vrf-version="v2plus" \ +--vrf-version="v2" \ --vrf-primary-node-url=http://localhost:6610 \ --vrf-primary-creds-file \ --vrf-backup-node-url=http://localhost:6611 \ @@ -36,10 +38,48 @@ go run . \ --subscription-balance="1e19" \ --subscription-balance-native="1e18" \ --batch-fulfillment-enabled="true" \ +--batch-fulfillment-gas-multiplier=1.1 \ +--estimate-gas-multiplier=1.1 \ +--poll-period="5s" \ +--request-timeout="30m0s" \ +--reverts-pipeline-enabled="true" \ --min-confs=3 \ --register-vrf-key-against-address= \ ---deploy-vrfv2-owner="true" +--deploy-vrfv2-owner="true" \ +--use-test-coordinator="true" +``` +#### VRF V2 Plus +* does not need to register VRF key against address +* does not need to deploy VRFV2Owner contract +* does not need to use test coordinator + +VRF V2 Plus example: +``` +go run . \ +--vrf-version="v2plus" \ +--vrf-primary-node-url=http://localhost:6610 \ +--vrf-primary-creds-file \ +--vrf-backup-node-url=http://localhost:6611 \ +--vrf-bk-creds-file \ +--bhs-node-url=http://localhost:6612 \ +--bhs-creds-file \ +--bhs-backup-node-url=http://localhost:6613 \ +--bhs-bk-creds-file \ +--bhf-node-url=http://localhost:6614 \ +--bhf-creds-file \ +--num-eth-keys=1 \ +--num-vrf-keys=1 \ +--sending-key-funding-amount="1e17" \ +--deploy-contracts-and-create-jobs="true" \ +--subscription-balance="1e19" \ +--subscription-balance-native="1e18" \ +--batch-fulfillment-enabled="true" \ +--batch-fulfillment-gas-multiplier=1.1 \ +--estimate-gas-multiplier=1.1 \ +--poll-period="5s" \ +--request-timeout="30m0s" \ +--min-confs=3 ``` Optional parameters - will not be deployed if specified diff --git a/core/scripts/common/vrf/setup-envs/main.go b/core/scripts/common/vrf/setup-envs/main.go index 598238bebef..57e4817d804 100644 --- a/core/scripts/common/vrf/setup-envs/main.go +++ b/core/scripts/common/vrf/setup-envs/main.go @@ -70,7 +70,12 @@ func main() { numEthKeys := flag.Int("num-eth-keys", 5, "Number of eth keys to create") maxGasPriceGwei := flag.Int("max-gas-price-gwei", -1, "Max gas price gwei of the eth keys") numVRFKeys := flag.Int("num-vrf-keys", 1, "Number of vrf keys to create") - batchFulfillmentEnabled := flag.Bool("batch-fulfillment-enabled", constants.BatchFulfillmentEnabled, "whether to enable batch fulfillment on Cl node") + batchFulfillmentEnabled := flag.Bool("batch-fulfillment-enabled", constants.BatchFulfillmentEnabled, "whether send randomness fulfillments in batches inside one tx from CL node") + batchFulfillmentGasMultiplier := flag.Float64("batch-fulfillment-gas-multiplier", 1.1, "") + estimateGasMultiplier := flag.Float64("estimate-gas-multiplier", 1.1, "") + pollPeriod := flag.String("poll-period", "300ms", "") + requestTimeout := flag.String("request-timeout", "30m0s", "") + revertsPipelineEnabled := flag.Bool("reverts-pipeline-enabled", true, "") vrfVersion := flag.String("vrf-version", "v2", "VRF version to use") deployContractsAndCreateJobs := flag.Bool("deploy-contracts-and-create-jobs", false, "whether to deploy contracts and create jobs") @@ -88,6 +93,7 @@ func main() { registerVRFKeyAgainstAddress := flag.String("register-vrf-key-against-address", "", "VRF Key registration against address - "+ "from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments") deployVRFOwner := flag.Bool("deploy-vrfv2-owner", true, "whether to deploy VRF owner contracts") + useTestCoordinator := flag.Bool("use-test-coordinator", true, "whether to use test coordinator contract or use the normal one") e := helpers.SetupEnv(false) flag.Parse() @@ -204,15 +210,25 @@ func main() { FeeConfig: feeConfigV2, } + coordinatorJobSpecConfig := model.CoordinatorJobSpecConfig{ + BatchFulfillmentEnabled: *batchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *batchFulfillmentGasMultiplier, + EstimateGasMultiplier: *estimateGasMultiplier, + PollPeriod: *pollPeriod, + RequestTimeout: *requestTimeout, + RevertsPipelineEnabled: *revertsPipelineEnabled, + } + jobSpecs = v2scripts.VRFV2DeployUniverse( e, subscriptionBalanceJuels, vrfKeyRegistrationConfig, contractAddresses, coordinatorConfigV2, - *batchFulfillmentEnabled, nodesMap, *deployVRFOwner, + coordinatorJobSpecConfig, + *useTestCoordinator, ) case "v2plus": feeConfigV2Plus := vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig{ @@ -228,6 +244,14 @@ func main() { FeeConfig: feeConfigV2Plus, } + coordinatorJobSpecConfig := model.CoordinatorJobSpecConfig{ + BatchFulfillmentEnabled: *batchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *batchFulfillmentGasMultiplier, + EstimateGasMultiplier: *estimateGasMultiplier, + PollPeriod: *pollPeriod, + RequestTimeout: *requestTimeout, + } + jobSpecs = v2plusscripts.VRFV2PlusDeployUniverse( e, subscriptionBalanceJuels, @@ -235,8 +259,8 @@ func main() { vrfKeyRegistrationConfig, contractAddresses, coordinatorConfigV2Plus, - *batchFulfillmentEnabled, nodesMap, + coordinatorJobSpecConfig, ) } diff --git a/core/scripts/vrfv2/testnet/main.go b/core/scripts/vrfv2/testnet/main.go index 100440d6fb0..34070c90d8a 100644 --- a/core/scripts/vrfv2/testnet/main.go +++ b/core/scripts/vrfv2/testnet/main.go @@ -10,6 +10,7 @@ import ( "math/big" "os" "strings" + "sync" "github.com/smartcontractkit/chainlink/core/scripts/vrfv2/testnet/v2scripts" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner_test_consumer" @@ -721,14 +722,19 @@ func main() { case "eoa-vrf-owner-test-consumer-deploy": loadTestConsumerDeployCmd := flag.NewFlagSet("eoa-vrf-owner-test-consumer-deploy", flag.ExitOnError) consumerCoordinator := loadTestConsumerDeployCmd.String("coordinator-address", "", "coordinator address") - helpers.ParseArgs(loadTestConsumerDeployCmd, os.Args[2:], "coordinator-address") + consumerLinkAddress := loadTestConsumerDeployCmd.String("link-address", "", "link-address") + + helpers.ParseArgs(loadTestConsumerDeployCmd, os.Args[2:], "coordinator-address", "link-address") + _, tx, _, err := vrf_owner_test_consumer.DeployVRFV2OwnerTestConsumer( e.Owner, e.Ec, common.HexToAddress(*consumerCoordinator), + common.HexToAddress(*consumerLinkAddress), ) helpers.PanicErr(err) helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID) + case "eoa-create-sub": createSubCmd := flag.NewFlagSet("eoa-create-sub", flag.ExitOnError) coordinatorAddress := createSubCmd.String("coordinator-address", "", "coordinator address") @@ -853,57 +859,103 @@ func main() { keyHash := request.String("key-hash", "", "key hash") cbGasLimit := request.Uint("cb-gas-limit", 100_000, "request callback gas limit") numWords := request.Uint("num-words", 1, "num words to request") - requests := request.Uint("requests", 10, "number of randomness requests to make per run") + requests := request.Uint("requests", 1, "number of randomness requests to make per run") runs := request.Uint("runs", 1, "number of runs to do. total randomness requests will be (requests * runs).") + subFundingAmountJuels := request.String("sub-funding-amount-juels", "0", "amount of Juels to fund subscription with") + vrfOwnerAddress := request.String("vrf-owner-address", "", "vrf owner address") + linkAddress := request.String("link-address", "", "link-address") + helpers.ParseArgs(request, os.Args[2:], "consumer-address", "key-hash") keyHashBytes := common.HexToHash(*keyHash) + link, err := link_token_interface.NewLinkToken(common.HexToAddress(*linkAddress), e.Ec) + helpers.PanicErr(err) + + linkTransferTX, err := link.Transfer(e.Owner, common.HexToAddress(*consumerAddress), decimal.RequireFromString(*subFundingAmountJuels).BigInt()) + helpers.PanicErr(err) + helpers.ConfirmTXMined(context.Background(), e.Ec, linkTransferTX, e.ChainID, "transfer", *subFundingAmountJuels, "juels to", *consumerAddress) + + consumerBalanceJuels, err := link.BalanceOf(nil, common.HexToAddress(*consumerAddress)) + helpers.PanicErr(err) + fmt.Println("Consumer Balance:", consumerBalanceJuels.String(), "juels") + consumer, err := vrf_owner_test_consumer.NewVRFV2OwnerTestConsumer( common.HexToAddress(*consumerAddress), e.Ec) helpers.PanicErr(err) var txes []*types.Transaction for i := 0; i < int(*runs); i++ { - tx, err := consumer.RequestRandomWords( + requestRandTX, errRequestRandomWords := consumer.RequestRandomWords( e.Owner, uint16(*requestConfirmations), keyHashBytes, uint32(*cbGasLimit), uint32(*numWords), uint16(*requests), + decimal.RequireFromString(*subFundingAmountJuels).BigInt(), ) - helpers.PanicErr(err) - fmt.Printf("TX %d: %s\n", i+1, helpers.ExplorerLink(e.ChainID, tx.Hash())) - txes = append(txes, tx) + helpers.PanicErr(errRequestRandomWords) + fmt.Printf("TX %d: %s\n", i+1, helpers.ExplorerLink(e.ChainID, requestRandTX.Hash())) + txes = append(txes, requestRandTX) } - fmt.Println("Total number of requests sent:", (*requests)*(*runs)) + + coordinatorAddress, err := consumer.COORDINATOR(nil) + helpers.PanicErr(err) + coordinator, err := vrf_coordinator_v2.NewVRFCoordinatorV2(coordinatorAddress, e.Ec) + helpers.PanicErr(err) + + coordinatorOwnerAddress, err := coordinator.Owner(nil) + helpers.PanicErr(err) + + fmt.Println("Actual Coordinator Owner Address:", coordinatorOwnerAddress.String()) + fmt.Println("Provided VRF Owner Address:", *vrfOwnerAddress) + if coordinatorOwnerAddress.String() != *vrfOwnerAddress { + panic("Actual Coordinator Owner and provided Coordinator Owner Addresses does not match") + } + + var subId uint64 + var wg sync.WaitGroup + + wg.Add(1) + + go func() { + subCreatedChan := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated) + subCreatedSubscription, errw := coordinator.WatchSubscriptionCreated(nil, subCreatedChan, nil) + helpers.PanicErr(errw) + defer subCreatedSubscription.Unsubscribe() + subscriptionCreatedEvent := <-subCreatedChan + subId = subscriptionCreatedEvent.SubId + fmt.Println("VRF Owner Test Consumer's Sub ID:", subId) + defer wg.Done() + }() + + wg.Wait() + + totalNumberOfRequests := (*requests) * (*runs) + fmt.Println("Total number of requests sent:", totalNumberOfRequests) fmt.Println("fetching receipts for all transactions") + + var receipt *types.Receipt for i, tx := range txes { - helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, fmt.Sprintf("load test %d", i+1)) + receipt = helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, fmt.Sprintf("load test %d", i+1)) + } + blockNumber := receipt.BlockNumber.Uint64() + fmt.Println("subId", subId) + subFundedIterator, err := coordinator.FilterSubscriptionFunded(&bind.FilterOpts{End: &blockNumber}, []uint64{subId}) + helpers.PanicErr(err) + + if !subFundedIterator.Next() { + panic("Sub Funded Event not found") } + + fmt.Println("Sub Funded, sub ID:", subFundedIterator.Event.SubId, ", Old Balance:", subFundedIterator.Event.OldBalance, ", New Balance:", subFundedIterator.Event.NewBalance) case "eoa-vrf-owner-test-read-metrics": request := flag.NewFlagSet("eoa-load-test-read-metrics", flag.ExitOnError) consumerAddress := request.String("consumer-address", "", "consumer address") helpers.ParseArgs(request, os.Args[2:], "consumer-address") - consumer, err := vrf_owner_test_consumer.NewVRFV2OwnerTestConsumer( - common.HexToAddress(*consumerAddress), - e.Ec) - helpers.PanicErr(err) - responseCount, err := consumer.SResponseCount(nil) - helpers.PanicErr(err) - fmt.Println("Response Count: ", responseCount) - requestCount, err := consumer.SRequestCount(nil) - helpers.PanicErr(err) - fmt.Println("Request Count: ", requestCount) - averageFulfillmentInMillions, err := consumer.SAverageFulfillmentInMillions(nil) - helpers.PanicErr(err) - fmt.Println("Average Fulfillment In Millions: ", averageFulfillmentInMillions) - slowestFulfillment, err := consumer.SSlowestFulfillment(nil) - helpers.PanicErr(err) - fmt.Println("Slowest Fulfillment: ", slowestFulfillment) - fastestFulfillment, err := consumer.SFastestFulfillment(nil) - helpers.PanicErr(err) - fmt.Println("Fastest Fulfillment: ", fastestFulfillment) + metrics := getVRFOwnerTestConsumerMetrics(*consumerAddress, e) + printLoadTestMetrics(metrics) + case "eoa-vrf-owner-test-reset-metrics": request := flag.NewFlagSet("eoa-vrf-owner-test-reset-metrics", flag.ExitOnError) consumerAddress := request.String("consumer-address", "", "consumer address") @@ -1352,3 +1404,45 @@ func main() { panic("unrecognized subcommand: " + os.Args[1]) } } + +func getVRFOwnerTestConsumerMetrics(consumerAddress string, e helpers.Environment) LoadTestMetrics { + consumer, err := vrf_owner_test_consumer.NewVRFV2OwnerTestConsumer( + common.HexToAddress(consumerAddress), + e.Ec) + helpers.PanicErr(err) + responseCount, err := consumer.SResponseCount(nil) + helpers.PanicErr(err) + requestCount, err := consumer.SRequestCount(nil) + helpers.PanicErr(err) + averageFulfillmentInMillions, err := consumer.SAverageFulfillmentInMillions(nil) + helpers.PanicErr(err) + slowestFulfillment, err := consumer.SSlowestFulfillment(nil) + helpers.PanicErr(err) + fastestFulfillment, err := consumer.SFastestFulfillment(nil) + helpers.PanicErr(err) + + metrics := LoadTestMetrics{ + ResponseCount: responseCount, + RequestCount: requestCount, + AverageFulfillmentInMillions: averageFulfillmentInMillions, + SlowestFulfillment: slowestFulfillment, + FastestFulfillment: fastestFulfillment, + } + return metrics +} + +func printLoadTestMetrics(metrics LoadTestMetrics) { + fmt.Println("Response Count: ", metrics.ResponseCount) + fmt.Println("Request Count: ", metrics.RequestCount) + fmt.Println("Average Fulfillment In Millions: ", metrics.AverageFulfillmentInMillions) + fmt.Println("Slowest Fulfillment: ", metrics.SlowestFulfillment) + fmt.Println("Fastest Fulfillment: ", metrics.FastestFulfillment) +} + +type LoadTestMetrics struct { + ResponseCount *big.Int + RequestCount *big.Int + AverageFulfillmentInMillions *big.Int + SlowestFulfillment *big.Int + FastestFulfillment *big.Int +} diff --git a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go index 2608873fcfd..1397274656c 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go +++ b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go @@ -52,8 +52,14 @@ func DeployUniverseViaCLI(e helpers.Environment) { nodeSendingKeyFundingAmount := deployCmd.String("sending-key-funding-amount", constants.NodeSendingKeyFundingAmount, "CL node sending key funding amount") batchFulfillmentEnabled := deployCmd.Bool("batch-fulfillment-enabled", constants.BatchFulfillmentEnabled, "whether send randomness fulfillments in batches inside one tx from CL node") + batchFulfillmentGasMultiplier := deployCmd.Float64("batch-fulfillment-gas-multiplier", 1.1, "") + estimateGasMultiplier := deployCmd.Float64("estimate-gas-multiplier", 1.1, "") + pollPeriod := deployCmd.String("poll-period", "300ms", "") + requestTimeout := deployCmd.String("request-timeout", "30m0s", "") + revertsPipelineEnabled := deployCmd.Bool("reverts-pipeline-enabled", true, "") - deployVRFOwner := deployCmd.Bool("deploy-vrf-owner", false, "whether to deploy VRF owner contracts") + deployVRFOwner := deployCmd.Bool("deploy-vrf-owner", true, "whether to deploy VRF owner contracts") + useTestCoordinator := deployCmd.Bool("use-test-coordinator", true, "whether to use test coordinator") // optional flags fallbackWeiPerUnitLinkString := deployCmd.String("fallback-wei-per-unit-link", constants.FallbackWeiPerUnitLink.String(), "fallback wei/link ratio") @@ -137,15 +143,25 @@ func DeployUniverseViaCLI(e helpers.Environment) { RegisterAgainstAddress: *registerVRFKeyAgainstAddress, } + coordinatorJobSpecConfig := model.CoordinatorJobSpecConfig{ + BatchFulfillmentEnabled: *batchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *batchFulfillmentGasMultiplier, + EstimateGasMultiplier: *estimateGasMultiplier, + PollPeriod: *pollPeriod, + RequestTimeout: *requestTimeout, + RevertsPipelineEnabled: *revertsPipelineEnabled, + } + VRFV2DeployUniverse( e, subscriptionBalanceJuels, vrfKeyRegistrationConfig, contractAddresses, coordinatorConfig, - *batchFulfillmentEnabled, nodesMap, *deployVRFOwner, + coordinatorJobSpecConfig, + *useTestCoordinator, ) vrfPrimaryNode := nodesMap[model.VRFPrimaryNodeName] @@ -161,9 +177,10 @@ func VRFV2DeployUniverse( vrfKeyRegistrationConfig model.VRFKeyRegistrationConfig, contractAddresses model.ContractAddresses, coordinatorConfig CoordinatorConfigV2, - batchFulfillmentEnabled bool, nodesMap map[string]model.Node, deployVRFOwner bool, + coordinatorJobSpecConfig model.CoordinatorJobSpecConfig, + useTestCoordinator bool, ) model.JobSpecs { var compressedPkHex string var keyHash common.Hash @@ -212,9 +229,16 @@ func VRFV2DeployUniverse( contractAddresses.BatchBHSAddress = DeployBatchBHS(e, contractAddresses.BhsContractAddress) } - if contractAddresses.CoordinatorAddress.String() == "0x0000000000000000000000000000000000000000" { - fmt.Println("\nDeploying Coordinator...") - contractAddresses.CoordinatorAddress = DeployCoordinator(e, contractAddresses.LinkAddress, contractAddresses.BhsContractAddress.String(), contractAddresses.LinkEthAddress) + if useTestCoordinator { + if contractAddresses.CoordinatorAddress.String() == "0x0000000000000000000000000000000000000000" { + fmt.Println("\nDeploying Test Coordinator...") + contractAddresses.CoordinatorAddress = DeployTestCoordinator(e, contractAddresses.LinkAddress, contractAddresses.BhsContractAddress.String(), contractAddresses.LinkEthAddress) + } + } else { + if contractAddresses.CoordinatorAddress.String() == "0x0000000000000000000000000000000000000000" { + fmt.Println("\nDeploying Coordinator...") + contractAddresses.CoordinatorAddress = DeployCoordinator(e, contractAddresses.LinkAddress, contractAddresses.BhsContractAddress.String(), contractAddresses.LinkEthAddress) + } } coordinator, err := vrf_coordinator_v2.NewVRFCoordinatorV2(contractAddresses.CoordinatorAddress, e.Ec) @@ -310,14 +334,19 @@ func VRFV2DeployUniverse( formattedVrfPrimaryJobSpec := fmt.Sprintf( jobs.VRFV2JobFormatted, - contractAddresses.CoordinatorAddress, //coordinatorAddress - contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress - batchFulfillmentEnabled, //batchFulfillmentEnabled - compressedPkHex, //publicKey - coordinatorConfig.MinConfs, //minIncomingConfirmations - e.ChainID, //evmChainID + contractAddresses.CoordinatorAddress, //coordinatorAddress + contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress + coordinatorJobSpecConfig.BatchFulfillmentEnabled, //batchFulfillmentEnabled + coordinatorJobSpecConfig.BatchFulfillmentGasMultiplier, //batchFulfillmentGasMultiplier + coordinatorJobSpecConfig.RevertsPipelineEnabled, //revertsPipelineEnabled + compressedPkHex, //publicKey + coordinatorConfig.MinConfs, //minIncomingConfirmations + e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses + coordinatorJobSpecConfig.PollPeriod, //pollPeriod + coordinatorJobSpecConfig.RequestTimeout, //requestTimeout contractAddresses.CoordinatorAddress, + coordinatorJobSpecConfig.EstimateGasMultiplier, //estimateGasMultiplier func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address @@ -336,14 +365,19 @@ func VRFV2DeployUniverse( formattedVrfBackupJobSpec := fmt.Sprintf( jobs.VRFV2JobFormatted, - contractAddresses.CoordinatorAddress, //coordinatorAddress - contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress - batchFulfillmentEnabled, //batchFulfillmentEnabled - compressedPkHex, //publicKey - 100, //minIncomingConfirmations - e.ChainID, //evmChainID + contractAddresses.CoordinatorAddress, //coordinatorAddress + contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress + coordinatorJobSpecConfig.BatchFulfillmentEnabled, //batchFulfillmentEnabled + coordinatorJobSpecConfig.BatchFulfillmentGasMultiplier, //batchFulfillmentGasMultiplier + coordinatorJobSpecConfig.RevertsPipelineEnabled, //revertsPipelineEnabled + compressedPkHex, //publicKey + 100, //minIncomingConfirmations + e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFBackupNodeName].SendingKeys), "\",\""), //fromAddresses + coordinatorJobSpecConfig.PollPeriod, //pollPeriod + coordinatorJobSpecConfig.RequestTimeout, //requestTimeout contractAddresses.CoordinatorAddress, + coordinatorJobSpecConfig.EstimateGasMultiplier, //estimateGasMultiplier func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address diff --git a/core/scripts/vrfv2/testnet/v2scripts/util.go b/core/scripts/vrfv2/testnet/v2scripts/util.go index 45cda3d2aa9..325ced2ab21 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/util.go +++ b/core/scripts/vrfv2/testnet/v2scripts/util.go @@ -7,6 +7,8 @@ import ( "fmt" "math/big" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_test_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_load_test_with_metrics" "github.com/ethereum/go-ethereum/common" @@ -19,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_external_sub_owner_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2_wrapper_consumer_example" @@ -53,6 +54,22 @@ func DeployCoordinator( return helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID) } +func DeployTestCoordinator( + e helpers.Environment, + linkAddress string, + bhsAddress string, + linkEthAddress string, +) (coordinatorAddress common.Address) { + _, tx, _, err := vrf_coordinator_test_v2.DeployVRFCoordinatorTestV2( + e.Owner, + e.Ec, + common.HexToAddress(linkAddress), + common.HexToAddress(bhsAddress), + common.HexToAddress(linkEthAddress)) + helpers.PanicErr(err) + return helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID) +} + func DeployBatchCoordinatorV2(e helpers.Environment, coordinatorAddress common.Address) (batchCoordinatorAddress common.Address) { _, tx, _, err := batch_vrf_coordinator_v2.DeployBatchVRFCoordinatorV2(e.Owner, e.Ec, coordinatorAddress) helpers.PanicErr(err) diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index 97d987dac29..320576055b5 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -564,7 +564,6 @@ func main() { coordinatorRegisterKey := flag.NewFlagSet("coordinator-register-key", flag.ExitOnError) registerKeyAddress := coordinatorRegisterKey.String("address", "", "coordinator address") registerKeyUncompressedPubKey := coordinatorRegisterKey.String("pubkey", "", "uncompressed pubkey") - registerKeyOracleAddress := coordinatorRegisterKey.String("oracle-address", "", "oracle address") helpers.ParseArgs(coordinatorRegisterKey, os.Args[2:], "address", "pubkey", "oracle-address") coordinator, err := vrf_coordinator_v2_5.NewVRFCoordinatorV25(common.HexToAddress(*registerKeyAddress), e.Ec) helpers.PanicErr(err) @@ -574,7 +573,7 @@ func main() { *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) } - v2plusscripts.RegisterCoordinatorProvingKey(e, *coordinator, *registerKeyUncompressedPubKey, *registerKeyOracleAddress) + v2plusscripts.RegisterCoordinatorProvingKey(e, *coordinator, *registerKeyUncompressedPubKey) case "coordinator-deregister-key": coordinatorDeregisterKey := flag.NewFlagSet("coordinator-deregister-key", flag.ExitOnError) deregisterKeyAddress := coordinatorDeregisterKey.String("address", "", "coordinator address") diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go index 65e4cca2abf..09c93ff879b 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go @@ -476,12 +476,14 @@ func DeployUniverseViaCLI(e helpers.Environment) { subscriptionBalanceNativeWeiString := deployCmd.String("subscription-balance-native", "1e18", "amount to fund subscription with native token (Wei)") batchFulfillmentEnabled := deployCmd.Bool("batch-fulfillment-enabled", constants.BatchFulfillmentEnabled, "whether send randomness fulfillments in batches inside one tx from CL node") + batchFulfillmentGasMultiplier := deployCmd.Float64("batch-fulfillment-gas-multiplier", 1.1, "") + estimateGasMultiplier := deployCmd.Float64("estimate-gas-multiplier", 1.1, "") + pollPeriod := deployCmd.String("poll-period", "300ms", "") + requestTimeout := deployCmd.String("request-timeout", "30m0s", "") // optional flags fallbackWeiPerUnitLinkString := deployCmd.String("fallback-wei-per-unit-link", "6e16", "fallback wei/link ratio") registerVRFKeyUncompressedPubKey := deployCmd.String("uncompressed-pub-key", "", "uncompressed public key") - registerVRFKeyAgainstAddress := deployCmd.String("register-vrf-key-against-address", "", "VRF Key registration against address - "+ - "from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments") vrfPrimaryNodeSendingKeysString := deployCmd.String("vrf-primary-node-sending-keys", "", "VRF Primary Node sending keys") minConfs := deployCmd.Int("min-confs", constants.MinConfs, "min confs") @@ -512,10 +514,7 @@ func DeployUniverseViaCLI(e helpers.Environment) { nodesMap := make(map[string]model.Node) - fundingAmount, ok := new(big.Int).SetString(*nodeSendingKeyFundingAmount, 10) - if !ok { - panic(fmt.Sprintf("failed to parse node sending key funding amount '%s'", *nodeSendingKeyFundingAmount)) - } + fundingAmount := decimal.RequireFromString(*nodeSendingKeyFundingAmount).BigInt() nodesMap[model.VRFPrimaryNodeName] = model.Node{ SendingKeys: util.MapToSendingKeyArr(vrfPrimaryNodeSendingKeys), SendingKeyFundingAmount: fundingAmount, @@ -546,7 +545,14 @@ func DeployUniverseViaCLI(e helpers.Environment) { vrfKeyRegistrationConfig := model.VRFKeyRegistrationConfig{ VRFKeyUncompressedPubKey: *registerVRFKeyUncompressedPubKey, - RegisterAgainstAddress: *registerVRFKeyAgainstAddress, + } + + coordinatorJobSpecConfig := model.CoordinatorJobSpecConfig{ + BatchFulfillmentEnabled: *batchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *batchFulfillmentGasMultiplier, + EstimateGasMultiplier: *estimateGasMultiplier, + PollPeriod: *pollPeriod, + RequestTimeout: *requestTimeout, } VRFV2PlusDeployUniverse( @@ -556,8 +562,8 @@ func DeployUniverseViaCLI(e helpers.Environment) { vrfKeyRegistrationConfig, contractAddresses, coordinatorConfig, - *batchFulfillmentEnabled, nodesMap, + coordinatorJobSpecConfig, ) vrfPrimaryNode := nodesMap[model.VRFPrimaryNodeName] @@ -573,8 +579,8 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, vrfKeyRegistrationConfig model.VRFKeyRegistrationConfig, contractAddresses model.ContractAddresses, coordinatorConfig CoordinatorConfigV2Plus, - batchFulfillmentEnabled bool, nodesMap map[string]model.Node, + coordinatorJobSpecConfig model.CoordinatorJobSpecConfig, ) model.JobSpecs { var compressedPkHex string var keyHash common.Hash @@ -656,7 +662,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, //NOTE - register proving key against EOA account, and not against Oracle's sending address in other to be able // easily withdraw funds from Coordinator contract back to EOA account - RegisterCoordinatorProvingKey(e, *coordinator, vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, vrfKeyRegistrationConfig.RegisterAgainstAddress) + RegisterCoordinatorProvingKey(e, *coordinator, vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) fmt.Println("\nProving key registered, getting proving key hashes from deployed contract...") _, _, provingKeyHashes, configErr := coordinator.GetRequestConfig(nil) @@ -698,14 +704,19 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, formattedVrfV2PlusPrimaryJobSpec := fmt.Sprintf( jobs.VRFV2PlusJobFormatted, - contractAddresses.CoordinatorAddress, //coordinatorAddress - contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress - batchFulfillmentEnabled, //batchFulfillmentEnabled - compressedPkHex, //publicKey - coordinatorConfig.MinConfs, //minIncomingConfirmations - e.ChainID, //evmChainID + contractAddresses.CoordinatorAddress, //coordinatorAddress + contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress + coordinatorJobSpecConfig.BatchFulfillmentEnabled, //batchFulfillmentEnabled + coordinatorJobSpecConfig.BatchFulfillmentGasMultiplier, //batchFulfillmentGasMultiplier + strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses + compressedPkHex, //publicKey + coordinatorConfig.MinConfs, //minIncomingConfirmations + e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses + coordinatorJobSpecConfig.PollPeriod, //pollPeriod + coordinatorJobSpecConfig.RequestTimeout, //requestTimeout contractAddresses.CoordinatorAddress, + coordinatorJobSpecConfig.EstimateGasMultiplier, //estimateGasMultiplier func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address @@ -718,14 +729,18 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, formattedVrfV2PlusBackupJobSpec := fmt.Sprintf( jobs.VRFV2PlusJobFormatted, - contractAddresses.CoordinatorAddress, //coordinatorAddress - contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress - batchFulfillmentEnabled, //batchFulfillmentEnabled - compressedPkHex, //publicKey - 100, //minIncomingConfirmations - e.ChainID, //evmChainID + contractAddresses.CoordinatorAddress, //coordinatorAddress + contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress + coordinatorJobSpecConfig.BatchFulfillmentEnabled, //batchFulfillmentEnabled + coordinatorJobSpecConfig.BatchFulfillmentGasMultiplier, //batchFulfillmentGasMultiplier + compressedPkHex, //publicKey + 100, //minIncomingConfirmations + e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFBackupNodeName].SendingKeys), "\",\""), //fromAddresses + coordinatorJobSpecConfig.PollPeriod, //pollPeriod + coordinatorJobSpecConfig.RequestTimeout, //requestTimeout contractAddresses.CoordinatorAddress, + coordinatorJobSpecConfig.EstimateGasMultiplier, //estimateGasMultiplier func() string { if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { return keys[0].Address @@ -737,7 +752,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, ) formattedBHSJobSpec := fmt.Sprintf( - jobs.BHSJobFormatted, + jobs.BHSPlusJobFormatted, contractAddresses.CoordinatorAddress, //coordinatorAddress 30, //waitBlocks 200, //lookbackBlocks @@ -747,7 +762,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, ) formattedBHSBackupJobSpec := fmt.Sprintf( - jobs.BHSJobFormatted, + jobs.BHSPlusJobFormatted, contractAddresses.CoordinatorAddress, //coordinatorAddress 100, //waitBlocks 200, //lookbackBlocks @@ -757,7 +772,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, ) formattedBHFJobSpec := fmt.Sprintf( - jobs.BHFJobFormatted, + jobs.BHFPlusJobFormatted, contractAddresses.CoordinatorAddress, //coordinatorAddress contractAddresses.BhsContractAddress, //bhs adreess contractAddresses.BatchBHSAddress, //batchBHS diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go index 48b196b409f..7b38dc7b391 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go @@ -176,7 +176,7 @@ func SetCoordinatorConfig( } func RegisterCoordinatorProvingKey(e helpers.Environment, - coordinator vrf_coordinator_v2_5.VRFCoordinatorV25, uncompressed string, oracleAddress string) { + coordinator vrf_coordinator_v2_5.VRFCoordinatorV25, uncompressed string) { pubBytes, err := hex.DecodeString(uncompressed) helpers.PanicErr(err) pk, err := crypto.UnmarshalPubkey(pubBytes) @@ -190,7 +190,6 @@ func RegisterCoordinatorProvingKey(e helpers.Environment, tx, e.ChainID, fmt.Sprintf("Uncompressed public key: %s,", uncompressed), - fmt.Sprintf("Oracle address: %s,", oracleAddress), ) } diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go b/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go index aa2ac7f59ea..36d266fec2d 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go @@ -4,7 +4,7 @@ import "time" type VRFV2Config struct { ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with - CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"1000"` // Max gas price in GWei for the chainlink node + CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"10"` // Max gas price in GWei for the chainlink node IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_models.go b/integration-tests/actions/vrfv2_actions/vrfv2_models.go index aa594a753dd..54449eef372 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_models.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_models.go @@ -19,6 +19,7 @@ type VRFV2JobInfo struct { type VRFV2Contracts struct { Coordinator contracts.VRFCoordinatorV2 + VRFOwner contracts.VRFOwner BHS contracts.BlockHashStore LoadTestConsumers []contracts.VRFv2LoadTestConsumer } diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index d6288e58bf6..15148ce9682 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" "github.com/google/uuid" @@ -48,54 +49,106 @@ var ( ErrAddConsumerToSub = "error adding consumer to VRF Subscription" ErrFundSubWithLinkToken = "error funding subscription with Link tokens" ErrCreateVRFV2Jobs = "error creating VRF V2 Jobs" - ErrGetPrimaryKey = "error getting primary ETH key address" ErrRestartCLNode = "error restarting CL node" ErrWaitTXsComplete = "error waiting for TXs to complete" ErrRequestRandomness = "error requesting randomness" + ErrLoadingCoordinator = "error loading coordinator contract" ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event" ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event" ErrDeployWrapper = "error deploying VRFV2PlusWrapper" ) +type VRFOwnerConfig struct { + OwnerAddress string + useVRFOwner bool +} + +type VRFJobSpecConfig struct { + ForwardingAllowed bool + CoordinatorAddress string + FromAddresses []string + EVMChainID string + MinIncomingConfirmations int + PublicKey string + BatchFulfillmentEnabled bool + BatchFulfillmentGasMultiplier float64 + EstimateGasMultiplier float64 + PollPeriod time.Duration + RequestTimeout time.Duration + VRFOwnerConfig VRFOwnerConfig +} + func DeployVRFV2Contracts( - contractDeployer contracts.ContractDeployer, - chainClient blockchain.EVMClient, + env *test_env.CLClusterTestEnv, linkTokenContract contracts.LinkToken, linkEthFeedContract contracts.MockETHLINKFeed, consumerContractsAmount int, + useVRFOwner bool, + useTestCoordinator bool, ) (*VRFV2Contracts, error) { - bhs, err := contractDeployer.DeployBlockhashStore() + bhs, err := env.ContractDeployer.DeployBlockhashStore() if err != nil { return nil, fmt.Errorf("%s, err %w", ErrDeployBlockHashStore, err) } - err = chainClient.WaitForEvents() + err = env.EVMClient.WaitForEvents() if err != nil { return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - coordinator, err := contractDeployer.DeployVRFCoordinatorV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) + + var coordinatorAddress string + if useTestCoordinator { + testCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorTestV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + coordinatorAddress = testCoordinator.Address() + } else { + coordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + coordinatorAddress = coordinator.Address() + } + consumers, err := DeployVRFV2Consumers(env.ContractDeployer, coordinatorAddress, consumerContractsAmount) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + return nil, err } - err = chainClient.WaitForEvents() + err = env.EVMClient.WaitForEvents() if err != nil { return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - consumers, err := DeployVRFV2Consumers(contractDeployer, coordinator, consumerContractsAmount) + + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(coordinatorAddress) if err != nil { - return nil, err + return nil, fmt.Errorf("%s, err %w", ErrLoadingCoordinator, err) } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + if useVRFOwner { + vrfOwner, err := env.ContractDeployer.DeployVRFOwner(coordinatorAddress) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return &VRFV2Contracts{coordinator, vrfOwner, bhs, consumers}, nil } - return &VRFV2Contracts{coordinator, bhs, consumers}, nil + return &VRFV2Contracts{coordinator, nil, bhs, consumers}, nil } -func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinator contracts.VRFCoordinatorV2, consumerContractsAmount int) ([]contracts.VRFv2LoadTestConsumer, error) { +func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinatorAddress string, consumerContractsAmount int) ([]contracts.VRFv2LoadTestConsumer, error) { var consumers []contracts.VRFv2LoadTestConsumer for i := 1; i <= consumerContractsAmount; i++ { - loadTestConsumer, err := contractDeployer.DeployVRFv2LoadTestConsumer(coordinator.Address()) + loadTestConsumer, err := contractDeployer.DeployVRFv2LoadTestConsumer(coordinatorAddress) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) } @@ -146,36 +199,47 @@ func DeployVRFV2DirectFundingContracts( func CreateVRFV2Job( chainlinkNode *client.ChainlinkClient, - coordinatorAddress string, - nativeTokenKeyAddresses []string, - pubKeyCompressed string, - chainID string, - minIncomingConfirmations uint16, + vrfJobSpecConfig VRFJobSpecConfig, ) (*client.Job, error) { jobUUID := uuid.New() os := &client.VRFV2TxPipelineSpec{ - Address: coordinatorAddress, + Address: vrfJobSpecConfig.CoordinatorAddress, + EstimateGasMultiplier: vrfJobSpecConfig.EstimateGasMultiplier, + FromAddress: vrfJobSpecConfig.FromAddresses[0], } ost, err := os.String() if err != nil { return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) } - job, err := chainlinkNode.MustCreateJob(&client.VRFV2JobSpec{ - Name: fmt.Sprintf("vrf-v2-%s", jobUUID), - CoordinatorAddress: coordinatorAddress, - FromAddresses: nativeTokenKeyAddresses, - EVMChainID: chainID, - MinIncomingConfirmations: int(minIncomingConfirmations), - PublicKey: pubKeyCompressed, - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - BatchFulfillmentEnabled: false, - }) + spec := &client.VRFV2JobSpec{ + Name: fmt.Sprintf("vrf-v2-%s", jobUUID), + ForwardingAllowed: vrfJobSpecConfig.ForwardingAllowed, + CoordinatorAddress: vrfJobSpecConfig.CoordinatorAddress, + FromAddresses: vrfJobSpecConfig.FromAddresses, + EVMChainID: vrfJobSpecConfig.EVMChainID, + MinIncomingConfirmations: vrfJobSpecConfig.MinIncomingConfirmations, + PublicKey: vrfJobSpecConfig.PublicKey, + ExternalJobID: jobUUID.String(), + ObservationSource: ost, + BatchFulfillmentEnabled: vrfJobSpecConfig.BatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: vrfJobSpecConfig.BatchFulfillmentGasMultiplier, + PollPeriod: vrfJobSpecConfig.PollPeriod, + RequestTimeout: vrfJobSpecConfig.RequestTimeout, + } + if vrfJobSpecConfig.VRFOwnerConfig.useVRFOwner { + spec.VRFOwner = vrfJobSpecConfig.VRFOwnerConfig.OwnerAddress + spec.UseVRFOwner = true + } + + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) + + } + job, err := chainlinkNode.MustCreateJob(spec) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) } - return job, nil } @@ -220,6 +284,8 @@ func FundVRFCoordinatorV2Subscription( func SetupVRFV2Environment( env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, + useVRFOwner bool, + useTestCoordinator bool, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, registerProvingKeyAgainstAddress string, @@ -231,15 +297,17 @@ func SetupVRFV2Environment( l.Info().Msg("Starting VRFV2 environment setup") l.Info().Msg("Deploying VRFV2 contracts") vrfv2Contracts, err := DeployVRFV2Contracts( - env.ContractDeployer, - env.EVMClient, + env, linkToken, mockNativeLINKFeed, numberOfConsumers, + useVRFOwner, + useTestCoordinator, ) if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) } + vrfCoordinatorV2FeeConfig := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ FulfillmentFlatFeeLinkPPMTier1: vrfv2Config.FulfillmentFlatFeeLinkPPMTier1, FulfillmentFlatFeeLinkPPMTier2: vrfv2Config.FulfillmentFlatFeeLinkPPMTier2, @@ -257,7 +325,7 @@ func SetupVRFV2Environment( vrfv2Config.MaxGasLimitCoordinatorConfig, vrfv2Config.StalenessSeconds, vrfv2Config.GasAfterPaymentCalculation, - big.NewInt(vrfv2Config.LinkNativeFeedResponse), + big.NewInt(vrfv2Config.FallbackWeiPerUnitLink), vrfCoordinatorV2FeeConfig, ) if err != nil { @@ -305,16 +373,49 @@ func SetupVRFV2Environment( if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } - allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) + allNativeTokenKeyAddressStrings := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) + allNativeTokenKeyAddresses := make([]common.Address, len(allNativeTokenKeyAddressStrings)) + + for _, addressString := range allNativeTokenKeyAddressStrings { + allNativeTokenKeyAddresses = append(allNativeTokenKeyAddresses, common.HexToAddress(addressString)) + } + + var vrfOwnerConfig VRFOwnerConfig + if useVRFOwner { + err := setupVRFOwnerContract(env, vrfv2Contracts, allNativeTokenKeyAddressStrings, allNativeTokenKeyAddresses, l) + if err != nil { + return nil, nil, nil, err + } + vrfOwnerConfig = VRFOwnerConfig{ + OwnerAddress: vrfv2Contracts.VRFOwner.Address(), + useVRFOwner: useVRFOwner, + } + } else { + vrfOwnerConfig = VRFOwnerConfig{ + OwnerAddress: "", + useVRFOwner: useVRFOwner, + } + } + + vrfJobSpecConfig := VRFJobSpecConfig{ + ForwardingAllowed: false, + CoordinatorAddress: vrfv2Contracts.Coordinator.Address(), + FromAddresses: allNativeTokenKeyAddressStrings, + EVMChainID: chainID.String(), + MinIncomingConfirmations: int(vrfv2Config.MinimumConfirmations), + PublicKey: pubKeyCompressed, + EstimateGasMultiplier: 1, + BatchFulfillmentEnabled: false, + BatchFulfillmentGasMultiplier: 1.15, + PollPeriod: time.Second * 1, + RequestTimeout: time.Hour * 24, + VRFOwnerConfig: vrfOwnerConfig, + } - l.Info().Msg("Creating VRFV2 Job") + l.Info().Msg("Creating VRFV2 Job") vrfV2job, err := CreateVRFV2Job( env.ClCluster.NodeAPIs()[0], - vrfv2Contracts.Coordinator.Address(), - allNativeTokenKeyAddresses, - pubKeyCompressed, - chainID.String(), - vrfv2Config.MinimumConfirmations, + vrfJobSpecConfig, ) if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreateVRFV2Jobs, err) @@ -324,7 +425,7 @@ func SetupVRFV2Environment( // [[EVM.KeySpecific]] // Key = '...' nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2Config.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddressStrings, vrfv2Config.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -349,6 +450,46 @@ func SetupVRFV2Environment( return vrfv2Contracts, subIDs, &data, nil } +func setupVRFOwnerContract(env *test_env.CLClusterTestEnv, vrfv2Contracts *VRFV2Contracts, allNativeTokenKeyAddressStrings []string, allNativeTokenKeyAddresses []common.Address, l zerolog.Logger) error { + l.Info().Msg("Setting up VRFOwner contract") + l.Info(). + Str("Coordinator", vrfv2Contracts.Coordinator.Address()). + Str("VRFOwner", vrfv2Contracts.VRFOwner.Address()). + Msg("Transferring ownership of Coordinator to VRFOwner") + err := vrfv2Contracts.Coordinator.TransferOwnership(common.HexToAddress(vrfv2Contracts.VRFOwner.Address())) + if err != nil { + return nil + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil + } + l.Info(). + Str("VRFOwner", vrfv2Contracts.VRFOwner.Address()). + Msg("Accepting VRF Ownership") + err = vrfv2Contracts.VRFOwner.AcceptVRFOwnership() + if err != nil { + return nil + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil + } + l.Info(). + Strs("Authorized Senders", allNativeTokenKeyAddressStrings). + Str("VRFOwner", vrfv2Contracts.VRFOwner.Address()). + Msg("Setting authorized senders for VRFOwner contract") + err = vrfv2Contracts.VRFOwner.SetAuthorizedSenders(allNativeTokenKeyAddresses) + if err != nil { + return nil + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return err +} + func SetupVRFV2WrapperEnvironment( env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, @@ -402,7 +543,7 @@ func SetupVRFV2WrapperEnvironment( } // Fund wrapper subscription - err = FundSubscriptions(env, vrfv2Config, linkToken, coordinator, []uint64{wrapperSubID}) + err = FundSubscriptions(env, vrfv2Config.SubscriptionFundingAmountLink, linkToken, coordinator, []uint64{wrapperSubID}) if err != nil { return nil, nil, err } @@ -491,7 +632,7 @@ func CreateSubsAndFund( if err != nil { return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = FundSubscriptions(env, vrfv2Config, linkToken, coordinator, subs) + err = FundSubscriptions(env, vrfv2Config.SubscriptionFundingAmountLink, linkToken, coordinator, subs) if err != nil { return nil, err } @@ -553,14 +694,14 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts func FundSubscriptions( env *test_env.CLClusterTestEnv, - vrfv2Config vrfv2_config.VRFV2Config, + fundingAmountLink float64, linkAddress contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, subIDs []uint64, ) error { for _, subID := range subIDs { //Link Billing - amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2Config.SubscriptionFundingAmountLink)) + amountJuels := conversions.EtherToWei(big.NewFloat(fundingAmountLink)) err := FundVRFCoordinatorV2Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) @@ -642,6 +783,104 @@ func RequestRandomnessAndWaitForFulfillment( return fulfillmentEvents, err } +func RequestRandomnessWithForceFulfillAndWaitForFulfillment( + consumer contracts.VRFv2LoadTestConsumer, + coordinator contracts.VRFCoordinatorV2, + vrfOwner contracts.VRFOwner, + vrfv2Data *VRFV2Data, + randomnessRequestCountPerRequest uint16, + vrfv2Config vrfv2_config.VRFV2Config, + subTopUpAmount *big.Int, + linkAddress common.Address, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, *vrf_owner.VRFOwnerRandomWordsForced, error) { + logRandRequest(consumer.Address(), coordinator.Address(), 0, vrfv2Config, l) + _, err := consumer.RequestRandomWordsWithForceFulfill( + vrfv2Data.KeyHash, + vrfv2Config.MinimumConfirmations, + vrfv2Config.CallbackGasLimit, + vrfv2Config.NumberOfWords, + randomnessRequestCountPerRequest, + subTopUpAmount, + linkAddress, + ) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + } + + randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfv2Data.KeyHash}, + nil, + []common.Address{common.HexToAddress(consumer.Address())}, + time.Minute*1, + ) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) + } + LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) + + errorChannel := make(chan error) + configSetEventChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2ConfigSet) + randWordsFulfilledEventChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled) + randWordsForcedEventChannel := make(chan *vrf_owner.VRFOwnerRandomWordsForced) + + go func() { + configSetEvent, err := coordinator.WaitForConfigSetEvent( + randomWordsFulfilledEventTimeout, + ) + if err != nil { + l.Error().Err(err).Msg("error waiting for ConfigSetEvent") + errorChannel <- err + } + configSetEventChannel <- configSetEvent + }() + + go func() { + randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( + []*big.Int{randomWordsRequestedEvent.RequestId}, + randomWordsFulfilledEventTimeout, + ) + if err != nil { + l.Error().Err(err).Msg("error waiting for RandomWordsFulfilledEvent") + errorChannel <- err + } + randWordsFulfilledEventChannel <- randomWordsFulfilledEvent + }() + + go func() { + randomWordsForcedEvent, err := vrfOwner.WaitForRandomWordsForcedEvent( + []*big.Int{randomWordsRequestedEvent.RequestId}, + nil, + nil, + randomWordsFulfilledEventTimeout, + ) + if err != nil { + l.Error().Err(err).Msg("error waiting for RandomWordsForcedEvent") + errorChannel <- err + } + randWordsForcedEventChannel <- randomWordsForcedEvent + }() + + var configSetEvent *vrf_coordinator_v2.VRFCoordinatorV2ConfigSet + var randomWordsFulfilledEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled + var randomWordsForcedEvent *vrf_owner.VRFOwnerRandomWordsForced + for i := 0; i < 3; i++ { + select { + case err = <-errorChannel: + return nil, nil, nil, err + case configSetEvent = <-configSetEventChannel: + case randomWordsFulfilledEvent = <-randWordsFulfilledEventChannel: + LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent) + case randomWordsForcedEvent = <-randWordsForcedEventChannel: + LogRandomWordsForcedEvent(l, vrfOwner, randomWordsForcedEvent) + case <-time.After(randomWordsFulfilledEventTimeout): + err = fmt.Errorf("timeout waiting for ConfigSet, RandomWordsFulfilled and RandomWordsForced events") + } + } + return configSetEvent, randomWordsFulfilledEvent, randomWordsForcedEvent, err +} + func WaitForRequestAndFulfillmentEvents( consumerAddress string, coordinator contracts.VRFCoordinatorV2, @@ -759,6 +998,20 @@ func LogRandomWordsFulfilledEvent( Msg("RandomWordsFulfilled Event (TX metadata)") } +func LogRandomWordsForcedEvent( + l zerolog.Logger, + vrfOwner contracts.VRFOwner, + randomWordsForcedEvent *vrf_owner.VRFOwnerRandomWordsForced, +) { + l.Debug(). + Str("VRFOwner", vrfOwner.Address()). + Uint64("Sub ID", randomWordsForcedEvent.SubId). + Str("TX Hash", randomWordsForcedEvent.Raw.TxHash.String()). + Str("Request ID", randomWordsForcedEvent.RequestId.String()). + Str("Sender", randomWordsForcedEvent.Sender.String()). + Msg("RandomWordsForced Event (TX metadata)") +} + func logRandRequest( consumer string, coordinator string, diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go index 8cd6e0a8ce8..233b35a2383 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go @@ -2,10 +2,16 @@ package vrfv2plus_config import "time" +const ( + BILLING_TYPE_LINK = "LINK" + BILLING_TYPE_NATIVE = "NATIVE" + BILLING_TYPE_LINK_AND_NATIVE = "LINK_AND_NATIVE" +) + type VRFV2PlusConfig struct { ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with - CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"1000"` // Max gas price in GWei for the chainlink node - IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token + CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"10"` // Max gas price in GWei for the chainlink node + SubscriptionBillingType string `envconfig:"SUBSCRIPTION_BILLING_TYPE" default:"LINK_AND_NATIVE"` // Whether to use native payment or LINK token, or both LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator SubscriptionFundingAmountLink float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"5"` // Amount of LINK to fund the subscription with diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index d0c83e85841..b19efd1d677 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -65,6 +65,20 @@ var ( ErrDeployWrapper = "error deploying VRFV2PlusWrapper" ) +type VRFJobSpecConfig struct { + ForwardingAllowed bool + CoordinatorAddress string + FromAddresses []string + EVMChainID string + MinIncomingConfirmations int + PublicKey string + BatchFulfillmentEnabled bool + BatchFulfillmentGasMultiplier float64 + EstimateGasMultiplier float64 + PollPeriod time.Duration + RequestTimeout time.Duration +} + func DeployVRFV2_5Contracts( contractDeployer contracts.ContractDeployer, chainClient blockchain.EVMClient, @@ -111,15 +125,13 @@ func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coord func CreateVRFV2PlusJob( chainlinkNode *client.ChainlinkClient, - coordinatorAddress string, - nativeTokenKeyAddresses []string, - pubKeyCompressed string, - chainID string, - minIncomingConfirmations uint16, + vrfJobSpecConfig VRFJobSpecConfig, ) (*client.Job, error) { jobUUID := uuid.New() os := &client.VRFV2PlusTxPipelineSpec{ - Address: coordinatorAddress, + Address: vrfJobSpecConfig.CoordinatorAddress, + EstimateGasMultiplier: vrfJobSpecConfig.EstimateGasMultiplier, + FromAddress: vrfJobSpecConfig.FromAddresses[0], } ost, err := os.String() if err != nil { @@ -127,16 +139,18 @@ func CreateVRFV2PlusJob( } job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ - Name: fmt.Sprintf("vrf-v2-plus-%s", jobUUID), - CoordinatorAddress: coordinatorAddress, - FromAddresses: nativeTokenKeyAddresses, - EVMChainID: chainID, - MinIncomingConfirmations: int(minIncomingConfirmations), - PublicKey: pubKeyCompressed, - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - BatchFulfillmentEnabled: false, - PollPeriod: time.Second, + Name: fmt.Sprintf("vrf-v2-plus-%s", jobUUID), + CoordinatorAddress: vrfJobSpecConfig.CoordinatorAddress, + FromAddresses: vrfJobSpecConfig.FromAddresses, + EVMChainID: vrfJobSpecConfig.EVMChainID, + MinIncomingConfirmations: vrfJobSpecConfig.MinIncomingConfirmations, + PublicKey: vrfJobSpecConfig.PublicKey, + ExternalJobID: jobUUID.String(), + ObservationSource: ost, + BatchFulfillmentEnabled: vrfJobSpecConfig.BatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: vrfJobSpecConfig.BatchFulfillmentGasMultiplier, + PollPeriod: vrfJobSpecConfig.PollPeriod, + RequestTimeout: vrfJobSpecConfig.RequestTimeout, }) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusJob, err) @@ -221,7 +235,7 @@ func SetupVRFV2_5Environment( vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, vrfv2PlusConfig.StalenessSeconds, vrfv2PlusConfig.GasAfterPaymentCalculation, - big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse), + big.NewInt(vrfv2PlusConfig.FallbackWeiPerUnitLink), vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig{ FulfillmentFlatFeeLinkPPM: vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, FulfillmentFlatFeeNativePPM: vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, @@ -280,14 +294,24 @@ func SetupVRFV2_5Environment( } allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) + vrfJobSpecConfig := VRFJobSpecConfig{ + ForwardingAllowed: false, + CoordinatorAddress: vrfv2_5Contracts.Coordinator.Address(), + FromAddresses: allNativeTokenKeyAddresses, + EVMChainID: chainID.String(), + MinIncomingConfirmations: int(vrfv2PlusConfig.MinimumConfirmations), + PublicKey: pubKeyCompressed, + EstimateGasMultiplier: 1, + BatchFulfillmentEnabled: false, + BatchFulfillmentGasMultiplier: 1.15, + PollPeriod: time.Second * 1, + RequestTimeout: time.Hour * 24, + } + l.Info().Msg("Creating VRFV2 Plus Job") job, err := CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], - vrfv2_5Contracts.Coordinator.Address(), - allNativeTokenKeyAddresses, - pubKeyCompressed, - chainID.String(), - vrfv2PlusConfig.MinimumConfirmations, + vrfJobSpecConfig, ) if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreateVRFV2PlusJobs, err) @@ -459,20 +483,42 @@ func FundSubscriptions( subIDs []*big.Int, ) error { for _, subID := range subIDs { - //Native Billing - amountWei := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) - err := coordinator.FundSubscriptionWithNative( - subID, - amountWei, - ) - if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) - } - //Link Billing - amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) - err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) - if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) + switch vrfv2PlusConfig.SubscriptionBillingType { + case vrfv2plus_config.BILLING_TYPE_NATIVE: + //Native Billing + amountWei := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) + err := coordinator.FundSubscriptionWithNative( + subID, + amountWei, + ) + if err != nil { + return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) + } + case vrfv2plus_config.BILLING_TYPE_LINK: + //Link Billing + amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + err := FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) + if err != nil { + return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) + } + case vrfv2plus_config.BILLING_TYPE_LINK_AND_NATIVE: + //Native Billing + amountWei := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) + err := coordinator.FundSubscriptionWithNative( + subID, + amountWei, + ) + if err != nil { + return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) + } + //Link Billing + amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) + if err != nil { + return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) + } + default: + return fmt.Errorf("invalid billing type: %s", vrfv2PlusConfig.SubscriptionBillingType) } } err := env.EVMClient.WaitForEvents() diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index 9268968800d..665ff3c7465 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -629,7 +629,9 @@ func (d *PipelineSpec) String() (string, error) { // VRFV2TxPipelineSpec VRFv2 request with tx callback type VRFV2PlusTxPipelineSpec struct { - Address string + Address string + EstimateGasMultiplier float64 + FromAddress string } // Type returns the type of the pipeline @@ -651,9 +653,10 @@ generate_proof [type=vrfv2plus topics="$(jobRun.logTopics)"] estimate_gas [type=estimategaslimit to="{{ .Address }}" - multiplier="1.1" + multiplier="{{ .EstimateGasMultiplier }}" data="$(generate_proof.output)"] simulate_fulfillment [type=ethcall + from="{{ .FromAddress }}" to="{{ .Address }}" gas="$(estimate_gas)" gasPrice="$(jobSpec.maxGasPrice)" @@ -666,7 +669,9 @@ decode_log->generate_proof->estimate_gas->simulate_fulfillment` // VRFV2TxPipelineSpec VRFv2 request with tx callback type VRFV2TxPipelineSpec struct { - Address string + Address string + EstimateGasMultiplier float64 + FromAddress string } // Type returns the type of the pipeline @@ -688,9 +693,10 @@ vrf [type=vrfv2 topics="$(jobRun.logTopics)"] estimate_gas [type=estimategaslimit to="{{ .Address }}" - multiplier="1.1" + multiplier="{{ .EstimateGasMultiplier }}" data="$(vrf.output)"] simulate [type=ethcall + from="{{ .FromAddress }}" to="{{ .Address }}" gas="$(estimate_gas)" gasPrice="$(jobSpec.maxGasPrice)" @@ -1123,18 +1129,21 @@ observationSource = """ // VRFV2PlusJobSpec represents a VRFV2 job type VRFV2PlusJobSpec struct { - Name string `toml:"name"` - CoordinatorAddress string `toml:"coordinatorAddress"` // Address of the VRF CoordinatorV2 contract - PublicKey string `toml:"publicKey"` // Public key of the proving key - ExternalJobID string `toml:"externalJobID"` - ObservationSource string `toml:"observationSource"` // List of commands for the Chainlink node - MinIncomingConfirmations int `toml:"minIncomingConfirmations"` - FromAddresses []string `toml:"fromAddresses"` - EVMChainID string `toml:"evmChainID"` - BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` - BackOffInitialDelay time.Duration `toml:"backOffInitialDelay"` - BackOffMaxDelay time.Duration `toml:"backOffMaxDelay"` - PollPeriod time.Duration `toml:"pollPeriod"` + Name string `toml:"name"` + CoordinatorAddress string `toml:"coordinatorAddress"` // Address of the VRF CoordinatorV2 contract + PublicKey string `toml:"publicKey"` // Public key of the proving key + ExternalJobID string `toml:"externalJobID"` + ObservationSource string `toml:"observationSource"` // List of commands for the Chainlink node + MinIncomingConfirmations int `toml:"minIncomingConfirmations"` + FromAddresses []string `toml:"fromAddresses"` + EVMChainID string `toml:"evmChainID"` + ForwardingAllowed bool `toml:"forwardingAllowed"` + BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` + BatchFulfillmentGasMultiplier float64 `toml:"batchFulfillmentGasMultiplier"` + BackOffInitialDelay time.Duration `toml:"backOffInitialDelay"` + BackOffMaxDelay time.Duration `toml:"backOffMaxDelay"` + PollPeriod time.Duration `toml:"pollPeriod"` + RequestTimeout time.Duration `toml:"requestTimeout"` } // Type returns the type of the job @@ -1153,8 +1162,11 @@ minIncomingConfirmations = {{.MinIncomingConfirmations}} publicKey = "{{.PublicKey}}" externalJobID = "{{.ExternalJobID}}" batchFulfillmentEnabled = {{.BatchFulfillmentEnabled}} +batchFulfillmentGasMultiplier = {{.BatchFulfillmentGasMultiplier}} backoffInitialDelay = "{{.BackOffInitialDelay}}" backoffMaxDelay = "{{.BackOffMaxDelay}}" +pollPeriod = "{{.PollPeriod}}" +requestTimeout = "{{.RequestTimeout}}" observationSource = """ {{.ObservationSource}} """ @@ -1164,17 +1176,24 @@ observationSource = """ // VRFV2JobSpec represents a VRFV2 job type VRFV2JobSpec struct { - Name string `toml:"name"` - CoordinatorAddress string `toml:"coordinatorAddress"` // Address of the VRF CoordinatorV2 contract - PublicKey string `toml:"publicKey"` // Public key of the proving key - ExternalJobID string `toml:"externalJobID"` - ObservationSource string `toml:"observationSource"` // List of commands for the Chainlink node - MinIncomingConfirmations int `toml:"minIncomingConfirmations"` - FromAddresses []string `toml:"fromAddresses"` - EVMChainID string `toml:"evmChainID"` - BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` - BackOffInitialDelay time.Duration `toml:"backOffInitialDelay"` - BackOffMaxDelay time.Duration `toml:"backOffMaxDelay"` + Name string `toml:"name"` + CoordinatorAddress string `toml:"coordinatorAddress"` // Address of the VRF CoordinatorV2 contract + PublicKey string `toml:"publicKey"` // Public key of the proving key + ExternalJobID string `toml:"externalJobID"` + ObservationSource string `toml:"observationSource"` // List of commands for the Chainlink node + MinIncomingConfirmations int `toml:"minIncomingConfirmations"` + FromAddresses []string `toml:"fromAddresses"` + EVMChainID string `toml:"evmChainID"` + UseVRFOwner bool `toml:"useVRFOwner"` + VRFOwner string `toml:"vrfOwnerAddress"` + ForwardingAllowed bool `toml:"forwardingAllowed"` + CustomRevertsPipelineEnabled bool `toml:"customRevertsPipelineEnabled"` + PollPeriod time.Duration `toml:"pollPeriod"` + RequestTimeout time.Duration `toml:"requestTimeout"` + BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` + BatchFulfillmentGasMultiplier float64 `toml:"batchFulfillmentGasMultiplier"` + BackOffInitialDelay time.Duration `toml:"backOffInitialDelay"` + BackOffMaxDelay time.Duration `toml:"backOffMaxDelay"` } // Type returns the type of the job @@ -1186,6 +1205,7 @@ func (v *VRFV2JobSpec) String() (string, error) { type = "vrf" schemaVersion = 1 name = "{{.Name}}" +forwardingAllowed = {{.ForwardingAllowed}} coordinatorAddress = "{{.CoordinatorAddress}}" fromAddresses = [{{range .FromAddresses}}"{{.}}",{{end}}] evmChainID = "{{.EVMChainID}}" @@ -1193,8 +1213,13 @@ minIncomingConfirmations = {{.MinIncomingConfirmations}} publicKey = "{{.PublicKey}}" externalJobID = "{{.ExternalJobID}}" batchFulfillmentEnabled = {{.BatchFulfillmentEnabled}} +batchFulfillmentGasMultiplier = {{.BatchFulfillmentGasMultiplier}} backoffInitialDelay = "{{.BackOffInitialDelay}}" backoffMaxDelay = "{{.BackOffMaxDelay}}" +pollPeriod = "{{.PollPeriod}}" +requestTimeout = "{{.RequestTimeout}}" +customRevertsPipelineEnabled = true +{{ if .UseVRFOwner }}vrfOwnerAddress = "{{.VRFOwner}}"{{ else }}{{ end }} observationSource = """ {{.ObservationSource}} """ diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 40694aa5e4e..2acc0a2109e 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -21,6 +21,7 @@ import ( ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_mock_ethlink_aggregator" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_load_test_client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_v1_events_mock" @@ -87,6 +88,7 @@ type ContractDeployer interface { LoadOffChainAggregator(address *common.Address) (OffchainAggregator, error) DeployVRFContract() (VRF, error) DeployMockETHLINKFeed(answer *big.Int) (MockETHLINKFeed, error) + DeployVRFMockETHLINKFeed(answer *big.Int) (VRFMockETHLINKFeed, error) LoadETHLINKFeed(address common.Address) (MockETHLINKFeed, error) DeployMockGasFeed(answer *big.Int) (MockGasFeed, error) LoadGasFeed(address common.Address) (MockGasFeed, error) @@ -113,6 +115,8 @@ type ContractDeployer interface { DeployUpkeepCounter(testRange *big.Int, interval *big.Int) (UpkeepCounter, error) DeployUpkeepPerformCounterRestrictive(testRange *big.Int, averageEligibilityCadence *big.Int) (UpkeepPerformCounterRestrictive, error) DeployVRFConsumer(linkAddr string, coordinatorAddr string) (VRFConsumer, error) + DeployVRFOwner(coordinatorAddr string) (VRFOwner, error) + DeployVRFCoordinatorTestV2(linkAddr string, bhsAddr string, linkEthFeedAddr string) (*EthereumVRFCoordinatorTestV2, error) DeployVRFConsumerV2(linkAddr string, coordinatorAddr string) (VRFConsumerV2, error) DeployVRFv2Consumer(coordinatorAddr string) (VRFv2Consumer, error) DeployVRFv2LoadTestConsumer(coordinatorAddr string) (VRFv2LoadTestConsumer, error) @@ -700,6 +704,23 @@ func (e *EthereumContractDeployer) DeployMockETHLINKFeed(answer *big.Int) (MockE }, err } +func (e *EthereumContractDeployer) DeployVRFMockETHLINKFeed(answer *big.Int) (VRFMockETHLINKFeed, error) { + address, _, instance, err := e.client.DeployContract("VRFMockETHLINKAggregator", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return vrf_mock_ethlink_aggregator.DeployVRFMockETHLINKAggregator(auth, backend, answer) + }) + if err != nil { + return nil, err + } + return &EthereumVRFMockETHLINKFeed{ + client: e.client, + feed: instance.(*vrf_mock_ethlink_aggregator.VRFMockETHLINKAggregator), + address: address, + }, err +} + // LoadETHLINKFeed returns deployed on given address EthereumMockETHLINKFeed func (e *EthereumContractDeployer) LoadETHLINKFeed(address common.Address) (MockETHLINKFeed, error) { instance, err := e.client.LoadContract("MockETHLINKFeed", address, func( diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 44bc3ec2a2c..2faf97df16f 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_load_test_with_metrics" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2_consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" @@ -38,6 +39,9 @@ type VRFCoordinator interface { type VRFCoordinatorV2 interface { GetRequestConfig(ctx context.Context) (GetRequestConfig, error) + GetConfig(ctx context.Context) (vrf_coordinator_v2.GetConfig, error) + GetFallbackWeiPerUnitLink(ctx context.Context) (*big.Int, error) + GetFeeConfig(ctx context.Context) (vrf_coordinator_v2.GetFeeConfig, error) SetConfig( minimumRequestConfirmations uint16, maxGasLimit uint32, @@ -50,11 +54,13 @@ type VRFCoordinatorV2 interface { oracleAddr string, publicProvingKey [2]*big.Int, ) error + TransferOwnership(to common.Address) error HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) CreateSubscription() (*types.Transaction, error) AddConsumer(subId uint64, consumerAddress string) error Address() string GetSubscription(ctx context.Context, subID uint64) (vrf_coordinator_v2.GetSubscription, error) + GetOwner(ctx context.Context) (common.Address, error) PendingRequestsExist(ctx context.Context, subID uint64) (bool, error) OwnerCancelSubscription(subID uint64) (*types.Transaction, error) CancelSubscription(subID uint64, to common.Address) (*types.Transaction, error) @@ -62,6 +68,11 @@ type VRFCoordinatorV2 interface { WaitForRandomWordsFulfilledEvent(requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) + WaitForSubscriptionConsumerAdded(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded, error) + WaitForSubscriptionConsumerRemoved(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved, error) + WaitForSubscriptionCreatedEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated, error) + WaitForSubscriptionFunded(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded, error) + WaitForConfigSetEvent(timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, error) OracleWithdraw(recipient common.Address, amount *big.Int) error } @@ -150,6 +161,13 @@ type VRFV2PlusWrapper interface { GetSubID(ctx context.Context) (*big.Int, error) } +type VRFOwner interface { + Address() string + SetAuthorizedSenders(senders []common.Address) error + AcceptVRFOwnership() error + WaitForRandomWordsForcedEvent(requestIDs []*big.Int, subIds []uint64, senders []common.Address, timeout time.Duration) (*vrf_owner.VRFOwnerRandomWordsForced, error) +} + type VRFConsumer interface { Address() string RequestRandomness(hash [32]byte, fee *big.Int) error @@ -180,6 +198,15 @@ type VRFv2Consumer interface { type VRFv2LoadTestConsumer interface { Address() string RequestRandomness(hash [32]byte, subID uint64, confs uint16, gasLimit uint32, numWords uint32, requestCount uint16) (*types.Transaction, error) + RequestRandomWordsWithForceFulfill( + keyHash [32]byte, + requestConfirmations uint16, + callbackGasLimit uint32, + numWords uint32, + requestCount uint16, + subTopUpAmount *big.Int, + linkAddress common.Address, + ) (*types.Transaction, error) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_load_test_with_metrics.GetRequestStatus, error) GetLastRequestId(ctx context.Context) (*big.Int, error) GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) @@ -281,6 +308,13 @@ type BatchBlockhashStore interface { Address() string } +type VRFMockETHLINKFeed interface { + Address() string + LatestRoundData() (*big.Int, error) + LatestRoundDataUpdatedAt() (*big.Int, error) + SetBlockTimestampDeduction(blockTimestampDeduction *big.Int) error +} + type RequestStatus struct { Fulfilled bool RandomWords []*big.Int diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index 579174e07c6..7501521ac33 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -14,6 +14,9 @@ import ( "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_test_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_mock_ethlink_aggregator" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_consumer_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" @@ -31,6 +34,18 @@ type EthereumVRFCoordinatorV2 struct { coordinator *vrf_coordinator_v2.VRFCoordinatorV2 } +type EthereumVRFOwner struct { + address *common.Address + client blockchain.EVMClient + vrfOwner *vrf_owner.VRFOwner +} + +type EthereumVRFCoordinatorTestV2 struct { + address *common.Address + client blockchain.EVMClient + coordinator *vrf_coordinator_test_v2.VRFCoordinatorTestV2 +} + // EthereumVRFConsumerV2 represents VRFv2 consumer contract type EthereumVRFConsumerV2 struct { address *common.Address @@ -70,6 +85,12 @@ type GetRequestConfig struct { ProvingKeyHashes [32]byte } +type EthereumVRFMockETHLINKFeed struct { + client blockchain.EVMClient + feed *vrf_mock_ethlink_aggregator.VRFMockETHLINKAggregator + address *common.Address +} + // DeployVRFCoordinatorV2 deploys VRFV2 coordinator contract func (e *EthereumContractDeployer) DeployVRFCoordinatorV2(linkAddr string, bhsAddr string, linkEthFeedAddr string) (VRFCoordinatorV2, error) { address, _, instance, err := e.client.DeployContract("VRFCoordinatorV2", func( @@ -88,6 +109,40 @@ func (e *EthereumContractDeployer) DeployVRFCoordinatorV2(linkAddr string, bhsAd }, err } +func (e *EthereumContractDeployer) DeployVRFOwner(coordinatorAddr string) (VRFOwner, error) { + address, _, instance, err := e.client.DeployContract("VRFOwner", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return vrf_owner.DeployVRFOwner(auth, backend, common.HexToAddress(coordinatorAddr)) + }) + if err != nil { + return nil, err + } + return &EthereumVRFOwner{ + client: e.client, + vrfOwner: instance.(*vrf_owner.VRFOwner), + address: address, + }, err +} + +func (e *EthereumContractDeployer) DeployVRFCoordinatorTestV2(linkAddr string, bhsAddr string, linkEthFeedAddr string) (*EthereumVRFCoordinatorTestV2, error) { + address, _, instance, err := e.client.DeployContract("VRFCoordinatorTestV2", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return vrf_coordinator_test_v2.DeployVRFCoordinatorTestV2(auth, backend, common.HexToAddress(linkAddr), common.HexToAddress(bhsAddr), common.HexToAddress(linkEthFeedAddr)) + }) + if err != nil { + return nil, err + } + return &EthereumVRFCoordinatorTestV2{ + client: e.client, + coordinator: instance.(*vrf_coordinator_test_v2.VRFCoordinatorTestV2), + address: address, + }, err +} + // DeployVRFConsumerV2 deploys VRFv@ consumer contract func (e *EthereumContractDeployer) DeployVRFConsumerV2(linkAddr string, coordinatorAddr string) (VRFConsumerV2, error) { address, _, instance, err := e.client.DeployContract("VRFConsumerV2", func( @@ -203,6 +258,18 @@ func (v *EthereumVRFCoordinatorV2) GetSubscription(ctx context.Context, subID ui return subscription, nil } +func (v *EthereumVRFCoordinatorV2) GetOwner(ctx context.Context) (common.Address, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + coordinatorOwnerAddress, err := v.coordinator.Owner(opts) + if err != nil { + return common.Address{}, err + } + return coordinatorOwnerAddress, nil +} + func (v *EthereumVRFCoordinatorV2) GetRequestConfig(ctx context.Context) (GetRequestConfig, error) { opts := &bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), @@ -221,6 +288,42 @@ func (v *EthereumVRFCoordinatorV2) GetRequestConfig(ctx context.Context) (GetReq return requestConfig, nil } +func (v *EthereumVRFCoordinatorV2) GetConfig(ctx context.Context) (vrf_coordinator_v2.GetConfig, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + config, err := v.coordinator.GetConfig(opts) + if err != nil { + return vrf_coordinator_v2.GetConfig{}, err + } + return config, nil +} + +func (v *EthereumVRFCoordinatorV2) GetFallbackWeiPerUnitLink(ctx context.Context) (*big.Int, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + fallbackWeiPerUnitLink, err := v.coordinator.GetFallbackWeiPerUnitLink(opts) + if err != nil { + return nil, err + } + return fallbackWeiPerUnitLink, nil +} + +func (v *EthereumVRFCoordinatorV2) GetFeeConfig(ctx context.Context) (vrf_coordinator_v2.GetFeeConfig, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + config, err := v.coordinator.GetFeeConfig(opts) + if err != nil { + return vrf_coordinator_v2.GetFeeConfig{}, err + } + return config, nil +} + func (v *EthereumVRFCoordinatorV2) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig vrf_coordinator_v2.VRFCoordinatorV2FeeConfig) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { @@ -256,6 +359,18 @@ func (v *EthereumVRFCoordinatorV2) RegisterProvingKey( return v.client.ProcessTransaction(tx) } +func (v *EthereumVRFCoordinatorV2) TransferOwnership(to common.Address) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.coordinator.TransferOwnership(opts, to) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + func (v *EthereumVRFCoordinatorV2) CreateSubscription() (*types.Transaction, error) { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { @@ -403,6 +518,26 @@ func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsRequestedEvent(keyHash [][3 } } +func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionFunded(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded, error) { + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionFunded) + subscription, err := v.coordinator.WatchSubscriptionFunded(nil, eventsChannel, subID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for SubscriptionFunded event") + case event := <-eventsChannel: + return event, nil + } + } +} + func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) { eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled) subscription, err := v.coordinator.WatchSubscriptionCanceled(nil, eventsChannel, subID) @@ -423,6 +558,86 @@ func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionCanceledEvent(subID []uint } } +func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionCreatedEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated, error) { + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCreated) + subscription, err := v.coordinator.WatchSubscriptionCreated(nil, eventsChannel, subID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for SubscriptionCreated event") + case event := <-eventsChannel: + return event, nil + } + } +} + +func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionConsumerAdded(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded, error) { + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerAdded) + subscription, err := v.coordinator.WatchSubscriptionConsumerAdded(nil, eventsChannel, subID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for SubscriptionConsumerAdded event") + case event := <-eventsChannel: + return event, nil + } + } +} + +func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionConsumerRemoved(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved, error) { + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionConsumerRemoved) + subscription, err := v.coordinator.WatchSubscriptionConsumerRemoved(nil, eventsChannel, subID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for SubscriptionConsumerRemoved event") + case event := <-eventsChannel: + return event, nil + } + } +} + +func (v *EthereumVRFCoordinatorV2) WaitForConfigSetEvent(timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, error) { + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2ConfigSet) + subscription, err := v.coordinator.WatchConfigSet(nil, eventsChannel) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for ConfigSet event") + case event := <-eventsChannel: + return event, nil + } + } +} + // GetAllRandomWords get all VRFv2 randomness output words func (v *EthereumVRFConsumerV2) GetAllRandomWords(ctx context.Context, num int) ([]*big.Int, error) { words := make([]*big.Int, 0) @@ -584,6 +799,35 @@ func (v *EthereumVRFv2LoadTestConsumer) RequestRandomness( return tx, v.client.ProcessTransaction(tx) } +func (v *EthereumVRFv2LoadTestConsumer) RequestRandomWordsWithForceFulfill( + keyHash [32]byte, + requestConfirmations uint16, + callbackGasLimit uint32, + numWords uint32, + requestCount uint16, + subTopUpAmount *big.Int, + linkAddress common.Address, +) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.consumer.RequestRandomWordsWithForceFulfill( + opts, + requestConfirmations, + keyHash, + callbackGasLimit, + numWords, + requestCount, + subTopUpAmount, + linkAddress, + ) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + func (v *EthereumVRFv2Consumer) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_v2_consumer_wrapper.GetRequestStatus, error) { return v.consumer.GetRequestStatus(&bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), @@ -794,3 +1038,96 @@ func (v *EthereumVRFV2WrapperLoadTestConsumer) GetLoadTestMetrics(ctx context.Co fastestFulfillment, }, nil } + +func (v *EthereumVRFOwner) Address() string { + return v.address.Hex() +} + +func (v *EthereumVRFOwner) SetAuthorizedSenders(senders []common.Address) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.vrfOwner.SetAuthorizedSenders( + opts, + senders, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFOwner) AcceptVRFOwnership() error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.vrfOwner.AcceptVRFOwnership(opts) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFOwner) WaitForRandomWordsForcedEvent(requestIDs []*big.Int, subIds []uint64, senders []common.Address, timeout time.Duration) (*vrf_owner.VRFOwnerRandomWordsForced, error) { + eventsChannel := make(chan *vrf_owner.VRFOwnerRandomWordsForced) + subscription, err := v.vrfOwner.WatchRandomWordsForced(nil, eventsChannel, requestIDs, subIds, senders) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for RandomWordsForced event") + case event := <-eventsChannel: + return event, nil + } + } +} + +func (v *EthereumVRFCoordinatorTestV2) Address() string { + return v.address.Hex() +} + +func (v *EthereumVRFMockETHLINKFeed) Address() string { + return v.address.Hex() +} + +func (v *EthereumVRFMockETHLINKFeed) LatestRoundData() (*big.Int, error) { + data, err := v.feed.LatestRoundData(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: context.Background(), + }) + if err != nil { + return nil, err + } + return data.Ans, nil +} + +func (v *EthereumVRFMockETHLINKFeed) LatestRoundDataUpdatedAt() (*big.Int, error) { + data, err := v.feed.LatestRoundData(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: context.Background(), + }) + if err != nil { + return nil, err + } + return data.UpdatedAt, nil +} + +func (v *EthereumVRFMockETHLINKFeed) SetBlockTimestampDeduction(blockTimestampDeduction *big.Int) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.feed.SetBlockTimestampDeduction(opts, blockTimestampDeduction) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index c48f3488594..b31f02ab46f 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -113,7 +113,7 @@ func TestVRFV2Performance(t *testing.T) { if cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { linkToken, err := env.ContractLoader.LoadLINKToken(vrfv2Config.LinkAddress) require.NoError(t, err) - consumers, err = vrfv2_actions.DeployVRFV2Consumers(env.ContractDeployer, coordinator, 1) + consumers, err = vrfv2_actions.DeployVRFV2Consumers(env.ContractDeployer, coordinator.Address(), 1) require.NoError(t, err) err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) @@ -161,9 +161,12 @@ func TestVRFV2Performance(t *testing.T) { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented vrfv2Config.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding vrfv2Config.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink + + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). - WithGeth(). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithCustomCleanup( @@ -196,9 +199,14 @@ func TestVRFV2Performance(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") + useVRFOwner := true + useTestCoordinator := true + vrfv2Contracts, subIDs, vrfv2Data, err = vrfv2_actions.SetupVRFV2Environment( env, vrfv2Config, + useVRFOwner, + useTestCoordinator, linkToken, mockETHLinkFeed, //register proving key against EOA address in order to return funds to this address diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index 2fc6b6a4c7f..25f295a4039 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -53,6 +53,7 @@ type NewEnvConfig struct { type Common struct { MinimumConfirmations uint16 `toml:"minimum_confirmations"` CancelSubsAfterTestRun bool `toml:"cancel_subs_after_test_run"` + SubBillingType string `toml:"sub_billing_type"` } type Funding struct { diff --git a/integration-tests/load/vrfv2plus/config.toml b/integration-tests/load/vrfv2plus/config.toml index 8ccf60c6d80..9572fb9c45f 100644 --- a/integration-tests/load/vrfv2plus/config.toml +++ b/integration-tests/load/vrfv2plus/config.toml @@ -1,7 +1,7 @@ - [Common] -minimum_confirmations = 3 +minimum_confirmations = 1 cancel_subs_after_test_run = true +sub_billing_type = "LINK_AND_NATIVE" # LINK_AND_NATIVE, LINK, NATIVE [NewEnvConfig] sub_funds_link = 1 @@ -12,20 +12,17 @@ node_sending_key_funding = 1000 [ExistingEnvConfig] coordinator_address = "" consumer_address = "" -sub_id = 1 +sub_id = "" key_hash = "" create_fund_subs_and_add_consumers = true -link_address = "" -sub_funds_link = 10 -node_sending_key_funding_min = 1 +node_sending_key_funding_min = 10 node_sending_keys = [ "", - "", - "", - "", - "", - "", + "" ] +sub_funds_native = 10 +link_address = "" +sub_funds_link = 10 # 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds [Soak] @@ -37,10 +34,10 @@ number_of_sub_to_create = 1 # approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds [Load] -rate_limit_unit_duration = "3s" +rate_limit_unit_duration = "1s" rps = 1 -randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 # approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index 77b60e30002..03603e7bb05 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -1,6 +1,7 @@ package loadvrfv2plus import ( + "fmt" "math/big" "math/rand" @@ -41,9 +42,14 @@ func NewSingleHashGun( func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets + billingType, err := selectBillingType(m.vrfv2PlusConfig.SubscriptionBillingType) + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } + //randomly increase/decrease randomness request count per TX randomnessRequestCountPerRequest := deviateValue(m.vrfv2PlusConfig.RandomnessRequestCountPerRequest, m.vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation) - _, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( + _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( //the same consumer is used for all requests and in all subs m.contracts.LoadTestConsumers[0], m.contracts.Coordinator, @@ -51,7 +57,7 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //randomly pick a subID from pool of subIDs m.subIDs[randInRange(0, len(m.subIDs)-1)], //randomly pick payment type - randBool(), + billingType, randomnessRequestCountPerRequest, m.vrfv2PlusConfig, m.vrfv2PlusConfig.RandomWordsFulfilledEventTimeout, @@ -78,3 +84,16 @@ func randBool() bool { func randInRange(min int, max int) int { return rand.Intn(max-min+1) + min } + +func selectBillingType(billingType string) (bool, error) { + switch billingType { + case vrfv2plus_config.BILLING_TYPE_LINK: + return false, nil + case vrfv2plus_config.BILLING_TYPE_NATIVE: + return true, nil + case vrfv2plus_config.BILLING_TYPE_LINK_AND_NATIVE: + return randBool(), nil + default: + return false, fmt.Errorf("invalid billing type: %s", billingType) + } +} diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 74be537c753..cb68386d117 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -57,6 +57,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { l := logging.GetTestLogger(t) //todo: temporary solution with envconfig and toml config until VRF-662 is implemented vrfv2PlusConfig.MinimumConfirmations = cfg.Common.MinimumConfirmations + vrfv2PlusConfig.SubscriptionBillingType = cfg.Common.SubBillingType lokiConfig := wasp.NewEnvLokiConfig() lc, err := wasp.NewLokiClient(lokiConfig) @@ -166,9 +167,13 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusConfig.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.NewEnvConfig.Funding.SubFundsNative + + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). - WithGeth(). + WithPrivateEthereumNetwork(network). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). WithCustomCleanup( diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 8958480d51e..7274d8b8772 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -13,9 +13,9 @@ import ( "github.com/stretchr/testify/require" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -28,6 +28,9 @@ func TestVRFv2Basic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + useVRFOwner := false + useTestCoordinator := false + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) require.NoError(t, err, "Error building ethereum network config") @@ -58,6 +61,8 @@ func TestVRFv2Basic(t *testing.T) { vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, vrfv2Config, + useVRFOwner, + useTestCoordinator, linkToken, mockETHLinkFeed, defaultWalletAddress, @@ -433,6 +438,9 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + useVRFOwner := false + useTestCoordinator := false + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) require.NoError(t, err, "Error building ethereum network config") @@ -463,6 +471,8 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, vrfv2Config, + useVRFOwner, + useTestCoordinator, linkToken, mockETHLinkFeed, defaultWalletAddress, @@ -519,3 +529,133 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { require.True(t, equalIgnoreOrder) }) } + +func TestVRFOwner(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + useVRFOwner := true + useTestCoordinator := true + + network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + require.NoError(t, err, "Error building ethereum network config") + + var vrfv2Config vrfv2_config.VRFV2Config + err = envconfig.Process("VRFV2", &vrfv2Config) + require.NoError(t, err) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithPrivateEthereumNetwork(network). + WithCLNodes(1). + WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + + require.NoError(t, err) + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err) + + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + numberOfTxKeysToCreate := 1 + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + env, + vrfv2Config, + useVRFOwner, + useTestCoordinator, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + numberOfTxKeysToCreate, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up VRF v2 env") + + subID := subIDs[0] + + subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + + t.Run("Request Randomness With Force-Fulfill", func(t *testing.T) { + testConfig := vrfv2Config + + vrfCoordinatorOwner, err := vrfv2Contracts.Coordinator.GetOwner(testcontext.Get(t)) + require.NoError(t, err) + require.Equal(t, vrfv2Contracts.VRFOwner.Address(), vrfCoordinatorOwner.String()) + + err = linkToken.Transfer( + vrfv2Contracts.LoadTestConsumers[0].Address(), + conversions.EtherToWei(big.NewFloat(5)), + ) + require.NoError(t, err, "error transferring link to consumer contract") + + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + + consumerLinkBalance, err := linkToken.BalanceOf(testcontext.Get(t), vrfv2Contracts.LoadTestConsumers[0].Address()) + require.NoError(t, err, "error getting consumer link balance") + l.Info(). + Str("Balance", conversions.WeiToEther(consumerLinkBalance).String()). + Str("Consumer", vrfv2Contracts.LoadTestConsumers[0].Address()). + Msg("Consumer Link Balance") + + err = mockETHLinkFeed.SetBlockTimestampDeduction(big.NewInt(3)) + require.NoError(t, err) + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + + // test and assert + _, randFulfilledEvent, _, err := vrfv2_actions.RequestRandomnessWithForceFulfillAndWaitForFulfillment( + vrfv2Contracts.LoadTestConsumers[0], + vrfv2Contracts.Coordinator, + vrfv2Contracts.VRFOwner, + vrfv2Data, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + conversions.EtherToWei(big.NewFloat(5)), + common.HexToAddress(linkToken.Address()), + time.Minute*2, + l, + ) + require.NoError(t, err, "error requesting randomness with force-fulfillment and waiting for fulfilment") + require.Equal(t, 0, randFulfilledEvent.Payment.Cmp(big.NewInt(0)), "Forced Fulfilled Randomness's Payment should be 0") + + status, err := vrfv2Contracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) + l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") + + require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) + for _, w := range status.RandomWords { + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") + } + + coordinatorConfig, err := vrfv2Contracts.Coordinator.GetConfig(testcontext.Get(t)) + require.NoError(t, err, "error getting coordinator config") + + coordinatorFeeConfig, err := vrfv2Contracts.Coordinator.GetFeeConfig(testcontext.Get(t)) + require.NoError(t, err, "error getting coordinator fee config") + + coordinatorFallbackWeiPerUnitLinkConfig, err := vrfv2Contracts.Coordinator.GetFallbackWeiPerUnitLink(testcontext.Get(t)) + require.NoError(t, err, "error getting coordinator FallbackWeiPerUnitLink") + + require.Equal(t, testConfig.StalenessSeconds, coordinatorConfig.StalenessSeconds) + require.Equal(t, testConfig.GasAfterPaymentCalculation, coordinatorConfig.GasAfterPaymentCalculation) + require.Equal(t, testConfig.MinimumConfirmations, coordinatorConfig.MinimumRequestConfirmations) + require.Equal(t, testConfig.FulfillmentFlatFeeLinkPPMTier1, coordinatorFeeConfig.FulfillmentFlatFeeLinkPPMTier1) + require.Equal(t, testConfig.ReqsForTier2, coordinatorFeeConfig.ReqsForTier2.Int64()) + require.Equal(t, testConfig.FallbackWeiPerUnitLink, coordinatorFallbackWeiPerUnitLinkConfig.Int64()) + }) +} diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 6cc4a07237f..5417e61d5ed 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -776,13 +776,23 @@ func TestVRFv2PlusMigration(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + vrfJobSpecConfig := vrfv2plus.VRFJobSpecConfig{ + ForwardingAllowed: false, + CoordinatorAddress: newCoordinator.Address(), + FromAddresses: []string{vrfv2PlusData.PrimaryEthAddress}, + EVMChainID: vrfv2PlusData.ChainID.String(), + MinIncomingConfirmations: int(vrfv2PlusConfig.MinimumConfirmations), + PublicKey: vrfv2PlusData.VRFKey.Data.ID, + EstimateGasMultiplier: 1, + BatchFulfillmentEnabled: false, + BatchFulfillmentGasMultiplier: 1.15, + PollPeriod: time.Second * 1, + RequestTimeout: time.Hour * 24, + } + _, err = vrfv2plus.CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], - newCoordinator.Address(), - []string{vrfv2PlusData.PrimaryEthAddress}, - vrfv2PlusData.VRFKey.Data.ID, - vrfv2PlusData.ChainID.String(), - vrfv2PlusConfig.MinimumConfirmations, + vrfJobSpecConfig, ) require.NoError(t, err, vrfv2plus.ErrCreateVRFV2PlusJobs) From 4b8988b3bdcdc4375f50a88e9b61acc14ee48772 Mon Sep 17 00:00:00 2001 From: george-dorin <120329946+george-dorin@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:09:59 +0200 Subject: [PATCH 187/234] Remove old way of configuring telemetry pubkey and url (#11780) * Remove old way of configuring telemetry pubkey and url * Update mock * Update mock version * Change mock - mockery version * Update CHANGELOG.md --- core/config/docs/core.toml | 4 -- core/config/docs/docs_test.go | 7 --- core/config/mocks/telemetry_ingress.go | 40 ------------- core/config/telemetry_ingress_config.go | 3 - core/config/toml/types.go | 23 ------- .../chainlink/config_telemetry_ingress.go | 10 ---- core/services/chainlink/config_test.go | 15 ----- .../testdata/config-empty-effective.toml | 2 - .../chainlink/testdata/config-full.toml | 2 - .../config-multi-chain-effective.toml | 2 - core/services/telemetry/manager.go | 60 +------------------ core/services/telemetry/manager_test.go | 53 ---------------- .../testdata/config-empty-effective.toml | 2 - core/web/resolver/testdata/config-full.toml | 2 - .../config-multi-chain-effective.toml | 2 - docs/CHANGELOG.md | 11 ++++ docs/CONFIG.md | 14 ----- testdata/scripts/node/validate/default.txtar | 2 - .../disk-based-logging-disabled.txtar | 2 - .../validate/disk-based-logging-no-dir.txtar | 2 - .../node/validate/disk-based-logging.txtar | 2 - testdata/scripts/node/validate/invalid.txtar | 2 - testdata/scripts/node/validate/valid.txtar | 2 - testdata/scripts/node/validate/warnings.txtar | 2 - 24 files changed, 13 insertions(+), 253 deletions(-) diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 3cb6bb0aaa7..95d59cca062 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -88,10 +88,6 @@ LeaseRefreshInterval = '1s' # Default UniConn = true # Default # Logging toggles verbose logging of the raw telemetry messages being sent. Logging = false # Default -# ServerPubKey is the public key of the telemetry server. This field will be removed in a furture version -ServerPubKey = 'test-pub-key' # Example -# URL is where to send telemetry. This field will be removed in a furture version -URL = 'https://prom.test' # Example # BufferSize is the number of telemetry messages to buffer before dropping new ones. BufferSize = 100 # Default # MaxBatchSize is the maximum number of messages to batch into one telemetry request. diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 508bd62e338..dc1e0f2af12 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -15,7 +15,6 @@ import ( stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink-common/pkg/config" - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/docs" @@ -38,12 +37,6 @@ func TestDoc(t *testing.T) { require.NoError(t, err) } - // Except for TelemetryIngress.ServerPubKey and TelemetryIngress.URL as this will be removed in the future - // and its only use is to signal to NOPs that these fields are no longer allowed - emptyString := "" - c.TelemetryIngress.ServerPubKey = &emptyString - c.TelemetryIngress.URL = new(commonconfig.URL) - cfgtest.AssertFieldsNotNil(t, c) var defaults chainlink.Config diff --git a/core/config/mocks/telemetry_ingress.go b/core/config/mocks/telemetry_ingress.go index ed5dbc4cf52..1a21c89e9ad 100644 --- a/core/config/mocks/telemetry_ingress.go +++ b/core/config/mocks/telemetry_ingress.go @@ -7,8 +7,6 @@ import ( mock "github.com/stretchr/testify/mock" time "time" - - url "net/url" ) // TelemetryIngress is an autogenerated mock type for the TelemetryIngress type @@ -126,44 +124,6 @@ func (_m *TelemetryIngress) SendTimeout() time.Duration { return r0 } -// ServerPubKey provides a mock function with given fields: -func (_m *TelemetryIngress) ServerPubKey() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ServerPubKey") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// URL provides a mock function with given fields: -func (_m *TelemetryIngress) URL() *url.URL { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for URL") - } - - var r0 *url.URL - if rf, ok := ret.Get(0).(func() *url.URL); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*url.URL) - } - } - - return r0 -} - // UniConn provides a mock function with given fields: func (_m *TelemetryIngress) UniConn() bool { ret := _m.Called() diff --git a/core/config/telemetry_ingress_config.go b/core/config/telemetry_ingress_config.go index f6c9fa3f858..a4923391011 100644 --- a/core/config/telemetry_ingress_config.go +++ b/core/config/telemetry_ingress_config.go @@ -16,9 +16,6 @@ type TelemetryIngress interface { SendTimeout() time.Duration UseBatchSend() bool Endpoints() []TelemetryIngressEndpoint - - ServerPubKey() string // Deprecated: Use TelemetryIngressEndpoint.ServerPubKey instead, this field will be removed in future versions - URL() *url.URL // Deprecated: Use TelemetryIngressEndpoint.URL instead, this field will be removed in future versions } //go:generate mockery --quiet --name TelemetryIngressEndpoint --output ./mocks/ --case=underscore --filename telemetry_ingress_endpoint.go diff --git a/core/config/toml/types.go b/core/config/toml/types.go index eae5b2f533a..7b4656da1f6 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -438,9 +438,6 @@ type TelemetryIngress struct { SendTimeout *commonconfig.Duration UseBatchSend *bool Endpoints []TelemetryIngressEndpoint `toml:",omitempty"` - - URL *commonconfig.URL `toml:",omitempty"` // Deprecated: Use TelemetryIngressEndpoint.URL instead, this field will be removed in future versions - ServerPubKey *string `toml:",omitempty"` // Deprecated: Use TelemetryIngressEndpoint.ServerPubKey instead, this field will be removed in future versions } type TelemetryIngressEndpoint struct { @@ -475,26 +472,6 @@ func (t *TelemetryIngress) setFrom(f *TelemetryIngress) { if v := f.Endpoints; v != nil { t.Endpoints = v } - if v := f.ServerPubKey; v != nil { - t.ServerPubKey = v - } - if v := f.URL; v != nil { - t.URL = v - } -} - -func (t *TelemetryIngress) ValidateConfig() (err error) { - if (!t.URL.IsZero() || *t.ServerPubKey != "") && len(t.Endpoints) > 0 { - return configutils.ErrInvalid{Name: "URL", Value: t.URL.String(), - Msg: `Cannot set both TelemetryIngress.URL and TelemetryIngress.ServerPubKey alongside TelemetryIngress.Endpoints. Please use only TelemetryIngress.Endpoints: - [[TelemetryIngress.Endpoints]] - Network = '...' # e.g. EVM. Solana, Starknet, Cosmos - ChainID = '...' # e.g. 1, 5, devnet, mainnet-beta - URL = '...' - ServerPubKey = '...'`} - } - - return nil } type AuditLogger struct { diff --git a/core/services/chainlink/config_telemetry_ingress.go b/core/services/chainlink/config_telemetry_ingress.go index 5126833134e..3ad721ad303 100644 --- a/core/services/chainlink/config_telemetry_ingress.go +++ b/core/services/chainlink/config_telemetry_ingress.go @@ -46,16 +46,6 @@ func (t *telemetryIngressConfig) UseBatchSend() bool { return *t.c.UseBatchSend } -// Deprecated: Use TelemetryIngressEndpoint.ServerPubKey, this field will be removed in future versions -func (t *telemetryIngressConfig) ServerPubKey() string { - return *t.c.ServerPubKey -} - -// Deprecated: Use TelemetryIngressEndpoint.URL instead, this field will be removed in future versions -func (t *telemetryIngressConfig) URL() *url.URL { - return t.c.URL.URL() -} - func (t *telemetryIngressConfig) Endpoints() []config.TelemetryIngressEndpoint { var endpoints []config.TelemetryIngressEndpoint for _, e := range t.c.Endpoints { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 7e4be202702..b16551912b2 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -293,8 +293,6 @@ func TestConfig_Marshal(t *testing.T) { SendInterval: commonconfig.MustNewDuration(time.Minute), SendTimeout: commonconfig.MustNewDuration(5 * time.Second), UseBatchSend: ptr(true), - URL: ptr(commonconfig.URL{}), - ServerPubKey: ptr(""), Endpoints: []toml.TelemetryIngressEndpoint{{ Network: ptr("EVM"), ChainID: ptr("1"), @@ -744,8 +742,6 @@ MaxBatchSize = 4321 SendInterval = '1m0s' SendTimeout = '5s' UseBatchSend = true -URL = '' -ServerPubKey = '' [[TelemetryIngress.Endpoints]] Network = 'EVM' @@ -1141,17 +1137,6 @@ func TestConfig_full(t *testing.T) { } } - // Except for TelemetryIngress.ServerPubKey as this will be removed in the future - // and its only use is to signal to NOPs that these fields are no longer allowed - if got.TelemetryIngress.ServerPubKey == nil { - got.TelemetryIngress.ServerPubKey = ptr("") - } - // Except for TelemetryIngress.URL as this will be removed in the future - // and its only use is to signal to NOPs that these fields are no longer allowed - if got.TelemetryIngress.URL == nil { - got.TelemetryIngress.URL = new(commonconfig.URL) - } - cfgtest.AssertFieldsNotNil(t, got) } diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index cc4dddcf8af..148f6b24ff5 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -40,8 +40,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index da8b68cfdb1..bc1b124ccb6 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -40,8 +40,6 @@ MaxBatchSize = 4321 SendInterval = '1m0s' SendTimeout = '5s' UseBatchSend = true -URL = '' -ServerPubKey = '' [[TelemetryIngress.Endpoints]] Network = 'EVM' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index d5352a685c4..bd64ae04812 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -40,8 +40,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = true diff --git a/core/services/telemetry/manager.go b/core/services/telemetry/manager.go index c457aca5bf8..228a997eeca 100644 --- a/core/services/telemetry/manager.go +++ b/core/services/telemetry/manager.go @@ -18,13 +18,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" ) -//// Client encapsulates all the functionality needed to -//// send telemetry to the ingress server using wsrpc -//type Client interface { -// services.ServiceCtx -// Send(context.Context, synchronization.TelemPayload) -//} - type Manager struct { services.StateMachine bufferSize uint @@ -38,32 +31,6 @@ type Manager struct { uniConn bool useBatchSend bool MonitoringEndpointGenerator MonitoringEndpointGenerator - - //legacyMode means that we are sending all telemetry to a single endpoint. - //In order for this to be set as true, we need to have no endpoints defined with TelemetryIngress.URL and TelemetryIngress.ServerPubKey set. - //This mode will be supported until we completely switch to TelemetryIngress.Endpoints in config.toml - legacyMode bool -} - -type legacyEndpointConfig struct { - Url *url.URL - PubKey string -} - -func (l *legacyEndpointConfig) Network() string { - return "-" -} - -func (l *legacyEndpointConfig) ChainID() string { - return "-" -} - -func (l *legacyEndpointConfig) ServerPubKey() string { - return l.PubKey -} - -func (l *legacyEndpointConfig) URL() *url.URL { - return l.Url } type telemetryEndpoint struct { @@ -87,30 +54,12 @@ func NewManager(cfg config.TelemetryIngress, csaKeyStore keystore.CSA, lggr logg sendTimeout: cfg.SendTimeout(), uniConn: cfg.UniConn(), useBatchSend: cfg.UseBatchSend(), - legacyMode: false, } for _, e := range cfg.Endpoints() { if err := m.addEndpoint(e); err != nil { m.lggr.Error(err) } } - - if len(cfg.Endpoints()) == 0 && cfg.URL() != nil && cfg.ServerPubKey() != "" { - m.lggr.Error(`TelemetryIngress.URL and TelemetryIngress.ServerPubKey will be removed in a future version, please switch to TelemetryIngress.Endpoints: - [[TelemetryIngress.Endpoints]] - Network = '...' # e.g. EVM. Solana, Starknet, Cosmos - ChainID = '...' # e.g. 1, 5, devnet, mainnet-beta - URL = '...' - ServerPubKey = '...'`) - m.legacyMode = true - if err := m.addEndpoint(&legacyEndpointConfig{ - Url: cfg.URL(), - PubKey: cfg.ServerPubKey(), - }); err != nil { - m.lggr.Error(err) - } - } - return m } @@ -165,11 +114,11 @@ func (m *Manager) GenMonitoringEndpoint(network string, chainID string, contract } func (m *Manager) addEndpoint(e config.TelemetryIngressEndpoint) error { - if e.Network() == "" && !m.legacyMode { + if e.Network() == "" { return errors.New("cannot add telemetry endpoint, network cannot be empty") } - if e.ChainID() == "" && !m.legacyMode { + if e.ChainID() == "" { return errors.New("cannot add telemetry endpoint, chainID cannot be empty") } @@ -205,11 +154,6 @@ func (m *Manager) addEndpoint(e config.TelemetryIngressEndpoint) error { } func (m *Manager) getEndpoint(network string, chainID string) (*telemetryEndpoint, bool) { - //in legacy mode we send telemetry to a single endpoint - if m.legacyMode && len(m.endpoints) == 1 { - return m.endpoints[0], true - } - for _, e := range m.endpoints { if e.Network == strings.ToUpper(network) && e.ChainID == strings.ToUpper(chainID) { return e, true diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 23752151cd0..2d3409ba569 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -9,12 +9,10 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -201,7 +199,6 @@ func TestNewManager(t *testing.T) { func TestCorrectEndpointRouting(t *testing.T) { tic := setupMockConfig(t, true) tic.On("Endpoints").Return(nil) - tic.On("URL").Return(nil) lggr, obsLogs := logger.TestLoggerObserved(t, zapcore.InfoLevel) ks := mocks3.NewCSA(t) @@ -287,53 +284,3 @@ func TestCorrectEndpointRouting(t *testing.T) { } } - -func TestLegacyMode(t *testing.T) { - tic := setupMockConfig(t, true) - tic.On("Endpoints").Return(nil) - url, err := commonconfig.ParseURL("test.test") - require.NoError(t, err) - tic.On("URL").Return(url.URL()) - tic.On("ServerPubKey").Return("some-pub-key") - - lggr, obsLogs := logger.TestLoggerObserved(t, zapcore.InfoLevel) - ks := mocks3.NewCSA(t) - - tm := NewManager(tic, ks, lggr) - require.Equal(t, true, tm.legacyMode) - require.Len(t, tm.endpoints, 1) - - var clientSent []synchronization.TelemPayload - clientMock := mocks2.NewTelemetryService(t) - clientMock.On("Send", mock.Anything, mock.AnythingOfType("[]uint8"), mock.AnythingOfType("string"), mock.AnythingOfType("TelemetryType")).Return().Run(func(args mock.Arguments) { - clientSent = append(clientSent, synchronization.TelemPayload{ - Telemetry: args[1].([]byte), - ContractID: args[2].(string), - TelemType: args[3].(synchronization.TelemetryType), - }) - }) - tm.endpoints[0].client = clientMock - - e := tm.GenMonitoringEndpoint("unknown-network", "unknown-chainID", "some-contractID", "some-type") - require.Equal(t, "*telemetry.IngressAgentBatch", reflect.TypeOf(e).String()) - - e.SendLog([]byte("endpoint-1-message-1")) - e.SendLog([]byte("endpoint-1-message-2")) - e.SendLog([]byte("endpoint-1-message-3")) - require.Len(t, clientSent, 3) - - e2 := tm.GenMonitoringEndpoint("another-unknown-network", "another-unknown-chainID", "another-contractID", "another-type") - require.Equal(t, "*telemetry.IngressAgentBatch", reflect.TypeOf(e).String()) - - e2.SendLog([]byte("endpoint-2-message-1")) - e2.SendLog([]byte("endpoint-2-message-2")) - e2.SendLog([]byte("endpoint-2-message-3")) - require.Len(t, clientSent, 6) - assert.Equal(t, []byte("endpoint-1-message-1"), clientSent[0].Telemetry) - assert.Equal(t, []byte("endpoint-1-message-2"), clientSent[1].Telemetry) - assert.Equal(t, []byte("endpoint-1-message-3"), clientSent[2].Telemetry) - assert.Equal(t, []byte("endpoint-2-message-1"), clientSent[3].Telemetry) - assert.Equal(t, []byte("endpoint-2-message-2"), clientSent[4].Telemetry) - assert.Equal(t, []byte("endpoint-2-message-3"), clientSent[5].Telemetry) - assert.Equal(t, 1, obsLogs.Len()) // Deprecation warning for TelemetryIngress.URL and TelemetryIngress.ServerPubKey -} diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index cc4dddcf8af..148f6b24ff5 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -40,8 +40,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 957ffdc968e..67ddbb33efd 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -40,8 +40,6 @@ MaxBatchSize = 4321 SendInterval = '1m0s' SendTimeout = '5s' UseBatchSend = true -URL = '' -ServerPubKey = '' [[TelemetryIngress.Endpoints]] Network = 'EVM' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index d5352a685c4..bd64ae04812 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -40,8 +40,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = true diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6b70fd51c63..ec2c72852c2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -18,6 +18,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the encoding used for transactions when resending in batches +### Removed + +- Removed `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` from TOML configuration, these fields are replaced by `[[TelemetryIngress.Endpoints]]`: +```toml + [[TelemetryIngress.Endpoints]] + Network = '...' # e.g. EVM. Solana, Starknet, Cosmos + ChainID = '...' # e.g. 1, 5, devnet, mainnet-beta + URL = '...' + ServerPubKey = '...' +``` + ## 2.8.0 - UNRELEASED ### Added diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 3ae4afa3bcf..2c98a41c7bf 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -244,8 +244,6 @@ LeaseRefreshInterval determines how often to refresh the lease lock. Also contro [TelemetryIngress] UniConn = true # Default Logging = false # Default -ServerPubKey = 'test-pub-key' # Example -URL = 'https://prom.test' # Example BufferSize = 100 # Default MaxBatchSize = 50 # Default SendInterval = '500ms' # Default @@ -266,18 +264,6 @@ Logging = false # Default ``` Logging toggles verbose logging of the raw telemetry messages being sent. -### ServerPubKey -```toml -ServerPubKey = 'test-pub-key' # Example -``` -ServerPubKey is the public key of the telemetry server. This field will be removed in a furture version - -### URL -```toml -URL = 'https://prom.test' # Example -``` -URL is where to send telemetry. This field will be removed in a furture version - ### BufferSize ```toml BufferSize = 100 # Default diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 32a7e5f71b3..8a3c99ee8da 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -52,8 +52,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 9cca8764eed..187e4d16328 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -96,8 +96,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index d1658a1772d..cd61c8e477b 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -96,8 +96,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 18ac99db785..f87352fd482 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -96,8 +96,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index f048e35547e..f2f1f0a4ee1 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -86,8 +86,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 47c63e4c03e..0f99fb3f6d8 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -93,8 +93,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index c6daa829ddb..01968ffd65d 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -75,8 +75,6 @@ MaxBatchSize = 50 SendInterval = '500ms' SendTimeout = '10s' UseBatchSend = true -URL = '' -ServerPubKey = '' [AuditLogger] Enabled = false From 5e7b2b2bc1c0e9b94434b01e4b217e9021f8a27d Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Tue, 23 Jan 2024 10:50:01 -0500 Subject: [PATCH 188/234] Make missing fields return invalid type instead of sometimes defaulting and other times not. (#11826) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/relay/evm/chain_reader_test.go | 13 +++--- core/services/relay/evm/codec.go | 44 +++++++++++++------ core/services/relay/evm/event_binding.go | 19 ++++++++ core/services/relay/evm/types/codec_entry.go | 25 ++++++----- .../relay/evm/types/codec_entry_test.go | 27 +++++++----- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 11 files changed, 97 insertions(+), 49 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7d82e2b4d75..fea7da60757 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 2142547c1ea..4ecbf283746 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1167,8 +1167,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a h1:lgM0yPo0KqSntLY4Y42RAH3avdv+Kyne8n+VM7cwlxo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 h1:uSLn+JbClhldYn7zm0GEALGvi6KAfrY5Kp46IX9sy0E= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index 4981dc3696a..02e9d4e3f6a 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -76,10 +76,10 @@ func TestChainReader(t *testing.T) { return cr.GetLatestValue(ctx, AnyContractName, triggerWithDynamicTopic, input, output) == nil }, it.MaxWaitTimeForEvents(), time.Millisecond*10) - assert.Equal(t, anyString, rOutput.FieldByName("Field").Interface()) + assert.Equal(t, &anyString, rOutput.FieldByName("Field").Interface()) topic, err := abi.MakeTopics([]any{anyString}) require.NoError(t, err) - assert.Equal(t, topic[0][0], rOutput.FieldByName("FieldHash").Interface()) + assert.Equal(t, &topic[0][0], rOutput.FieldByName("FieldHash").Interface()) }) t.Run("Multiple topics can filter together", func(t *testing.T) { @@ -143,7 +143,9 @@ func (it *chainReaderInterfaceTester) MaxWaitTimeForEvents() time.Duration { func (it *chainReaderInterfaceTester) Setup(t *testing.T) { t.Cleanup(func() { // DB may be closed by the test already, ignore errors - _ = it.cr.Close() + if it.cr != nil { + _ = it.cr.Close() + } it.cr = nil it.evmTest = nil }) @@ -213,6 +215,7 @@ func (it *chainReaderInterfaceTester) Setup(t *testing.T) { "Account": hexutil.Encode(testStruct.Account), }, }, + &codec.RenameModifierConfig{Fields: map[string]string{"NestedStruct.Inner.IntVal": "I"}}, }, OutputModifications: codec.ModifiersConfig{ &codec.HardCodeModifierConfig{OffChainValues: map[string]any{"ExtraField": anyExtraValue}}, @@ -285,7 +288,7 @@ func (it *chainReaderInterfaceTester) sendTxWithTestStruct(t *testing.T, testStr tx, err := fn( &it.evmTest.LatestValueHolderTransactor, it.auth, - testStruct.Field, + *testStruct.Field, testStruct.DifferentField, uint8(testStruct.OracleID), convertOracleIDs(testStruct.OracleIDs), @@ -401,7 +404,7 @@ func getOracleIDs(first TestStruct) [32]byte { func toInternalType(testStruct TestStruct) chain_reader_example.TestStruct { return chain_reader_example.TestStruct{ - Field: testStruct.Field, + Field: *testStruct.Field, DifferentField: testStruct.DifferentField, OracleId: byte(testStruct.OracleID), OracleIds: convertOracleIDs(testStruct.OracleIDs), diff --git a/core/services/relay/evm/codec.go b/core/services/relay/evm/codec.go index 090240c666c..a87976da54f 100644 --- a/core/services/relay/evm/codec.go +++ b/core/services/relay/evm/codec.go @@ -18,14 +18,18 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) -// decodeAccountHook allows strings to be converted to [32]byte allowing config to represent them as 0x... +// decodeAccountAndAllowArraySliceHook allows: +// +// strings to be converted to [32]byte allowing config to represent them as 0x... +// slices or arrays to be converted to a pointer to that type +// // BigIntHook allows *big.Int to be represented as any integer type or a string and to go back to them. // Useful for config, or if when a model may use a go type that isn't a *big.Int when Pack expects one. // Eg: int32 in a go struct from a plugin could require a *big.Int in Pack for int24, if it fits, we shouldn't care. // SliceToArrayVerifySizeHook verifies that slices have the correct size when converting to an array // sizeVerifyBigIntHook allows our custom types that verify the number fits in the on-chain type to be converted as-if // it was a *big.Int -var evmDecoderHooks = []mapstructure.DecodeHookFunc{decodeAccountHook, codec.BigIntHook, codec.SliceToArrayVerifySizeHook, sizeVerifyBigIntHook} +var evmDecoderHooks = []mapstructure.DecodeHookFunc{decodeAccountAndAllowArraySliceHook, codec.BigIntHook, codec.SliceToArrayVerifySizeHook, sizeVerifyBigIntHook} // NewCodec creates a new [commontypes.RemoteCodec] for EVM. // Note that names in the ABI are converted to Go names using [abi.ToCamelCase], @@ -113,18 +117,30 @@ func sizeVerifyBigIntHook(from, to reflect.Type, data any) (any, error) { return converted, converted.Verify() } -func decodeAccountHook(from, to reflect.Type, data any) (any, error) { - if from.Kind() == reflect.String && to == reflect.TypeOf(common.Address{}) { - decoded, err := hexutil.Decode(data.(string)) - if err != nil { - return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) - } else if len(decoded) != common.AddressLength { - return nil, fmt.Errorf( - "%w: wrong number size for address expected %v got %v", - commontypes.ErrSliceWrongLen, - common.AddressLength, len(decoded)) - } - return common.Address(decoded), nil +func decodeAccountAndAllowArraySliceHook(from, to reflect.Type, data any) (any, error) { + if from.Kind() == reflect.String && + (to == reflect.TypeOf(common.Address{}) || to == reflect.TypeOf(&common.Address{})) { + return decodeAddress(data) } + + if from.Kind() == reflect.Pointer && to.Kind() != reflect.Pointer && from != nil && + (from.Elem().Kind() == reflect.Slice || from.Elem().Kind() == reflect.Array) { + return reflect.ValueOf(data).Elem().Interface(), nil + } + return data, nil } + +func decodeAddress(data any) (any, error) { + decoded, err := hexutil.Decode(data.(string)) + if err != nil { + return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidType, err) + } else if len(decoded) != common.AddressLength { + return nil, fmt.Errorf( + "%w: wrong number size for address expected %v got %v", + commontypes.ErrSliceWrongLen, + common.AddressLength, len(decoded)) + } + + return common.Address(decoded), nil +} diff --git a/core/services/relay/evm/event_binding.go b/core/services/relay/evm/event_binding.go index 85028853c79..b7148348e4b 100644 --- a/core/services/relay/evm/event_binding.go +++ b/core/services/relay/evm/event_binding.go @@ -224,6 +224,11 @@ func (e *eventBinding) encodeParams(item reflect.Value) ([]common.Hash, error) { return nil, fmt.Errorf("%w: cannot encode kind %v", commontypes.ErrInvalidType, item.Kind()) } + // abi params allow you to Pack a pointers, but MakeTopics doesn't work with pointers. + if err := e.derefTopics(topics); err != nil { + return nil, err + } + hashes, err := abi.MakeTopics(topics) if err != nil { return nil, wrapInternalErr(err) @@ -236,6 +241,20 @@ func (e *eventBinding) encodeParams(item reflect.Value) ([]common.Hash, error) { return hashes[0], nil } +func (e *eventBinding) derefTopics(topics []any) error { + for i, topic := range topics { + rTopic := reflect.ValueOf(topic) + if rTopic.Kind() == reflect.Pointer { + if rTopic.IsNil() { + return fmt.Errorf( + "%w: input topic %s cannot be nil", commontypes.ErrInvalidType, e.inputInfo.Args()[i].Name) + } + topics[i] = rTopic.Elem().Interface() + } + } + return nil +} + func (e *eventBinding) decodeLog(ctx context.Context, log *logpoller.Log, into any) error { dataType := wrapItemType(e.contractName, e.eventName, false) if err := e.codec.Decode(ctx, log.Data, into, dataType); err != nil { diff --git a/core/services/relay/evm/types/codec_entry.go b/core/services/relay/evm/types/codec_entry.go index 70948ecd6b3..b87f7ced721 100644 --- a/core/services/relay/evm/types/codec_entry.go +++ b/core/services/relay/evm/types/codec_entry.go @@ -138,8 +138,8 @@ func (entry *codecEntry) Init() error { checked[i] = reflect.StructField{Name: name, Type: checkedArg} } - entry.nativeType = reflect.StructOf(native) - entry.checkedType = reflect.StructOf(checked) + entry.nativeType = structOfPointers(native) + entry.checkedType = structOfPointers(checked) return nil } @@ -227,13 +227,9 @@ func createTupleType(curType *abi.Type, converter func(reflect.Type) reflect.Typ return curType.TupleType, curType.TupleType, nil } - // Create native type ourselves to assure that it'll always have the exact memory layout of checked types - // Otherwise, the "unsafe" casting that will be done to convert from checked to native won't be safe. - // At the time of writing, the way the TupleType is built it will be the same, but I don't want to rely on that - // If they ever add private fields for internal tracking - // or anything it would break us if we don't build the native type. - // As an example of how it could possibly change in the future, I've seen struct{} - // added with tags to the top of generated structs to allow metadata exploration. + // Our naive types always have the same layout as the checked ones. + // This differs intentionally from the type.GetType() in abi as fields on structs are pointers in ours to + // verify that fields are intentionally set. nativeFields := make([]reflect.StructField, len(curType.TupleElems)) checkedFields := make([]reflect.StructField, len(curType.TupleElems)) for i, elm := range curType.TupleElems { @@ -247,5 +243,14 @@ func createTupleType(curType *abi.Type, converter func(reflect.Type) reflect.Typ nativeFields[i].Type = nativeArgType checkedFields[i].Type = checkedArgType } - return converter(reflect.StructOf(nativeFields)), converter(reflect.StructOf(checkedFields)), nil + return converter(structOfPointers(nativeFields)), converter(structOfPointers(checkedFields)), nil +} + +func structOfPointers(fields []reflect.StructField) reflect.Type { + for i := range fields { + if fields[i].Type.Kind() != reflect.Pointer { + fields[i].Type = reflect.PointerTo(fields[i].Type) + } + } + return reflect.StructOf(fields) } diff --git a/core/services/relay/evm/types/codec_entry_test.go b/core/services/relay/evm/types/codec_entry_test.go index 8cd5e661c9f..1ea3a9ae576 100644 --- a/core/services/relay/evm/types/codec_entry_test.go +++ b/core/services/relay/evm/types/codec_entry_test.go @@ -36,8 +36,10 @@ func TestCodecEntry(t *testing.T) { require.NoError(t, entry.Init()) checked := reflect.New(entry.CheckedType()) iChecked := reflect.Indirect(checked) - iChecked.FieldByName("Field1").Set(reflect.ValueOf(uint16(2))) - iChecked.FieldByName("Field2").Set(reflect.ValueOf("any string")) + f1 := uint16(2) + iChecked.FieldByName("Field1").Set(reflect.ValueOf(&f1)) + f2 := "any string" + iChecked.FieldByName("Field2").Set(reflect.ValueOf(&f2)) f3 := big.NewInt( /*2^24 - 1*/ 16777215) setAndVerifyLimit(t, (*uint24)(f3), f3, iChecked.FieldByName("Field3")) @@ -72,8 +74,11 @@ func TestCodecEntry(t *testing.T) { checked := reflect.New(entry.CheckedType()) iChecked := reflect.Indirect(checked) - iChecked.FieldByName("Field1").Set(reflect.ValueOf(uint16(2))) + f1 := uint16(2) + iChecked.FieldByName("Field1").Set(reflect.ValueOf(&f1)) f2 := iChecked.FieldByName("Field2") + f2.Set(reflect.New(f2.Type().Elem())) + f2 = reflect.Indirect(f2) f3 := big.NewInt( /*2^24 - 1*/ 16777215) setAndVerifyLimit(t, (*uint24)(f3), f3, f2.FieldByName("Field3")) f4 := big.NewInt( /*2^23 - 1*/ 8388607) @@ -83,7 +88,7 @@ func TestCodecEntry(t *testing.T) { require.NoError(t, err) iNative := reflect.Indirect(native) require.Equal(t, iNative.Field(0).Interface(), iChecked.Field(0).Interface()) - nF2 := iNative.Field(1) + nF2 := reflect.Indirect(iNative.Field(1)) assert.Equal(t, nF2.Field(0).Interface(), f3) assert.Equal(t, nF2.Field(1).Interface(), f4) assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) @@ -100,11 +105,11 @@ func TestCodecEntry(t *testing.T) { checked := reflect.New(entry.CheckedType()) iChecked := reflect.Indirect(checked) anyValue := int16(2) - iChecked.FieldByName("Field1").Set(reflect.ValueOf(anyValue)) + iChecked.FieldByName("Field1").Set(reflect.ValueOf(&anyValue)) native, err := entry.ToNative(checked) require.NoError(t, err) iNative := reflect.Indirect(native) - assert.Equal(t, anyValue, iNative.FieldByName("Field1").Interface()) + assert.Equal(t, &anyValue, iNative.FieldByName("Field1").Interface()) assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) }) @@ -116,7 +121,7 @@ func TestCodecEntry(t *testing.T) { require.NoError(t, entry.Init()) checked := reflect.New(entry.CheckedType()) iChecked := reflect.Indirect(checked) - anySliceValue := []int16{2, 3} + anySliceValue := &[]int16{2, 3} iChecked.FieldByName("Field1").Set(reflect.ValueOf(anySliceValue)) native, err := entry.ToNative(checked) require.NoError(t, err) @@ -132,7 +137,7 @@ func TestCodecEntry(t *testing.T) { require.NoError(t, entry.Init()) checked := reflect.New(entry.CheckedType()) iChecked := reflect.Indirect(checked) - anySliceValue := [3]int16{2, 3, 30} + anySliceValue := &[3]int16{2, 3, 30} iChecked.FieldByName("Field1").Set(reflect.ValueOf(anySliceValue)) native, err := entry.ToNative(checked) require.NoError(t, err) @@ -157,7 +162,7 @@ func TestCodecEntry(t *testing.T) { checked := reflect.New(entry.CheckedType()) iChecked := reflect.Indirect(checked) - anyAddr := common.Address{1, 2, 3} + anyAddr := &common.Address{1, 2, 3} iChecked.FieldByName("Foo").Set(reflect.ValueOf(anyAddr)) native, err := entry.ToNative(checked) @@ -188,7 +193,7 @@ func TestCodecEntry(t *testing.T) { require.NoError(t, entry.Init()) checkedField, ok := entry.CheckedType().FieldByName("Name") require.True(t, ok) - assert.Equal(t, reflect.TypeOf(int16(0)), checkedField.Type) + assert.Equal(t, reflect.TypeOf((*int16)(nil)), checkedField.Type) native, err := entry.ToNative(reflect.New(entry.CheckedType())) require.NoError(t, err) iNative := reflect.Indirect(native) @@ -202,7 +207,7 @@ func TestCodecEntry(t *testing.T) { require.NoError(t, entry.Init()) nativeField, ok := entry.CheckedType().FieldByName("Name") require.True(t, ok) - assert.Equal(t, reflect.TypeOf(common.Hash{}), nativeField.Type) + assert.Equal(t, reflect.TypeOf(&common.Hash{}), nativeField.Type) native, err := entry.ToNative(reflect.New(entry.CheckedType())) require.NoError(t, err) assertHaveSameStructureAndNames(t, native.Type().Elem(), entry.CheckedType()) diff --git a/go.mod b/go.mod index b8180998e92..ab63dbd6475 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 diff --git a/go.sum b/go.sum index 0eccd2bba0b..b71fcd79bc2 100644 --- a/go.sum +++ b/go.sum @@ -1153,8 +1153,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a h1:lgM0yPo0KqSntLY4Y42RAH3avdv+Kyne8n+VM7cwlxo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 h1:uSLn+JbClhldYn7zm0GEALGvi6KAfrY5Kp46IX9sy0E= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f559c517f0b..8e47c411123 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 github.com/smartcontractkit/chainlink-testing-framework v1.22.6 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ab678c43b5b..10671c432a3 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1486,8 +1486,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a h1:lgM0yPo0KqSntLY4Y42RAH3avdv+Kyne8n+VM7cwlxo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240119143538-04c7f63ad53a/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 h1:uSLn+JbClhldYn7zm0GEALGvi6KAfrY5Kp46IX9sy0E= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From d7fe683812367a8d32e6feae0436044c54282c9a Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 23 Jan 2024 10:44:52 -0600 Subject: [PATCH 189/234] update changelog for P2P.V1 removal (#11856) --- docs/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ec2c72852c2..0718d9f8286 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- `P2P.V1` is no longer supported and must not be set in TOML configuration in order to boot. Use `P2P.V2` instead. If you are using both, `V1` can simply be removed. - Removed `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` from TOML configuration, these fields are replaced by `[[TelemetryIngress.Endpoints]]`: ```toml [[TelemetryIngress.Endpoints]] From 5057899e96a1b914ca4b785eacf898827ab742fe Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 23 Jan 2024 12:49:37 -0500 Subject: [PATCH 190/234] Temporarily disable tracker (#11857) * Disable tracker * Remove all tracker calls --- common/txmgr/resender.go | 3 ++- common/txmgr/txmgr.go | 9 ++++++++- core/chains/evm/txmgr/tracker_test.go | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index dded59f55d4..384c0c7a2c0 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -140,7 +140,8 @@ func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) resendUnco return fmt.Errorf("Resender failed getting enabled keys for chain %s: %w", er.chainID.String(), err) } - resendAddresses = append(resendAddresses, er.tracker.GetAbandonedAddresses()...) + // Tracker currently disabled for BCI-2638; refactor required + // resendAddresses = append(resendAddresses, er.tracker.GetAbandonedAddresses()...) ageThreshold := er.txConfig.ResendAfterThreshold() maxInFlightTransactions := er.txConfig.MaxInFlight() diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 358554947cc..c6676674fbf 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -191,10 +191,12 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return fmt.Errorf("Txm: Estimator failed to start: %w", err) } + /* Tracker currently disabled for BCI-2638; refactor required b.logger.Info("Txm starting tracker") if err := ms.Start(ctx, b.tracker); err != nil { return fmt.Errorf("Txm: Tracker failed to start: %w", err) } + */ b.logger.Info("Txm starting runLoop") b.wg.Add(1) @@ -274,9 +276,11 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() (m merr = errors.Join(merr, fmt.Errorf("Txm: failed to close TxAttemptBuilder: %w", err)) } + /* Tracker currently disabled for BCI-2638; refactor required if err := b.tracker.Close(); err != nil { merr = errors.Join(merr, fmt.Errorf("Txm: failed to close Tracker: %w", err)) } + */ return nil }) @@ -391,7 +395,8 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() b.broadcaster.Trigger(address) case head := <-b.chHeads: b.confirmer.mb.Deliver(head) - b.tracker.mb.Deliver(head.BlockNumber()) + // Tracker currently disabled for BCI-2638; refactor required + // b.tracker.mb.Deliver(head.BlockNumber()) case reset := <-b.reset: // This check prevents the weird edge-case where you can select // into this block after chStop has already been closed and the @@ -419,10 +424,12 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { b.logger.Errorw(fmt.Sprintf("Failed to Close Confirmer: %v", err), "err", err) } + /* Tracker currently disabled for BCI-2638; refactor required err = b.tracker.Close() if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { b.logger.Errorw(fmt.Sprintf("Failed to Close Tracker: %v", err), "err", err) } + */ return case <-keysChanged: // This check prevents the weird edge-case where you can select diff --git a/core/chains/evm/txmgr/tracker_test.go b/core/chains/evm/txmgr/tracker_test.go index 3e0bef51568..d3083372789 100644 --- a/core/chains/evm/txmgr/tracker_test.go +++ b/core/chains/evm/txmgr/tracker_test.go @@ -49,6 +49,7 @@ func containsID(txes []*txmgr.Tx, id int64) bool { } func TestEvmTracker_Initialization(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") t.Parallel() tracker, _, _, _ := newTestEvmTrackerSetup(t) @@ -65,6 +66,7 @@ func TestEvmTracker_Initialization(t *testing.T) { } func TestEvmTracker_AddressTracking(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") t.Parallel() t.Run("track abandoned addresses", func(t *testing.T) { @@ -94,6 +96,7 @@ func TestEvmTracker_AddressTracking(t *testing.T) { }) t.Run("stop tracking finalized tx", func(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") tracker, txStore, _, _ := newTestEvmTrackerSetup(t) confirmedAddr := cltest.MustGenerateRandomKey(t).Address _ = mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) @@ -118,6 +121,7 @@ func TestEvmTracker_AddressTracking(t *testing.T) { } func TestEvmTracker_ExceedingTTL(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") t.Parallel() t.Run("confirmed but unfinalized transaction still tracked", func(t *testing.T) { From 737d349144c672f94daa2a1d830453de347646c3 Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Tue, 23 Jan 2024 14:15:14 -0500 Subject: [PATCH 191/234] Auto 8378 create copy of 2 1 contracts and test suits (#11853) * create automation v2.2 contracts * update licenses * adjust solhit rules * update native compilation step * fix foundry tests and AutomationRegistry file name * fix failing foundry test * update nate compile all * fix import path bug * replace automation registry contract * fix typo --- contracts/.solhint.json | 1 + contracts/.solhintignore | 17 +- contracts/package.json | 2 +- .../generate-automation-master-interface.ts | 12 +- .../native_solc_compile_all_automation | 12 +- .../{v2_1 => }/AutomationForwarder.sol | 2 +- .../{v2_1 => }/AutomationForwarderLogic.sol | 4 +- .../v2_2/IAutomationRegistryMaster.sol | 316 + .../dev/v2_2/AutomationRegistrar2_2.sol | 535 ++ .../dev/v2_2/AutomationRegistry2_2.sol | 397 ++ .../dev/v2_2/AutomationRegistryBase2_2.sol | 959 +++ .../dev/v2_2/AutomationRegistryLogicA2_2.sol | 438 ++ .../dev/v2_2/AutomationRegistryLogicB2_2.sol | 516 ++ .../dev/v2_2/AutomationUtils2_2.sol | 40 + .../src/v0.8/automation/dev/v2_2/LICENSE | 57 + .../src/v0.8/automation/dev/v2_2/README.md | 40 + .../{v2_1 => }/test/AutomationForwarder.t.sol | 10 +- .../src/v0.8/automation/test/BaseTest.t.sol | 2 +- .../{v2_1 => }/test/StructFactory.sol | 2 +- .../v2_1/KeeperRegistryLogicA2_1.sol | 2 +- contracts/src/v0.8/automation/v2_1/LICENSE | 3 +- .../v0.8/automation/v2_1/test/BaseTest.t.sol | 12 - .../automation/AutomationRegistrar2_2.test.ts | 1013 +++ .../automation/AutomationRegistry2_2.test.ts | 5757 +++++++++++++++++ 24 files changed, 10115 insertions(+), 34 deletions(-) rename contracts/src/v0.8/automation/{v2_1 => }/AutomationForwarder.sol (97%) rename contracts/src/v0.8/automation/{v2_1 => }/AutomationForwarderLogic.sol (80%) create mode 100644 contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/AutomationRegistrar2_2.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicA2_2.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/AutomationUtils2_2.sol create mode 100644 contracts/src/v0.8/automation/dev/v2_2/LICENSE create mode 100644 contracts/src/v0.8/automation/dev/v2_2/README.md rename contracts/src/v0.8/automation/{v2_1 => }/test/AutomationForwarder.t.sol (88%) rename contracts/src/v0.8/automation/{v2_1 => }/test/StructFactory.sol (85%) delete mode 100644 contracts/src/v0.8/automation/v2_1/test/BaseTest.t.sol create mode 100644 contracts/test/v0.8/automation/AutomationRegistrar2_2.test.ts create mode 100644 contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts diff --git a/contracts/.solhint.json b/contracts/.solhint.json index e66b915d679..ea220d0a030 100644 --- a/contracts/.solhint.json +++ b/contracts/.solhint.json @@ -9,6 +9,7 @@ "func-named-parameters": "off", "immutable-vars-naming": "off", "no-inline-assembly": "off", + "contract-name-camelcase": "off", "no-unused-import": "error", "func-visibility": [ "error", diff --git a/contracts/.solhintignore b/contracts/.solhintignore index 569c8c51df3..4246bbd77ca 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -6,6 +6,21 @@ ./src/v0.8/automation/v1_3 ./src/v0.8/automation/v2_0 ./src/v0.8/automation/v2_1 +./src/v0.8/automation/interfaces/v2_1/ +./src/v0.8/automation/interfaces/MigratableKeeperRegistryInterface.sol +./src/v0.8/automation/upkeeps/UpkeepBalanceMonitor.sol +./src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +./src/v0.8/automation/upkeeps/EthBalanceMonitor.sol +./src/v0.8/automation/upkeeps/ERC20BalanceMonitor.sol +./src/v0.8/automation/upkeeps/CronUpkeepFactory.sol +./src/v0.8/automation/upkeeps/CronUpkeepDelegate.sol +./src/v0.8/automation/upkeeps/CronUpkeep.sol +./src/v0.8/automation/libraries/internal/Cron.sol +./src/v0.8/automation/AutomationForwarder.sol +./src/v0.8/automation/AutomationForwarderLogic.sol + + +# Ignore tests / test helpers (for now) ./src/v0.8/automation/mocks ./src/v0.8/automation/testhelpers @@ -22,4 +37,4 @@ # Always ignore vendor ./src/v0.8/vendor -./node_modules/ \ No newline at end of file +./node_modules/ diff --git a/contracts/package.json b/contracts/package.json index 4dabbdb0509..5d4b6324eee 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 83 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 33 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", diff --git a/contracts/scripts/generate-automation-master-interface.ts b/contracts/scripts/generate-automation-master-interface.ts index de71c56806b..2837fc34923 100644 --- a/contracts/scripts/generate-automation-master-interface.ts +++ b/contracts/scripts/generate-automation-master-interface.ts @@ -2,15 +2,15 @@ * @description this script generates a master interface for interacting with the automation registry * @notice run this script with pnpm ts-node ./scripts/generate-automation-master-interface.ts */ -import { KeeperRegistry2_1__factory as KeeperRegistry } from '../typechain/factories/KeeperRegistry2_1__factory' -import { KeeperRegistryLogicA2_1__factory as KeeperRegistryLogicA } from '../typechain/factories/KeeperRegistryLogicA2_1__factory' -import { KeeperRegistryLogicB2_1__factory as KeeperRegistryLogicB } from '../typechain/factories/KeeperRegistryLogicB2_1__factory' +import { KeeperRegistry2_2__factory as KeeperRegistry } from '../typechain/factories/KeeperRegistry2_2__factory' +import { KeeperRegistryLogicA2_2__factory as KeeperRegistryLogicA } from '../typechain/factories/KeeperRegistryLogicA2_2__factory' +import { KeeperRegistryLogicB2_2__factory as KeeperRegistryLogicB } from '../typechain/factories/KeeperRegistryLogicB2_2__factory' import { utils } from 'ethers' import fs from 'fs' import { exec } from 'child_process' -const dest = 'src/v0.8/automation/interfaces/v2_1' -const srcDest = `${dest}/IKeeperRegistryMaster.sol` +const dest = 'src/v0.8/automation/dev/interfaces/v2_2' +const srcDest = `${dest}/IAutomationRegistryMaster.sol` const tmpDest = `${dest}/tmp.txt` const combinedABI = [] @@ -44,7 +44,7 @@ const checksum = utils.id(abis.join('')) fs.writeFileSync(`${tmpDest}`, JSON.stringify(combinedABI)) const cmd = ` -cat ${tmpDest} | pnpm abi-to-sol --solidity-version ^0.8.4 --license MIT > ${srcDest} IKeeperRegistryMaster; +cat ${tmpDest} | pnpm abi-to-sol --solidity-version ^0.8.4 --license MIT > ${srcDest} IAutomationRegistryMaster; echo "// abi-checksum: ${checksum}" | cat - ${srcDest} > ${tmpDest} && mv ${tmpDest} ${srcDest}; pnpm prettier --write ${srcDest}; ` diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index ddf6c2c8bfa..379b647ffe5 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -57,10 +57,18 @@ compileContract automation/v2_1/AutomationRegistrar2_1.sol compileContract automation/v2_1/KeeperRegistry2_1.sol compileContract automation/v2_1/KeeperRegistryLogicA2_1.sol compileContract automation/v2_1/KeeperRegistryLogicB2_1.sol +compileContract automation/v2_1/AutomationUtils2_1.sol compileContract automation/interfaces/v2_1/IKeeperRegistryMaster.sol + +compileContract automation/dev/v2_2/AutomationRegistrar2_2.sol +compileContract automation/dev/v2_2/AutomationRegistry2_2.sol +compileContract automation/dev/v2_2/AutomationRegistryLogicA2_2.sol +compileContract automation/dev/v2_2/AutomationRegistryLogicB2_2.sol +compileContract automation/dev/v2_2/AutomationUtils2_2.sol +compileContract automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol + compileContract automation/interfaces/ILogAutomation.sol -compileContract automation/v2_1/AutomationUtils2_1.sol -compileContract automation/v2_1/AutomationForwarderLogic.sol +compileContract automation/AutomationForwarderLogic.sol compileContract automation/testhelpers/LogTriggeredStreamsLookup.sol compileContract automation/testhelpers/DummyProtocol.sol diff --git a/contracts/src/v0.8/automation/v2_1/AutomationForwarder.sol b/contracts/src/v0.8/automation/AutomationForwarder.sol similarity index 97% rename from contracts/src/v0.8/automation/v2_1/AutomationForwarder.sol rename to contracts/src/v0.8/automation/AutomationForwarder.sol index e5a80ce3252..f4088ed07fd 100644 --- a/contracts/src/v0.8/automation/v2_1/AutomationForwarder.sol +++ b/contracts/src/v0.8/automation/AutomationForwarder.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; -import {IAutomationRegistryConsumer} from "../interfaces/IAutomationRegistryConsumer.sol"; +import {IAutomationRegistryConsumer} from "./interfaces/IAutomationRegistryConsumer.sol"; uint256 constant PERFORM_GAS_CUSHION = 5_000; diff --git a/contracts/src/v0.8/automation/v2_1/AutomationForwarderLogic.sol b/contracts/src/v0.8/automation/AutomationForwarderLogic.sol similarity index 80% rename from contracts/src/v0.8/automation/v2_1/AutomationForwarderLogic.sol rename to contracts/src/v0.8/automation/AutomationForwarderLogic.sol index a7903c379df..76da5f68cb9 100644 --- a/contracts/src/v0.8/automation/v2_1/AutomationForwarderLogic.sol +++ b/contracts/src/v0.8/automation/AutomationForwarderLogic.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; -import {IAutomationRegistryConsumer} from "../interfaces/IAutomationRegistryConsumer.sol"; -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; +import {IAutomationRegistryConsumer} from "./interfaces/IAutomationRegistryConsumer.sol"; +import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; contract AutomationForwarderLogic is ITypeAndVersion { IAutomationRegistryConsumer private s_registry; diff --git a/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol b/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol new file mode 100644 index 00000000000..b967c7d4b60 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol @@ -0,0 +1,316 @@ +// abi-checksum: 0x0ed34e4b36bd7b4a5447152c2d61491e6ba7ed944b11e4dfef4fea184708975e +// SPDX-License-Identifier: MIT +// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.6.6. SEE SOURCE BELOW. !! +pragma solidity ^0.8.4; + +interface IAutomationRegistryMaster { + error ArrayHasNoEntries(); + error CannotCancel(); + error CheckDataExceedsLimit(); + error ConfigDigestMismatch(); + error DuplicateEntry(); + error DuplicateSigners(); + error GasLimitCanOnlyIncrease(); + error GasLimitOutsideRange(); + error IncorrectNumberOfFaultyOracles(); + error IncorrectNumberOfSignatures(); + error IncorrectNumberOfSigners(); + error IndexOutOfRange(); + error InsufficientFunds(); + error InvalidDataLength(); + error InvalidPayee(); + error InvalidRecipient(); + error InvalidReport(); + error InvalidSigner(); + error InvalidTransmitter(); + error InvalidTrigger(); + error InvalidTriggerType(); + error MaxCheckDataSizeCanOnlyIncrease(); + error MaxPerformDataSizeCanOnlyIncrease(); + error MigrationNotPermitted(); + error NotAContract(); + error OnlyActiveSigners(); + error OnlyActiveTransmitters(); + error OnlyCallableByAdmin(); + error OnlyCallableByLINKToken(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedAdmin(); + error OnlyCallableByProposedPayee(); + error OnlyCallableByUpkeepPrivilegeManager(); + error OnlyPausedUpkeep(); + error OnlySimulatedBackend(); + error OnlyUnpausedUpkeep(); + error ParameterLengthError(); + error PaymentGreaterThanAllLINK(); + error ReentrantCall(); + error RegistryPaused(); + error RepeatedSigner(); + error RepeatedTransmitter(); + error TargetCheckReverted(bytes reason); + error TooManyOracles(); + error TranscoderNotSet(); + error UpkeepAlreadyExists(); + error UpkeepCancelled(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error ValueNotChanged(); + event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); + event CancelledUpkeepReport(uint256 indexed id, bytes trigger); + event ConfigSet( + uint32 previousConfigBlockNumber, + bytes32 configDigest, + uint64 configCount, + address[] signers, + address[] transmitters, + uint8 f, + bytes onchainConfig, + uint64 offchainConfigVersion, + bytes offchainConfig + ); + event DedupKeyAdded(bytes32 indexed dedupKey); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); + event OwnerFundsWithdrawn(uint96 amount); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event Paused(address account); + event PayeesUpdated(address[] transmitters, address[] payees); + event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); + event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); + event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); + event StaleUpkeepReport(uint256 indexed id, bytes trigger); + event Transmitted(bytes32 configDigest, uint32 epoch); + event Unpaused(address account); + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); + event UpkeepPaused(uint256 indexed id); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + uint96 totalPayment, + uint256 gasUsed, + uint256 gasOverhead, + bytes trigger + ); + event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); + event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); + event UpkeepUnpaused(uint256 indexed id); + + fallback() external; + function acceptOwnership() external; + function fallbackTo() external view returns (address); + function latestConfigDetails() external view returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); + function latestConfigDigestAndEpoch() external view returns (bool scanLogs, bytes32 configDigest, uint32 epoch); + function onTokenTransfer(address sender, uint256 amount, bytes memory data) external; + function owner() external view returns (address); + function setConfig( + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfigBytes, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external; + function setConfigTypeSafe( + address[] memory signers, + address[] memory transmitters, + uint8 f, + KeeperRegistryBase2_2.OnchainConfig memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external; + function simulatePerformUpkeep( + uint256 id, + bytes memory performData + ) external view returns (bool success, uint256 gasUsed); + function transferOwnership(address to) external; + function transmit( + bytes32[3] memory reportContext, + bytes memory rawReport, + bytes32[] memory rs, + bytes32[] memory ss, + bytes32 rawVs + ) external; + function typeAndVersion() external view returns (string memory); + + function addFunds(uint256 id, uint96 amount) external; + function cancelUpkeep(uint256 id) external; + function checkCallback( + uint256 id, + bytes[] memory values, + bytes memory extraData + ) external view returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); + function checkUpkeep( + uint256 id, + bytes memory triggerData + ) + external + view + returns ( + bool upkeepNeeded, + bytes memory performData, + uint8 upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkNative + ); + function checkUpkeep( + uint256 id + ) + external + view + returns ( + bool upkeepNeeded, + bytes memory performData, + uint8 upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkNative + ); + function executeCallback( + uint256 id, + bytes memory payload + ) external returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); + function migrateUpkeeps(uint256[] memory ids, address destination) external; + function receiveUpkeeps(bytes memory encodedUpkeeps) external; + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + uint8 triggerType, + bytes memory checkData, + bytes memory triggerConfig, + bytes memory offchainConfig + ) external returns (uint256 id); + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes memory checkData, + bytes memory offchainConfig + ) external returns (uint256 id); + function setUpkeepTriggerConfig(uint256 id, bytes memory triggerConfig) external; + + function acceptPayeeship(address transmitter) external; + function acceptUpkeepAdmin(uint256 id) external; + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); + function getAdminPrivilegeConfig(address admin) external view returns (bytes memory); + function getAutomationForwarderLogic() external view returns (address); + function getBalance(uint256 id) external view returns (uint96 balance); + function getCancellationDelay() external pure returns (uint256); + function getConditionalGasOverhead() external pure returns (uint256); + function getFastGasFeedAddress() external view returns (address); + function getForwarder(uint256 upkeepID) external view returns (address); + function getLinkAddress() external view returns (address); + function getLinkNativeFeedAddress() external view returns (address); + function getLogGasOverhead() external pure returns (uint256); + function getMaxPaymentForGas(uint8 triggerType, uint32 gasLimit) external view returns (uint96 maxPayment); + function getMinBalance(uint256 id) external view returns (uint96); + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance); + function getMode() external view returns (uint8); + function getPeerRegistryMigrationPermission(address peer) external view returns (uint8); + function getPerPerformByteGasOverhead() external pure returns (uint256); + function getPerSignerGasOverhead() external pure returns (uint256); + function getSignerInfo(address query) external view returns (bool active, uint8 index); + function getState() + external + view + returns ( + KeeperRegistryBase2_2.State memory state, + KeeperRegistryBase2_2.OnchainConfig memory config, + address[] memory signers, + address[] memory transmitters, + uint8 f + ); + function getTransmitterInfo( + address query + ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee); + function getTriggerType(uint256 upkeepId) external pure returns (uint8); + function getUpkeep(uint256 id) external view returns (KeeperRegistryBase2_2.UpkeepInfo memory upkeepInfo); + function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory); + function getUpkeepTriggerConfig(uint256 upkeepId) external view returns (bytes memory); + function hasDedupKey(bytes32 dedupKey) external view returns (bool); + function pause() external; + function pauseUpkeep(uint256 id) external; + function recoverFunds() external; + function setAdminPrivilegeConfig(address admin, bytes memory newPrivilegeConfig) external; + function setPayees(address[] memory payees) external; + function setPeerRegistryMigrationPermission(address peer, uint8 permission) external; + function setUpkeepCheckData(uint256 id, bytes memory newCheckData) external; + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; + function setUpkeepOffchainConfig(uint256 id, bytes memory config) external; + function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes memory newPrivilegeConfig) external; + function transferPayeeship(address transmitter, address proposed) external; + function transferUpkeepAdmin(uint256 id, address proposed) external; + function unpause() external; + function unpauseUpkeep(uint256 id) external; + function upkeepTranscoderVersion() external pure returns (uint8); + function upkeepVersion() external pure returns (uint8); + function withdrawFunds(uint256 id, address to) external; + function withdrawOwnerFunds() external; + function withdrawPayment(address from, address to) external; +} + +interface KeeperRegistryBase2_2 { + struct OnchainConfig { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint32 maxCheckDataSize; + uint32 maxPerformDataSize; + uint32 maxRevertDataSize; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address[] registrars; + address upkeepPrivilegeManager; + } + + struct State { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint96 totalPremium; + uint256 numUpkeeps; + uint32 configCount; + uint32 latestConfigBlockNumber; + bytes32 latestConfigDigest; + uint32 latestEpoch; + bool paused; + } + + struct UpkeepInfo { + address target; + uint32 performGas; + bytes checkData; + uint96 balance; + address admin; + uint64 maxValidBlocknumber; + uint32 lastPerformedBlockNumber; + uint96 amountSpent; + bool paused; + bytes offchainConfig; + } +} + +// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON: +/* +[{"inputs":[{"internalType":"contract KeeperRegistryLogicB2_2","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MaxCheckDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MaxPerformDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"PaymentGreaterThanAllLINK","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"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":"address[]","name":"signers","type":"address[]"},{"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":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"OwnerFundsWithdrawn","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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","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":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct KeeperRegistryBase2_2.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract KeeperRegistryLogicB2_2","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum KeeperRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum KeeperRegistryBase2_2.Mode","name":"mode","type":"uint8"},{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkNativeFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkNativeFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum KeeperRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMode","outputs":[{"internalType":"enum KeeperRegistryBase2_2.Mode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum KeeperRegistryBase2_2.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerPerformByteGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct KeeperRegistryBase2_2.State","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct KeeperRegistryBase2_2.OnchainConfig","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum KeeperRegistryBase2_2.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct KeeperRegistryBase2_2.UpkeepInfo","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum KeeperRegistryBase2_2.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepTranscoderVersion","outputs":[{"internalType":"enum UpkeepFormat","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwnerFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] +*/ diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistrar2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistrar2_2.sol new file mode 100644 index 00000000000..1fa1feeb578 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistrar2_2.sol @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {LinkTokenInterface} from "../../../shared/interfaces/LinkTokenInterface.sol"; +import {IAutomationRegistryMaster} from "../interfaces/v2_2/IAutomationRegistryMaster.sol"; +import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; +import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; +import {IERC677Receiver} from "../../../shared/interfaces/IERC677Receiver.sol"; + +/** + * @notice Contract to accept requests for upkeep registrations + * @dev There are 2 registration workflows in this contract + * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually + * calls `approve` to register upkeep and emit events to inform UI and others interested. + * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on + * keeper registry and then emits approved event to finish the flow automatically without manual intervention. + * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not. + * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations. + */ +contract AutomationRegistrar2_2 is TypeAndVersionInterface, ConfirmedOwner, IERC677Receiver { + /** + * DISABLED: No auto approvals, all new upkeeps should be approved manually. + * ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest. + * ENABLED_ALL: Auto approvals for all new upkeeps subject to max allowed. + */ + enum AutoApproveType { + DISABLED, + ENABLED_SENDER_ALLOWLIST, + ENABLED_ALL + } + + bytes4 private constant REGISTER_REQUEST_SELECTOR = this.register.selector; + + mapping(bytes32 => PendingRequest) private s_pendingRequests; + mapping(uint8 => TriggerRegistrationStorage) private s_triggerRegistrations; + + LinkTokenInterface public immutable LINK; + + /** + * @notice versions: + * - KeeperRegistrar 2.1.0: Update for compatability with registry 2.1.0 + * Add auto approval levels by type + * - KeeperRegistrar 2.0.0: Remove source from register + * Breaks our example of "Register an Upkeep using your own deployed contract" + * - KeeperRegistrar 1.1.0: Add functionality for sender allowlist in auto approve + * : Remove rate limit and add max allowed for auto approve + * - KeeperRegistrar 1.0.0: initial release + */ + string public constant override typeAndVersion = "AutomationRegistrar 2.1.0"; + + /** + * @notice TriggerRegistrationStorage stores the auto-approval levels for upkeeps by type + * @member autoApproveType the auto approval setting (see enum) + * @member autoApproveMaxAllowed the max number of upkeeps that can be auto approved of this type + * @member approvedCount the count of upkeeps auto approved of this type + */ + struct TriggerRegistrationStorage { + AutoApproveType autoApproveType; + uint32 autoApproveMaxAllowed; + uint32 approvedCount; + } + + /** + * @notice InitialTriggerConfig configures the auto-approval levels for upkeeps by trigger type + * @dev this struct is only used in the constructor to set the initial values for various trigger configs + * @member triggerType the upkeep type to configure + * @member autoApproveType the auto approval setting (see enum) + * @member autoApproveMaxAllowed the max number of upkeeps that can be auto approved of this type + */ + struct InitialTriggerConfig { + uint8 triggerType; + AutoApproveType autoApproveType; + uint32 autoApproveMaxAllowed; + } + + struct RegistrarConfig { + IAutomationRegistryMaster AutomationRegistry; + uint96 minLINKJuels; + } + + struct PendingRequest { + address admin; + uint96 balance; + } + + struct RegistrationParams { + string name; + bytes encryptedEmail; + address upkeepContract; + uint32 gasLimit; + address adminAddress; + uint8 triggerType; + bytes checkData; + bytes triggerConfig; + bytes offchainConfig; + uint96 amount; + } + + RegistrarConfig private s_config; + // Only applicable if s_config.configType is ENABLED_SENDER_ALLOWLIST + mapping(address => bool) private s_autoApproveAllowedSenders; + + event RegistrationRequested( + bytes32 indexed hash, + string name, + bytes encryptedEmail, + address indexed upkeepContract, + uint32 gasLimit, + address adminAddress, + uint8 triggerType, + bytes triggerConfig, + bytes offchainConfig, + bytes checkData, + uint96 amount + ); + + event RegistrationApproved(bytes32 indexed hash, string displayName, uint256 indexed upkeepId); + + event RegistrationRejected(bytes32 indexed hash); + + event AutoApproveAllowedSenderSet(address indexed senderAddress, bool allowed); + + event ConfigChanged(address AutomationRegistry, uint96 minLINKJuels); + + event TriggerConfigSet(uint8 triggerType, AutoApproveType autoApproveType, uint32 autoApproveMaxAllowed); + + error InvalidAdminAddress(); + error RequestNotFound(); + error HashMismatch(); + error OnlyAdminOrOwner(); + error InsufficientPayment(); + error RegistrationRequestFailed(); + error OnlyLink(); + error AmountMismatch(); + error SenderMismatch(); + error FunctionNotPermitted(); + error LinkTransferFailed(address to); + error InvalidDataLength(); + + /** + * @param LINKAddress Address of Link token + * @param AutomationRegistry keeper registry address + * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with + * @param triggerConfigs the initial config for individual triggers + */ + constructor( + address LINKAddress, + address AutomationRegistry, + uint96 minLINKJuels, + InitialTriggerConfig[] memory triggerConfigs + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(LINKAddress); + setConfig(AutomationRegistry, minLINKJuels); + for (uint256 idx = 0; idx < triggerConfigs.length; idx++) { + setTriggerConfig( + triggerConfigs[idx].triggerType, + triggerConfigs[idx].autoApproveType, + triggerConfigs[idx].autoApproveMaxAllowed + ); + } + } + + //EXTERNAL + + /** + * @notice register can only be called through transferAndCall on LINK contract + * @param name string of the upkeep to be registered + * @param encryptedEmail email address of upkeep contact + * @param upkeepContract address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when performing upkeep + * @param adminAddress address to cancel upkeep and withdraw remaining funds + * @param triggerType the type of trigger for the upkeep + * @param checkData data passed to the contract when checking for upkeep + * @param triggerConfig the config for the trigger + * @param offchainConfig offchainConfig for upkeep in bytes + * @param amount quantity of LINK upkeep is funded with (specified in Juels) + * @param sender address of the sender making the request + */ + function register( + string memory name, + bytes calldata encryptedEmail, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + uint8 triggerType, + bytes memory checkData, + bytes memory triggerConfig, + bytes memory offchainConfig, + uint96 amount, + address sender + ) external onlyLINK { + _register( + RegistrationParams({ + name: name, + encryptedEmail: encryptedEmail, + upkeepContract: upkeepContract, + gasLimit: gasLimit, + adminAddress: adminAddress, + triggerType: triggerType, + checkData: checkData, + triggerConfig: triggerConfig, + offchainConfig: offchainConfig, + amount: amount + }), + sender + ); + } + + /** + * @notice Allows external users to register upkeeps; assumes amount is approved for transfer by the contract + * @param requestParams struct of all possible registration parameters + */ + function registerUpkeep(RegistrationParams calldata requestParams) external returns (uint256) { + if (requestParams.amount < s_config.minLINKJuels) { + revert InsufficientPayment(); + } + + LINK.transferFrom(msg.sender, address(this), requestParams.amount); + + return _register(requestParams, msg.sender); + } + + /** + * @dev register upkeep on AutomationRegistry contract and emit RegistrationApproved event + */ + function approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + uint8 triggerType, + bytes calldata checkData, + bytes memory triggerConfig, + bytes calldata offchainConfig, + bytes32 hash + ) external onlyOwner { + PendingRequest memory request = s_pendingRequests[hash]; + if (request.admin == address(0)) { + revert RequestNotFound(); + } + bytes32 expectedHash = keccak256( + abi.encode(upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig) + ); + if (hash != expectedHash) { + revert HashMismatch(); + } + delete s_pendingRequests[hash]; + _approve( + RegistrationParams({ + name: name, + encryptedEmail: "", + upkeepContract: upkeepContract, + gasLimit: gasLimit, + adminAddress: adminAddress, + triggerType: triggerType, + checkData: checkData, + triggerConfig: triggerConfig, + offchainConfig: offchainConfig, + amount: request.balance + }), + expectedHash + ); + } + + /** + * @notice cancel will remove a registration request and return the refunds to the request.admin + * @param hash the request hash + */ + function cancel(bytes32 hash) external { + PendingRequest memory request = s_pendingRequests[hash]; + if (!(msg.sender == request.admin || msg.sender == owner())) { + revert OnlyAdminOrOwner(); + } + if (request.admin == address(0)) { + revert RequestNotFound(); + } + delete s_pendingRequests[hash]; + bool success = LINK.transfer(request.admin, request.balance); + if (!success) { + revert LinkTransferFailed(request.admin); + } + emit RegistrationRejected(hash); + } + + /** + * @notice owner calls this function to set contract config + * @param AutomationRegistry new keeper registry address + * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with + */ + function setConfig(address AutomationRegistry, uint96 minLINKJuels) public onlyOwner { + s_config = RegistrarConfig({ + minLINKJuels: minLINKJuels, + AutomationRegistry: IAutomationRegistryMaster(AutomationRegistry) + }); + emit ConfigChanged(AutomationRegistry, minLINKJuels); + } + + /** + * @notice owner calls to set the config for this upkeep type + * @param triggerType the upkeep type to configure + * @param autoApproveType the auto approval setting (see enum) + * @param autoApproveMaxAllowed the max number of upkeeps that can be auto approved of this type + */ + function setTriggerConfig( + uint8 triggerType, + AutoApproveType autoApproveType, + uint32 autoApproveMaxAllowed + ) public onlyOwner { + s_triggerRegistrations[triggerType].autoApproveType = autoApproveType; + s_triggerRegistrations[triggerType].autoApproveMaxAllowed = autoApproveMaxAllowed; + emit TriggerConfigSet(triggerType, autoApproveType, autoApproveMaxAllowed); + } + + /** + * @notice owner calls this function to set allowlist status for senderAddress + * @param senderAddress senderAddress to set the allowlist status for + * @param allowed true if senderAddress needs to be added to allowlist, false if needs to be removed + */ + function setAutoApproveAllowedSender(address senderAddress, bool allowed) external onlyOwner { + s_autoApproveAllowedSenders[senderAddress] = allowed; + + emit AutoApproveAllowedSenderSet(senderAddress, allowed); + } + + /** + * @notice read the allowlist status of senderAddress + * @param senderAddress address to read the allowlist status for + */ + function getAutoApproveAllowedSender(address senderAddress) external view returns (bool) { + return s_autoApproveAllowedSenders[senderAddress]; + } + + /** + * @notice read the current registration configuration + */ + function getConfig() external view returns (address AutomationRegistry, uint256 minLINKJuels) { + RegistrarConfig memory config = s_config; + return (address(config.AutomationRegistry), config.minLINKJuels); + } + + /** + * @notice read the config for this upkeep type + * @param triggerType upkeep type to read config for + */ + function getTriggerRegistrationDetails(uint8 triggerType) external view returns (TriggerRegistrationStorage memory) { + return s_triggerRegistrations[triggerType]; + } + + /** + * @notice gets the admin address and the current balance of a registration request + */ + function getPendingRequest(bytes32 hash) external view returns (address, uint96) { + PendingRequest memory request = s_pendingRequests[hash]; + return (request.admin, request.balance); + } + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @param sender Address of the sender transfering LINK + * @param amount Amount of LINK sent (specified in Juels) + * @param data Payload of the transaction + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) + external + override + onlyLINK + permittedFunctionsForLINK(data) + isActualAmount(amount, data) + isActualSender(sender, data) + { + if (amount < s_config.minLINKJuels) { + revert InsufficientPayment(); + } + (bool success, ) = address(this).delegatecall(data); + // calls register + if (!success) { + revert RegistrationRequestFailed(); + } + } + + // ================================================================ + // | PRIVATE | + // ================================================================ + + /** + * @dev verify registration request and emit RegistrationRequested event + */ + function _register(RegistrationParams memory params, address sender) private returns (uint256) { + if (params.adminAddress == address(0)) { + revert InvalidAdminAddress(); + } + bytes32 hash = keccak256( + abi.encode( + params.upkeepContract, + params.gasLimit, + params.adminAddress, + params.triggerType, + params.checkData, + params.triggerConfig, + params.offchainConfig + ) + ); + + emit RegistrationRequested( + hash, + params.name, + params.encryptedEmail, + params.upkeepContract, + params.gasLimit, + params.adminAddress, + params.triggerType, + params.triggerConfig, + params.offchainConfig, + params.checkData, + params.amount + ); + + uint256 upkeepId; + if (_shouldAutoApprove(s_triggerRegistrations[params.triggerType], sender)) { + s_triggerRegistrations[params.triggerType].approvedCount++; + upkeepId = _approve(params, hash); + } else { + uint96 newBalance = s_pendingRequests[hash].balance + params.amount; + s_pendingRequests[hash] = PendingRequest({admin: params.adminAddress, balance: newBalance}); + } + + return upkeepId; + } + + /** + * @dev register upkeep on AutomationRegistry contract and emit RegistrationApproved event + */ + function _approve(RegistrationParams memory params, bytes32 hash) private returns (uint256) { + IAutomationRegistryMaster AutomationRegistry = s_config.AutomationRegistry; + uint256 upkeepId = AutomationRegistry.registerUpkeep( + params.upkeepContract, + params.gasLimit, + params.adminAddress, + params.triggerType, + params.checkData, + params.triggerConfig, + params.offchainConfig + ); + bool success = LINK.transferAndCall(address(AutomationRegistry), params.amount, abi.encode(upkeepId)); + if (!success) { + revert LinkTransferFailed(address(AutomationRegistry)); + } + emit RegistrationApproved(hash, params.name, upkeepId); + return upkeepId; + } + + /** + * @dev verify sender allowlist if needed and check max limit + */ + function _shouldAutoApprove(TriggerRegistrationStorage memory config, address sender) private view returns (bool) { + if (config.autoApproveType == AutoApproveType.DISABLED) { + return false; + } + if (config.autoApproveType == AutoApproveType.ENABLED_SENDER_ALLOWLIST && (!s_autoApproveAllowedSenders[sender])) { + return false; + } + if (config.approvedCount < config.autoApproveMaxAllowed) { + return true; + } + return false; + } + + // ================================================================ + // | MODIFIERS | + // ================================================================ + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + if (msg.sender != address(LINK)) { + revert OnlyLink(); + } + _; + } + + /** + * @dev Reverts if the given data does not begin with the `register` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory _data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(_data, 32)) // First 32 bytes contain length of data + } + if (funcSelector != REGISTER_REQUEST_SELECTOR) { + revert FunctionNotPermitted(); + } + _; + } + + /** + * @dev Reverts if the actual amount passed does not match the expected amount + * @param expected amount that should match the actual amount + * @param data bytes + */ + modifier isActualAmount(uint256 expected, bytes calldata data) { + // decode register function arguments to get actual amount + (, , , , , , , , , uint96 amount, ) = abi.decode( + data[4:], + (string, bytes, address, uint32, address, uint8, bytes, bytes, bytes, uint96, address) + ); + if (expected != amount) { + revert AmountMismatch(); + } + _; + } + + /** + * @dev Reverts if the actual sender address does not match the expected sender address + * @param expected address that should match the actual sender address + * @param data bytes + */ + modifier isActualSender(address expected, bytes calldata data) { + // decode register function arguments to get actual sender + (, , , , , , , , , , address sender) = abi.decode( + data[4:], + (string, bytes, address, uint32, address, uint8, bytes, bytes, bytes, uint96, address) + ); + if (expected != sender) { + revert SenderMismatch(); + } + _; + } +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol new file mode 100644 index 00000000000..e21387936e0 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {AutomationRegistryBase2_2} from "./AutomationRegistryBase2_2.sol"; +import {AutomationRegistryLogicB2_2} from "./AutomationRegistryLogicB2_2.sol"; +import {Chainable} from "../../Chainable.sol"; +import {IERC677Receiver} from "../../../shared/interfaces/IERC677Receiver.sol"; +import {OCR2Abstract} from "../../../shared/ocr2/OCR2Abstract.sol"; + +/** + * @notice Registry for adding work for Chainlink nodes to perform on client + * contracts. Clients must support the AutomationCompatibleInterface interface. + */ +contract AutomationRegistry2_2 is AutomationRegistryBase2_2, OCR2Abstract, Chainable, IERC677Receiver { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + /** + * @notice versions: + * AutomationRegistry 2.2.0: moves chain-spicific integration code into a separate module + * KeeperRegistry 2.1.0: introduces support for log triggers + * removes the need for "wrapped perform data" + * KeeperRegistry 2.0.2: pass revert bytes as performData when target contract reverts + * fixes issue with arbitrum block number + * does an early return in case of stale report instead of revert + * KeeperRegistry 2.0.1: implements workaround for buggy migrate function in 1.X + * KeeperRegistry 2.0.0: implement OCR interface + * KeeperRegistry 1.3.0: split contract into Proxy and Logic + * account for Arbitrum and Optimism L1 gas fee + * allow users to configure upkeeps + * KeeperRegistry 1.2.0: allow funding within performUpkeep + * allow configurable registry maxPerformGas + * add function to let admin change upkeep gas limit + * add minUpkeepSpend requirement + * upgrade to solidity v0.8 + * KeeperRegistry 1.1.0: added flatFeeMicroLink + * KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "AutomationRegistry 2.2.0"; + + /** + * @param logicA the address of the first logic contract, but cast as logicB in order to call logicB functions + */ + constructor( + AutomationRegistryLogicB2_2 logicA + ) + AutomationRegistryBase2_2( + logicA.getMode(), + logicA.getLinkAddress(), + logicA.getLinkNativeFeedAddress(), + logicA.getFastGasFeedAddress(), + logicA.getAutomationForwarderLogic() + ) + Chainable(address(logicA)) + {} + + // ================================================================ + // | ACTIONS | + // ================================================================ + + /** + * @inheritdoc OCR2Abstract + */ + function transmit( + bytes32[3] calldata reportContext, + bytes calldata rawReport, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs + ) external override { + uint256 gasOverhead = gasleft(); + HotVars memory hotVars = s_hotVars; + + if (hotVars.paused) revert RegistryPaused(); + if (!s_transmitters[msg.sender].active) revert OnlyActiveTransmitters(); + + // Verify signatures + if (s_latestConfigDigest != reportContext[0]) revert ConfigDigestMismatch(); + if (rs.length != hotVars.f + 1 || rs.length != ss.length) revert IncorrectNumberOfSignatures(); + _verifyReportSignature(reportContext, rawReport, rs, ss, rawVs); + + Report memory report = _decodeReport(rawReport); + UpkeepTransmitInfo[] memory upkeepTransmitInfo = new UpkeepTransmitInfo[](report.upkeepIds.length); + uint16 numUpkeepsPassedChecks; + + for (uint256 i = 0; i < report.upkeepIds.length; i++) { + upkeepTransmitInfo[i].upkeep = s_upkeep[report.upkeepIds[i]]; + upkeepTransmitInfo[i].triggerType = _getTriggerType(report.upkeepIds[i]); + upkeepTransmitInfo[i].maxLinkPayment = _getMaxLinkPayment( + hotVars, + upkeepTransmitInfo[i].triggerType, + uint32(report.gasLimits[i]), + uint32(report.performDatas[i].length), + report.fastGasWei, + report.linkNative, + true + ); + (upkeepTransmitInfo[i].earlyChecksPassed, upkeepTransmitInfo[i].dedupID) = _prePerformChecks( + report.upkeepIds[i], + report.triggers[i], + upkeepTransmitInfo[i] + ); + + if (upkeepTransmitInfo[i].earlyChecksPassed) { + numUpkeepsPassedChecks += 1; + } else { + continue; + } + + // Actually perform the target upkeep + (upkeepTransmitInfo[i].performSuccess, upkeepTransmitInfo[i].gasUsed) = _performUpkeep( + upkeepTransmitInfo[i].upkeep.forwarder, + report.gasLimits[i], + report.performDatas[i] + ); + + // Deduct that gasUsed by upkeep from our running counter + gasOverhead -= upkeepTransmitInfo[i].gasUsed; + + // Store last perform block number / deduping key for upkeep + _updateTriggerMarker(report.upkeepIds[i], upkeepTransmitInfo[i]); + } + // No upkeeps to be performed in this report + if (numUpkeepsPassedChecks == 0) { + return; + } + + // This is the overall gas overhead that will be split across performed upkeeps + // Take upper bound of 16 gas per callData bytes, which is approximated to be reportLength + // Rest of msg.data is accounted for in accounting overheads + gasOverhead = + (gasOverhead - gasleft() + 16 * rawReport.length) + + ACCOUNTING_FIXED_GAS_OVERHEAD + + (ACCOUNTING_PER_SIGNER_GAS_OVERHEAD * (hotVars.f + 1)); + gasOverhead = gasOverhead / numUpkeepsPassedChecks + ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD; + + uint96 totalReimbursement; + uint96 totalPremium; + { + uint96 reimbursement; + uint96 premium; + for (uint256 i = 0; i < report.upkeepIds.length; i++) { + if (upkeepTransmitInfo[i].earlyChecksPassed) { + upkeepTransmitInfo[i].gasOverhead = _getCappedGasOverhead( + gasOverhead, + upkeepTransmitInfo[i].triggerType, + uint32(report.performDatas[i].length), + hotVars.f + ); + + (reimbursement, premium) = _postPerformPayment( + hotVars, + report.upkeepIds[i], + upkeepTransmitInfo[i], + report.fastGasWei, + report.linkNative, + numUpkeepsPassedChecks + ); + totalPremium += premium; + totalReimbursement += reimbursement; + + emit UpkeepPerformed( + report.upkeepIds[i], + upkeepTransmitInfo[i].performSuccess, + reimbursement + premium, + upkeepTransmitInfo[i].gasUsed, + upkeepTransmitInfo[i].gasOverhead, + report.triggers[i] + ); + } + } + } + // record payments + s_transmitters[msg.sender].balance += totalReimbursement; + s_hotVars.totalPremium += totalPremium; + + uint40 epochAndRound = uint40(uint256(reportContext[1])); + uint32 epoch = uint32(epochAndRound >> 8); + if (epoch > hotVars.latestEpoch) { + s_hotVars.latestEpoch = epoch; + } + } + + /** + * @notice simulates the upkeep with the perform data returned from checkUpkeep + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + * @return success whether the call reverted or not + * @return gasUsed the amount of gas the target contract consumed + */ + function simulatePerformUpkeep( + uint256 id, + bytes calldata performData + ) external cannotExecute returns (bool success, uint256 gasUsed) { + if (s_hotVars.paused) revert RegistryPaused(); + Upkeep memory upkeep = s_upkeep[id]; + (success, gasUsed) = _performUpkeep(upkeep.forwarder, upkeep.performGas, performData); + return (success, gasUsed); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external override { + if (msg.sender != address(i_link)) revert OnlyCallableByLINKToken(); + if (data.length != 32) revert InvalidDataLength(); + uint256 id = abi.decode(data, (uint256)); + if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); + s_expectedLinkBalance = s_expectedLinkBalance + amount; + emit FundsAdded(id, sender, uint96(amount)); + } + + // ================================================================ + // | SETTERS | + // ================================================================ + + /** + * @inheritdoc OCR2Abstract + * @dev prefer the type-safe version of setConfig (below) whenever possible + */ + function setConfig( + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfigBytes, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external override { + setConfigTypeSafe( + signers, + transmitters, + f, + abi.decode(onchainConfigBytes, (OnchainConfig)), + offchainConfigVersion, + offchainConfig + ); + } + + function setConfigTypeSafe( + address[] memory signers, + address[] memory transmitters, + uint8 f, + OnchainConfig memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) public onlyOwner { + if (signers.length > MAX_NUM_ORACLES) revert TooManyOracles(); + if (f == 0) revert IncorrectNumberOfFaultyOracles(); + if (signers.length != transmitters.length || signers.length <= 3 * f) revert IncorrectNumberOfSigners(); + + // move all pooled payments out of the pool to each transmitter's balance + uint96 totalPremium = s_hotVars.totalPremium; + uint96 oldLength = uint96(s_transmittersList.length); + for (uint256 i = 0; i < oldLength; i++) { + _updateTransmitterBalanceFromPool(s_transmittersList[i], totalPremium, oldLength); + } + + // remove any old signer/transmitter addresses + address signerAddress; + address transmitterAddress; + for (uint256 i = 0; i < oldLength; i++) { + signerAddress = s_signersList[i]; + transmitterAddress = s_transmittersList[i]; + delete s_signers[signerAddress]; + // Do not delete the whole transmitter struct as it has balance information stored + s_transmitters[transmitterAddress].active = false; + } + delete s_signersList; + delete s_transmittersList; + + // add new signer/transmitter addresses + { + Transmitter memory transmitter; + address temp; + for (uint256 i = 0; i < signers.length; i++) { + if (s_signers[signers[i]].active) revert RepeatedSigner(); + if (signers[i] == ZERO_ADDRESS) revert InvalidSigner(); + s_signers[signers[i]] = Signer({active: true, index: uint8(i)}); + + temp = transmitters[i]; + if (temp == ZERO_ADDRESS) revert InvalidTransmitter(); + transmitter = s_transmitters[temp]; + if (transmitter.active) revert RepeatedTransmitter(); + transmitter.active = true; + transmitter.index = uint8(i); + // new transmitters start afresh from current totalPremium + // some spare change of premium from previous pool will be forfeited + transmitter.lastCollected = totalPremium; + s_transmitters[temp] = transmitter; + } + } + s_signersList = signers; + s_transmittersList = transmitters; + + s_hotVars = HotVars({ + f: f, + paymentPremiumPPB: onchainConfig.paymentPremiumPPB, + flatFeeMicroLink: onchainConfig.flatFeeMicroLink, + stalenessSeconds: onchainConfig.stalenessSeconds, + gasCeilingMultiplier: onchainConfig.gasCeilingMultiplier, + paused: s_hotVars.paused, + reentrancyGuard: s_hotVars.reentrancyGuard, + totalPremium: totalPremium, + latestEpoch: 0 // DON restarts epoch + }); + + s_storage = Storage({ + checkGasLimit: onchainConfig.checkGasLimit, + minUpkeepSpend: onchainConfig.minUpkeepSpend, + maxPerformGas: onchainConfig.maxPerformGas, + transcoder: onchainConfig.transcoder, + maxCheckDataSize: onchainConfig.maxCheckDataSize, + maxPerformDataSize: onchainConfig.maxPerformDataSize, + maxRevertDataSize: onchainConfig.maxRevertDataSize, + upkeepPrivilegeManager: onchainConfig.upkeepPrivilegeManager, + nonce: s_storage.nonce, + configCount: s_storage.configCount, + latestConfigBlockNumber: s_storage.latestConfigBlockNumber, + ownerLinkBalance: s_storage.ownerLinkBalance + }); + s_fallbackGasPrice = onchainConfig.fallbackGasPrice; + s_fallbackLinkPrice = onchainConfig.fallbackLinkPrice; + + uint32 previousConfigBlockNumber = s_storage.latestConfigBlockNumber; + s_storage.latestConfigBlockNumber = uint32(_blockNum()); + s_storage.configCount += 1; + + bytes memory onchainConfigBytes = abi.encode(onchainConfig); + + s_latestConfigDigest = _configDigestFromConfigData( + block.chainid, + address(this), + s_storage.configCount, + signers, + transmitters, + f, + onchainConfigBytes, + offchainConfigVersion, + offchainConfig + ); + + for (uint256 idx = 0; idx < s_registrars.length(); idx++) { + s_registrars.remove(s_registrars.at(idx)); + } + + for (uint256 idx = 0; idx < onchainConfig.registrars.length; idx++) { + s_registrars.add(onchainConfig.registrars[idx]); + } + + emit ConfigSet( + previousConfigBlockNumber, + s_latestConfigDigest, + s_storage.configCount, + signers, + transmitters, + f, + onchainConfigBytes, + offchainConfigVersion, + offchainConfig + ); + } + + // ================================================================ + // | GETTERS | + // ================================================================ + + /** + * @inheritdoc OCR2Abstract + */ + function latestConfigDetails() + external + view + override + returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) + { + return (s_storage.configCount, s_storage.latestConfigBlockNumber, s_latestConfigDigest); + } + + /** + * @inheritdoc OCR2Abstract + */ + function latestConfigDigestAndEpoch() + external + view + override + returns (bool scanLogs, bytes32 configDigest, uint32 epoch) + { + return (false, s_latestConfigDigest, s_hotVars.latestEpoch); + } +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol new file mode 100644 index 00000000000..b3e970e6c35 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import {OVM_GasPriceOracle} from "../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; +import {ExecutionPrevention} from "../../ExecutionPrevention.sol"; +import {ArbSys} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; +import {StreamsLookupCompatibleInterface} from "../../interfaces/StreamsLookupCompatibleInterface.sol"; +import {ILogAutomation, Log} from "../../interfaces/ILogAutomation.sol"; +import {IAutomationForwarder} from "../../interfaces/IAutomationForwarder.sol"; +import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; +import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; +import {LinkTokenInterface} from "../../../shared/interfaces/LinkTokenInterface.sol"; +import {KeeperCompatibleInterface} from "../../interfaces/KeeperCompatibleInterface.sol"; +import {UpkeepFormat} from "../../interfaces/UpkeepTranscoderInterface.sol"; + +/** + * @notice Base Keeper Registry contract, contains shared logic between + * AutomationRegistry and AutomationRegistryLogic + * @dev all errors, events, and internal functions should live here + */ +abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPrevention { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + address internal constant ZERO_ADDRESS = address(0); + address internal constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 internal constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 internal constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + bytes4 internal constant CHECK_CALLBACK_SELECTOR = StreamsLookupCompatibleInterface.checkCallback.selector; + bytes4 internal constant CHECK_LOG_SELECTOR = ILogAutomation.checkLog.selector; + uint256 internal constant PERFORM_GAS_MIN = 2_300; + uint256 internal constant CANCELLATION_DELAY = 50; + uint256 internal constant PERFORM_GAS_CUSHION = 5_000; + uint256 internal constant PPB_BASE = 1_000_000_000; + uint32 internal constant UINT32_MAX = type(uint32).max; + uint96 internal constant LINK_TOTAL_SUPPLY = 1e27; + // The first byte of the mask can be 0, because we only ever have 31 oracles + uint256 internal constant ORACLE_MASK = 0x0001010101010101010101010101010101010101010101010101010101010101; + /** + * @dev UPKEEP_TRANSCODER_VERSION_BASE is temporary necessity for backwards compatibility with + * MigratableAutomationRegistryInterfaceV1 - it should be removed in future versions in favor of + * UPKEEP_VERSION_BASE and MigratableAutomationRegistryInterfaceV2 + */ + UpkeepFormat internal constant UPKEEP_TRANSCODER_VERSION_BASE = UpkeepFormat.V1; + uint8 internal constant UPKEEP_VERSION_BASE = 3; + // L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism + bytes internal constant L1_FEE_DATA_PADDING = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + + uint256 internal constant REGISTRY_CONDITIONAL_OVERHEAD = 90_000; // Used in maxPayment estimation, and in capping overheads during actual payment + uint256 internal constant REGISTRY_LOG_OVERHEAD = 110_000; // Used only in maxPayment estimation, and in capping overheads during actual payment. + uint256 internal constant REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD = 20; // Used only in maxPayment estimation, and in capping overheads during actual payment. Value scales with performData length. + uint256 internal constant REGISTRY_PER_SIGNER_GAS_OVERHEAD = 7_500; // Used only in maxPayment estimation, and in capping overheads during actual payment. Value scales with f. + + uint256 internal constant ACCOUNTING_FIXED_GAS_OVERHEAD = 27_500; // Used in actual payment. Fixed overhead per tx + uint256 internal constant ACCOUNTING_PER_SIGNER_GAS_OVERHEAD = 1_100; // Used in actual payment. overhead per signer + uint256 internal constant ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD = 7_000; // Used in actual payment. overhead per upkeep performed + + OVM_GasPriceOracle internal constant OPTIMISM_ORACLE = OVM_GasPriceOracle(0x420000000000000000000000000000000000000F); + ArbGasInfo internal constant ARB_NITRO_ORACLE = ArbGasInfo(0x000000000000000000000000000000000000006C); + ArbSys internal constant ARB_SYS = ArbSys(0x0000000000000000000000000000000000000064); + + LinkTokenInterface internal immutable i_link; + AggregatorV3Interface internal immutable i_linkNativeFeed; + AggregatorV3Interface internal immutable i_fastGasFeed; + Mode internal immutable i_mode; + address internal immutable i_automationForwarderLogic; + + /** + * @dev - The storage is gas optimised for one and only one function - transmit. All the storage accessed in transmit + * is stored compactly. Rest of the storage layout is not of much concern as transmit is the only hot path + */ + + // Upkeep storage + EnumerableSet.UintSet internal s_upkeepIDs; + mapping(uint256 => Upkeep) internal s_upkeep; // accessed during transmit + mapping(uint256 => address) internal s_upkeepAdmin; + mapping(uint256 => address) internal s_proposedAdmin; + mapping(uint256 => bytes) internal s_checkData; + mapping(bytes32 => bool) internal s_dedupKeys; + // Registry config and state + EnumerableSet.AddressSet internal s_registrars; + mapping(address => Transmitter) internal s_transmitters; + mapping(address => Signer) internal s_signers; + address[] internal s_signersList; // s_signersList contains the signing address of each oracle + address[] internal s_transmittersList; // s_transmittersList contains the transmission address of each oracle + mapping(address => address) internal s_transmitterPayees; // s_payees contains the mapping from transmitter to payee. + mapping(address => address) internal s_proposedPayee; // proposed payee for a transmitter + bytes32 internal s_latestConfigDigest; // Read on transmit path in case of signature verification + HotVars internal s_hotVars; // Mixture of config and state, used in transmit + Storage internal s_storage; // Mixture of config and state, not used in transmit + uint256 internal s_fallbackGasPrice; + uint256 internal s_fallbackLinkPrice; + uint256 internal s_expectedLinkBalance; // Used in case of erroneous LINK transfers to contract + mapping(address => MigrationPermission) internal s_peerRegistryMigrationPermission; // Permissions for migration to and fro + mapping(uint256 => bytes) internal s_upkeepTriggerConfig; // upkeep triggers + mapping(uint256 => bytes) internal s_upkeepOffchainConfig; // general config set by users for each upkeep + mapping(uint256 => bytes) internal s_upkeepPrivilegeConfig; // general config set by an administrative role for an upkeep + mapping(address => bytes) internal s_adminPrivilegeConfig; // general config set by an administrative role for an admin + + error ArrayHasNoEntries(); + error CannotCancel(); + error CheckDataExceedsLimit(); + error ConfigDigestMismatch(); + error DuplicateEntry(); + error DuplicateSigners(); + error GasLimitCanOnlyIncrease(); + error GasLimitOutsideRange(); + error IncorrectNumberOfFaultyOracles(); + error IncorrectNumberOfSignatures(); + error IncorrectNumberOfSigners(); + error IndexOutOfRange(); + error InsufficientFunds(); + error InvalidDataLength(); + error InvalidTrigger(); + error InvalidPayee(); + error InvalidRecipient(); + error InvalidReport(); + error InvalidSigner(); + error InvalidTransmitter(); + error InvalidTriggerType(); + error MaxCheckDataSizeCanOnlyIncrease(); + error MaxPerformDataSizeCanOnlyIncrease(); + error MigrationNotPermitted(); + error NotAContract(); + error OnlyActiveSigners(); + error OnlyActiveTransmitters(); + error OnlyCallableByAdmin(); + error OnlyCallableByLINKToken(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedAdmin(); + error OnlyCallableByProposedPayee(); + error OnlyCallableByUpkeepPrivilegeManager(); + error OnlyPausedUpkeep(); + error OnlyUnpausedUpkeep(); + error ParameterLengthError(); + error PaymentGreaterThanAllLINK(); + error ReentrantCall(); + error RegistryPaused(); + error RepeatedSigner(); + error RepeatedTransmitter(); + error TargetCheckReverted(bytes reason); + error TooManyOracles(); + error TranscoderNotSet(); + error UpkeepAlreadyExists(); + error UpkeepCancelled(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error ValueNotChanged(); + + enum MigrationPermission { + NONE, + OUTGOING, + INCOMING, + BIDIRECTIONAL + } + + enum Mode { + DEFAULT, + ARBITRUM, + OPTIMISM + } + + enum Trigger { + CONDITION, + LOG + } + + enum UpkeepFailureReason { + NONE, + UPKEEP_CANCELLED, + UPKEEP_PAUSED, + TARGET_CHECK_REVERTED, + UPKEEP_NOT_NEEDED, + PERFORM_DATA_EXCEEDS_LIMIT, + INSUFFICIENT_BALANCE, + CALLBACK_REVERTED, + REVERT_DATA_EXCEEDS_LIMIT, + REGISTRY_PAUSED + } + + /** + * @notice OnchainConfig of the registry + * @dev only used in params and return values + * @member paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @member checkGasLimit gas limit when checking for upkeep + * @member stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling + * @member maxPerformGas max performGas allowed for an upkeep on this registry + * @member maxCheckDataSize max length of checkData bytes + * @member maxPerformDataSize max length of performData bytes + * @member maxRevertDataSize max length of revertData bytes + * @member fallbackGasPrice gas price used if the gas price feed is stale + * @member fallbackLinkPrice LINK price used if the LINK price feed is stale + * @member transcoder address of the transcoder contract + * @member registrars addresses of the registrar contracts + * @member upkeepPrivilegeManager address which can set privilege for upkeeps + */ + struct OnchainConfig { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint32 maxCheckDataSize; + uint32 maxPerformDataSize; + uint32 maxRevertDataSize; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address[] registrars; + address upkeepPrivilegeManager; + } + + /** + * @notice state of the registry + * @dev only used in params and return values + * @dev this will likely be deprecated in a future version of the registry in favor of individual getters + * @member nonce used for ID generation + * @member ownerLinkBalance withdrawable balance of LINK by contract owner + * @member expectedLinkBalance the expected balance of LINK of the registry + * @member totalPremium the total premium collected on registry so far + * @member numUpkeeps total number of upkeeps on the registry + * @member configCount ordinal number of current config, out of all configs applied to this contract so far + * @member latestConfigBlockNumber last block at which this config was set + * @member latestConfigDigest domain-separation tag for current config + * @member latestEpoch for which a report was transmitted + * @member paused freeze on execution scoped to the entire registry + */ + struct State { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint96 totalPremium; + uint256 numUpkeeps; + uint32 configCount; + uint32 latestConfigBlockNumber; + bytes32 latestConfigDigest; + uint32 latestEpoch; + bool paused; + } + + /** + * @notice relevant state of an upkeep which is used in transmit function + * @member paused if this upkeep has been paused + * @member performGas the gas limit of upkeep execution + * @member maxValidBlocknumber until which block this upkeep is valid + * @member forwarder the forwarder contract to use for this upkeep + * @member amountSpent the amount this upkeep has spent + * @member balance the balance of this upkeep + * @member lastPerformedBlockNumber the last block number when this upkeep was performed + */ + struct Upkeep { + bool paused; + uint32 performGas; + uint32 maxValidBlocknumber; + IAutomationForwarder forwarder; + // 0 bytes left in 1st EVM word - not written to in transmit + uint96 amountSpent; + uint96 balance; + uint32 lastPerformedBlockNumber; + // 2 bytes left in 2nd EVM word - written in transmit path + } + + /** + * @notice all information about an upkeep + * @dev only used in return values + * @dev this will likely be deprecated in a future version of the registry + * @member target the contract which needs to be serviced + * @member performGas the gas limit of upkeep execution + * @member checkData the checkData bytes for this upkeep + * @member balance the balance of this upkeep + * @member admin for this upkeep + * @member maxValidBlocknumber until which block this upkeep is valid + * @member lastPerformedBlockNumber the last block number when this upkeep was performed + * @member amountSpent the amount this upkeep has spent + * @member paused if this upkeep has been paused + * @member offchainConfig the off-chain config of this upkeep + */ + struct UpkeepInfo { + address target; + uint32 performGas; + bytes checkData; + uint96 balance; + address admin; + uint64 maxValidBlocknumber; + uint32 lastPerformedBlockNumber; + uint96 amountSpent; + bool paused; + bytes offchainConfig; + } + + /// @dev Config + State storage struct which is on hot transmit path + struct HotVars { + uint8 f; // maximum number of faulty oracles + uint32 paymentPremiumPPB; // premium percentage charged to user over tx cost + uint32 flatFeeMicroLink; // flat fee charged to user for every perform + uint24 stalenessSeconds; // Staleness tolerance for feeds + uint16 gasCeilingMultiplier; // multiplier on top of fast gas feed for upper bound + bool paused; // pause switch for all upkeeps in the registry + bool reentrancyGuard; // guard against reentrancy + uint96 totalPremium; // total historical payment to oracles for premium + uint32 latestEpoch; // latest epoch for which a report was transmitted + // 1 EVM word full + } + + /// @dev Config + State storage struct which is not on hot transmit path + struct Storage { + uint96 minUpkeepSpend; // Minimum amount an upkeep must spend + address transcoder; // Address of transcoder contract used in migrations + // 1 EVM word full + uint96 ownerLinkBalance; // Balance of owner, accumulates minUpkeepSpend in case it is not spent + uint32 checkGasLimit; // Gas limit allowed in checkUpkeep + uint32 maxPerformGas; // Max gas an upkeep can use on this registry + uint32 nonce; // Nonce for each upkeep created + uint32 configCount; // incremented each time a new config is posted, The count + // is incorporated into the config digest to prevent replay attacks. + uint32 latestConfigBlockNumber; // makes it easier for offchain systems to extract config from logs + // 2 EVM word full + uint32 maxCheckDataSize; // max length of checkData bytes + uint32 maxPerformDataSize; // max length of performData bytes + uint32 maxRevertDataSize; // max length of revertData bytes + address upkeepPrivilegeManager; // address which can set privilege for upkeeps + // 3 EVM word full + } + + /// @dev Report transmitted by OCR to transmit function + struct Report { + uint256 fastGasWei; + uint256 linkNative; + uint256[] upkeepIds; + uint256[] gasLimits; + bytes[] triggers; + bytes[] performDatas; + } + + /** + * @dev This struct is used to maintain run time information about an upkeep in transmit function + * @member upkeep the upkeep struct + * @member earlyChecksPassed whether the upkeep passed early checks before perform + * @member maxLinkPayment the max amount this upkeep could pay for work + * @member performSuccess whether the perform was successful + * @member triggerType the type of trigger + * @member gasUsed gasUsed by this upkeep in perform + * @member gasOverhead gasOverhead for this upkeep + * @member dedupID unique ID used to dedup an upkeep/trigger combo + */ + struct UpkeepTransmitInfo { + Upkeep upkeep; + bool earlyChecksPassed; + uint96 maxLinkPayment; + bool performSuccess; + Trigger triggerType; + uint256 gasUsed; + uint256 gasOverhead; + bytes32 dedupID; + } + + struct Transmitter { + bool active; + uint8 index; // Index of oracle in s_signersList/s_transmittersList + uint96 balance; + uint96 lastCollected; + } + + struct Signer { + bool active; + // Index of oracle in s_signersList/s_transmittersList + uint8 index; + } + + /** + * @notice the trigger structure conditional trigger type + */ + struct ConditionalTrigger { + uint32 blockNum; + bytes32 blockHash; + } + + /** + * @notice the trigger structure of log upkeeps + * @dev NOTE that blockNum / blockHash describe the block used for the callback, + * not necessarily the block number that the log was emitted in!!!! + */ + struct LogTrigger { + bytes32 logBlockHash; + bytes32 txHash; + uint32 logIndex; + uint32 blockNum; + bytes32 blockHash; + } + + event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); + event CancelledUpkeepReport(uint256 indexed id, bytes trigger); + event DedupKeyAdded(bytes32 indexed dedupKey); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); + event OwnerFundsWithdrawn(uint96 amount); + event Paused(address account); + event PayeesUpdated(address[] transmitters, address[] payees); + event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); + event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); + event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); + event StaleUpkeepReport(uint256 indexed id, bytes trigger); + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); + event UpkeepPaused(uint256 indexed id); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + uint96 totalPayment, + uint256 gasUsed, + uint256 gasOverhead, + bytes trigger + ); + event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); + event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); + event UpkeepUnpaused(uint256 indexed id); + event Unpaused(address account); + + /** + * @param mode the contract mode of default, Arbitrum, or Optimism + * @param link address of the LINK Token + * @param linkNativeFeed address of the LINK/Native price feed + * @param fastGasFeed address of the Fast Gas price feed + */ + constructor( + Mode mode, + address link, + address linkNativeFeed, + address fastGasFeed, + address automationForwarderLogic + ) ConfirmedOwner(msg.sender) { + i_mode = mode; + i_link = LinkTokenInterface(link); + i_linkNativeFeed = AggregatorV3Interface(linkNativeFeed); + i_fastGasFeed = AggregatorV3Interface(fastGasFeed); + i_automationForwarderLogic = automationForwarderLogic; + } + + // ================================================================ + // | INTERNAL FUNCTIONS ONLY | + // ================================================================ + + /** + * @dev creates a new upkeep with the given fields + * @param id the id of the upkeep + * @param upkeep the upkeep to create + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data which is passed to user's checkUpkeep + * @param triggerConfig the trigger config for this upkeep + * @param offchainConfig the off-chain config of this upkeep + */ + function _createUpkeep( + uint256 id, + Upkeep memory upkeep, + address admin, + bytes memory checkData, + bytes memory triggerConfig, + bytes memory offchainConfig + ) internal { + if (s_hotVars.paused) revert RegistryPaused(); + if (checkData.length > s_storage.maxCheckDataSize) revert CheckDataExceedsLimit(); + if (upkeep.performGas < PERFORM_GAS_MIN || upkeep.performGas > s_storage.maxPerformGas) + revert GasLimitOutsideRange(); + if (address(s_upkeep[id].forwarder) != address(0)) revert UpkeepAlreadyExists(); + s_upkeep[id] = upkeep; + s_upkeepAdmin[id] = admin; + s_checkData[id] = checkData; + s_expectedLinkBalance = s_expectedLinkBalance + upkeep.balance; + s_upkeepTriggerConfig[id] = triggerConfig; + s_upkeepOffchainConfig[id] = offchainConfig; + s_upkeepIDs.add(id); + } + + /** + * @dev creates an ID for the upkeep based on the upkeep's type + * @dev the format of the ID looks like this: + * ****00000000000X**************** + * 4 bytes of entropy + * 11 bytes of zeros + * 1 identifying byte for the trigger type + * 16 bytes of entropy + * @dev this maintains the same level of entropy as eth addresses, so IDs will still be unique + * @dev we add the "identifying" part in the middle so that it is mostly hidden from users who usually only + * see the first 4 and last 4 hex values ex 0x1234...ABCD + */ + function _createID(Trigger triggerType) internal view returns (uint256) { + bytes1 empty; + bytes memory idBytes = abi.encodePacked( + keccak256(abi.encode(_blockHash(_blockNum() - 1), address(this), s_storage.nonce)) + ); + for (uint256 idx = 4; idx < 15; idx++) { + idBytes[idx] = empty; + } + idBytes[15] = bytes1(uint8(triggerType)); + return uint256(bytes32(idBytes)); + } + + /** + * @dev retrieves feed data for fast gas/native and link/native prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function _getFeedData(HotVars memory hotVars) internal view returns (uint256 gasWei, uint256 linkNative) { + uint32 stalenessSeconds = hotVars.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = i_fastGasFeed.latestRoundData(); + if ( + feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp) + ) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = i_linkNativeFeed.latestRoundData(); + if ( + feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp) + ) { + linkNative = s_fallbackLinkPrice; + } else { + linkNative = uint256(feedValue); + } + return (gasWei, linkNative); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + * @param gasLimit the amount of gas used + * @param gasOverhead the amount of gas overhead + * @param fastGasWei the fast gas price + * @param linkNative the exchange ratio between LINK and Native token + * @param numBatchedUpkeeps the number of upkeeps in this batch. Used to divide the L1 cost + * @param isExecution if this is triggered by a perform upkeep function + */ + function _calculatePaymentAmount( + HotVars memory hotVars, + uint256 gasLimit, + uint256 gasOverhead, + uint256 fastGasWei, + uint256 linkNative, + uint16 numBatchedUpkeeps, + bool isExecution + ) internal view returns (uint96, uint96) { + uint256 gasWei = fastGasWei * hotVars.gasCeilingMultiplier; + // in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier + if (isExecution && tx.gasprice < gasWei) { + gasWei = tx.gasprice; + } + + uint256 l1CostWei = 0; + if (i_mode == Mode.OPTIMISM) { + bytes memory txCallData = new bytes(0); + if (isExecution) { + txCallData = bytes.concat(msg.data, L1_FEE_DATA_PADDING); + } else { + // fee is 4 per 0 byte, 16 per non-zero byte. Worst case we can have + // s_storage.maxPerformDataSize non zero-bytes. Instead of setting bytes to non-zero + // we initialize 'new bytes' of length 4*maxPerformDataSize to cover for zero bytes. + txCallData = new bytes(4 * s_storage.maxPerformDataSize); + } + l1CostWei = OPTIMISM_ORACLE.getL1Fee(txCallData); + } else if (i_mode == Mode.ARBITRUM) { + if (isExecution) { + l1CostWei = ARB_NITRO_ORACLE.getCurrentTxL1GasFees(); + } else { + // fee is 4 per 0 byte, 16 per non-zero byte - we assume all non-zero and + // max data size to calculate max payment + (, uint256 perL1CalldataUnit, , , , ) = ARB_NITRO_ORACLE.getPricesInWei(); + l1CostWei = perL1CalldataUnit * s_storage.maxPerformDataSize * 16; + } + } + // if it's not performing upkeeps, use gas ceiling multiplier to estimate the upper bound + if (!isExecution) { + l1CostWei = hotVars.gasCeilingMultiplier * l1CostWei; + } + // Divide l1CostWei among all batched upkeeps. Spare change from division is not charged + l1CostWei = l1CostWei / numBatchedUpkeeps; + + uint256 gasPayment = ((gasWei * (gasLimit + gasOverhead) + l1CostWei) * 1e18) / linkNative; + uint256 premium = (((gasWei * gasLimit) + l1CostWei) * 1e9 * hotVars.paymentPremiumPPB) / + linkNative + + uint256(hotVars.flatFeeMicroLink) * + 1e12; + // LINK_TOTAL_SUPPLY < UINT96_MAX + if (gasPayment + premium > LINK_TOTAL_SUPPLY) revert PaymentGreaterThanAllLINK(); + return (uint96(gasPayment), uint96(premium)); + } + + /** + * @dev calculates the max LINK payment for an upkeep + */ + function _getMaxLinkPayment( + HotVars memory hotVars, + Trigger triggerType, + uint32 performGas, + uint32 performDataLength, + uint256 fastGasWei, + uint256 linkNative, + bool isExecution // Whether this is an actual perform execution or just a simulation + ) internal view returns (uint96) { + uint256 gasOverhead = _getMaxGasOverhead(triggerType, performDataLength, hotVars.f); + (uint96 reimbursement, uint96 premium) = _calculatePaymentAmount( + hotVars, + performGas, + gasOverhead, + fastGasWei, + linkNative, + 1, // Consider only 1 upkeep in batch to get maxPayment + isExecution + ); + + return reimbursement + premium; + } + + /** + * @dev returns the max gas overhead that can be charged for an upkeep + */ + function _getMaxGasOverhead(Trigger triggerType, uint32 performDataLength, uint8 f) internal pure returns (uint256) { + // performData causes additional overhead in report length and memory operations + uint256 baseOverhead; + if (triggerType == Trigger.CONDITION) { + baseOverhead = REGISTRY_CONDITIONAL_OVERHEAD; + } else if (triggerType == Trigger.LOG) { + baseOverhead = REGISTRY_LOG_OVERHEAD; + } else { + revert InvalidTriggerType(); + } + return + baseOverhead + + (REGISTRY_PER_SIGNER_GAS_OVERHEAD * (f + 1)) + + (REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD * performDataLength); + } + + /** + * @dev move a transmitter's balance from total pool to withdrawable balance + */ + function _updateTransmitterBalanceFromPool( + address transmitterAddress, + uint96 totalPremium, + uint96 payeeCount + ) internal returns (uint96) { + Transmitter memory transmitter = s_transmitters[transmitterAddress]; + + if (transmitter.active) { + uint96 uncollected = totalPremium - transmitter.lastCollected; + uint96 due = uncollected / payeeCount; + transmitter.balance += due; + transmitter.lastCollected += due * payeeCount; + s_transmitters[transmitterAddress] = transmitter; + } + + return transmitter.balance; + } + + /** + * @dev gets the trigger type from an upkeepID (trigger type is encoded in the middle of the ID) + */ + function _getTriggerType(uint256 upkeepId) internal pure returns (Trigger) { + bytes32 rawID = bytes32(upkeepId); + bytes1 empty = bytes1(0); + for (uint256 idx = 4; idx < 15; idx++) { + if (rawID[idx] != empty) { + // old IDs that were created before this standard and migrated to this registry + return Trigger.CONDITION; + } + } + return Trigger(uint8(rawID[15])); + } + + function _checkPayload( + uint256 upkeepId, + Trigger triggerType, + bytes memory triggerData + ) internal view returns (bytes memory) { + if (triggerType == Trigger.CONDITION) { + return abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[upkeepId]); + } else if (triggerType == Trigger.LOG) { + Log memory log = abi.decode(triggerData, (Log)); + return abi.encodeWithSelector(CHECK_LOG_SELECTOR, log, s_checkData[upkeepId]); + } + revert InvalidTriggerType(); + } + + /** + * @dev _decodeReport decodes a serialized report into a Report struct + */ + function _decodeReport(bytes calldata rawReport) internal pure returns (Report memory) { + Report memory report = abi.decode(rawReport, (Report)); + uint256 expectedLength = report.upkeepIds.length; + if ( + report.gasLimits.length != expectedLength || + report.triggers.length != expectedLength || + report.performDatas.length != expectedLength + ) { + revert InvalidReport(); + } + return report; + } + + /** + * @dev Does some early sanity checks before actually performing an upkeep + * @return bool whether the upkeep should be performed + * @return bytes32 dedupID for preventing duplicate performances of this trigger + */ + function _prePerformChecks( + uint256 upkeepId, + bytes memory rawTrigger, + UpkeepTransmitInfo memory transmitInfo + ) internal returns (bool, bytes32) { + bytes32 dedupID; + if (transmitInfo.triggerType == Trigger.CONDITION) { + if (!_validateConditionalTrigger(upkeepId, rawTrigger, transmitInfo)) return (false, dedupID); + } else if (transmitInfo.triggerType == Trigger.LOG) { + bool valid; + (valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger, transmitInfo); + if (!valid) return (false, dedupID); + } else { + revert InvalidTriggerType(); + } + if (transmitInfo.upkeep.maxValidBlocknumber <= _blockNum()) { + // Can happen when an upkeep got cancelled after report was generated. + // However we have a CANCELLATION_DELAY of 50 blocks so shouldn't happen in practice + emit CancelledUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + if (transmitInfo.upkeep.balance < transmitInfo.maxLinkPayment) { + // Can happen due to fluctuations in gas / link prices + emit InsufficientFundsUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + return (true, dedupID); + } + + /** + * @dev Does some early sanity checks before actually performing an upkeep + */ + function _validateConditionalTrigger( + uint256 upkeepId, + bytes memory rawTrigger, + UpkeepTransmitInfo memory transmitInfo + ) internal returns (bool) { + ConditionalTrigger memory trigger = abi.decode(rawTrigger, (ConditionalTrigger)); + if (trigger.blockNum < transmitInfo.upkeep.lastPerformedBlockNumber) { + // Can happen when another report performed this upkeep after this report was generated + emit StaleUpkeepReport(upkeepId, rawTrigger); + return false; + } + if ( + (trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash) || + trigger.blockNum >= _blockNum() + ) { + // There are two cases of reorged report + // 1. trigger block number is in future: this is an edge case during extreme deep reorgs of chain + // which is always protected against + // 2. blockHash at trigger block number was same as trigger time. This is an optional check which is + // applied if DON sends non empty trigger.blockHash. Note: It only works for last 256 blocks on chain + // when it is sent + emit ReorgedUpkeepReport(upkeepId, rawTrigger); + return false; + } + return true; + } + + function _validateLogTrigger( + uint256 upkeepId, + bytes memory rawTrigger, + UpkeepTransmitInfo memory transmitInfo + ) internal returns (bool, bytes32) { + LogTrigger memory trigger = abi.decode(rawTrigger, (LogTrigger)); + bytes32 dedupID = keccak256(abi.encodePacked(upkeepId, trigger.logBlockHash, trigger.txHash, trigger.logIndex)); + if ( + (trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash) || + trigger.blockNum >= _blockNum() + ) { + // Reorg protection is same as conditional trigger upkeeps + emit ReorgedUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + if (s_dedupKeys[dedupID]) { + emit StaleUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + return (true, dedupID); + } + + /** + * @dev Verify signatures attached to report + */ + function _verifyReportSignature( + bytes32[3] calldata reportContext, + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs + ) internal view { + bytes32 h = keccak256(abi.encode(keccak256(report), reportContext)); + // i-th byte counts number of sigs made by i-th signer + uint256 signedCount = 0; + + Signer memory signer; + address signerAddress; + for (uint256 i = 0; i < rs.length; i++) { + signerAddress = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); + signer = s_signers[signerAddress]; + if (!signer.active) revert OnlyActiveSigners(); + unchecked { + signedCount += 1 << (8 * signer.index); + } + } + + if (signedCount & ORACLE_MASK != signedCount) revert DuplicateSigners(); + } + + /** + * @dev updates a storage marker for this upkeep to prevent duplicate and out of order performances + * @dev for conditional triggers we set the latest block number, for log triggers we store a dedupID + */ + function _updateTriggerMarker(uint256 upkeepID, UpkeepTransmitInfo memory upkeepTransmitInfo) internal { + if (upkeepTransmitInfo.triggerType == Trigger.CONDITION) { + s_upkeep[upkeepID].lastPerformedBlockNumber = uint32(_blockNum()); + } else if (upkeepTransmitInfo.triggerType == Trigger.LOG) { + s_dedupKeys[upkeepTransmitInfo.dedupID] = true; + emit DedupKeyAdded(upkeepTransmitInfo.dedupID); + } + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * transmitter and the exact gas required by the Upkeep + */ + function _performUpkeep( + IAutomationForwarder forwarder, + uint256 performGas, + bytes memory performData + ) internal nonReentrant returns (bool success, uint256 gasUsed) { + performData = abi.encodeWithSelector(PERFORM_SELECTOR, performData); + return forwarder.forward(performGas, performData); + } + + /** + * @dev does postPerform payment processing for an upkeep. Deducts upkeep's balance and increases + * amount spent. + */ + function _postPerformPayment( + HotVars memory hotVars, + uint256 upkeepId, + UpkeepTransmitInfo memory upkeepTransmitInfo, + uint256 fastGasWei, + uint256 linkNative, + uint16 numBatchedUpkeeps + ) internal returns (uint96 gasReimbursement, uint96 premium) { + (gasReimbursement, premium) = _calculatePaymentAmount( + hotVars, + upkeepTransmitInfo.gasUsed, + upkeepTransmitInfo.gasOverhead, + fastGasWei, + linkNative, + numBatchedUpkeeps, + true + ); + + uint96 payment = gasReimbursement + premium; + s_upkeep[upkeepId].balance -= payment; + s_upkeep[upkeepId].amountSpent += payment; + + return (gasReimbursement, premium); + } + + /** + * @dev Caps the gas overhead by the constant overhead used within initial payment checks in order to + * prevent a revert in payment processing. + */ + function _getCappedGasOverhead( + uint256 calculatedGasOverhead, + Trigger triggerType, + uint32 performDataLength, + uint8 f + ) internal pure returns (uint256 cappedGasOverhead) { + cappedGasOverhead = _getMaxGasOverhead(triggerType, performDataLength, f); + if (calculatedGasOverhead < cappedGasOverhead) { + return calculatedGasOverhead; + } + return cappedGasOverhead; + } + + /** + * @dev ensures the upkeep is not cancelled and the caller is the upkeep admin + */ + function _requireAdminAndNotCancelled(uint256 upkeepId) internal view { + if (msg.sender != s_upkeepAdmin[upkeepId]) revert OnlyCallableByAdmin(); + if (s_upkeep[upkeepId].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + } + + /** + * @dev returns the current block number in a chain agnostic manner + */ + function _blockNum() internal view returns (uint256) { + if (i_mode == Mode.ARBITRUM) { + return ARB_SYS.arbBlockNumber(); + } else { + return block.number; + } + } + + /** + * @dev returns the blockhash of the provided block number in a chain agnostic manner + * @param n the blocknumber to retrieve the blockhash for + * @return blockhash the blockhash of block number n, or 0 if n is out queryable of range + */ + function _blockHash(uint256 n) internal view returns (bytes32) { + if (i_mode == Mode.ARBITRUM) { + uint256 blockNum = ARB_SYS.arbBlockNumber(); + if (n >= blockNum || blockNum - n > 256) { + return ""; + } + return ARB_SYS.arbBlockHash(n); + } else { + return blockhash(n); + } + } + + /** + * @dev replicates Open Zeppelin's ReentrancyGuard but optimized to fit our storage + */ + modifier nonReentrant() { + if (s_hotVars.reentrancyGuard) revert ReentrantCall(); + s_hotVars.reentrancyGuard = true; + _; + s_hotVars.reentrancyGuard = false; + } +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicA2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicA2_2.sol new file mode 100644 index 00000000000..8c2183c4e23 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicA2_2.sol @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {AutomationRegistryBase2_2} from "./AutomationRegistryBase2_2.sol"; +import {AutomationRegistryLogicB2_2} from "./AutomationRegistryLogicB2_2.sol"; +import {Chainable} from "../../Chainable.sol"; +import {AutomationForwarder} from "../../AutomationForwarder.sol"; +import {IAutomationForwarder} from "../../interfaces/IAutomationForwarder.sol"; +import {UpkeepTranscoderInterfaceV2} from "../../interfaces/UpkeepTranscoderInterfaceV2.sol"; +import {MigratableKeeperRegistryInterfaceV2} from "../../interfaces/MigratableKeeperRegistryInterfaceV2.sol"; + +/** + * @notice Logic contract, works in tandem with AutomationRegistry as a proxy + */ +contract AutomationRegistryLogicA2_2 is AutomationRegistryBase2_2, Chainable { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + /** + * @param logicB the address of the second logic contract + */ + constructor( + AutomationRegistryLogicB2_2 logicB + ) + AutomationRegistryBase2_2( + logicB.getMode(), + logicB.getLinkAddress(), + logicB.getLinkNativeFeedAddress(), + logicB.getFastGasFeedAddress(), + logicB.getAutomationForwarderLogic() + ) + Chainable(address(logicB)) + {} + + /** + * @notice called by the automation DON to check if work is needed + * @param id the upkeep ID to check for work needed + * @param triggerData extra contextual data about the trigger (not used in all code paths) + * @dev this one of the core functions called in the hot path + * @dev there is a 2nd checkUpkeep function (below) that is being maintained for backwards compatibility + * @dev there is an incongruency on what gets returned during failure modes + * ex sometimes we include price data, sometimes we omit it depending on the failure + */ + function checkUpkeep( + uint256 id, + bytes memory triggerData + ) + public + cannotExecute + returns ( + bool upkeepNeeded, + bytes memory performData, + UpkeepFailureReason upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkNative + ) + { + Trigger triggerType = _getTriggerType(id); + HotVars memory hotVars = s_hotVars; + Upkeep memory upkeep = s_upkeep[id]; + + if (hotVars.paused) return (false, bytes(""), UpkeepFailureReason.REGISTRY_PAUSED, 0, upkeep.performGas, 0, 0); + if (upkeep.maxValidBlocknumber != UINT32_MAX) + return (false, bytes(""), UpkeepFailureReason.UPKEEP_CANCELLED, 0, upkeep.performGas, 0, 0); + if (upkeep.paused) return (false, bytes(""), UpkeepFailureReason.UPKEEP_PAUSED, 0, upkeep.performGas, 0, 0); + + (fastGasWei, linkNative) = _getFeedData(hotVars); + uint96 maxLinkPayment = _getMaxLinkPayment( + hotVars, + triggerType, + upkeep.performGas, + s_storage.maxPerformDataSize, + fastGasWei, + linkNative, + false + ); + if (upkeep.balance < maxLinkPayment) { + return (false, bytes(""), UpkeepFailureReason.INSUFFICIENT_BALANCE, 0, upkeep.performGas, 0, 0); + } + + bytes memory callData = _checkPayload(id, triggerType, triggerData); + + gasUsed = gasleft(); + (bool success, bytes memory result) = upkeep.forwarder.getTarget().call{gas: s_storage.checkGasLimit}(callData); + gasUsed = gasUsed - gasleft(); + + if (!success) { + // User's target check reverted. We capture the revert data here and pass it within performData + if (result.length > s_storage.maxRevertDataSize) { + return ( + false, + bytes(""), + UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, + gasUsed, + upkeep.performGas, + fastGasWei, + linkNative + ); + } + return ( + upkeepNeeded, + result, + UpkeepFailureReason.TARGET_CHECK_REVERTED, + gasUsed, + upkeep.performGas, + fastGasWei, + linkNative + ); + } + + (upkeepNeeded, performData) = abi.decode(result, (bool, bytes)); + if (!upkeepNeeded) + return ( + false, + bytes(""), + UpkeepFailureReason.UPKEEP_NOT_NEEDED, + gasUsed, + upkeep.performGas, + fastGasWei, + linkNative + ); + + if (performData.length > s_storage.maxPerformDataSize) + return ( + false, + bytes(""), + UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, + gasUsed, + upkeep.performGas, + fastGasWei, + linkNative + ); + + return (upkeepNeeded, performData, upkeepFailureReason, gasUsed, upkeep.performGas, fastGasWei, linkNative); + } + + /** + * @notice see other checkUpkeep function for description + * @dev this function may be deprecated in a future version of chainlink automation + */ + function checkUpkeep( + uint256 id + ) + external + returns ( + bool upkeepNeeded, + bytes memory performData, + UpkeepFailureReason upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkNative + ) + { + return checkUpkeep(id, bytes("")); + } + + /** + * @dev checkCallback is used specifically for automation data streams lookups (see StreamsLookupCompatibleInterface.sol) + * @param id the upkeepID to execute a callback for + * @param values the values returned from the data streams lookup + * @param extraData the user-provided extra context data + */ + function checkCallback( + uint256 id, + bytes[] memory values, + bytes calldata extraData + ) + external + cannotExecute + returns (bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed) + { + bytes memory payload = abi.encodeWithSelector(CHECK_CALLBACK_SELECTOR, values, extraData); + return executeCallback(id, payload); + } + + /** + * @notice this is a generic callback executor that forwards a call to a user's contract with the configured + * gas limit + * @param id the upkeepID to execute a callback for + * @param payload the data (including function selector) to call on the upkeep target contract + */ + function executeCallback( + uint256 id, + bytes memory payload + ) + public + cannotExecute + returns (bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed) + { + Upkeep memory upkeep = s_upkeep[id]; + gasUsed = gasleft(); + (bool success, bytes memory result) = upkeep.forwarder.getTarget().call{gas: s_storage.checkGasLimit}(payload); + gasUsed = gasUsed - gasleft(); + if (!success) { + return (false, bytes(""), UpkeepFailureReason.CALLBACK_REVERTED, gasUsed); + } + (upkeepNeeded, performData) = abi.decode(result, (bool, bytes)); + if (!upkeepNeeded) { + return (false, bytes(""), UpkeepFailureReason.UPKEEP_NOT_NEEDED, gasUsed); + } + if (performData.length > s_storage.maxPerformDataSize) { + return (false, bytes(""), UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, gasUsed); + } + return (upkeepNeeded, performData, upkeepFailureReason, gasUsed); + } + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param triggerType the trigger for the upkeep + * @param checkData data passed to the contract when checking for upkeep + * @param triggerConfig the config for the trigger + * @param offchainConfig arbitrary offchain config for the upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + Trigger triggerType, + bytes calldata checkData, + bytes memory triggerConfig, + bytes memory offchainConfig + ) public returns (uint256 id) { + if (msg.sender != owner() && !s_registrars.contains(msg.sender)) revert OnlyCallableByOwnerOrRegistrar(); + if (!target.isContract()) revert NotAContract(); + id = _createID(triggerType); + IAutomationForwarder forwarder = IAutomationForwarder( + address(new AutomationForwarder(target, address(this), i_automationForwarderLogic)) + ); + _createUpkeep( + id, + Upkeep({ + performGas: gasLimit, + balance: 0, + maxValidBlocknumber: UINT32_MAX, + lastPerformedBlockNumber: 0, + amountSpent: 0, + paused: false, + forwarder: forwarder + }), + admin, + checkData, + triggerConfig, + offchainConfig + ); + s_storage.nonce++; + emit UpkeepRegistered(id, gasLimit, admin); + emit UpkeepCheckDataSet(id, checkData); + emit UpkeepTriggerConfigSet(id, triggerConfig); + emit UpkeepOffchainConfigSet(id, offchainConfig); + return (id); + } + + /** + * @notice this function registers a conditional upkeep, using a backwards compatible function signature + * @dev this function is backwards compatible with versions <=2.0, but may be removed in a future version + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData, + bytes calldata offchainConfig + ) external returns (uint256 id) { + return registerUpkeep(target, gasLimit, admin, Trigger.CONDITION, checkData, bytes(""), offchainConfig); + } + + /** + * @notice cancels an upkeep + * @param id the upkeepID to cancel + * @dev if a user cancels an upkeep, their funds are locked for CANCELLATION_DELAY blocks to + * allow any pending performUpkeep txs time to get confirmed + */ + function cancelUpkeep(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + bool canceled = upkeep.maxValidBlocknumber != UINT32_MAX; + bool isOwner = msg.sender == owner(); + + if (canceled && !(isOwner && upkeep.maxValidBlocknumber > _blockNum())) revert CannotCancel(); + if (!isOwner && msg.sender != s_upkeepAdmin[id]) revert OnlyCallableByOwnerOrAdmin(); + + uint256 height = _blockNum(); + if (!isOwner) { + height = height + CANCELLATION_DELAY; + } + s_upkeep[id].maxValidBlocknumber = uint32(height); + s_upkeepIDs.remove(id); + + // charge the cancellation fee if the minUpkeepSpend is not met + uint96 minUpkeepSpend = s_storage.minUpkeepSpend; + uint96 cancellationFee = 0; + // cancellationFee is supposed to be min(max(minUpkeepSpend - amountSpent,0), amountLeft) + if (upkeep.amountSpent < minUpkeepSpend) { + cancellationFee = minUpkeepSpend - upkeep.amountSpent; + if (cancellationFee > upkeep.balance) { + cancellationFee = upkeep.balance; + } + } + s_upkeep[id].balance = upkeep.balance - cancellationFee; + s_storage.ownerLinkBalance = s_storage.ownerLinkBalance + cancellationFee; + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @notice adds fund to an upkeep + * @param id the upkeepID + * @param amount the amount of LINK to fund, in jules (jules = "wei" of LINK) + */ + function addFunds(uint256 id, uint96 amount) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + s_upkeep[id].balance = upkeep.balance + amount; + s_expectedLinkBalance = s_expectedLinkBalance + amount; + i_link.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @notice migrates upkeeps from one registry to another + * @param ids the upkeepIDs to migrate + * @param destination the destination registry address + * @dev a transcoder must be set in order to enable migration + * @dev migration permissions must be set on *both* sending and receiving registries + * @dev only an upkeep admin can migrate their upkeeps + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external { + if ( + s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && + s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + if (s_storage.transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); + if (ids.length == 0) revert ArrayHasNoEntries(); + uint256 id; + Upkeep memory upkeep; + uint256 totalBalanceRemaining; + address[] memory admins = new address[](ids.length); + Upkeep[] memory upkeeps = new Upkeep[](ids.length); + bytes[] memory checkDatas = new bytes[](ids.length); + bytes[] memory triggerConfigs = new bytes[](ids.length); + bytes[] memory offchainConfigs = new bytes[](ids.length); + for (uint256 idx = 0; idx < ids.length; idx++) { + id = ids[idx]; + upkeep = s_upkeep[id]; + _requireAdminAndNotCancelled(id); + upkeep.forwarder.updateRegistry(destination); + upkeeps[idx] = upkeep; + admins[idx] = s_upkeepAdmin[id]; + checkDatas[idx] = s_checkData[id]; + triggerConfigs[idx] = s_upkeepTriggerConfig[id]; + offchainConfigs[idx] = s_upkeepOffchainConfig[id]; + totalBalanceRemaining = totalBalanceRemaining + upkeep.balance; + delete s_upkeep[id]; + delete s_checkData[id]; + delete s_upkeepTriggerConfig[id]; + delete s_upkeepOffchainConfig[id]; + // nullify existing proposed admin change if an upkeep is being migrated + delete s_proposedAdmin[id]; + s_upkeepIDs.remove(id); + emit UpkeepMigrated(id, upkeep.balance, destination); + } + s_expectedLinkBalance = s_expectedLinkBalance - totalBalanceRemaining; + bytes memory encodedUpkeeps = abi.encode( + ids, + upkeeps, + new address[](ids.length), + admins, + checkDatas, + triggerConfigs, + offchainConfigs + ); + MigratableKeeperRegistryInterfaceV2(destination).receiveUpkeeps( + UpkeepTranscoderInterfaceV2(s_storage.transcoder).transcodeUpkeeps( + UPKEEP_VERSION_BASE, + MigratableKeeperRegistryInterfaceV2(destination).upkeepVersion(), + encodedUpkeeps + ) + ); + i_link.transfer(destination, totalBalanceRemaining); + } + + /** + * @notice received upkeeps migrated from another registry + * @param encodedUpkeeps the raw upkeep data to import + * @dev this function is never called directly, it is only called by another registry's migrate function + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external { + if ( + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + ( + uint256[] memory ids, + Upkeep[] memory upkeeps, + address[] memory targets, + address[] memory upkeepAdmins, + bytes[] memory checkDatas, + bytes[] memory triggerConfigs, + bytes[] memory offchainConfigs + ) = abi.decode(encodedUpkeeps, (uint256[], Upkeep[], address[], address[], bytes[], bytes[], bytes[])); + for (uint256 idx = 0; idx < ids.length; idx++) { + if (address(upkeeps[idx].forwarder) == ZERO_ADDRESS) { + upkeeps[idx].forwarder = IAutomationForwarder( + address(new AutomationForwarder(targets[idx], address(this), i_automationForwarderLogic)) + ); + } + _createUpkeep( + ids[idx], + upkeeps[idx], + upkeepAdmins[idx], + checkDatas[idx], + triggerConfigs[idx], + offchainConfigs[idx] + ); + emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); + } + } + + /** + * @notice sets the upkeep trigger config + * @param id the upkeepID to change the trigger for + * @param triggerConfig the new trigger config + */ + function setUpkeepTriggerConfig(uint256 id, bytes calldata triggerConfig) external { + _requireAdminAndNotCancelled(id); + s_upkeepTriggerConfig[id] = triggerConfig; + emit UpkeepTriggerConfigSet(id, triggerConfig); + } +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol new file mode 100644 index 00000000000..0724fbd53f0 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {AutomationRegistryBase2_2} from "./AutomationRegistryBase2_2.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {UpkeepFormat} from "../../interfaces/UpkeepTranscoderInterface.sol"; +import {IAutomationForwarder} from "../../interfaces/IAutomationForwarder.sol"; + +contract AutomationRegistryLogicB2_2 is AutomationRegistryBase2_2 { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + /** + * @dev see AutomationRegistry master contract for constructor description + */ + constructor( + Mode mode, + address link, + address linkNativeFeed, + address fastGasFeed, + address automationForwarderLogic + ) AutomationRegistryBase2_2(mode, link, linkNativeFeed, fastGasFeed, automationForwarderLogic) {} + + // ================================================================ + // | UPKEEP MANAGEMENT | + // ================================================================ + + /** + * @notice transfers the address of an admin for an upkeep + */ + function transferUpkeepAdmin(uint256 id, address proposed) external { + _requireAdminAndNotCancelled(id); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedAdmin[id] != proposed) { + s_proposedAdmin[id] = proposed; + emit UpkeepAdminTransferRequested(id, msg.sender, proposed); + } + } + + /** + * @notice accepts the transfer of an upkeep admin + */ + function acceptUpkeepAdmin(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (s_proposedAdmin[id] != msg.sender) revert OnlyCallableByProposedAdmin(); + address past = s_upkeepAdmin[id]; + s_upkeepAdmin[id] = msg.sender; + s_proposedAdmin[id] = ZERO_ADDRESS; + + emit UpkeepAdminTransferred(id, past, msg.sender); + } + + /** + * @notice pauses an upkeep - an upkeep will be neither checked nor performed while paused + */ + function pauseUpkeep(uint256 id) external { + _requireAdminAndNotCancelled(id); + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.paused) revert OnlyUnpausedUpkeep(); + s_upkeep[id].paused = true; + s_upkeepIDs.remove(id); + emit UpkeepPaused(id); + } + + /** + * @notice unpauses an upkeep + */ + function unpauseUpkeep(uint256 id) external { + _requireAdminAndNotCancelled(id); + Upkeep memory upkeep = s_upkeep[id]; + if (!upkeep.paused) revert OnlyPausedUpkeep(); + s_upkeep[id].paused = false; + s_upkeepIDs.add(id); + emit UpkeepUnpaused(id); + } + + /** + * @notice updates the checkData for an upkeep + */ + function setUpkeepCheckData(uint256 id, bytes calldata newCheckData) external { + _requireAdminAndNotCancelled(id); + if (newCheckData.length > s_storage.maxCheckDataSize) revert CheckDataExceedsLimit(); + s_checkData[id] = newCheckData; + emit UpkeepCheckDataSet(id, newCheckData); + } + + /** + * @notice updates the gas limit for an upkeep + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external { + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + _requireAdminAndNotCancelled(id); + s_upkeep[id].performGas = gasLimit; + + emit UpkeepGasLimitSet(id, gasLimit); + } + + /** + * @notice updates the offchain config for an upkeep + */ + function setUpkeepOffchainConfig(uint256 id, bytes calldata config) external { + _requireAdminAndNotCancelled(id); + s_upkeepOffchainConfig[id] = config; + emit UpkeepOffchainConfigSet(id, config); + } + + /** + * @notice withdraws LINK funds from an upkeep + * @dev note that an upkeep must be cancelled first!! + */ + function withdrawFunds(uint256 id, address to) external nonReentrant { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + Upkeep memory upkeep = s_upkeep[id]; + if (s_upkeepAdmin[id] != msg.sender) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber > _blockNum()) revert UpkeepNotCanceled(); + uint96 amountToWithdraw = s_upkeep[id].balance; + s_expectedLinkBalance = s_expectedLinkBalance - amountToWithdraw; + s_upkeep[id].balance = 0; + i_link.transfer(to, amountToWithdraw); + emit FundsWithdrawn(id, amountToWithdraw, to); + } + + // ================================================================ + // | NODE MANAGEMENT | + // ================================================================ + + /** + * @notice transfers the address of payee for a transmitter + */ + function transferPayeeship(address transmitter, address proposed) external { + if (s_transmitterPayees[transmitter] != msg.sender) revert OnlyCallableByPayee(); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedPayee[transmitter] != proposed) { + s_proposedPayee[transmitter] = proposed; + emit PayeeshipTransferRequested(transmitter, msg.sender, proposed); + } + } + + /** + * @notice accepts the transfer of the payee + */ + function acceptPayeeship(address transmitter) external { + if (s_proposedPayee[transmitter] != msg.sender) revert OnlyCallableByProposedPayee(); + address past = s_transmitterPayees[transmitter]; + s_transmitterPayees[transmitter] = msg.sender; + s_proposedPayee[transmitter] = ZERO_ADDRESS; + + emit PayeeshipTransferred(transmitter, past, msg.sender); + } + + /** + * @notice withdraws LINK received as payment for work performed + */ + function withdrawPayment(address from, address to) external { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + if (s_transmitterPayees[from] != msg.sender) revert OnlyCallableByPayee(); + uint96 balance = _updateTransmitterBalanceFromPool(from, s_hotVars.totalPremium, uint96(s_transmittersList.length)); + s_transmitters[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance - balance; + i_link.transfer(to, balance); + emit PaymentWithdrawn(from, balance, to, msg.sender); + } + + // ================================================================ + // | OWNER / MANAGER ACTIONS | + // ================================================================ + + /** + * @notice sets the privilege config for an upkeep + */ + function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes calldata newPrivilegeConfig) external { + if (msg.sender != s_storage.upkeepPrivilegeManager) { + revert OnlyCallableByUpkeepPrivilegeManager(); + } + s_upkeepPrivilegeConfig[upkeepId] = newPrivilegeConfig; + emit UpkeepPrivilegeConfigSet(upkeepId, newPrivilegeConfig); + } + + /** + * @notice withdraws the owner's LINK balance + */ + function withdrawOwnerFunds() external onlyOwner { + uint96 amount = s_storage.ownerLinkBalance; + s_expectedLinkBalance = s_expectedLinkBalance - amount; + s_storage.ownerLinkBalance = 0; + emit OwnerFundsWithdrawn(amount); + i_link.transfer(msg.sender, amount); + } + + /** + * @notice allows the owner to withdraw any LINK accidentally sent to the contract + */ + function recoverFunds() external onlyOwner { + uint256 total = i_link.balanceOf(address(this)); + i_link.transfer(msg.sender, total - s_expectedLinkBalance); + } + + /** + * @notice sets the payees for the transmitters + */ + function setPayees(address[] calldata payees) external onlyOwner { + if (s_transmittersList.length != payees.length) revert ParameterLengthError(); + for (uint256 i = 0; i < s_transmittersList.length; i++) { + address transmitter = s_transmittersList[i]; + address oldPayee = s_transmitterPayees[transmitter]; + address newPayee = payees[i]; + if ( + (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) + ) revert InvalidPayee(); + if (newPayee != IGNORE_ADDRESS) { + s_transmitterPayees[transmitter] = newPayee; + } + } + emit PayeesUpdated(s_transmittersList, payees); + } + + /** + * @notice sets the migration permission for a peer registry + * @dev this must be done before upkeeps can be migrated to/from another registry + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { + s_peerRegistryMigrationPermission[peer] = permission; + } + + /** + * @notice pauses the entire registry + */ + function pause() external onlyOwner { + s_hotVars.paused = true; + emit Paused(msg.sender); + } + + /** + * @notice unpauses the entire registry + */ + function unpause() external onlyOwner { + s_hotVars.paused = false; + emit Unpaused(msg.sender); + } + + /** + * @notice sets a generic bytes field used to indicate the privilege that this admin address had + * @param admin the address to set privilege for + * @param newPrivilegeConfig the privileges that this admin has + */ + function setAdminPrivilegeConfig(address admin, bytes calldata newPrivilegeConfig) external { + if (msg.sender != s_storage.upkeepPrivilegeManager) { + revert OnlyCallableByUpkeepPrivilegeManager(); + } + s_adminPrivilegeConfig[admin] = newPrivilegeConfig; + emit AdminPrivilegeConfigSet(admin, newPrivilegeConfig); + } + + // ================================================================ + // | GETTERS | + // ================================================================ + + function getConditionalGasOverhead() external pure returns (uint256) { + return REGISTRY_CONDITIONAL_OVERHEAD; + } + + function getLogGasOverhead() external pure returns (uint256) { + return REGISTRY_LOG_OVERHEAD; + } + + function getPerPerformByteGasOverhead() external pure returns (uint256) { + return REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD; + } + + function getPerSignerGasOverhead() external pure returns (uint256) { + return REGISTRY_PER_SIGNER_GAS_OVERHEAD; + } + + function getCancellationDelay() external pure returns (uint256) { + return CANCELLATION_DELAY; + } + + function getMode() external view returns (Mode) { + return i_mode; + } + + function getLinkAddress() external view returns (address) { + return address(i_link); + } + + function getLinkNativeFeedAddress() external view returns (address) { + return address(i_linkNativeFeed); + } + + function getFastGasFeedAddress() external view returns (address) { + return address(i_fastGasFeed); + } + + function getAutomationForwarderLogic() external view returns (address) { + return i_automationForwarderLogic; + } + + function upkeepTranscoderVersion() public pure returns (UpkeepFormat) { + return UPKEEP_TRANSCODER_VERSION_BASE; + } + + function upkeepVersion() public pure returns (uint8) { + return UPKEEP_VERSION_BASE; + } + + /** + * @notice read all of the details about an upkeep + * @dev this function may be deprecated in a future version of automation in favor of individual + * getters for each field + */ + function getUpkeep(uint256 id) external view returns (UpkeepInfo memory upkeepInfo) { + Upkeep memory reg = s_upkeep[id]; + address target = address(reg.forwarder) == address(0) ? address(0) : reg.forwarder.getTarget(); + upkeepInfo = UpkeepInfo({ + target: target, + performGas: reg.performGas, + checkData: s_checkData[id], + balance: reg.balance, + admin: s_upkeepAdmin[id], + maxValidBlocknumber: reg.maxValidBlocknumber, + lastPerformedBlockNumber: reg.lastPerformedBlockNumber, + amountSpent: reg.amountSpent, + paused: reg.paused, + offchainConfig: s_upkeepOffchainConfig[id] + }); + return upkeepInfo; + } + + /** + * @notice retrieve active upkeep IDs. Active upkeep is defined as an upkeep which is not paused and not canceled. + * @param startIndex starting index in list + * @param maxCount max count to retrieve (0 = unlimited) + * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one + * should consider keeping the blockheight constant to ensure a holistic picture of the contract state + */ + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory) { + uint256 numUpkeeps = s_upkeepIDs.length(); + if (startIndex >= numUpkeeps) revert IndexOutOfRange(); + uint256 endIndex = startIndex + maxCount; + endIndex = endIndex > numUpkeeps || maxCount == 0 ? numUpkeeps : endIndex; + uint256[] memory ids = new uint256[](endIndex - startIndex); + for (uint256 idx = 0; idx < ids.length; idx++) { + ids[idx] = s_upkeepIDs.at(idx + startIndex); + } + return ids; + } + + /** + * @notice returns the upkeep's trigger type + */ + function getTriggerType(uint256 upkeepId) external pure returns (Trigger) { + return _getTriggerType(upkeepId); + } + + /** + * @notice returns the trigger config for an upkeeep + */ + function getUpkeepTriggerConfig(uint256 upkeepId) public view returns (bytes memory) { + return s_upkeepTriggerConfig[upkeepId]; + } + + /** + * @notice read the current info about any transmitter address + */ + function getTransmitterInfo( + address query + ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee) { + Transmitter memory transmitter = s_transmitters[query]; + + uint96 pooledShare = 0; + if (transmitter.active) { + uint96 totalDifference = s_hotVars.totalPremium - transmitter.lastCollected; + pooledShare = totalDifference / uint96(s_transmittersList.length); + } + + return ( + transmitter.active, + transmitter.index, + (transmitter.balance + pooledShare), + transmitter.lastCollected, + s_transmitterPayees[query] + ); + } + + /** + * @notice read the current info about any signer address + */ + function getSignerInfo(address query) external view returns (bool active, uint8 index) { + Signer memory signer = s_signers[query]; + return (signer.active, signer.index); + } + + /** + * @notice read the current state of the registry + */ + function getState() + external + view + returns ( + State memory state, + OnchainConfig memory config, + address[] memory signers, + address[] memory transmitters, + uint8 f + ) + { + state = State({ + nonce: s_storage.nonce, + ownerLinkBalance: s_storage.ownerLinkBalance, + expectedLinkBalance: s_expectedLinkBalance, + totalPremium: s_hotVars.totalPremium, + numUpkeeps: s_upkeepIDs.length(), + configCount: s_storage.configCount, + latestConfigBlockNumber: s_storage.latestConfigBlockNumber, + latestConfigDigest: s_latestConfigDigest, + latestEpoch: s_hotVars.latestEpoch, + paused: s_hotVars.paused + }); + + config = OnchainConfig({ + paymentPremiumPPB: s_hotVars.paymentPremiumPPB, + flatFeeMicroLink: s_hotVars.flatFeeMicroLink, + checkGasLimit: s_storage.checkGasLimit, + stalenessSeconds: s_hotVars.stalenessSeconds, + gasCeilingMultiplier: s_hotVars.gasCeilingMultiplier, + minUpkeepSpend: s_storage.minUpkeepSpend, + maxPerformGas: s_storage.maxPerformGas, + maxCheckDataSize: s_storage.maxCheckDataSize, + maxPerformDataSize: s_storage.maxPerformDataSize, + maxRevertDataSize: s_storage.maxRevertDataSize, + fallbackGasPrice: s_fallbackGasPrice, + fallbackLinkPrice: s_fallbackLinkPrice, + transcoder: s_storage.transcoder, + registrars: s_registrars.values(), + upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager + }); + + return (state, config, s_signersList, s_transmittersList, s_hotVars.f); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getBalance(uint256 id) external view returns (uint96 balance) { + return s_upkeep[id].balance; + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getMinBalance(uint256 id) external view returns (uint96) { + return getMinBalanceForUpkeep(id); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + * @dev this will be deprecated in a future version in favor of getMinBalance + */ + function getMinBalanceForUpkeep(uint256 id) public view returns (uint96 minBalance) { + return getMaxPaymentForGas(_getTriggerType(id), s_upkeep[id].performGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + * @param gasLimit the gas to calculate payment for + */ + function getMaxPaymentForGas(Trigger triggerType, uint32 gasLimit) public view returns (uint96 maxPayment) { + HotVars memory hotVars = s_hotVars; + (uint256 fastGasWei, uint256 linkNative) = _getFeedData(hotVars); + return + _getMaxLinkPayment(hotVars, triggerType, gasLimit, s_storage.maxPerformDataSize, fastGasWei, linkNative, false); + } + + /** + * @notice retrieves the migration permission for a peer registry + */ + function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { + return s_peerRegistryMigrationPermission[peer]; + } + + /** + * @notice returns the upkeep privilege config + */ + function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory) { + return s_upkeepPrivilegeConfig[upkeepId]; + } + + /** + * @notice returns the upkeep privilege config + */ + function getAdminPrivilegeConfig(address admin) external view returns (bytes memory) { + return s_adminPrivilegeConfig[admin]; + } + + /** + * @notice returns the upkeep's forwarder contract + */ + function getForwarder(uint256 upkeepID) external view returns (IAutomationForwarder) { + return s_upkeep[upkeepID].forwarder; + } + + /** + * @notice returns the upkeep's forwarder contract + */ + function hasDedupKey(bytes32 dedupKey) external view returns (bool) { + return s_dedupKeys[dedupKey]; + } +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationUtils2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationUtils2_2.sol new file mode 100644 index 00000000000..2b8fd8df6ac --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationUtils2_2.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {AutomationRegistryBase2_2} from "./AutomationRegistryBase2_2.sol"; +import {Log} from "../../interfaces/ILogAutomation.sol"; + +/** + * @notice this file exposes structs that are otherwise internal to the automation registry + * doing this allows those structs to be encoded and decoded with type safety in offchain code + * and tests because generated wrappers are made available + */ + +/** + * @notice structure of trigger for log triggers + */ +struct LogTriggerConfig { + address contractAddress; + uint8 filterSelector; // denotes which topics apply to filter ex 000, 101, 111...only last 3 bits apply + bytes32 topic0; + bytes32 topic1; + bytes32 topic2; + bytes32 topic3; +} + +contract AutomationUtils2_2 { + /** + * @dev this can be removed as OnchainConfig is now exposed directly from the registry + */ + function _onChainConfig(AutomationRegistryBase2_2.OnchainConfig memory) external {} // 0x2ff92a81 + + function _report(AutomationRegistryBase2_2.Report memory) external {} // 0xe65d6546 + + function _logTriggerConfig(LogTriggerConfig memory) external {} // 0x21f373d7 + + function _logTrigger(AutomationRegistryBase2_2.LogTrigger memory) external {} // 0x1c8d8260 + + function _conditionalTrigger(AutomationRegistryBase2_2.ConditionalTrigger memory) external {} // 0x4b6df294 + + function _log(Log memory) external {} // 0xe9720a49 +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/LICENSE b/contracts/src/v0.8/automation/dev/v2_2/LICENSE new file mode 100644 index 00000000000..9230d392a98 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/LICENSE @@ -0,0 +1,57 @@ +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +--- + +Parameters + +Licensor: SmartContract Chainlink Limited SEZC + +Licensed Work: Automation v2.2 +The Licensed Work is (c) 2024 SmartContract Chainlink Limited SEZC + +Additional Use Grant(s): +You may make use of Automation v2.1, v2.2 (which is available subject to the license here the “Licensed Work”) solely for purposes listed below: +https://github.com/smartcontractkit/chainlink-automation/tree/main/Automation_Grants.md + +Change Date: January 22, 2028 + +Change License: MIT + +--- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above, permitting limited production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate. + +If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. + +MariaDB hereby grants you permission to use this License’s text to license your works, and to refer to it using the trademark "Business Source License", as long as you comply with the Covenants of Licensor below. + +--- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business Source License" name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where "compatible" means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation. + +2. To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. diff --git a/contracts/src/v0.8/automation/dev/v2_2/README.md b/contracts/src/v0.8/automation/dev/v2_2/README.md new file mode 100644 index 00000000000..abd3eb0ca84 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/v2_2/README.md @@ -0,0 +1,40 @@ +# Automation Contract Structure + +The on-chain component of Chainlink automation is too large to fit into the [size requirements][size-limit-eip] of a single contract. It is also too large to fit into 2 contracts, a solution that works for most large projects. Therefore, we included this explanation of how the pieces fit together and various tradeoffs incurred. + +### Glossary + +**Master Contract** - also known as the “storage” contract. This is the contract whose state we care about. It is the entry-point into the chain of delegatecalls. (We avoid the term "proxy" because it is commonly associated with upgradability, and this system _is not upgradable_ even though it relies on some of the same mechanics.) + +**Logic Contract** - this a contract whose sole purpose is to hold code. We use the code at this address and execute it in the context of the master contract in order to increase our total capacity for on-chain code. + +### Overview + +We chain multiple logic contracts together using [fallback functions][fallback] and [delegatecall][delegatecall]. If a function definition is not found on one contract, we fall back to the next, always executing the function in the scope of the master contract. The actual implementation of this is based off of [OZ's Proxy contract][oz-proxy]. + +### Diagram + +```mermaid +graph LR + Master -- delegatecall --> la[Logic A] + la -- delegatecall --> lb[Logic B] + lb -. delegatecall .-> lx[Logic X] +``` + +### Special Considerations + +- functions on the master contract have the least gas overhead, therefore, our most price-sensitive functions live there +- functions on the master contract have first-class support from tools like etherscan and tenderly - functions that we (or users) call often to debug should live there +- etherscan supports executing logic contract functions that are once removed from the master - therefore we give secondary preference to the first logic contract for user and debugging functions +- functions on logic A through logic X (as of writing) have no support on etherscan and will essentially be "invisible" to everyone but advanced users - we will try to reserve this space for uncommon interactions that are mostly done progamatically +- We use Logic A, B, C... to avoid confusion with the version ex `AutomationRegistryLogicA2_2.sol` --> Logic Contract A verion 2.1 +- Storage locations for logic contract addresses MUST BE BYTECODE (this is done by marking them as "immutable") otherwise the chaining mechanism will break + +### Master Interface + +The Master Interface is a deduped combination of all the interfaces from all contracts in the chain. We generate this interface programatically using the script `generate-automation-master-interface.ts`. This process is not a hardened one. Users of this script should take great care to ensure it's efficacy. + +[size-limit-eip]: https://eips.ethereum.org/EIPS/eip-170 +[fallback]: https://docs.soliditylang.org/en/v0.8.12/contracts.html#fallback-function +[delegatecall]: https://docs.soliditylang.org/en/v0.8.12/introduction-to-smart-contracts.html?highlight=delegatecall#delegatecall-callcode-and-libraries +[oz-proxy]: https://docs.openzeppelin.com/contracts/4.x/api/proxy#Proxy diff --git a/contracts/src/v0.8/automation/v2_1/test/AutomationForwarder.t.sol b/contracts/src/v0.8/automation/test/AutomationForwarder.t.sol similarity index 88% rename from contracts/src/v0.8/automation/v2_1/test/AutomationForwarder.t.sol rename to contracts/src/v0.8/automation/test/AutomationForwarder.t.sol index 078d22d6c96..aee2fcfbd29 100644 --- a/contracts/src/v0.8/automation/v2_1/test/AutomationForwarder.t.sol +++ b/contracts/src/v0.8/automation/test/AutomationForwarder.t.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; -import {IAutomationRegistryConsumer} from "../../interfaces/IAutomationRegistryConsumer.sol"; -import {IAutomationForwarder} from "../../interfaces/IAutomationForwarder.sol"; +import {IAutomationRegistryConsumer} from "../interfaces/IAutomationRegistryConsumer.sol"; +import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; import {AutomationForwarder} from "../AutomationForwarder.sol"; import {AutomationForwarderLogic} from "../AutomationForwarderLogic.sol"; -import {MockKeeperRegistry2_1} from "../../mocks/MockKeeperRegistry2_1.sol"; -import {UpkeepCounter} from "../../testhelpers/UpkeepCounter.sol"; +import {MockKeeperRegistry2_1} from "../mocks/MockKeeperRegistry2_1.sol"; +import {UpkeepCounter} from "../testhelpers/UpkeepCounter.sol"; import {BaseTest} from "./BaseTest.t.sol"; // in contracts directory, run -// forge test --match-path src/v0.8/automation/v2_1/test/AutomationForwarder.t.sol +// forge test --match-path src/v0.8/automation/test/AutomationForwarder.t.sol contract AutomationForwarderSetUp is BaseTest { IAutomationForwarder internal forwarder; diff --git a/contracts/src/v0.8/automation/test/BaseTest.t.sol b/contracts/src/v0.8/automation/test/BaseTest.t.sol index bcde8a11480..ecba6521c64 100644 --- a/contracts/src/v0.8/automation/test/BaseTest.t.sol +++ b/contracts/src/v0.8/automation/test/BaseTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.6; +pragma solidity ^0.8.0; import "forge-std/Test.sol"; diff --git a/contracts/src/v0.8/automation/v2_1/test/StructFactory.sol b/contracts/src/v0.8/automation/test/StructFactory.sol similarity index 85% rename from contracts/src/v0.8/automation/v2_1/test/StructFactory.sol rename to contracts/src/v0.8/automation/test/StructFactory.sol index 83bb9e89050..9317244a31e 100644 --- a/contracts/src/v0.8/automation/v2_1/test/StructFactory.sol +++ b/contracts/src/v0.8/automation/test/StructFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.16; +pragma solidity ^0.8.0; contract StructFactory { address internal OWNER; diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol index e3845ce63bd..517d14d8c9b 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol @@ -6,7 +6,7 @@ import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils import {KeeperRegistryBase2_1} from "./KeeperRegistryBase2_1.sol"; import {KeeperRegistryLogicB2_1} from "./KeeperRegistryLogicB2_1.sol"; import {Chainable} from "../Chainable.sol"; -import {AutomationForwarder} from "./AutomationForwarder.sol"; +import {AutomationForwarder} from "../AutomationForwarder.sol"; import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; import {UpkeepTranscoderInterfaceV2} from "../interfaces/UpkeepTranscoderInterfaceV2.sol"; import {MigratableKeeperRegistryInterfaceV2} from "../interfaces/MigratableKeeperRegistryInterfaceV2.sol"; diff --git a/contracts/src/v0.8/automation/v2_1/LICENSE b/contracts/src/v0.8/automation/v2_1/LICENSE index 7d09d647f5b..f60ab657175 100644 --- a/contracts/src/v0.8/automation/v2_1/LICENSE +++ b/contracts/src/v0.8/automation/v2_1/LICENSE @@ -12,7 +12,8 @@ Licensor: SmartContract Chainlink Limited SEZC Licensed Work: Automation v2.1 The Licensed Work is (c) 2023 SmartContract Chainlink Limited SEZC -Additional Use Grant: Any uses listed and defined at https://github.com/smartcontractkit/chainlink-automation/tree/main/pkg/v3/Automation_v2.1_Grants.md +Additional Use Grant: Any uses listed and defined at https://github.com/smartcontractkit/chainlink-automation/tree/main/Automation_Grants.md + Change Date: September 12, 2027 diff --git a/contracts/src/v0.8/automation/v2_1/test/BaseTest.t.sol b/contracts/src/v0.8/automation/v2_1/test/BaseTest.t.sol deleted file mode 100644 index f5bc74286a4..00000000000 --- a/contracts/src/v0.8/automation/v2_1/test/BaseTest.t.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.16; - -import {StructFactory} from "./StructFactory.sol"; -import "forge-std/Test.sol"; - -contract BaseTest is Test, StructFactory { - function setUp() public virtual { - vm.startPrank(OWNER); - deal(OWNER, 1e20); - } -} diff --git a/contracts/test/v0.8/automation/AutomationRegistrar2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistrar2_2.test.ts new file mode 100644 index 00000000000..b7585462067 --- /dev/null +++ b/contracts/test/v0.8/automation/AutomationRegistrar2_2.test.ts @@ -0,0 +1,1013 @@ +import { ethers } from 'hardhat' +import { ContractFactory, Contract } from 'ethers' +import { assert, expect } from 'chai' +import { evmRevert } from '../../test-helpers/matchers' +import { getUsers, Personas } from '../../test-helpers/setup' +import { BigNumber, Signer } from 'ethers' +import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory' +import { UpkeepMock__factory as UpkeepMockFactory } from '../../../typechain/factories/UpkeepMock__factory' +import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' +import { UpkeepMock } from '../../../typechain/UpkeepMock' +import { toWei } from '../../test-helpers/helpers' +import { IKeeperRegistryMaster as IKeeperRegistry } from '../../../typechain/IKeeperRegistryMaster' +import { AutomationRegistrar2_2 as Registrar } from '../../../typechain/AutomationRegistrar2_2' +import { deployRegistry21 } from './helpers' + +// copied from KeeperRegistryBase2_2.sol +enum Trigger { + CONDITION, + LOG, +} + +let linkTokenFactory: ContractFactory +let mockV3AggregatorFactory: MockV3AggregatorFactory +let upkeepMockFactory: UpkeepMockFactory + +let personas: Personas + +before(async () => { + personas = (await getUsers()).personas + + linkTokenFactory = await ethers.getContractFactory( + 'src/v0.4/LinkToken.sol:LinkToken', + ) + mockV3AggregatorFactory = (await ethers.getContractFactory( + 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator', + )) as unknown as MockV3AggregatorFactory + upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') +}) + +const errorMsgs = { + onlyOwner: 'revert Only callable by owner', + onlyAdmin: 'OnlyAdminOrOwner()', + hashPayload: 'HashMismatch()', + requestNotFound: 'RequestNotFound()', +} + +describe('AutomationRegistrar2_2', () => { + const upkeepName = 'SampleUpkeep' + + const linkEth = BigNumber.from(300000000) + const gasWei = BigNumber.from(100) + const performGas = BigNumber.from(100000) + const paymentPremiumPPB = BigNumber.from(250000000) + const flatFeeMicroLink = BigNumber.from(0) + const maxAllowedAutoApprove = 5 + const trigger = '0xdeadbeef' + const offchainConfig = '0x01234567' + + const emptyBytes = '0x00' + const stalenessSeconds = BigNumber.from(43820) + const gasCeilingMultiplier = BigNumber.from(1) + const checkGasLimit = BigNumber.from(20000000) + const fallbackGasPrice = BigNumber.from(200) + const fallbackLinkPrice = BigNumber.from(200000000) + const maxCheckDataSize = BigNumber.from(10000) + const maxPerformDataSize = BigNumber.from(10000) + const maxRevertDataSize = BigNumber.from(1000) + const maxPerformGas = BigNumber.from(5000000) + const minUpkeepSpend = BigNumber.from('1000000000000000000') + const amount = BigNumber.from('5000000000000000000') + const amount1 = BigNumber.from('6000000000000000000') + const transcoder = ethers.constants.AddressZero + const upkeepManager = ethers.Wallet.createRandom().address + + // Enum values are not auto exported in ABI so have to manually declare + const autoApproveType_DISABLED = 0 + const autoApproveType_ENABLED_SENDER_ALLOWLIST = 1 + const autoApproveType_ENABLED_ALL = 2 + + let owner: Signer + let admin: Signer + let someAddress: Signer + let registrarOwner: Signer + let stranger: Signer + let requestSender: Signer + + let linkToken: Contract + let linkEthFeed: MockV3Aggregator + let gasPriceFeed: MockV3Aggregator + let mock: UpkeepMock + let registry: IKeeperRegistry + let registrar: Registrar + + beforeEach(async () => { + owner = personas.Default + admin = personas.Neil + someAddress = personas.Ned + registrarOwner = personas.Nelly + stranger = personas.Nancy + requestSender = personas.Norbert + + linkToken = await linkTokenFactory.connect(owner).deploy() + gasPriceFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(0, gasWei) + linkEthFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(9, linkEth) + + registry = await deployRegistry21( + owner, + 0, + linkToken.address, + linkEthFeed.address, + gasPriceFeed.address, + ) + + mock = await upkeepMockFactory.deploy() + + const registrarFactory = await ethers.getContractFactory( + 'AutomationRegistrar2_2', + ) + registrar = await registrarFactory + .connect(registrarOwner) + .deploy(linkToken.address, registry.address, minUpkeepSpend, [ + { + triggerType: Trigger.CONDITION, + autoApproveType: autoApproveType_DISABLED, + autoApproveMaxAllowed: 0, + }, + { + triggerType: Trigger.LOG, + autoApproveType: autoApproveType_DISABLED, + autoApproveMaxAllowed: 0, + }, + ]) + + await linkToken + .connect(owner) + .transfer(await requestSender.getAddress(), toWei('1000')) + + const keepers = [ + await personas.Carol.getAddress(), + await personas.Nancy.getAddress(), + await personas.Ned.getAddress(), + await personas.Neil.getAddress(), + ] + const onchainConfig = { + paymentPremiumPPB, + flatFeeMicroLink, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder, + registrars: [registrar.address], + upkeepPrivilegeManager: upkeepManager, + } + await registry + .connect(owner) + .setConfigTypeSafe(keepers, keepers, 1, onchainConfig, 1, '0x') + }) + + describe('#typeAndVersion', () => { + it('uses the correct type and version', async () => { + const typeAndVersion = await registrar.typeAndVersion() + assert.equal(typeAndVersion, 'AutomationRegistrar 2.1.0') + }) + }) + + describe('#register', () => { + it('reverts if not called by the LINK token', async () => { + await evmRevert( + registrar + .connect(someAddress) + .register( + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ), + 'OnlyLink()', + ) + }) + + it('reverts if the amount passed in data mismatches actual amount sent', async () => { + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_ENABLED_ALL, + maxAllowedAutoApprove, + ) + + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount1, + await requestSender.getAddress(), + ], + ) + + await evmRevert( + linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes), + 'AmountMismatch()', + ) + }) + + it('reverts if the sender passed in data mismatches actual sender', async () => { + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await admin.getAddress(), // Should have been requestSender.getAddress() + ], + ) + await evmRevert( + linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes), + 'SenderMismatch()', + ) + }) + + it('reverts if the admin address is 0x0000...', async () => { + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + '0x0000000000000000000000000000000000000000', + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + + await evmRevert( + linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes), + 'RegistrationRequestFailed()', + ) + }) + + it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { + //set auto approve ON with high threshold limits + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_ENABLED_ALL, + maxAllowedAutoApprove, + ) + + //register with auto approve ON + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + const tx = await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + + const [id] = await registry.getActiveUpkeepIDs(0, 1) + + //confirm if a new upkeep has been registered and the details are the same as the one just registered + const newupkeep = await registry.getUpkeep(id) + assert.equal(newupkeep.target, mock.address) + assert.equal(newupkeep.admin, await admin.getAddress()) + assert.equal(newupkeep.checkData, emptyBytes) + assert.equal(newupkeep.balance.toString(), amount.toString()) + assert.equal(newupkeep.performGas, performGas.toNumber()) + assert.equal(newupkeep.offchainConfig, offchainConfig) + + await expect(tx).to.emit(registrar, 'RegistrationRequested') + await expect(tx).to.emit(registrar, 'RegistrationApproved') + }) + + it('Auto Approve OFF - does not registers an upkeep on KeeperRegistry, emits only RegistrationRequested event', async () => { + //get upkeep count before attempting registration + const beforeCount = (await registry.getState()).state.numUpkeeps + + //set auto approve OFF, threshold limits dont matter in this case + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_DISABLED, + maxAllowedAutoApprove, + ) + + //register with auto approve OFF + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + const tx = await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + const receipt = await tx.wait() + + //get upkeep count after attempting registration + const afterCount = (await registry.getState()).state.numUpkeeps + //confirm that a new upkeep has NOT been registered and upkeep count is still the same + assert.deepEqual(beforeCount, afterCount) + + //confirm that only RegistrationRequested event is emitted and RegistrationApproved event is not + await expect(tx).to.emit(registrar, 'RegistrationRequested') + await expect(tx).not.to.emit(registrar, 'RegistrationApproved') + + const hash = receipt.logs[2].topics[1] + const pendingRequest = await registrar.getPendingRequest(hash) + assert.equal(await admin.getAddress(), pendingRequest[0]) + assert.ok(amount.eq(pendingRequest[1])) + }) + + it('Auto Approve ON - Throttle max approvals - does not register an upkeep on KeeperRegistry beyond the max limit, emits only RegistrationRequested event after limit is hit', async () => { + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 0) + + //set auto approve on, with max 1 allowed + await registrar + .connect(registrarOwner) + .setTriggerConfig(Trigger.CONDITION, autoApproveType_ENABLED_ALL, 1) + + //set auto approve on, with max 1 allowed + await registrar + .connect(registrarOwner) + .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 1) + + // register within threshold, new upkeep should be registered + let abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ]) + await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // 0 -> 1 + + // try registering another one, new upkeep should not be registered + abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ + upkeepName, + emptyBytes, + mock.address, + performGas.toNumber() + 1, // make unique hash + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ]) + await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // Still 1 + + // register a second type of upkeep, different limit + abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + Trigger.LOG, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ]) + await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 2) // 1 -> 2 + + // Now set new max limit to 2. One more upkeep should get auto approved + await registrar + .connect(registrarOwner) + .setTriggerConfig(Trigger.CONDITION, autoApproveType_ENABLED_ALL, 2) + + abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ + upkeepName, + emptyBytes, + mock.address, + performGas.toNumber() + 2, // make unique hash + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ]) + await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 3) // 2 -> 3 + + // One more upkeep should not get registered + abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ + upkeepName, + emptyBytes, + mock.address, + performGas.toNumber() + 3, // make unique hash + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ]) + await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 3) // Still 3 + }) + + it('Auto Approve Sender Allowlist - sender in allowlist - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { + const senderAddress = await requestSender.getAddress() + + //set auto approve to ENABLED_SENDER_ALLOWLIST type with high threshold limits + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_ENABLED_SENDER_ALLOWLIST, + maxAllowedAutoApprove, + ) + + // Add sender to allowlist + await registrar + .connect(registrarOwner) + .setAutoApproveAllowedSender(senderAddress, true) + + //register with auto approve ON + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + const tx = await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + + const [id] = await registry.getActiveUpkeepIDs(0, 1) + + //confirm if a new upkeep has been registered and the details are the same as the one just registered + const newupkeep = await registry.getUpkeep(id) + assert.equal(newupkeep.target, mock.address) + assert.equal(newupkeep.admin, await admin.getAddress()) + assert.equal(newupkeep.checkData, emptyBytes) + assert.equal(newupkeep.balance.toString(), amount.toString()) + assert.equal(newupkeep.performGas, performGas.toNumber()) + + await expect(tx).to.emit(registrar, 'RegistrationRequested') + await expect(tx).to.emit(registrar, 'RegistrationApproved') + }) + + it('Auto Approve Sender Allowlist - sender NOT in allowlist - does not registers an upkeep on KeeperRegistry, emits only RegistrationRequested event', async () => { + const beforeCount = (await registry.getState()).state.numUpkeeps + const senderAddress = await requestSender.getAddress() + + //set auto approve to ENABLED_SENDER_ALLOWLIST type with high threshold limits + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_ENABLED_SENDER_ALLOWLIST, + maxAllowedAutoApprove, + ) + + // Explicitly remove sender from allowlist + await registrar + .connect(registrarOwner) + .setAutoApproveAllowedSender(senderAddress, false) + + //register. auto approve shouldn't happen + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + const tx = await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + const receipt = await tx.wait() + + //get upkeep count after attempting registration + const afterCount = (await registry.getState()).state.numUpkeeps + //confirm that a new upkeep has NOT been registered and upkeep count is still the same + assert.deepEqual(beforeCount, afterCount) + + //confirm that only RegistrationRequested event is emitted and RegistrationApproved event is not + await expect(tx).to.emit(registrar, 'RegistrationRequested') + await expect(tx).not.to.emit(registrar, 'RegistrationApproved') + + const hash = receipt.logs[2].topics[1] + const pendingRequest = await registrar.getPendingRequest(hash) + assert.equal(await admin.getAddress(), pendingRequest[0]) + assert.ok(amount.eq(pendingRequest[1])) + }) + }) + + describe('#registerUpkeep', () => { + it('reverts with empty message if amount sent is not available in LINK allowance', async () => { + await evmRevert( + registrar.connect(someAddress).registerUpkeep({ + name: upkeepName, + upkeepContract: mock.address, + gasLimit: performGas, + adminAddress: await admin.getAddress(), + triggerType: 0, + checkData: emptyBytes, + triggerConfig: trigger, + offchainConfig: emptyBytes, + amount, + encryptedEmail: emptyBytes, + }), + '', + ) + }) + + it('reverts if the amount passed in data is less than configured minimum', async () => { + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_ENABLED_ALL, + maxAllowedAutoApprove, + ) + + // amt is one order of magnitude less than minUpkeepSpend + const amt = BigNumber.from('100000000000000000') + + await evmRevert( + registrar.connect(someAddress).registerUpkeep({ + name: upkeepName, + upkeepContract: mock.address, + gasLimit: performGas, + adminAddress: await admin.getAddress(), + triggerType: 0, + checkData: emptyBytes, + triggerConfig: trigger, + offchainConfig: emptyBytes, + amount: amt, + encryptedEmail: emptyBytes, + }), + 'InsufficientPayment()', + ) + }) + + it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { + //set auto approve ON with high threshold limits + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_ENABLED_ALL, + maxAllowedAutoApprove, + ) + + await linkToken.connect(requestSender).approve(registrar.address, amount) + + const tx = await registrar.connect(requestSender).registerUpkeep({ + name: upkeepName, + upkeepContract: mock.address, + gasLimit: performGas, + adminAddress: await admin.getAddress(), + triggerType: 0, + checkData: emptyBytes, + triggerConfig: trigger, + offchainConfig, + amount, + encryptedEmail: emptyBytes, + }) + assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // 0 -> 1 + + //confirm if a new upkeep has been registered and the details are the same as the one just registered + const [id] = await registry.getActiveUpkeepIDs(0, 1) + const newupkeep = await registry.getUpkeep(id) + assert.equal(newupkeep.target, mock.address) + assert.equal(newupkeep.admin, await admin.getAddress()) + assert.equal(newupkeep.checkData, emptyBytes) + assert.equal(newupkeep.balance.toString(), amount.toString()) + assert.equal(newupkeep.performGas, performGas.toNumber()) + assert.equal(newupkeep.offchainConfig, offchainConfig) + + await expect(tx).to.emit(registrar, 'RegistrationRequested') + await expect(tx).to.emit(registrar, 'RegistrationApproved') + }) + }) + + describe('#setAutoApproveAllowedSender', () => { + it('reverts if not called by the owner', async () => { + const tx = registrar + .connect(stranger) + .setAutoApproveAllowedSender(await admin.getAddress(), false) + await evmRevert(tx, 'Only callable by owner') + }) + + it('sets the allowed status correctly and emits log', async () => { + const senderAddress = await stranger.getAddress() + let tx = await registrar + .connect(registrarOwner) + .setAutoApproveAllowedSender(senderAddress, true) + await expect(tx) + .to.emit(registrar, 'AutoApproveAllowedSenderSet') + .withArgs(senderAddress, true) + + let senderAllowedStatus = await registrar + .connect(owner) + .getAutoApproveAllowedSender(senderAddress) + assert.isTrue(senderAllowedStatus) + + tx = await registrar + .connect(registrarOwner) + .setAutoApproveAllowedSender(senderAddress, false) + await expect(tx) + .to.emit(registrar, 'AutoApproveAllowedSenderSet') + .withArgs(senderAddress, false) + + senderAllowedStatus = await registrar + .connect(owner) + .getAutoApproveAllowedSender(senderAddress) + assert.isFalse(senderAllowedStatus) + }) + }) + + describe('#setTriggerConfig', () => { + it('reverts if not called by the owner', async () => { + const tx = registrar + .connect(stranger) + .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 100) + await evmRevert(tx, 'Only callable by owner') + }) + + it('changes the config', async () => { + const tx = await registrar + .connect(registrarOwner) + .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 100) + await registrar.getTriggerRegistrationDetails(Trigger.LOG) + await expect(tx) + .to.emit(registrar, 'TriggerConfigSet') + .withArgs(Trigger.LOG, autoApproveType_ENABLED_ALL, 100) + }) + }) + + describe('#approve', () => { + let hash: string + + beforeEach(async () => { + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_DISABLED, + maxAllowedAutoApprove, + ) + + //register with auto approve OFF + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + + const tx = await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + const receipt = await tx.wait() + hash = receipt.logs[2].topics[1] + }) + + it('reverts if not called by the owner', async () => { + const tx = registrar + .connect(stranger) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + emptyBytes, + hash, + ) + await evmRevert(tx, 'Only callable by owner') + }) + + it('reverts if the hash does not exist', async () => { + const tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + emptyBytes, + '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44', + ) + await evmRevert(tx, errorMsgs.requestNotFound) + }) + + it('reverts if any member of the payload changes', async () => { + let tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + ethers.Wallet.createRandom().address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + emptyBytes, + hash, + ) + await evmRevert(tx, errorMsgs.hashPayload) + tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + 10000, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + emptyBytes, + hash, + ) + await evmRevert(tx, errorMsgs.hashPayload) + tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + ethers.Wallet.createRandom().address, + 0, + emptyBytes, + trigger, + emptyBytes, + hash, + ) + await evmRevert(tx, errorMsgs.hashPayload) + tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + '0x1234', + trigger, + emptyBytes, + hash, + ) + await evmRevert(tx, errorMsgs.hashPayload) + }) + + it('approves an existing registration request', async () => { + const tx = await registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + hash, + ) + await expect(tx).to.emit(registrar, 'RegistrationApproved') + }) + + it('deletes the request afterwards / reverts if the request DNE', async () => { + await registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + hash, + ) + const tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + hash, + ) + await evmRevert(tx, errorMsgs.requestNotFound) + }) + }) + + describe('#cancel', () => { + let hash: string + + beforeEach(async () => { + await registrar + .connect(registrarOwner) + .setTriggerConfig( + Trigger.CONDITION, + autoApproveType_DISABLED, + maxAllowedAutoApprove, + ) + + //register with auto approve OFF + const abiEncodedBytes = registrar.interface.encodeFunctionData( + 'register', + [ + upkeepName, + emptyBytes, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + offchainConfig, + amount, + await requestSender.getAddress(), + ], + ) + const tx = await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + const receipt = await tx.wait() + hash = receipt.logs[2].topics[1] + // submit duplicate request (increase balance) + await linkToken + .connect(requestSender) + .transferAndCall(registrar.address, amount, abiEncodedBytes) + }) + + it('reverts if not called by the admin / owner', async () => { + const tx = registrar.connect(stranger).cancel(hash) + await evmRevert(tx, errorMsgs.onlyAdmin) + }) + + it('reverts if the hash does not exist', async () => { + const tx = registrar + .connect(registrarOwner) + .cancel( + '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44', + ) + await evmRevert(tx, errorMsgs.requestNotFound) + }) + + it('refunds the total request balance to the admin address if owner cancels', async () => { + const before = await linkToken.balanceOf(await admin.getAddress()) + const tx = await registrar.connect(registrarOwner).cancel(hash) + const after = await linkToken.balanceOf(await admin.getAddress()) + assert.isTrue(after.sub(before).eq(amount.mul(BigNumber.from(2)))) + await expect(tx).to.emit(registrar, 'RegistrationRejected') + }) + + it('refunds the total request balance to the admin address if admin cancels', async () => { + const before = await linkToken.balanceOf(await admin.getAddress()) + const tx = await registrar.connect(admin).cancel(hash) + const after = await linkToken.balanceOf(await admin.getAddress()) + assert.isTrue(after.sub(before).eq(amount.mul(BigNumber.from(2)))) + await expect(tx).to.emit(registrar, 'RegistrationRejected') + }) + + it('deletes the request hash', async () => { + await registrar.connect(registrarOwner).cancel(hash) + let tx = registrar.connect(registrarOwner).cancel(hash) + await evmRevert(tx, errorMsgs.requestNotFound) + tx = registrar + .connect(registrarOwner) + .approve( + upkeepName, + mock.address, + performGas, + await admin.getAddress(), + 0, + emptyBytes, + trigger, + emptyBytes, + hash, + ) + await evmRevert(tx, errorMsgs.requestNotFound) + }) + }) +}) diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts new file mode 100644 index 00000000000..3822d362240 --- /dev/null +++ b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts @@ -0,0 +1,5757 @@ +import { ethers } from 'hardhat' +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' +import { assert, expect } from 'chai' +import { + BigNumber, + BigNumberish, + BytesLike, + Contract, + ContractFactory, + ContractReceipt, + ContractTransaction, + Signer, + Wallet, +} from 'ethers' +import { evmRevert } from '../../test-helpers/matchers' +import { getUsers, Personas } from '../../test-helpers/setup' +import { randomAddress, toWei } from '../../test-helpers/helpers' +import { StreamsLookupUpkeep__factory as StreamsLookupUpkeepFactory } from '../../../typechain/factories/StreamsLookupUpkeep__factory' +import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory' +import { UpkeepMock__factory as UpkeepMockFactory } from '../../../typechain/factories/UpkeepMock__factory' +import { UpkeepAutoFunder__factory as UpkeepAutoFunderFactory } from '../../../typechain/factories/UpkeepAutoFunder__factory' +import { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typechain/factories/MockArbGasInfo__factory' +import { MockOVMGasPriceOracle__factory as MockOVMGasPriceOracleFactory } from '../../../typechain/factories/MockOVMGasPriceOracle__factory' +import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory' +import { IAutomationForwarder__factory as IAutomationForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory' +import { MockArbSys__factory as MockArbSysFactory } from '../../../typechain/factories/MockArbSys__factory' +import { AutomationUtils2_2 as AutomationUtils } from '../../../typechain/AutomationUtils2_2' +import { StreamsLookupUpkeep } from '../../../typechain/StreamsLookupUpkeep' +import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' +import { UpkeepMock } from '../../../typechain/UpkeepMock' +import { MockArbGasInfo } from '../../../typechain/MockArbGasInfo' +import { MockOVMGasPriceOracle } from '../../../typechain/MockOVMGasPriceOracle' +import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder' +import { UpkeepAutoFunder } from '../../../typechain' +import { + CancelledUpkeepReportEvent, + IKeeperRegistryMaster as IKeeperRegistry, + InsufficientFundsUpkeepReportEvent, + ReorgedUpkeepReportEvent, + StaleUpkeepReportEvent, + UpkeepPerformedEvent, +} from '../../../typechain/IKeeperRegistryMaster' +import { + deployMockContract, + MockContract, +} from '@ethereum-waffle/mock-contract' +import { deployRegistry21 } from './helpers' + +const describeMaybe = process.env.SKIP_SLOW ? describe.skip : describe +const itMaybe = process.env.SKIP_SLOW ? it.skip : it + +// copied from AutomationRegistryInterface2_2.sol +enum UpkeepFailureReason { + NONE, + UPKEEP_CANCELLED, + UPKEEP_PAUSED, + TARGET_CHECK_REVERTED, + UPKEEP_NOT_NEEDED, + PERFORM_DATA_EXCEEDS_LIMIT, + INSUFFICIENT_BALANCE, + CHECK_CALLBACK_REVERTED, + REVERT_DATA_EXCEEDS_LIMIT, + REGISTRY_PAUSED, +} + +// copied from AutomationRegistryInterface2_2.sol +enum Mode { + DEFAULT, + ARBITRUM, + OPTIMISM, +} + +// copied from KeeperRegistryBase2_2.sol +enum Trigger { + CONDITION, + LOG, +} + +// un-exported types that must be extracted from the utils contract +type Report = Parameters[0] +type OnChainConfig = Parameters[0] +type LogTrigger = Parameters[0] +type ConditionalTrigger = Parameters[0] +type Log = Parameters[0] + +// ----------------------------------------------------------------------------------------------- + +// These values should match the constants declared in registry +let registryConditionalOverhead: BigNumber +let registryLogOverhead: BigNumber +let registryPerSignerGasOverhead: BigNumber +let registryPerPerformByteGasOverhead: BigNumber +let cancellationDelay: number + +// This is the margin for gas that we test for. Gas charged should always be greater +// than total gas used in tx but should not increase beyond this margin +const gasCalculationMargin = BigNumber.from(8000) + +const linkEth = BigNumber.from(5000000000000000) // 1 Link = 0.005 Eth +const gasWei = BigNumber.from(1000000000) // 1 gwei +// ----------------------------------------------------------------------------------------------- +// test-wide configs for upkeeps +const linkDivisibility = BigNumber.from('1000000000000000000') +const performGas = BigNumber.from('1000000') +const paymentPremiumBase = BigNumber.from('1000000000') +const paymentPremiumPPB = BigNumber.from('250000000') +const flatFeeMicroLink = BigNumber.from(0) + +const randomBytes = '0x1234abcd' +const emptyBytes = '0x' +const emptyBytes32 = + '0x0000000000000000000000000000000000000000000000000000000000000000' + +const transmitGasOverhead = 1_000_000 +const checkGasOverhead = 400_000 + +const stalenessSeconds = BigNumber.from(43820) +const gasCeilingMultiplier = BigNumber.from(2) +const checkGasLimit = BigNumber.from(10000000) +const fallbackGasPrice = gasWei.mul(BigNumber.from('2')) +const fallbackLinkPrice = linkEth.div(BigNumber.from('2')) +const maxCheckDataSize = BigNumber.from(1000) +const maxPerformDataSize = BigNumber.from(1000) +const maxRevertDataSize = BigNumber.from(1000) +const maxPerformGas = BigNumber.from(5000000) +const minUpkeepSpend = BigNumber.from(0) +const f = 1 +const offchainVersion = 1 +const offchainBytes = '0x' +const zeroAddress = ethers.constants.AddressZero +const epochAndRound5_1 = + '0x0000000000000000000000000000000000000000000000000000000000000501' + +let logTriggerConfig: string + +// ----------------------------------------------------------------------------------------------- + +// Smart contract factories +let linkTokenFactory: ContractFactory +let mockV3AggregatorFactory: MockV3AggregatorFactory +let upkeepMockFactory: UpkeepMockFactory +let upkeepAutoFunderFactory: UpkeepAutoFunderFactory +let mockArbGasInfoFactory: MockArbGasInfoFactory +let mockOVMGasPriceOracleFactory: MockOVMGasPriceOracleFactory +let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory +let personas: Personas + +// contracts +let linkToken: Contract +let linkEthFeed: MockV3Aggregator +let gasPriceFeed: MockV3Aggregator +let registry: IKeeperRegistry // default registry, used for most tests +let arbRegistry: IKeeperRegistry // arbitrum registry +let opRegistry: IKeeperRegistry // optimism registry +let mgRegistry: IKeeperRegistry // "migrate registry" used in migration tests +let blankRegistry: IKeeperRegistry // used to test initial configurations +let mock: UpkeepMock +let autoFunderUpkeep: UpkeepAutoFunder +let ltUpkeep: MockContract +let transcoder: UpkeepTranscoder +let mockArbGasInfo: MockArbGasInfo +let mockOVMGasPriceOracle: MockOVMGasPriceOracle +let streamsLookupUpkeep: StreamsLookupUpkeep +let automationUtils: AutomationUtils + +function now() { + return Math.floor(Date.now() / 1000) +} + +async function getUpkeepID(tx: ContractTransaction): Promise { + const receipt = await tx.wait() + for (const event of receipt.events || []) { + if ( + event.args && + event.eventSignature == 'UpkeepRegistered(uint256,uint32,address)' + ) { + return event.args[0] + } + } + throw new Error('could not find upkeep ID in tx event logs') +} + +const getTriggerType = (upkeepId: BigNumber): Trigger => { + const hexBytes = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) + const bytes = ethers.utils.arrayify(hexBytes) + for (let idx = 4; idx < 15; idx++) { + if (bytes[idx] != 0) { + return Trigger.CONDITION + } + } + return bytes[15] as Trigger +} + +const encodeConfig = (onchainConfig: OnChainConfig) => { + return ( + '0x' + + automationUtils.interface + .encodeFunctionData('_onChainConfig', [onchainConfig]) + .slice(10) + ) +} + +const encodeBlockTrigger = (conditionalTrigger: ConditionalTrigger) => { + return ( + '0x' + + automationUtils.interface + .encodeFunctionData('_conditionalTrigger', [conditionalTrigger]) + .slice(10) + ) +} + +const encodeLogTrigger = (logTrigger: LogTrigger) => { + return ( + '0x' + + automationUtils.interface + .encodeFunctionData('_logTrigger', [logTrigger]) + .slice(10) + ) +} + +const encodeLog = (log: Log) => { + return ( + '0x' + automationUtils.interface.encodeFunctionData('_log', [log]).slice(10) + ) +} + +const encodeReport = (report: Report) => { + return ( + '0x' + + automationUtils.interface.encodeFunctionData('_report', [report]).slice(10) + ) +} + +type UpkeepData = { + Id: BigNumberish + performGas: BigNumberish + performData: BytesLike + trigger: BytesLike +} + +const makeReport = (upkeeps: UpkeepData[]) => { + const upkeepIds = upkeeps.map((u) => u.Id) + const performGases = upkeeps.map((u) => u.performGas) + const triggers = upkeeps.map((u) => u.trigger) + const performDatas = upkeeps.map((u) => u.performData) + return encodeReport({ + fastGasWei: gasWei, + linkNative: linkEth, + upkeepIds, + gasLimits: performGases, + triggers, + performDatas, + }) +} + +const makeLatestBlockReport = async (upkeepsIDs: BigNumberish[]) => { + const latestBlock = await ethers.provider.getBlock('latest') + const upkeeps: UpkeepData[] = [] + for (let i = 0; i < upkeepsIDs.length; i++) { + upkeeps.push({ + Id: upkeepsIDs[i], + performGas, + trigger: encodeBlockTrigger({ + blockNum: latestBlock.number, + blockHash: latestBlock.hash, + }), + performData: '0x', + }) + } + return makeReport(upkeeps) +} + +const signReport = ( + reportContext: string[], + report: any, + signers: Wallet[], +) => { + const reportDigest = ethers.utils.keccak256(report) + const packedArgs = ethers.utils.solidityPack( + ['bytes32', 'bytes32[3]'], + [reportDigest, reportContext], + ) + const packedDigest = ethers.utils.keccak256(packedArgs) + + const signatures = [] + for (const signer of signers) { + signatures.push(signer._signingKey().signDigest(packedDigest)) + } + const vs = signatures.map((i) => '0' + (i.v - 27).toString(16)).join('') + return { + vs: '0x' + vs.padEnd(64, '0'), + rs: signatures.map((i) => i.r), + ss: signatures.map((i) => i.s), + } +} + +const parseUpkeepPerformedLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events[ + 'UpkeepPerformed(uint256,bool,uint96,uint256,uint256,bytes)' + ].name + ) { + parsedLogs.push(log as unknown as UpkeepPerformedEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseReorgedUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events['ReorgedUpkeepReport(uint256,bytes)'].name + ) { + parsedLogs.push(log as unknown as ReorgedUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseStaleUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events['StaleUpkeepReport(uint256,bytes)'].name + ) { + parsedLogs.push(log as unknown as StaleUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseInsufficientFundsUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events[ + 'InsufficientFundsUpkeepReport(uint256,bytes)' + ].name + ) { + parsedLogs.push(log as unknown as InsufficientFundsUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseCancelledUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events['CancelledUpkeepReport(uint256,bytes)'].name + ) { + parsedLogs.push(log as unknown as CancelledUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +describe('KeeperRegistry2_2', () => { + let owner: Signer + let keeper1: Signer + let keeper2: Signer + let keeper3: Signer + let keeper4: Signer + let keeper5: Signer + let nonkeeper: Signer + let signer1: Wallet + let signer2: Wallet + let signer3: Wallet + let signer4: Wallet + let signer5: Wallet + let admin: Signer + let payee1: Signer + let payee2: Signer + let payee3: Signer + let payee4: Signer + let payee5: Signer + + let upkeepId: BigNumber // conditional upkeep + let afUpkeepId: BigNumber // auto funding upkeep + let logUpkeepId: BigNumber // log trigger upkeepID + let streamsLookupUpkeepId: BigNumber // streams lookup upkeep + const numUpkeeps = 4 // see above + let keeperAddresses: string[] + let payees: string[] + let signers: Wallet[] + let signerAddresses: string[] + let config: any + let baseConfig: Parameters + let upkeepManager: string + + before(async () => { + personas = (await getUsers()).personas + + const utilsFactory = await ethers.getContractFactory('AutomationUtils2_2') + automationUtils = await utilsFactory.deploy() + + linkTokenFactory = await ethers.getContractFactory( + 'src/v0.4/LinkToken.sol:LinkToken', + ) + // need full path because there are two contracts with name MockV3Aggregator + mockV3AggregatorFactory = (await ethers.getContractFactory( + 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator', + )) as unknown as MockV3AggregatorFactory + upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') + upkeepAutoFunderFactory = + await ethers.getContractFactory('UpkeepAutoFunder') + mockArbGasInfoFactory = await ethers.getContractFactory('MockArbGasInfo') + mockOVMGasPriceOracleFactory = await ethers.getContractFactory( + 'MockOVMGasPriceOracle', + ) + streamsLookupUpkeepFactory = await ethers.getContractFactory( + 'StreamsLookupUpkeep', + ) + + owner = personas.Default + keeper1 = personas.Carol + keeper2 = personas.Eddy + keeper3 = personas.Nancy + keeper4 = personas.Norbert + keeper5 = personas.Nick + nonkeeper = personas.Ned + admin = personas.Neil + payee1 = personas.Nelly + payee2 = personas.Norbert + payee3 = personas.Nick + payee4 = personas.Eddy + payee5 = personas.Carol + upkeepManager = await personas.Norbert.getAddress() + // signers + signer1 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000001', + ) + signer2 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000002', + ) + signer3 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000003', + ) + signer4 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000004', + ) + signer5 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000005', + ) + + keeperAddresses = [ + await keeper1.getAddress(), + await keeper2.getAddress(), + await keeper3.getAddress(), + await keeper4.getAddress(), + await keeper5.getAddress(), + ] + payees = [ + await payee1.getAddress(), + await payee2.getAddress(), + await payee3.getAddress(), + await payee4.getAddress(), + await payee5.getAddress(), + ] + signers = [signer1, signer2, signer3, signer4, signer5] + + // We append 26 random addresses to keepers, payees and signers to get a system of 31 oracles + // This allows f value of 1 - 10 + for (let i = 0; i < 26; i++) { + keeperAddresses.push(randomAddress()) + payees.push(randomAddress()) + signers.push(ethers.Wallet.createRandom()) + } + signerAddresses = [] + for (const signer of signers) { + signerAddresses.push(await signer.getAddress()) + } + + logTriggerConfig = + '0x' + + automationUtils.interface + .encodeFunctionData('_logTriggerConfig', [ + { + contractAddress: randomAddress(), + filterSelector: 0, + topic0: ethers.utils.randomBytes(32), + topic1: ethers.utils.randomBytes(32), + topic2: ethers.utils.randomBytes(32), + topic3: ethers.utils.randomBytes(32), + }, + ]) + .slice(10) + }) + + const linkForGas = ( + upkeepGasSpent: BigNumber, + gasOverhead: BigNumber, + gasMultiplier: BigNumber, + premiumPPB: BigNumber, + flatFee: BigNumber, + l1CostWei?: BigNumber, + numUpkeepsBatch?: BigNumber, + ) => { + l1CostWei = l1CostWei === undefined ? BigNumber.from(0) : l1CostWei + numUpkeepsBatch = + numUpkeepsBatch === undefined ? BigNumber.from(1) : numUpkeepsBatch + + const gasSpent = gasOverhead.add(BigNumber.from(upkeepGasSpent)) + const base = gasWei + .mul(gasMultiplier) + .mul(gasSpent) + .mul(linkDivisibility) + .div(linkEth) + const l1Fee = l1CostWei + .mul(gasMultiplier) + .div(numUpkeepsBatch) + .mul(linkDivisibility) + .div(linkEth) + const gasPayment = base.add(l1Fee) + + const premium = gasWei + .mul(gasMultiplier) + .mul(upkeepGasSpent) + .add(l1CostWei.mul(gasMultiplier).div(numUpkeepsBatch)) + .mul(linkDivisibility) + .div(linkEth) + .mul(premiumPPB) + .div(paymentPremiumBase) + .add(BigNumber.from(flatFee).mul('1000000000000')) + + return { + total: gasPayment.add(premium), + gasPaymemnt: gasPayment, + premium, + } + } + + const verifyMaxPayment = async ( + registry: IKeeperRegistry, + l1CostWei?: BigNumber, + ) => { + type TestCase = { + name: string + multiplier: number + gas: number + premium: number + flatFee: number + } + + const tests: TestCase[] = [ + { + name: 'no fees', + multiplier: 1, + gas: 100000, + premium: 0, + flatFee: 0, + }, + { + name: 'basic fees', + multiplier: 1, + gas: 100000, + premium: 250000000, + flatFee: 1000000, + }, + { + name: 'max fees', + multiplier: 3, + gas: 10000000, + premium: 250000000, + flatFee: 1000000, + }, + ] + + const fPlusOne = BigNumber.from(f + 1) + const totalConditionalOverhead = registryConditionalOverhead + .add(registryPerSignerGasOverhead.mul(fPlusOne)) + .add(registryPerPerformByteGasOverhead.mul(maxPerformDataSize)) + const totalLogOverhead = registryLogOverhead + .add(registryPerSignerGasOverhead.mul(fPlusOne)) + .add(registryPerPerformByteGasOverhead.mul(maxPerformDataSize)) + + for (const test of tests) { + await registry.connect(owner).setConfig( + signerAddresses, + keeperAddresses, + f, + encodeConfig({ + paymentPremiumPPB: test.premium, + flatFeeMicroLink: test.flatFee, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier: test.multiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + }), + offchainVersion, + offchainBytes, + ) + + const conditionalPrice = await registry.getMaxPaymentForGas( + Trigger.CONDITION, + test.gas, + ) + expect(conditionalPrice).to.equal( + linkForGas( + BigNumber.from(test.gas), + totalConditionalOverhead, + BigNumber.from(test.multiplier), + BigNumber.from(test.premium), + BigNumber.from(test.flatFee), + l1CostWei, + ).total, + ) + + const logPrice = await registry.getMaxPaymentForGas(Trigger.LOG, test.gas) + expect(logPrice).to.equal( + linkForGas( + BigNumber.from(test.gas), + totalLogOverhead, + BigNumber.from(test.multiplier), + BigNumber.from(test.premium), + BigNumber.from(test.flatFee), + l1CostWei, + ).total, + ) + } + } + + const verifyConsistentAccounting = async ( + maxAllowedSpareChange: BigNumber, + ) => { + const expectedLinkBalance = (await registry.getState()).state + .expectedLinkBalance + const linkTokenBalance = await linkToken.balanceOf(registry.address) + const upkeepIdBalance = (await registry.getUpkeep(upkeepId)).balance + let totalKeeperBalance = BigNumber.from(0) + for (let i = 0; i < keeperAddresses.length; i++) { + totalKeeperBalance = totalKeeperBalance.add( + (await registry.getTransmitterInfo(keeperAddresses[i])).balance, + ) + } + const ownerBalance = (await registry.getState()).state.ownerLinkBalance + assert.isTrue(expectedLinkBalance.eq(linkTokenBalance)) + assert.isTrue( + upkeepIdBalance + .add(totalKeeperBalance) + .add(ownerBalance) + .lte(expectedLinkBalance), + ) + assert.isTrue( + expectedLinkBalance + .sub(upkeepIdBalance) + .sub(totalKeeperBalance) + .sub(ownerBalance) + .lte(maxAllowedSpareChange), + ) + } + + interface GetTransmitTXOptions { + numSigners?: number + startingSignerIndex?: number + gasLimit?: BigNumberish + gasPrice?: BigNumberish + performGas?: BigNumberish + performData?: string + checkBlockNum?: number + checkBlockHash?: string + logBlockHash?: BytesLike + txHash?: BytesLike + logIndex?: number + timestamp?: number + } + + const getTransmitTx = async ( + registry: IKeeperRegistry, + transmitter: Signer, + upkeepIds: BigNumber[], + overrides: GetTransmitTXOptions = {}, + ) => { + const latestBlock = await ethers.provider.getBlock('latest') + const configDigest = (await registry.getState()).state.latestConfigDigest + const config = { + numSigners: f + 1, + startingSignerIndex: 0, + performData: '0x', + performGas, + checkBlockNum: latestBlock.number, + checkBlockHash: latestBlock.hash, + logIndex: 0, + txHash: undefined, // assigned uniquely below + logBlockHash: undefined, // assigned uniquely below + timestamp: now(), + gasLimit: undefined, + gasPrice: undefined, + } + Object.assign(config, overrides) + const upkeeps: UpkeepData[] = [] + for (let i = 0; i < upkeepIds.length; i++) { + let trigger: string + switch (getTriggerType(upkeepIds[i])) { + case Trigger.CONDITION: + trigger = encodeBlockTrigger({ + blockNum: config.checkBlockNum, + blockHash: config.checkBlockHash, + }) + break + case Trigger.LOG: + trigger = encodeLogTrigger({ + logBlockHash: config.logBlockHash || ethers.utils.randomBytes(32), + txHash: config.txHash || ethers.utils.randomBytes(32), + logIndex: config.logIndex, + blockNum: config.checkBlockNum, + blockHash: config.checkBlockHash, + }) + break + } + upkeeps.push({ + Id: upkeepIds[i], + performGas: config.performGas, + trigger, + performData: config.performData, + }) + } + + const report = makeReport(upkeeps) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] + const sigs = signReport( + reportContext, + report, + signers.slice( + config.startingSignerIndex, + config.startingSignerIndex + config.numSigners, + ), + ) + + type txOverride = { + gasLimit?: BigNumberish | Promise + gasPrice?: BigNumberish | Promise + } + const txOverrides: txOverride = {} + if (config.gasLimit) { + txOverrides.gasLimit = config.gasLimit + } + if (config.gasPrice) { + txOverrides.gasPrice = config.gasPrice + } + + return registry + .connect(transmitter) + .transmit( + [configDigest, epochAndRound5_1, emptyBytes32], + report, + sigs.rs, + sigs.ss, + sigs.vs, + txOverrides, + ) + } + + const getTransmitTxWithReport = async ( + registry: IKeeperRegistry, + transmitter: Signer, + report: BytesLike, + ) => { + const configDigest = (await registry.getState()).state.latestConfigDigest + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] + const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) + + return registry + .connect(transmitter) + .transmit( + [configDigest, epochAndRound5_1, emptyBytes32], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ) + } + + const setup = async () => { + linkToken = await linkTokenFactory.connect(owner).deploy() + gasPriceFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(0, gasWei) + linkEthFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(9, linkEth) + const upkeepTranscoderFactory = await ethers.getContractFactory( + 'UpkeepTranscoder4_0', + ) + transcoder = await upkeepTranscoderFactory.connect(owner).deploy() + mockArbGasInfo = await mockArbGasInfoFactory.connect(owner).deploy() + mockOVMGasPriceOracle = await mockOVMGasPriceOracleFactory + .connect(owner) + .deploy() + streamsLookupUpkeep = await streamsLookupUpkeepFactory + .connect(owner) + .deploy( + BigNumber.from('10000'), + BigNumber.from('100'), + false /* useArbBlock */, + true /* staging */, + false /* verify mercury response */, + ) + + const arbOracleCode = await ethers.provider.send('eth_getCode', [ + mockArbGasInfo.address, + ]) + await ethers.provider.send('hardhat_setCode', [ + '0x000000000000000000000000000000000000006C', + arbOracleCode, + ]) + + const optOracleCode = await ethers.provider.send('eth_getCode', [ + mockOVMGasPriceOracle.address, + ]) + await ethers.provider.send('hardhat_setCode', [ + '0x420000000000000000000000000000000000000F', + optOracleCode, + ]) + + const mockArbSys = await new MockArbSysFactory(owner).deploy() + const arbSysCode = await ethers.provider.send('eth_getCode', [ + mockArbSys.address, + ]) + await ethers.provider.send('hardhat_setCode', [ + '0x0000000000000000000000000000000000000064', + arbSysCode, + ]) + + config = { + paymentPremiumPPB, + flatFeeMicroLink, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + } + + baseConfig = [ + signerAddresses, + keeperAddresses, + f, + encodeConfig(config), + offchainVersion, + offchainBytes, + ] + + registry = await deployRegistry21( + owner, + Mode.DEFAULT, + linkToken.address, + linkEthFeed.address, + gasPriceFeed.address, + ) + + arbRegistry = await deployRegistry21( + owner, + Mode.ARBITRUM, + linkToken.address, + linkEthFeed.address, + gasPriceFeed.address, + ) + + opRegistry = await deployRegistry21( + owner, + Mode.OPTIMISM, + linkToken.address, + linkEthFeed.address, + gasPriceFeed.address, + ) + + mgRegistry = await deployRegistry21( + owner, + Mode.DEFAULT, + linkToken.address, + linkEthFeed.address, + gasPriceFeed.address, + ) + + blankRegistry = await deployRegistry21( + owner, + Mode.DEFAULT, + linkToken.address, + linkEthFeed.address, + gasPriceFeed.address, + ) + + registryConditionalOverhead = await registry.getConditionalGasOverhead() + registryLogOverhead = await registry.getLogGasOverhead() + registryPerSignerGasOverhead = await registry.getPerSignerGasOverhead() + registryPerPerformByteGasOverhead = + await registry.getPerPerformByteGasOverhead() + cancellationDelay = (await registry.getCancellationDelay()).toNumber() + + for (const reg of [registry, arbRegistry, opRegistry, mgRegistry]) { + await reg.connect(owner).setConfig(...baseConfig) + await reg.connect(owner).setPayees(payees) + await linkToken.connect(admin).approve(reg.address, toWei('1000')) + await linkToken.connect(owner).approve(reg.address, toWei('1000')) + } + + mock = await upkeepMockFactory.deploy() + await linkToken + .connect(owner) + .transfer(await admin.getAddress(), toWei('1000')) + let tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + upkeepId = await getUpkeepID(tx) + + autoFunderUpkeep = await upkeepAutoFunderFactory + .connect(owner) + .deploy(linkToken.address, registry.address) + tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + autoFunderUpkeep.address, + performGas, + autoFunderUpkeep.address, + randomBytes, + '0x', + ) + afUpkeepId = await getUpkeepID(tx) + + ltUpkeep = await deployMockContract(owner, ILogAutomationactory.abi) + tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)']( + ltUpkeep.address, + performGas, + await admin.getAddress(), + Trigger.LOG, + '0x', + logTriggerConfig, + emptyBytes, + ) + logUpkeepId = await getUpkeepID(tx) + + await autoFunderUpkeep.setUpkeepId(afUpkeepId) + // Give enough funds for upkeep as well as to the upkeep contract + await linkToken + .connect(owner) + .transfer(autoFunderUpkeep.address, toWei('1000')) + + tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + streamsLookupUpkeep.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + streamsLookupUpkeepId = await getUpkeepID(tx) + } + + const getMultipleUpkeepsDeployedAndFunded = async ( + numPassingConditionalUpkeeps: number, + numPassingLogUpkeeps: number, + numFailingUpkeeps: number, + ) => { + const passingConditionalUpkeepIds = [] + const passingLogUpkeepIds = [] + const failingUpkeepIds = [] + for (let i = 0; i < numPassingConditionalUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(BigNumber.from('0')) + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const condUpkeepId = await getUpkeepID(tx) + passingConditionalUpkeepIds.push(condUpkeepId) + + // Add funds to passing upkeeps + await registry.connect(admin).addFunds(condUpkeepId, toWei('100')) + } + for (let i = 0; i < numPassingLogUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(BigNumber.from('0')) + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + Trigger.LOG, + '0x', + logTriggerConfig, + emptyBytes, + ) + const logUpkeepId = await getUpkeepID(tx) + passingLogUpkeepIds.push(logUpkeepId) + + // Add funds to passing upkeeps + await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) + } + for (let i = 0; i < numFailingUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(BigNumber.from('0')) + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const failingUpkeepId = await getUpkeepID(tx) + failingUpkeepIds.push(failingUpkeepId) + } + return { + passingConditionalUpkeepIds, + passingLogUpkeepIds, + failingUpkeepIds, + } + } + + beforeEach(async () => { + await loadFixture(setup) + }) + + describe('#transmit', () => { + const fArray = [1, 5, 10] + + it('reverts when registry is paused', async () => { + await registry.connect(owner).pause() + await evmRevert( + getTransmitTx(registry, keeper1, [upkeepId]), + 'RegistryPaused()', + ) + }) + + it('reverts when called by non active transmitter', async () => { + await evmRevert( + getTransmitTx(registry, payee1, [upkeepId]), + 'OnlyActiveTransmitters()', + ) + }) + + it('reverts when report data lengths mismatches', async () => { + const upkeepIds = [] + const gasLimits: BigNumber[] = [] + const triggers: string[] = [] + const performDatas = [] + + upkeepIds.push(upkeepId) + gasLimits.push(performGas) + triggers.push('0x') + performDatas.push('0x') + // Push an extra perform data + performDatas.push('0x') + + const report = encodeReport({ + fastGasWei: 0, + linkNative: 0, + upkeepIds, + gasLimits, + triggers, + performDatas, + }) + + await evmRevert( + getTransmitTxWithReport(registry, keeper1, report), + 'InvalidReport()', + ) + }) + + it('returns early when invalid upkeepIds are included in report', async () => { + const tx = await getTransmitTx(registry, keeper1, [ + upkeepId.add(BigNumber.from('1')), + ]) + + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) + + it('returns early when upkeep has insufficient funds', async () => { + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + const receipt = await tx.wait() + const insufficientFundsUpkeepReportLogs = + parseInsufficientFundsUpkeepReportLogs(receipt) + // exactly 1 InsufficientFundsUpkeepReportLogs log should be emitted + assert.equal(insufficientFundsUpkeepReportLogs.length, 1) + }) + + it('permits retrying log triggers after funds are added', async () => { + const txHash = ethers.utils.randomBytes(32) + let tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { + txHash, + logIndex: 0, + }) + let receipt = await tx.wait() + const insufficientFundsLogs = + parseInsufficientFundsUpkeepReportLogs(receipt) + assert.equal(insufficientFundsLogs.length, 1) + registry.connect(admin).addFunds(logUpkeepId, toWei('100')) + tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { + txHash, + logIndex: 0, + }) + receipt = await tx.wait() + const performedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal(performedLogs.length, 1) + }) + + context('When the upkeep is funded', async () => { + beforeEach(async () => { + // Fund the upkeep + await Promise.all([ + registry.connect(admin).addFunds(upkeepId, toWei('100')), + registry.connect(admin).addFunds(logUpkeepId, toWei('100')), + ]) + }) + + it('handles duplicate upkeepIDs', async () => { + const tests: [string, BigNumber, number, number][] = [ + // [name, upkeep, num stale, num performed] + ['conditional', upkeepId, 1, 1], // checkBlocks must be sequential + ['log-trigger', logUpkeepId, 0, 2], // logs are deduped based on the "trigger ID" + ] + for (const [type, id, nStale, nPerformed] of tests) { + const tx = await getTransmitTx(registry, keeper1, [id, id]) + const receipt = await tx.wait() + const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + staleUpkeepReport.length, + nStale, + `wrong log count for ${type} upkeep`, + ) + assert.equal( + upkeepPerformedLogs.length, + nPerformed, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('handles duplicate log triggers', async () => { + const logBlockHash = ethers.utils.randomBytes(32) + const txHash = ethers.utils.randomBytes(32) + const logIndex = 0 + const expectedDedupKey = ethers.utils.solidityKeccak256( + ['uint256', 'bytes32', 'bytes32', 'uint32'], + [logUpkeepId, logBlockHash, txHash, logIndex], + ) + assert.isFalse(await registry.hasDedupKey(expectedDedupKey)) + const tx = await getTransmitTx( + registry, + keeper1, + [logUpkeepId, logUpkeepId], + { logBlockHash, txHash, logIndex }, // will result in the same dedup key + ) + const receipt = await tx.wait() + const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal(staleUpkeepReport.length, 1) + assert.equal(upkeepPerformedLogs.length, 1) + assert.isTrue(await registry.hasDedupKey(expectedDedupKey)) + await expect(tx) + .to.emit(registry, 'DedupKeyAdded') + .withArgs(expectedDedupKey) + }) + + it('returns early when check block number is less than last perform (block)', async () => { + // First perform an upkeep to put last perform block number on upkeep state + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + await tx.wait() + const lastPerformed = (await registry.getUpkeep(upkeepId)) + .lastPerformedBlockNumber + const lastPerformBlock = await ethers.provider.getBlock(lastPerformed) + assert.equal(lastPerformed.toString(), tx.blockNumber?.toString()) + // Try to transmit a report which has checkBlockNumber = lastPerformed-1, should result in stale report + const transmitTx = await getTransmitTx(registry, keeper1, [upkeepId], { + checkBlockNum: lastPerformBlock.number - 1, + checkBlockHash: lastPerformBlock.parentHash, + }) + const receipt = await transmitTx.wait() + const staleUpkeepReportLogs = parseStaleUpkeepReportLogs(receipt) + // exactly 1 StaleUpkeepReportLogs log should be emitted + assert.equal(staleUpkeepReportLogs.length, 1) + }) + + it('handles case when check block hash does not match', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + // Try to transmit a report which has incorrect checkBlockHash + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number - 1, + checkBlockHash: latestBlock.hash, // should be latestBlock.parentHash + }) + + const receipt = await tx.wait() + const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('handles case when check block number is older than 256 blocks', async () => { + for (let i = 0; i < 256; i++) { + await ethers.provider.send('evm_mine', []) + } + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + const old = await ethers.provider.getBlock(latestBlock.number - 256) + // Try to transmit a report which has incorrect checkBlockHash + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: old.number, + checkBlockHash: old.hash, + }) + + const receipt = await tx.wait() + const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('allows bypassing reorg protection with empty blockhash', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number, + checkBlockHash: emptyBytes32, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + upkeepPerformedLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('allows very old trigger block numbers when bypassing reorg protection with empty blockhash', async () => { + // mine enough blocks so that blockhash(1) is unavailable + for (let i = 0; i <= 256; i++) { + await ethers.provider.send('evm_mine', []) + } + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: 1, + checkBlockHash: emptyBytes32, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + upkeepPerformedLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('returns early when future block number is provided as trigger, irrespective of blockhash being present', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + + // Should fail when blockhash is empty + let tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number + 100, + checkBlockHash: emptyBytes32, + }) + let receipt = await tx.wait() + let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + + // Should also fail when blockhash is not empty + tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number + 100, + checkBlockHash: latestBlock.hash, + }) + receipt = await tx.wait() + reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('returns early when upkeep is cancelled and cancellation delay has gone', async () => { + const latestBlockReport = await makeLatestBlockReport([upkeepId]) + await registry.connect(admin).cancelUpkeep(upkeepId) + + for (let i = 0; i < cancellationDelay; i++) { + await ethers.provider.send('evm_mine', []) + } + + const tx = await getTransmitTxWithReport( + registry, + keeper1, + latestBlockReport, + ) + + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) + + it('does not revert if the target cannot execute', async () => { + await mock.setCanPerform(false) + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const success = upkeepPerformedLog.args.success + assert.equal(success, false) + }) + + it('does not revert if the target runs out of gas', async () => { + await mock.setCanPerform(false) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + performGas: 10, // too little gas + }) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const success = upkeepPerformedLog.args.success + assert.equal(success, false) + }) + + it('reverts if not enough gas supplied', async () => { + await evmRevert( + getTransmitTx(registry, keeper1, [upkeepId], { + gasLimit: performGas, + }), + ) + }) + + it('executes the data passed to the registry', async () => { + await mock.setCanPerform(true) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + performData: randomBytes, + }) + const receipt = await tx.wait() + + const upkeepPerformedWithABI = [ + 'event UpkeepPerformedWith(bytes upkeepData)', + ] + const iface = new ethers.utils.Interface(upkeepPerformedWithABI) + const parsedLogs = [] + for (let i = 0; i < receipt.logs.length; i++) { + const log = receipt.logs[i] + try { + parsedLogs.push(iface.parseLog(log)) + } catch (e) { + // ignore log + } + } + assert.equal(parsedLogs.length, 1) + assert.equal(parsedLogs[0].args.upkeepData, randomBytes) + }) + + it('uses actual execution price for payment and premium calculation', async () => { + // Actual multiplier is 2, but we set gasPrice to be 1x gasWei + const gasPrice = gasWei.mul(BigNumber.from('1')) + await mock.setCanPerform(true) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + gasPrice, + }) + const receipt = await tx.wait() + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const premium = registryPremiumAfter.sub(registryPremiumBefore) + + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + BigNumber.from('1'), // Not the config multiplier, but the actual gas used + paymentPremiumPPB, + flatFeeMicroLink, + ).total.toString(), + totalPayment.toString(), + ) + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + BigNumber.from('1'), // Not the config multiplier, but the actual gas used + paymentPremiumPPB, + flatFeeMicroLink, + ).premium.toString(), + premium.toString(), + ) + }) + + it('only pays at a rate up to the gas ceiling [ @skip-coverage ]', async () => { + // Actual multiplier is 2, but we set gasPrice to be 10x + const gasPrice = gasWei.mul(BigNumber.from('10')) + await mock.setCanPerform(true) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + gasPrice, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + gasCeilingMultiplier, // Should be same with exisitng multiplier + paymentPremiumPPB, + flatFeeMicroLink, + ).total.toString(), + totalPayment.toString(), + ) + }) + + it('correctly accounts for l payment', async () => { + await mock.setCanPerform(true) + // Same as MockArbGasInfo.sol + const l1CostWeiArb = BigNumber.from(1000000) + + let tx = await arbRegistry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const testUpkeepId = await getUpkeepID(tx) + await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100')) + + // Do the thing + tx = await getTransmitTx( + arbRegistry, + keeper1, + [testUpkeepId], + + { gasPrice: gasWei.mul('5') }, // High gas price so that it gets capped + ) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + gasCeilingMultiplier, + paymentPremiumPPB, + flatFeeMicroLink, + l1CostWeiArb.div(gasCeilingMultiplier), // Dividing by gasCeilingMultiplier as it gets multiplied later + ).total.toString(), + totalPayment.toString(), + ) + }) + + itMaybe('can self fund', async () => { + const maxPayment = await registry.getMaxPaymentForGas( + Trigger.CONDITION, + performGas, + ) + + // First set auto funding amount to 0 and verify that balance is deducted upon performUpkeep + let initialBalance = toWei('100') + await registry.connect(owner).addFunds(afUpkeepId, initialBalance) + await autoFunderUpkeep.setAutoFundLink(0) + await autoFunderUpkeep.setIsEligible(true) + await getTransmitTx(registry, keeper1, [afUpkeepId]) + + let postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance + assert.isTrue(postUpkeepBalance.lt(initialBalance)) // Balance should be deducted + assert.isTrue(postUpkeepBalance.gte(initialBalance.sub(maxPayment))) // Balance should not be deducted more than maxPayment + + // Now set auto funding amount to 100 wei and verify that the balance increases + initialBalance = postUpkeepBalance + const autoTopupAmount = toWei('100') + await autoFunderUpkeep.setAutoFundLink(autoTopupAmount) + await autoFunderUpkeep.setIsEligible(true) + await getTransmitTx(registry, keeper1, [afUpkeepId]) + + postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance + // Balance should increase by autoTopupAmount and decrease by max maxPayment + assert.isTrue( + postUpkeepBalance.gte( + initialBalance.add(autoTopupAmount).sub(maxPayment), + ), + ) + }) + + it('can self cancel', async () => { + await registry.connect(owner).addFunds(afUpkeepId, toWei('100')) + + await autoFunderUpkeep.setIsEligible(true) + await autoFunderUpkeep.setShouldCancel(true) + + let registration = await registry.getUpkeep(afUpkeepId) + const oldExpiration = registration.maxValidBlocknumber + + // Do the thing + await getTransmitTx(registry, keeper1, [afUpkeepId]) + + // Verify upkeep gets cancelled + registration = await registry.getUpkeep(afUpkeepId) + const newExpiration = registration.maxValidBlocknumber + assert.isTrue(newExpiration.lt(oldExpiration)) + }) + + it('reverts when configDigest mismatches', async () => { + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [emptyBytes32, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) + await evmRevert( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + 'ConfigDigestMismatch()', + ) + }) + + it('reverts with incorrect number of signatures', async () => { + const configDigest = (await registry.getState()).state + .latestConfigDigest + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, signers.slice(0, f + 2)) + await evmRevert( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + 'IncorrectNumberOfSignatures()', + ) + }) + + it('reverts with invalid signature for inactive signers', async () => { + const configDigest = (await registry.getState()).state + .latestConfigDigest + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, [ + new ethers.Wallet(ethers.Wallet.createRandom()), + new ethers.Wallet(ethers.Wallet.createRandom()), + ]) + await evmRevert( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + 'OnlyActiveSigners()', + ) + }) + + it('reverts with invalid signature for duplicated signers', async () => { + const configDigest = (await registry.getState()).state + .latestConfigDigest + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, [signer1, signer1]) + await evmRevert( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + 'DuplicateSigners()', + ) + }) + + itMaybe( + 'has a large enough gas overhead to cover upkeep that use all its gas [ @skip-coverage ]', + async () => { + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + 10, // maximise f to maximise overhead + config, + offchainVersion, + offchainBytes, + ) + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + maxPerformGas, // max allowed gas + await admin.getAddress(), + randomBytes, + '0x', + ) + const testUpkeepId = await getUpkeepID(tx) + await registry.connect(admin).addFunds(testUpkeepId, toWei('100')) + + let performData = '0x' + for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { + performData += '11' + } // max allowed performData + + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(maxPerformGas) + + await getTransmitTx(registry, keeper1, [testUpkeepId], { + gasLimit: maxPerformGas.add(transmitGasOverhead), + numSigners: 11, + performData, + }) // Should not revert + }, + ) + + itMaybe( + 'performs upkeep, deducts payment, updates lastPerformed and emits events', + async () => { + await mock.setCanPerform(true) + + for (const i in fArray) { + const newF = fArray[i] + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + newF, + config, + offchainVersion, + offchainBytes, + ) + const checkBlock = await ethers.provider.getBlock('latest') + + const keeperBefore = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationBefore = await registry.getUpkeep(upkeepId) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const keeperLinkBefore = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkBefore = await linkToken.balanceOf( + registry.address, + ) + + // Do the thing + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + checkBlockNum: checkBlock.number, + checkBlockHash: checkBlock.hash, + numSigners: newF + 1, + }) + + const receipt = await tx.wait() + + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const id = upkeepPerformedLog.args.id + const success = upkeepPerformedLog.args.success + const trigger = upkeepPerformedLog.args.trigger + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + assert.equal(id.toString(), upkeepId.toString()) + assert.equal(success, true) + assert.equal( + trigger, + encodeBlockTrigger({ + blockNum: checkBlock.number, + blockHash: checkBlock.hash, + }), + ) + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + assert.isTrue(totalPayment.gt(BigNumber.from('0'))) + + const keeperAfter = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationAfter = await registry.getUpkeep(upkeepId) + const keeperLinkAfter = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkAfter = await linkToken.balanceOf( + registry.address, + ) + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const premium = registryPremiumAfter.sub(registryPremiumBefore) + // Keeper payment is gasPayment + premium / num keepers + const keeperPayment = totalPayment + .sub(premium) + .add(premium.div(BigNumber.from(keeperAddresses.length))) + + assert.equal( + keeperAfter.balance.sub(keeperPayment).toString(), + keeperBefore.balance.toString(), + ) + assert.equal( + registrationBefore.balance.sub(totalPayment).toString(), + registrationAfter.balance.toString(), + ) + assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) + assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) + + // Amount spent should be updated correctly + assert.equal( + registrationAfter.amountSpent.sub(totalPayment).toString(), + registrationBefore.amountSpent.toString(), + ) + assert.isTrue( + registrationAfter.amountSpent + .sub(registrationBefore.amountSpent) + .eq(registrationBefore.balance.sub(registrationAfter.balance)), + ) + // Last perform block number should be updated + assert.equal( + registrationAfter.lastPerformedBlockNumber.toString(), + tx.blockNumber?.toString(), + ) + + // Latest epoch should be 5 + assert.equal((await registry.getState()).state.latestEpoch, 5) + } + }, + ) + + describeMaybe( + 'Gas benchmarking conditional upkeeps [ @skip-coverage ]', + function () { + const fs = [1, 10] + fs.forEach(function (newF) { + it( + 'When f=' + + newF + + ' calculates gas overhead appropriately within a margin for different scenarios', + async () => { + // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement + let tx = await getTransmitTx(registry, keeper1, [upkeepId]) + await tx.wait() + + // Different test scenarios + let longBytes = '0x' + for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { + longBytes += '11' + } + const upkeepSuccessArray = [true, false] + const performGasArray = [5000, performGas] + const performDataArray = ['0x', longBytes] + + for (const i in upkeepSuccessArray) { + for (const j in performGasArray) { + for (const k in performDataArray) { + const upkeepSuccess = upkeepSuccessArray[i] + const performGas = performGasArray[j] + const performData = performDataArray[k] + + await mock.setCanPerform(upkeepSuccess) + await mock.setPerformGasToBurn(performGas) + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + newF, + config, + offchainVersion, + offchainBytes, + ) + tx = await getTransmitTx(registry, keeper1, [upkeepId], { + numSigners: newF + 1, + performData, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = + parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const upkeepGasUsed = upkeepPerformedLog.args.gasUsed + const chargedGasOverhead = + upkeepPerformedLog.args.gasOverhead + const actualGasOverhead = + receipt.gasUsed.sub(upkeepGasUsed) + + assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) + assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) + + console.log( + 'Gas Benchmarking conditional upkeeps:', + 'upkeepSuccess=', + upkeepSuccess, + 'performGas=', + performGas.toString(), + 'performData length=', + performData.length / 2 - 1, + 'sig verification ( f =', + newF, + '): calculated overhead: ', + chargedGasOverhead.toString(), + ' actual overhead: ', + actualGasOverhead.toString(), + ' margin over gasUsed: ', + chargedGasOverhead.sub(actualGasOverhead).toString(), + ) + + // Overhead should not get capped + const gasOverheadCap = registryConditionalOverhead + .add( + registryPerSignerGasOverhead.mul( + BigNumber.from(newF + 1), + ), + ) + .add( + BigNumber.from( + registryPerPerformByteGasOverhead.toNumber() * + performData.length, + ), + ) + const gasCapMinusOverhead = + gasOverheadCap.sub(chargedGasOverhead) + assert.isTrue( + gasCapMinusOverhead.gt(BigNumber.from(0)), + 'Gas overhead got capped. Verify gas overhead variables in test match those in the registry. To not have the overheads capped increase REGISTRY_GAS_OVERHEAD by atleast ' + + gasCapMinusOverhead.toString(), + ) + // total gas charged should be greater than tx gas but within gasCalculationMargin + assert.isTrue( + chargedGasOverhead.gt(actualGasOverhead), + 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + + actualGasOverhead.sub(chargedGasOverhead).toString(), + ) + + assert.isTrue( + chargedGasOverhead + .sub(actualGasOverhead) + .lt(gasCalculationMargin), + ), + 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + + chargedGasOverhead + .sub(chargedGasOverhead) + .sub(gasCalculationMargin) + .toString() + } + } + } + }, + ) + }) + }, + ) + + describeMaybe( + 'Gas benchmarking log upkeeps [ @skip-coverage ]', + function () { + const fs = [1, 10] + fs.forEach(function (newF) { + it( + 'When f=' + + newF + + ' calculates gas overhead appropriately within a margin', + async () => { + // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement + let tx = await getTransmitTx(registry, keeper1, [logUpkeepId]) + await tx.wait() + const performData = '0x' + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(performGas) + await registry.setConfigTypeSafe( + signerAddresses, + keeperAddresses, + newF, + config, + offchainVersion, + offchainBytes, + ) + tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { + numSigners: newF + 1, + performData, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const upkeepGasUsed = upkeepPerformedLog.args.gasUsed + const chargedGasOverhead = upkeepPerformedLog.args.gasOverhead + const actualGasOverhead = receipt.gasUsed.sub(upkeepGasUsed) + + assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) + assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) + + console.log( + 'Gas Benchmarking log upkeeps:', + 'upkeepSuccess=', + true, + 'performGas=', + performGas.toString(), + 'performData length=', + performData.length / 2 - 1, + 'sig verification ( f =', + newF, + '): calculated overhead: ', + chargedGasOverhead.toString(), + ' actual overhead: ', + actualGasOverhead.toString(), + ' margin over gasUsed: ', + chargedGasOverhead.sub(actualGasOverhead).toString(), + ) + + // Overhead should not get capped + const gasOverheadCap = registryLogOverhead + .add( + registryPerSignerGasOverhead.mul(BigNumber.from(newF + 1)), + ) + .add( + BigNumber.from( + registryPerPerformByteGasOverhead.toNumber() * + performData.length, + ), + ) + const gasCapMinusOverhead = + gasOverheadCap.sub(chargedGasOverhead) + assert.isTrue( + gasCapMinusOverhead.gt(BigNumber.from(0)), + 'Gas overhead got capped. Verify gas overhead variables in test match those in the registry. To not have the overheads capped increase REGISTRY_GAS_OVERHEAD by atleast ' + + gasCapMinusOverhead.toString(), + ) + // total gas charged should be greater than tx gas but within gasCalculationMargin + assert.isTrue( + chargedGasOverhead.gt(actualGasOverhead), + 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + + actualGasOverhead.sub(chargedGasOverhead).toString(), + ) + + assert.isTrue( + chargedGasOverhead + .sub(actualGasOverhead) + .lt(gasCalculationMargin), + ), + 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by atleast ' + + chargedGasOverhead + .sub(chargedGasOverhead) + .sub(gasCalculationMargin) + .toString() + }, + ) + }) + }, + ) + }) + }) + + describeMaybe( + '#transmit with upkeep batches [ @skip-coverage ]', + function () { + const numPassingConditionalUpkeepsArray = [0, 1, 5] + const numPassingLogUpkeepsArray = [0, 1, 5] + const numFailingUpkeepsArray = [0, 3] + + for (let idx = 0; idx < numPassingConditionalUpkeepsArray.length; idx++) { + for (let jdx = 0; jdx < numPassingLogUpkeepsArray.length; jdx++) { + for (let kdx = 0; kdx < numFailingUpkeepsArray.length; kdx++) { + const numPassingConditionalUpkeeps = + numPassingConditionalUpkeepsArray[idx] + const numPassingLogUpkeeps = numPassingLogUpkeepsArray[jdx] + const numFailingUpkeeps = numFailingUpkeepsArray[kdx] + if ( + numPassingConditionalUpkeeps == 0 && + numPassingLogUpkeeps == 0 + ) { + continue + } + it( + '[Conditional:' + + numPassingConditionalUpkeeps + + ',Log:' + + numPassingLogUpkeeps + + ',Failures:' + + numFailingUpkeeps + + '] performs successful upkeeps and does not charge failing upkeeps', + async () => { + const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( + numPassingConditionalUpkeeps, + numPassingLogUpkeeps, + numFailingUpkeeps, + ) + const passingConditionalUpkeepIds = + allUpkeeps.passingConditionalUpkeepIds + const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds + const failingUpkeepIds = allUpkeeps.failingUpkeepIds + + const keeperBefore = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const keeperLinkBefore = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkBefore = await linkToken.balanceOf( + registry.address, + ) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const registrationConditionalPassingBefore = await Promise.all( + passingConditionalUpkeepIds.map(async (id) => { + const reg = await registry.getUpkeep(BigNumber.from(id)) + assert.equal(reg.lastPerformedBlockNumber.toString(), '0') + return reg + }), + ) + const registrationLogPassingBefore = await Promise.all( + passingLogUpkeepIds.map(async (id) => { + const reg = await registry.getUpkeep(BigNumber.from(id)) + assert.equal(reg.lastPerformedBlockNumber.toString(), '0') + return reg + }), + ) + const registrationFailingBefore = await Promise.all( + failingUpkeepIds.map(async (id) => { + const reg = await registry.getUpkeep(BigNumber.from(id)) + assert.equal(reg.lastPerformedBlockNumber.toString(), '0') + return reg + }), + ) + + const tx = await getTransmitTx( + registry, + keeper1, + passingConditionalUpkeepIds.concat( + passingLogUpkeepIds.concat(failingUpkeepIds), + ), + ) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly numPassingUpkeeps Upkeep Performed should be emitted + assert.equal( + upkeepPerformedLogs.length, + numPassingConditionalUpkeeps + numPassingLogUpkeeps, + ) + const insufficientFundsLogs = + parseInsufficientFundsUpkeepReportLogs(receipt) + // exactly numFailingUpkeeps Upkeep Performed should be emitted + assert.equal(insufficientFundsLogs.length, numFailingUpkeeps) + + const keeperAfter = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const keeperLinkAfter = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkAfter = await linkToken.balanceOf( + registry.address, + ) + const registrationConditionalPassingAfter = await Promise.all( + passingConditionalUpkeepIds.map(async (id) => { + return await registry.getUpkeep(BigNumber.from(id)) + }), + ) + const registrationLogPassingAfter = await Promise.all( + passingLogUpkeepIds.map(async (id) => { + return await registry.getUpkeep(BigNumber.from(id)) + }), + ) + const registrationFailingAfter = await Promise.all( + failingUpkeepIds.map(async (id) => { + return await registry.getUpkeep(BigNumber.from(id)) + }), + ) + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const premium = registryPremiumAfter.sub(registryPremiumBefore) + + let netPayment = BigNumber.from('0') + for (let i = 0; i < numPassingConditionalUpkeeps; i++) { + const id = upkeepPerformedLogs[i].args.id + const gasUsed = upkeepPerformedLogs[i].args.gasUsed + const gasOverhead = upkeepPerformedLogs[i].args.gasOverhead + const totalPayment = upkeepPerformedLogs[i].args.totalPayment + + expect(id).to.equal(passingConditionalUpkeepIds[i]) + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + assert.isTrue(totalPayment.gt(BigNumber.from('0'))) + + // Balance should be deducted + assert.equal( + registrationConditionalPassingBefore[i].balance + .sub(totalPayment) + .toString(), + registrationConditionalPassingAfter[i].balance.toString(), + ) + + // Amount spent should be updated correctly + assert.equal( + registrationConditionalPassingAfter[i].amountSpent + .sub(totalPayment) + .toString(), + registrationConditionalPassingBefore[ + i + ].amountSpent.toString(), + ) + + // Last perform block number should be updated + assert.equal( + registrationConditionalPassingAfter[ + i + ].lastPerformedBlockNumber.toString(), + tx.blockNumber?.toString(), + ) + + netPayment = netPayment.add(totalPayment) + } + + for (let i = 0; i < numPassingLogUpkeeps; i++) { + const id = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .id + const gasUsed = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasUsed + const gasOverhead = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasOverhead + const totalPayment = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .totalPayment + + expect(id).to.equal(passingLogUpkeepIds[i]) + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + assert.isTrue(totalPayment.gt(BigNumber.from('0'))) + + // Balance should be deducted + assert.equal( + registrationLogPassingBefore[i].balance + .sub(totalPayment) + .toString(), + registrationLogPassingAfter[i].balance.toString(), + ) + + // Amount spent should be updated correctly + assert.equal( + registrationLogPassingAfter[i].amountSpent + .sub(totalPayment) + .toString(), + registrationLogPassingBefore[i].amountSpent.toString(), + ) + + // Last perform block number should not be updated for log triggers + assert.equal( + registrationLogPassingAfter[ + i + ].lastPerformedBlockNumber.toString(), + '0', + ) + + netPayment = netPayment.add(totalPayment) + } + + for (let i = 0; i < numFailingUpkeeps; i++) { + // InsufficientFunds log should be emitted + const id = insufficientFundsLogs[i].args.id + expect(id).to.equal(failingUpkeepIds[i]) + + // Balance and amount spent should be same + assert.equal( + registrationFailingBefore[i].balance.toString(), + registrationFailingAfter[i].balance.toString(), + ) + assert.equal( + registrationFailingBefore[i].amountSpent.toString(), + registrationFailingAfter[i].amountSpent.toString(), + ) + + // Last perform block number should not be updated + assert.equal( + registrationFailingAfter[ + i + ].lastPerformedBlockNumber.toString(), + '0', + ) + } + + // Keeper payment is gasPayment + premium / num keepers + const keeperPayment = netPayment + .sub(premium) + .add(premium.div(BigNumber.from(keeperAddresses.length))) + + // Keeper should be paid net payment for all passed upkeeps + assert.equal( + keeperAfter.balance.sub(keeperPayment).toString(), + keeperBefore.balance.toString(), + ) + + assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) + assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) + }, + ) + + it( + '[Conditional:' + + numPassingConditionalUpkeeps + + ',Log' + + numPassingLogUpkeeps + + ',Failures:' + + numFailingUpkeeps + + '] splits gas overhead appropriately among performed upkeeps [ @skip-coverage ]', + async () => { + const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( + numPassingConditionalUpkeeps, + numPassingLogUpkeeps, + numFailingUpkeeps, + ) + const passingConditionalUpkeepIds = + allUpkeeps.passingConditionalUpkeepIds + const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds + const failingUpkeepIds = allUpkeeps.failingUpkeepIds + + // Perform the upkeeps once to remove non-zero storage slots and have predictable gas measurement + let tx = await getTransmitTx( + registry, + keeper1, + passingConditionalUpkeepIds.concat( + passingLogUpkeepIds.concat(failingUpkeepIds), + ), + ) + + await tx.wait() + + // Do the actual thing + + tx = await getTransmitTx( + registry, + keeper1, + passingConditionalUpkeepIds.concat( + passingLogUpkeepIds.concat(failingUpkeepIds), + ), + ) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly numPassingUpkeeps Upkeep Performed should be emitted + assert.equal( + upkeepPerformedLogs.length, + numPassingConditionalUpkeeps + numPassingLogUpkeeps, + ) + + const gasConditionalOverheadCap = + registryConditionalOverhead.add( + registryPerSignerGasOverhead.mul(BigNumber.from(f + 1)), + ) + const gasLogOverheadCap = registryLogOverhead.add( + registryPerSignerGasOverhead.mul(BigNumber.from(f + 1)), + ) + + const overheadCanGetCapped = + numFailingUpkeeps > 0 && + numPassingConditionalUpkeeps <= 1 && + numPassingLogUpkeeps <= 1 + // Can happen if there are failing upkeeps and only 1 successful upkeep of each type + let netGasUsedPlusOverhead = BigNumber.from('0') + + for (let i = 0; i < numPassingConditionalUpkeeps; i++) { + const gasUsed = upkeepPerformedLogs[i].args.gasUsed + const gasOverhead = upkeepPerformedLogs[i].args.gasOverhead + + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + + // Overhead should not exceed capped + assert.isTrue(gasOverhead.lte(gasConditionalOverheadCap)) + + // Overhead should be same for every upkeep since they have equal performData, hence same caps + assert.isTrue( + gasOverhead.eq(upkeepPerformedLogs[0].args.gasOverhead), + ) + + netGasUsedPlusOverhead = netGasUsedPlusOverhead + .add(gasUsed) + .add(gasOverhead) + } + for (let i = 0; i < numPassingLogUpkeeps; i++) { + const gasUsed = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasUsed + const gasOverhead = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasOverhead + + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + + // Overhead should not exceed capped + assert.isTrue(gasOverhead.lte(gasLogOverheadCap)) + + // Overhead should be same for every upkeep since they have equal performData, hence same caps + assert.isTrue( + gasOverhead.eq( + upkeepPerformedLogs[numPassingConditionalUpkeeps].args + .gasOverhead, + ), + ) + + netGasUsedPlusOverhead = netGasUsedPlusOverhead + .add(gasUsed) + .add(gasOverhead) + } + + const overheadsGotCapped = + (numPassingConditionalUpkeeps > 0 && + upkeepPerformedLogs[0].args.gasOverhead.eq( + gasConditionalOverheadCap, + )) || + (numPassingLogUpkeeps > 0 && + upkeepPerformedLogs[ + numPassingConditionalUpkeeps + ].args.gasOverhead.eq(gasLogOverheadCap)) + // Should only get capped in certain scenarios + if (overheadsGotCapped) { + assert.isTrue( + overheadCanGetCapped, + 'Gas overhead got capped. Verify gas overhead variables in test match those in the registry. To not have the overheads capped increase REGISTRY_GAS_OVERHEAD', + ) + } + + console.log( + 'Gas Benchmarking - batching (passedConditionalUpkeeps: ', + numPassingConditionalUpkeeps, + 'passedLogUpkeeps:', + numPassingLogUpkeeps, + 'failedUpkeeps:', + numFailingUpkeeps, + '): ', + 'overheadsGotCapped', + overheadsGotCapped, + numPassingConditionalUpkeeps > 0 + ? 'calculated conditional overhead' + : '', + numPassingConditionalUpkeeps > 0 + ? upkeepPerformedLogs[0].args.gasOverhead.toString() + : '', + numPassingLogUpkeeps > 0 ? 'calculated log overhead' : '', + numPassingLogUpkeeps > 0 + ? upkeepPerformedLogs[ + numPassingConditionalUpkeeps + ].args.gasOverhead.toString() + : '', + ' margin over gasUsed', + netGasUsedPlusOverhead.sub(receipt.gasUsed).toString(), + ) + + // If overheads dont get capped then total gas charged should be greater than tx gas + // We don't check whether the net is within gasMargin as the margin changes with numFailedUpkeeps + // Which is ok, as long as individual gas overhead is capped + if (!overheadsGotCapped) { + assert.isTrue( + netGasUsedPlusOverhead.gt(receipt.gasUsed), + 'Gas overhead is too low, increase ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD', + ) + } + }, + ) + } + } + } + + it('has enough perform gas overhead for large batches [ @skip-coverage ]', async () => { + const numUpkeeps = 20 + const upkeepIds: BigNumber[] = [] + let totalPerformGas = BigNumber.from('0') + for (let i = 0; i < numUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const testUpkeepId = await getUpkeepID(tx) + upkeepIds.push(testUpkeepId) + + // Add funds to passing upkeeps + await registry.connect(owner).addFunds(testUpkeepId, toWei('10')) + + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(performGas) + + totalPerformGas = totalPerformGas.add(performGas) + } + + // Should revert with no overhead added + await evmRevert( + getTransmitTx(registry, keeper1, upkeepIds, { + gasLimit: totalPerformGas, + }), + ) + // Should not revert with overhead added + await getTransmitTx(registry, keeper1, upkeepIds, { + gasLimit: totalPerformGas.add(transmitGasOverhead), + }) + }) + + it('splits l2 payment among performed upkeeps', async () => { + const numUpkeeps = 7 + const upkeepIds: BigNumber[] = [] + // Same as MockArbGasInfo.sol + const l1CostWeiArb = BigNumber.from(1000000) + + for (let i = 0; i < numUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + const tx = await arbRegistry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const testUpkeepId = await getUpkeepID(tx) + upkeepIds.push(testUpkeepId) + + // Add funds to passing upkeeps + await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100')) + } + + // Do the thing + const tx = await getTransmitTx( + arbRegistry, + keeper1, + upkeepIds, + + { gasPrice: gasWei.mul('5') }, // High gas price so that it gets capped + ) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly numPassingUpkeeps Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, numUpkeeps) + + // Verify the payment calculation in upkeepPerformed[0] + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + gasCeilingMultiplier, + paymentPremiumPPB, + flatFeeMicroLink, + l1CostWeiArb.div(gasCeilingMultiplier), // Dividing by gasCeilingMultiplier as it gets multiplied later + BigNumber.from(numUpkeeps), + ).total.toString(), + totalPayment.toString(), + ) + }) + }, + ) + + describe('#recoverFunds', () => { + const sent = toWei('7') + + beforeEach(async () => { + await linkToken.connect(admin).approve(registry.address, toWei('100')) + await linkToken + .connect(owner) + .transfer(await keeper1.getAddress(), toWei('1000')) + + // add funds to upkeep 1 and perform and withdraw some payment + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + emptyBytes, + emptyBytes, + ) + + const id1 = await getUpkeepID(tx) + await registry.connect(admin).addFunds(id1, toWei('5')) + + await getTransmitTx(registry, keeper1, [id1]) + await getTransmitTx(registry, keeper2, [id1]) + await getTransmitTx(registry, keeper3, [id1]) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + + // transfer funds directly to the registry + await linkToken.connect(keeper1).transfer(registry.address, sent) + + // add funds to upkeep 2 and perform and withdraw some payment + const tx2 = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + emptyBytes, + emptyBytes, + ) + const id2 = await getUpkeepID(tx2) + await registry.connect(admin).addFunds(id2, toWei('5')) + + await getTransmitTx(registry, keeper1, [id2]) + await getTransmitTx(registry, keeper2, [id2]) + await getTransmitTx(registry, keeper3, [id2]) + + await registry + .connect(payee2) + .withdrawPayment( + await keeper2.getAddress(), + await nonkeeper.getAddress(), + ) + + // transfer funds using onTokenTransfer + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [id2]) + await linkToken + .connect(owner) + .transferAndCall(registry.address, toWei('1'), data) + + // withdraw some funds + await registry.connect(owner).cancelUpkeep(id1) + await registry + .connect(admin) + .withdrawFunds(id1, await nonkeeper.getAddress()) + }) + + it('reverts if not called by owner', async () => { + await evmRevert( + registry.connect(keeper1).recoverFunds(), + 'Only callable by owner', + ) + }) + + it('allows any funds that have been accidentally transfered to be moved', async () => { + const balanceBefore = await linkToken.balanceOf(registry.address) + const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) + + await registry.connect(owner).recoverFunds() + + const balanceAfter = await linkToken.balanceOf(registry.address) + const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) + + assert.isTrue(balanceBefore.eq(balanceAfter.add(sent))) + assert.isTrue(ownerAfter.eq(ownerBefore.add(sent))) + }) + }) + + describe('#getMinBalanceForUpkeep / #checkUpkeep / #transmit', () => { + it('calculates the minimum balance appropriately', async () => { + await mock.setCanCheck(true) + + const oneWei = BigNumber.from(1) + const minBalance = await registry.getMinBalanceForUpkeep(upkeepId) + const tooLow = minBalance.sub(oneWei) + + await registry.connect(admin).addFunds(upkeepId, tooLow) + let checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.INSUFFICIENT_BALANCE, + ) + + await registry.connect(admin).addFunds(upkeepId, oneWei) + checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + assert.equal(checkUpkeepResult.upkeepNeeded, true) + }) + + it('uses maxPerformData size in checkUpkeep but actual performDataSize in transmit', async () => { + const tx1 = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const upkeepID1 = await getUpkeepID(tx1) + const tx2 = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + const upkeepID2 = await getUpkeepID(tx2) + await mock.setCanCheck(true) + await mock.setCanPerform(true) + + // upkeep 1 is underfunded, 2 is fully funded + const minBalance1 = ( + await registry.getMinBalanceForUpkeep(upkeepID1) + ).sub(1) + const minBalance2 = await registry.getMinBalanceForUpkeep(upkeepID2) + await registry.connect(owner).addFunds(upkeepID1, minBalance1) + await registry.connect(owner).addFunds(upkeepID2, minBalance2) + + // upkeep 1 check should return false, 2 should return true + let checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepID1) + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.INSUFFICIENT_BALANCE, + ) + + checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepID2) + assert.equal(checkUpkeepResult.upkeepNeeded, true) + + // upkeep 1 perform should return with insufficient balance using max performData size + let maxPerformData = '0x' + for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { + maxPerformData += '11' + } + + const tx = await getTransmitTx(registry, keeper1, [upkeepID1], { + gasPrice: gasWei.mul(gasCeilingMultiplier), + performData: maxPerformData, + }) + + const receipt = await tx.wait() + const insufficientFundsUpkeepReportLogs = + parseInsufficientFundsUpkeepReportLogs(receipt) + // exactly 1 InsufficientFundsUpkeepReportLogs log should be emitted + assert.equal(insufficientFundsUpkeepReportLogs.length, 1) + + // upkeep 1 perform should succeed with empty performData + await getTransmitTx(registry, keeper1, [upkeepID1], { + gasPrice: gasWei.mul(gasCeilingMultiplier), + }), + // upkeep 2 perform should succeed with max performData size + await getTransmitTx(registry, keeper1, [upkeepID2], { + gasPrice: gasWei.mul(gasCeilingMultiplier), + performData: maxPerformData, + }) + }) + }) + + describe('#withdrawFunds', () => { + let upkeepId2: BigNumber + + beforeEach(async () => { + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + randomBytes, + '0x', + ) + upkeepId2 = await getUpkeepID(tx) + + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + await registry.connect(admin).addFunds(upkeepId2, toWei('100')) + + // Do a perform so that upkeep is charged some amount + await getTransmitTx(registry, keeper1, [upkeepId]) + await getTransmitTx(registry, keeper1, [upkeepId2]) + }) + + it('reverts if called on a non existing ID', async () => { + await evmRevert( + registry + .connect(admin) + .withdrawFunds(upkeepId.add(1), await payee1.getAddress()), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if called by anyone but the admin', async () => { + await evmRevert( + registry + .connect(owner) + .withdrawFunds(upkeepId, await payee1.getAddress()), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if called on an uncanceled upkeep', async () => { + await evmRevert( + registry + .connect(admin) + .withdrawFunds(upkeepId, await payee1.getAddress()), + 'UpkeepNotCanceled()', + ) + }) + + it('reverts if called with the 0 address', async () => { + await evmRevert( + registry.connect(admin).withdrawFunds(upkeepId, zeroAddress), + 'InvalidRecipient()', + ) + }) + + describe('after the registration is paused, then cancelled', () => { + it('allows the admin to withdraw', async () => { + const balance = await registry.getBalance(upkeepId) + const payee = await payee1.getAddress() + await registry.connect(admin).pauseUpkeep(upkeepId) + await registry.connect(owner).cancelUpkeep(upkeepId) + await expect(() => + registry.connect(admin).withdrawFunds(upkeepId, payee), + ).to.changeTokenBalance(linkToken, payee1, balance) + }) + }) + + describe('after the registration is cancelled', () => { + beforeEach(async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + await registry.connect(owner).cancelUpkeep(upkeepId2) + }) + + it('can be called successively on two upkeeps', async () => { + await registry + .connect(admin) + .withdrawFunds(upkeepId, await payee1.getAddress()) + await registry + .connect(admin) + .withdrawFunds(upkeepId2, await payee1.getAddress()) + }) + + it('moves the funds out and updates the balance and emits an event', async () => { + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const registryBefore = await linkToken.balanceOf(registry.address) + + let registration = await registry.getUpkeep(upkeepId) + const previousBalance = registration.balance + + const tx = await registry + .connect(admin) + .withdrawFunds(upkeepId, await payee1.getAddress()) + await expect(tx) + .to.emit(registry, 'FundsWithdrawn') + .withArgs(upkeepId, previousBalance, await payee1.getAddress()) + + const payee1After = await linkToken.balanceOf(await payee1.getAddress()) + const registryAfter = await linkToken.balanceOf(registry.address) + + assert.isTrue(payee1Before.add(previousBalance).eq(payee1After)) + assert.isTrue(registryBefore.sub(previousBalance).eq(registryAfter)) + + registration = await registry.getUpkeep(upkeepId) + assert.equal(0, registration.balance.toNumber()) + }) + }) + }) + + describe('#simulatePerformUpkeep', () => { + it('reverts if called by non zero address', async () => { + await evmRevert( + registry + .connect(await owner.getAddress()) + .callStatic.simulatePerformUpkeep(upkeepId, '0x'), + 'OnlySimulatedBackend()', + ) + }) + + it('reverts when registry is paused', async () => { + await registry.connect(owner).pause() + await evmRevert( + registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x'), + 'RegistryPaused()', + ) + }) + + it('returns false and gasUsed when perform fails', async () => { + await mock.setCanPerform(false) + + const simulatePerformResult = await registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x') + + assert.equal(simulatePerformResult.success, false) + assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns true, gasUsed, and performGas when perform succeeds', async () => { + await mock.setCanPerform(true) + + const simulatePerformResult = await registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x') + + assert.equal(simulatePerformResult.success, true) + assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns correct amount of gasUsed when perform succeeds', async () => { + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(performGas) + + const simulatePerformResult = await registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x') + + assert.equal(simulatePerformResult.success, true) + // Full execute gas should be used, with some performGasBuffer(1000) + assert.isTrue( + simulatePerformResult.gasUsed.gt( + performGas.sub(BigNumber.from('1000')), + ), + ) + }) + }) + + describe('#checkUpkeep', () => { + it('reverts if called by non zero address', async () => { + await evmRevert( + registry + .connect(await owner.getAddress()) + .callStatic['checkUpkeep(uint256)'](upkeepId), + 'OnlySimulatedBackend()', + ) + }) + + it('returns false and error code if the upkeep is cancelled by admin', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_CANCELLED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the upkeep is cancelled by owner', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_CANCELLED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the registry is paused', async () => { + await registry.connect(owner).pause() + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.REGISTRY_PAUSED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the upkeep is paused', async () => { + await registry.connect(admin).pauseUpkeep(upkeepId) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_PAUSED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if user is out of funds', async () => { + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.INSUFFICIENT_BALANCE, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + context('when the registration is funded', () => { + beforeEach(async () => { + await linkToken.connect(admin).approve(registry.address, toWei('200')) + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) + }) + + it('returns false, error code, and revert data if the target check reverts', async () => { + await mock.setShouldRevertCheck(true) + await mock.setCheckRevertReason( + 'custom revert error, clever way to insert offchain data', + ) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + assert.equal(checkUpkeepResult.upkeepNeeded, false) + + const revertReasonBytes = `0x${checkUpkeepResult.performData.slice(10)}` // remove sighash + assert.equal( + ethers.utils.defaultAbiCoder.decode(['string'], revertReasonBytes)[0], + 'custom revert error, clever way to insert offchain data', + ) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.TARGET_CHECK_REVERTED, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + // Feed data should be returned here + assert.isTrue(checkUpkeepResult.fastGasWei.gt(BigNumber.from('0'))) + assert.isTrue(checkUpkeepResult.linkNative.gt(BigNumber.from('0'))) + }) + + it('returns false, error code, and no revert data if the target check revert data exceeds maxRevertDataSize', async () => { + await mock.setShouldRevertCheck(true) + let longRevertReason = '' + for (let i = 0; i <= maxRevertDataSize.toNumber(); i++) { + longRevertReason += 'x' + } + await mock.setCheckRevertReason(longRevertReason) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + assert.equal(checkUpkeepResult.upkeepNeeded, false) + + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the upkeep is not needed', async () => { + await mock.setCanCheck(false) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_NOT_NEEDED, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the performData exceeds limit', async () => { + let longBytes = '0x' + for (let i = 0; i < 5000; i++) { + longBytes += '1' + } + await mock.setCanCheck(true) + await mock.setPerformData(longBytes) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns true with gas used if the target can execute', async () => { + await mock.setCanCheck(true) + await mock.setPerformData(randomBytes) + + const latestBlock = await ethers.provider.getBlock('latest') + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId, { + blockTag: latestBlock.number, + }) + + assert.equal(checkUpkeepResult.upkeepNeeded, true) + assert.equal(checkUpkeepResult.performData, randomBytes) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.NONE, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + assert.isTrue(checkUpkeepResult.fastGasWei.eq(gasWei)) + assert.isTrue(checkUpkeepResult.linkNative.eq(linkEth)) + }) + + it('calls checkLog for log-trigger upkeeps', async () => { + const log: Log = { + index: 0, + timestamp: 0, + txHash: ethers.utils.randomBytes(32), + blockNumber: 100, + blockHash: ethers.utils.randomBytes(32), + source: randomAddress(), + topics: [ethers.utils.randomBytes(32), ethers.utils.randomBytes(32)], + data: ethers.utils.randomBytes(1000), + } + + await ltUpkeep.mock.checkLog.withArgs(log, '0x').returns(true, '0x1234') + + const checkData = encodeLog(log) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256,bytes)'](logUpkeepId, checkData) + + expect(checkUpkeepResult.upkeepNeeded).to.be.true + expect(checkUpkeepResult.performData).to.equal('0x1234') + }) + + itMaybe( + 'has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]', + async () => { + await mock.setCanCheck(true) + await mock.setCheckGasToBurn(checkGasLimit) + const gas = checkGasLimit.add(checkGasOverhead) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId, { + gasLimit: gas, + }) + + assert.equal(checkUpkeepResult.upkeepNeeded, true) + }, + ) + }) + }) + + describe('#addFunds', () => { + const amount = toWei('1') + + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry.connect(keeper1).addFunds(upkeepId.add(1), amount), + 'UpkeepCancelled()', + ) + }) + + it('adds to the balance of the registration', async () => { + await registry.connect(admin).addFunds(upkeepId, amount) + const registration = await registry.getUpkeep(upkeepId) + assert.isTrue(amount.eq(registration.balance)) + }) + + it('lets anyone add funds to an upkeep not just admin', async () => { + await linkToken.connect(owner).transfer(await payee1.getAddress(), amount) + await linkToken.connect(payee1).approve(registry.address, amount) + + await registry.connect(payee1).addFunds(upkeepId, amount) + const registration = await registry.getUpkeep(upkeepId) + assert.isTrue(amount.eq(registration.balance)) + }) + + it('emits a log', async () => { + const tx = await registry.connect(admin).addFunds(upkeepId, amount) + await expect(tx) + .to.emit(registry, 'FundsAdded') + .withArgs(upkeepId, await admin.getAddress(), amount) + }) + + it('reverts if the upkeep is canceled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + await evmRevert( + registry.connect(keeper1).addFunds(upkeepId, amount), + 'UpkeepCancelled()', + ) + }) + }) + + describe('#getActiveUpkeepIDs', () => { + it('reverts if startIndex is out of bounds ', async () => { + await evmRevert( + registry.getActiveUpkeepIDs(numUpkeeps, 0), + 'IndexOutOfRange()', + ) + await evmRevert( + registry.getActiveUpkeepIDs(numUpkeeps + 1, 0), + 'IndexOutOfRange()', + ) + }) + + it('returns upkeep IDs bounded by maxCount', async () => { + let upkeepIds = await registry.getActiveUpkeepIDs(0, 1) + assert(upkeepIds.length == 1) + assert(upkeepIds[0].eq(upkeepId)) + upkeepIds = await registry.getActiveUpkeepIDs(1, 3) + assert(upkeepIds.length == 3) + expect(upkeepIds).to.deep.equal([ + afUpkeepId, + logUpkeepId, + streamsLookupUpkeepId, + ]) + }) + + it('returns as many ids as possible if maxCount > num available', async () => { + const upkeepIds = await registry.getActiveUpkeepIDs(1, numUpkeeps + 100) + assert(upkeepIds.length == numUpkeeps - 1) + }) + + it('returns all upkeep IDs if maxCount is 0', async () => { + let upkeepIds = await registry.getActiveUpkeepIDs(0, 0) + assert(upkeepIds.length == numUpkeeps) + upkeepIds = await registry.getActiveUpkeepIDs(2, 0) + assert(upkeepIds.length == numUpkeeps - 2) + }) + }) + + describe('#getMaxPaymentForGas', () => { + const arbL1PriceinWei = BigNumber.from(1000) // Same as MockArbGasInfo.sol + const l1CostWeiArb = arbL1PriceinWei.mul(16).mul(maxPerformDataSize) + const l1CostWeiOpt = BigNumber.from(2000000) // Same as MockOVMGasPriceOracle.sol + itMaybe('calculates the max fee appropriately', async () => { + await verifyMaxPayment(registry) + }) + + itMaybe('calculates the max fee appropriately for Arbitrum', async () => { + await verifyMaxPayment(arbRegistry, l1CostWeiArb) + }) + + itMaybe('calculates the max fee appropriately for Optimism', async () => { + await verifyMaxPayment(opRegistry, l1CostWeiOpt) + }) + + it('uses the fallback gas price if the feed has issues', async () => { + const expectedFallbackMaxPayment = linkForGas( + performGas, + registryConditionalOverhead + .add(registryPerSignerGasOverhead.mul(f + 1)) + .add(maxPerformDataSize.mul(registryPerPerformByteGasOverhead)), + gasCeilingMultiplier.mul('2'), // fallbackGasPrice is 2x gas price + paymentPremiumPPB, + flatFeeMicroLink, + ).total + + // Stale feed + let roundId = 99 + const answer = 100 + let updatedAt = 946684800 // New Years 2000 🥳 + let startedAt = 946684799 + await gasPriceFeed + .connect(owner) + .updateRoundData(roundId, answer, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) + ).toString(), + ) + + // Negative feed price + roundId = 100 + updatedAt = now() + startedAt = 946684799 + await gasPriceFeed + .connect(owner) + .updateRoundData(roundId, -100, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) + ).toString(), + ) + + // Zero feed price + roundId = 101 + updatedAt = now() + startedAt = 946684799 + await gasPriceFeed + .connect(owner) + .updateRoundData(roundId, 0, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) + ).toString(), + ) + }) + + it('uses the fallback link price if the feed has issues', async () => { + const expectedFallbackMaxPayment = linkForGas( + performGas, + registryConditionalOverhead + .add(registryPerSignerGasOverhead.mul(f + 1)) + .add(maxPerformDataSize.mul(registryPerPerformByteGasOverhead)), + gasCeilingMultiplier.mul('2'), // fallbackLinkPrice is 1/2 link price, so multiply by 2 + paymentPremiumPPB, + flatFeeMicroLink, + ).total + + // Stale feed + let roundId = 99 + const answer = 100 + let updatedAt = 946684800 // New Years 2000 🥳 + let startedAt = 946684799 + await linkEthFeed + .connect(owner) + .updateRoundData(roundId, answer, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) + ).toString(), + ) + + // Negative feed price + roundId = 100 + updatedAt = now() + startedAt = 946684799 + await linkEthFeed + .connect(owner) + .updateRoundData(roundId, -100, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) + ).toString(), + ) + + // Zero feed price + roundId = 101 + updatedAt = now() + startedAt = 946684799 + await linkEthFeed + .connect(owner) + .updateRoundData(roundId, 0, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) + ).toString(), + ) + }) + }) + + describe('#typeAndVersion', () => { + it('uses the correct type and version', async () => { + const typeAndVersion = await registry.typeAndVersion() + assert.equal(typeAndVersion, 'KeeperRegistry 2.1.0') + }) + }) + + describe('#onTokenTransfer', () => { + const amount = toWei('1') + + it('reverts if not called by the LINK token', async () => { + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) + + await evmRevert( + registry + .connect(keeper1) + .onTokenTransfer(await keeper1.getAddress(), amount, data), + 'OnlyCallableByLINKToken()', + ) + }) + + it('reverts if not called with more or less than 32 bytes', async () => { + const longData = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + ['33', '34'], + ) + const shortData = '0x12345678' + + await evmRevert( + linkToken + .connect(owner) + .transferAndCall(registry.address, amount, longData), + ) + await evmRevert( + linkToken + .connect(owner) + .transferAndCall(registry.address, amount, shortData), + ) + }) + + it('reverts if the upkeep is canceled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + await evmRevert( + registry.connect(keeper1).addFunds(upkeepId, amount), + 'UpkeepCancelled()', + ) + }) + + it('updates the funds of the job id passed', async () => { + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) + + const before = (await registry.getUpkeep(upkeepId)).balance + await linkToken + .connect(owner) + .transferAndCall(registry.address, amount, data) + const after = (await registry.getUpkeep(upkeepId)).balance + + assert.isTrue(before.add(amount).eq(after)) + }) + }) + + describeMaybe('#setConfig - onchain', () => { + const payment = BigNumber.from(1) + const flatFee = BigNumber.from(2) + const maxGas = BigNumber.from(6) + const staleness = BigNumber.from(4) + const ceiling = BigNumber.from(5) + const newMinUpkeepSpend = BigNumber.from(9) + const newMaxCheckDataSize = BigNumber.from(10000) + const newMaxPerformDataSize = BigNumber.from(10000) + const newMaxRevertDataSize = BigNumber.from(10000) + const newMaxPerformGas = BigNumber.from(10000000) + const fbGasEth = BigNumber.from(7) + const fbLinkEth = BigNumber.from(8) + const newTranscoder = randomAddress() + const newRegistrars = [randomAddress(), randomAddress()] + const upkeepManager = randomAddress() + + const newConfig: OnChainConfig = { + paymentPremiumPPB: payment, + flatFeeMicroLink: flatFee, + checkGasLimit: maxGas, + stalenessSeconds: staleness, + gasCeilingMultiplier: ceiling, + minUpkeepSpend: newMinUpkeepSpend, + maxCheckDataSize: newMaxCheckDataSize, + maxPerformDataSize: newMaxPerformDataSize, + maxRevertDataSize: newMaxRevertDataSize, + maxPerformGas: newMaxPerformGas, + fallbackGasPrice: fbGasEth, + fallbackLinkPrice: fbLinkEth, + transcoder: newTranscoder, + registrars: newRegistrars, + upkeepPrivilegeManager: upkeepManager, + } + + it('reverts when called by anyone but the proposed owner', async () => { + await evmRevert( + registry + .connect(payee1) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + ), + 'Only callable by owner', + ) + }) + + it('reverts if signers or transmitters are the zero address', async () => { + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + [randomAddress(), randomAddress(), randomAddress(), zeroAddress], + [ + randomAddress(), + randomAddress(), + randomAddress(), + randomAddress(), + ], + f, + newConfig, + offchainVersion, + offchainBytes, + ), + 'InvalidSigner()', + ) + + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + [ + randomAddress(), + randomAddress(), + randomAddress(), + randomAddress(), + ], + [randomAddress(), randomAddress(), randomAddress(), zeroAddress], + f, + newConfig, + offchainVersion, + offchainBytes, + ), + 'InvalidTransmitter()', + ) + }) + + it('updates the onchainConfig and configDigest', async () => { + const old = await registry.getState() + const oldConfig = old.config + const oldState = old.state + assert.isTrue(paymentPremiumPPB.eq(oldConfig.paymentPremiumPPB)) + assert.isTrue(flatFeeMicroLink.eq(oldConfig.flatFeeMicroLink)) + assert.isTrue(stalenessSeconds.eq(oldConfig.stalenessSeconds)) + assert.isTrue(gasCeilingMultiplier.eq(oldConfig.gasCeilingMultiplier)) + + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + ) + + const updated = await registry.getState() + const updatedConfig = updated.config + const updatedState = updated.state + assert.equal(updatedConfig.paymentPremiumPPB, payment.toNumber()) + assert.equal(updatedConfig.flatFeeMicroLink, flatFee.toNumber()) + assert.equal(updatedConfig.stalenessSeconds, staleness.toNumber()) + assert.equal(updatedConfig.gasCeilingMultiplier, ceiling.toNumber()) + assert.equal( + updatedConfig.minUpkeepSpend.toString(), + newMinUpkeepSpend.toString(), + ) + assert.equal( + updatedConfig.maxCheckDataSize, + newMaxCheckDataSize.toNumber(), + ) + assert.equal( + updatedConfig.maxPerformDataSize, + newMaxPerformDataSize.toNumber(), + ) + assert.equal( + updatedConfig.maxRevertDataSize, + newMaxRevertDataSize.toNumber(), + ) + assert.equal(updatedConfig.maxPerformGas, newMaxPerformGas.toNumber()) + assert.equal(updatedConfig.checkGasLimit, maxGas.toNumber()) + assert.equal( + updatedConfig.fallbackGasPrice.toNumber(), + fbGasEth.toNumber(), + ) + assert.equal( + updatedConfig.fallbackLinkPrice.toNumber(), + fbLinkEth.toNumber(), + ) + assert.equal(updatedState.latestEpoch, 0) + + assert(oldState.configCount + 1 == updatedState.configCount) + assert( + oldState.latestConfigBlockNumber != + updatedState.latestConfigBlockNumber, + ) + assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) + + assert.equal(updatedConfig.transcoder, newTranscoder) + assert.deepEqual(updatedConfig.registrars, newRegistrars) + assert.equal(updatedConfig.upkeepPrivilegeManager, upkeepManager) + }) + + it('maintains paused state when config is changed', async () => { + await registry.pause() + const old = await registry.getState() + assert.isTrue(old.state.paused) + + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + ) + + const updated = await registry.getState() + assert.isTrue(updated.state.paused) + }) + + it('emits an event', async () => { + const tx = await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + ) + await expect(tx).to.emit(registry, 'ConfigSet') + }) + }) + + describe('#setConfig - offchain', () => { + let newKeepers: string[] + + beforeEach(async () => { + newKeepers = [ + await personas.Eddy.getAddress(), + await personas.Nick.getAddress(), + await personas.Neil.getAddress(), + await personas.Carol.getAddress(), + ] + }) + + it('reverts when called by anyone but the owner', async () => { + await evmRevert( + registry + .connect(payee1) + .setConfigTypeSafe( + newKeepers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + ), + 'Only callable by owner', + ) + }) + + it('reverts if too many keeperAddresses set', async () => { + for (let i = 0; i < 40; i++) { + newKeepers.push(randomAddress()) + } + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + ), + 'TooManyOracles()', + ) + }) + + it('reverts if f=0', async () => { + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newKeepers, + 0, + config, + offchainVersion, + offchainBytes, + ), + 'IncorrectNumberOfFaultyOracles()', + ) + }) + + it('reverts if signers != transmitters length', async () => { + const signers = [randomAddress()] + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + signers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + ), + 'IncorrectNumberOfSigners()', + ) + }) + + it('reverts if signers <= 3f', async () => { + newKeepers.pop() + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + ), + 'IncorrectNumberOfSigners()', + ) + }) + + it('reverts on repeated signers', async () => { + const newSigners = [ + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + ] + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + newSigners, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + ), + 'RepeatedSigner()', + ) + }) + + it('reverts on repeated transmitters', async () => { + const newTransmitters = [ + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + ] + await evmRevert( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newTransmitters, + f, + config, + offchainVersion, + offchainBytes, + ), + 'RepeatedTransmitter()', + ) + }) + + itMaybe('stores new config and emits event', async () => { + // Perform an upkeep so that totalPremium is updated + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + let tx = await getTransmitTx(registry, keeper1, [upkeepId]) + await tx.wait() + + const newOffChainVersion = BigNumber.from('2') + const newOffChainConfig = '0x1122' + + const old = await registry.getState() + const oldState = old.state + assert(oldState.totalPremium.gt(BigNumber.from('0'))) + + const newSigners = newKeepers + tx = await registry + .connect(owner) + .setConfigTypeSafe( + newSigners, + newKeepers, + f, + config, + newOffChainVersion, + newOffChainConfig, + ) + + const updated = await registry.getState() + const updatedState = updated.state + assert(oldState.totalPremium.eq(updatedState.totalPremium)) + + // Old signer addresses which are not in new signers should be non active + for (let i = 0; i < signerAddresses.length; i++) { + const signer = signerAddresses[i] + if (!newSigners.includes(signer)) { + assert((await registry.getSignerInfo(signer)).active == false) + assert((await registry.getSignerInfo(signer)).index == 0) + } + } + // New signer addresses should be active + for (let i = 0; i < newSigners.length; i++) { + const signer = newSigners[i] + assert((await registry.getSignerInfo(signer)).active == true) + assert((await registry.getSignerInfo(signer)).index == i) + } + // Old transmitter addresses which are not in new transmitter should be non active, update lastCollected but retain other info + for (let i = 0; i < keeperAddresses.length; i++) { + const transmitter = keeperAddresses[i] + if (!newKeepers.includes(transmitter)) { + assert( + (await registry.getTransmitterInfo(transmitter)).active == false, + ) + assert((await registry.getTransmitterInfo(transmitter)).index == i) + assert( + (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( + oldState.totalPremium.sub( + oldState.totalPremium.mod(keeperAddresses.length), + ), + ), + ) + } + } + // New transmitter addresses should be active + for (let i = 0; i < newKeepers.length; i++) { + const transmitter = newKeepers[i] + assert((await registry.getTransmitterInfo(transmitter)).active == true) + assert((await registry.getTransmitterInfo(transmitter)).index == i) + assert( + (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( + oldState.totalPremium, + ), + ) + } + + // config digest should be updated + assert(oldState.configCount + 1 == updatedState.configCount) + assert( + oldState.latestConfigBlockNumber != + updatedState.latestConfigBlockNumber, + ) + assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) + + //New config should be updated + assert.deepEqual(updated.signers, newKeepers) + assert.deepEqual(updated.transmitters, newKeepers) + + // Event should have been emitted + await expect(tx).to.emit(registry, 'ConfigSet') + }) + }) + + describe('#setPeerRegistryMigrationPermission() / #getPeerRegistryMigrationPermission()', () => { + const peer = randomAddress() + it('allows the owner to set the peer registries', async () => { + let permission = await registry.getPeerRegistryMigrationPermission(peer) + expect(permission).to.equal(0) + await registry.setPeerRegistryMigrationPermission(peer, 1) + permission = await registry.getPeerRegistryMigrationPermission(peer) + expect(permission).to.equal(1) + await registry.setPeerRegistryMigrationPermission(peer, 2) + permission = await registry.getPeerRegistryMigrationPermission(peer) + expect(permission).to.equal(2) + await registry.setPeerRegistryMigrationPermission(peer, 0) + permission = await registry.getPeerRegistryMigrationPermission(peer) + expect(permission).to.equal(0) + }) + it('reverts if passed an unsupported permission', async () => { + await expect( + registry.connect(admin).setPeerRegistryMigrationPermission(peer, 10), + ).to.be.reverted + }) + it('reverts if not called by the owner', async () => { + await expect( + registry.connect(admin).setPeerRegistryMigrationPermission(peer, 1), + ).to.be.revertedWith('Only callable by owner') + }) + }) + + describe('#registerUpkeep', () => { + it('reverts when registry is paused', async () => { + await registry.connect(owner).pause() + await evmRevert( + registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + emptyBytes, + '0x', + ), + 'RegistryPaused()', + ) + }) + + it('reverts if the target is not a contract', async () => { + await evmRevert( + registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + zeroAddress, + performGas, + await admin.getAddress(), + emptyBytes, + '0x', + ), + 'NotAContract()', + ) + }) + + it('reverts if called by a non-owner', async () => { + await evmRevert( + registry + .connect(keeper1) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + emptyBytes, + '0x', + ), + 'OnlyCallableByOwnerOrRegistrar()', + ) + }) + + it('reverts if execute gas is too low', async () => { + await evmRevert( + registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + 2299, + await admin.getAddress(), + emptyBytes, + '0x', + ), + 'GasLimitOutsideRange()', + ) + }) + + it('reverts if execute gas is too high', async () => { + await evmRevert( + registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + 5000001, + await admin.getAddress(), + emptyBytes, + '0x', + ), + 'GasLimitOutsideRange()', + ) + }) + + it('reverts if checkData is too long', async () => { + let longBytes = '0x' + for (let i = 0; i < 10000; i++) { + longBytes += '1' + } + await evmRevert( + registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + longBytes, + '0x', + ), + 'CheckDataExceedsLimit()', + ) + }) + + it('creates a record of the registration', async () => { + const performGases = [100000, 500000] + const checkDatas = [emptyBytes, '0x12'] + + for (let jdx = 0; jdx < performGases.length; jdx++) { + const performGas = performGases[jdx] + for (let kdx = 0; kdx < checkDatas.length; kdx++) { + const checkData = checkDatas[kdx] + const tx = await registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + checkData, + '0x', + ) + + //confirm the upkeep details and verify emitted events + const testUpkeepId = await getUpkeepID(tx) + await expect(tx) + .to.emit(registry, 'UpkeepRegistered') + .withArgs(testUpkeepId, performGas, await admin.getAddress()) + + await expect(tx) + .to.emit(registry, 'UpkeepCheckDataSet') + .withArgs(testUpkeepId, checkData) + await expect(tx) + .to.emit(registry, 'UpkeepTriggerConfigSet') + .withArgs(testUpkeepId, '0x') + + const registration = await registry.getUpkeep(testUpkeepId) + + assert.equal(mock.address, registration.target) + assert.notEqual( + ethers.constants.AddressZero, + await registry.getForwarder(testUpkeepId), + ) + assert.equal( + performGas.toString(), + registration.performGas.toString(), + ) + assert.equal(await admin.getAddress(), registration.admin) + assert.equal(0, registration.balance.toNumber()) + assert.equal(0, registration.amountSpent.toNumber()) + assert.equal(0, registration.lastPerformedBlockNumber) + assert.equal(checkData, registration.checkData) + assert.equal(registration.paused, false) + assert.equal(registration.offchainConfig, '0x') + assert(registration.maxValidBlocknumber.eq('0xffffffff')) + } + } + }) + }) + + describe('#pauseUpkeep', () => { + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry.connect(keeper1).pauseUpkeep(upkeepId.add(1)), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the upkeep is already canceled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + await evmRevert( + registry.connect(admin).pauseUpkeep(upkeepId), + 'UpkeepCancelled()', + ) + }) + + it('reverts if the upkeep is already paused', async () => { + await registry.connect(admin).pauseUpkeep(upkeepId) + + await evmRevert( + registry.connect(admin).pauseUpkeep(upkeepId), + 'OnlyUnpausedUpkeep()', + ) + }) + + it('reverts if the caller is not the upkeep admin', async () => { + await evmRevert( + registry.connect(keeper1).pauseUpkeep(upkeepId), + 'OnlyCallableByAdmin()', + ) + }) + + it('pauses the upkeep and emits an event', async () => { + const tx = await registry.connect(admin).pauseUpkeep(upkeepId) + await expect(tx).to.emit(registry, 'UpkeepPaused').withArgs(upkeepId) + + const registration = await registry.getUpkeep(upkeepId) + assert.equal(registration.paused, true) + }) + }) + + describe('#unpauseUpkeep', () => { + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry.connect(keeper1).unpauseUpkeep(upkeepId.add(1)), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the upkeep is already canceled', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + + await evmRevert( + registry.connect(admin).unpauseUpkeep(upkeepId), + 'UpkeepCancelled()', + ) + }) + + it('marks the contract as paused', async () => { + assert.isFalse((await registry.getState()).state.paused) + + await registry.connect(owner).pause() + + assert.isTrue((await registry.getState()).state.paused) + }) + + it('reverts if the upkeep is not paused', async () => { + await evmRevert( + registry.connect(admin).unpauseUpkeep(upkeepId), + 'OnlyPausedUpkeep()', + ) + }) + + it('reverts if the caller is not the upkeep admin', async () => { + await registry.connect(admin).pauseUpkeep(upkeepId) + + const registration = await registry.getUpkeep(upkeepId) + + assert.equal(registration.paused, true) + + await evmRevert( + registry.connect(keeper1).unpauseUpkeep(upkeepId), + 'OnlyCallableByAdmin()', + ) + }) + + it('unpauses the upkeep and emits an event', async () => { + const originalCount = (await registry.getActiveUpkeepIDs(0, 0)).length + + await registry.connect(admin).pauseUpkeep(upkeepId) + + const tx = await registry.connect(admin).unpauseUpkeep(upkeepId) + + await expect(tx).to.emit(registry, 'UpkeepUnpaused').withArgs(upkeepId) + + const registration = await registry.getUpkeep(upkeepId) + assert.equal(registration.paused, false) + + const upkeepIds = await registry.getActiveUpkeepIDs(0, 0) + assert.equal(upkeepIds.length, originalCount) + }) + }) + + describe('#setUpkeepCheckData', () => { + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry + .connect(keeper1) + .setUpkeepCheckData(upkeepId.add(1), randomBytes), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the caller is not upkeep admin', async () => { + await evmRevert( + registry.connect(keeper1).setUpkeepCheckData(upkeepId, randomBytes), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the upkeep is cancelled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + await evmRevert( + registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes), + 'UpkeepCancelled()', + ) + }) + + it('is allowed to update on paused upkeep', async () => { + await registry.connect(admin).pauseUpkeep(upkeepId) + await registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes) + + const registration = await registry.getUpkeep(upkeepId) + assert.equal(randomBytes, registration.checkData) + }) + + it('reverts if new data exceeds limit', async () => { + let longBytes = '0x' + for (let i = 0; i < 10000; i++) { + longBytes += '1' + } + + await evmRevert( + registry.connect(admin).setUpkeepCheckData(upkeepId, longBytes), + 'CheckDataExceedsLimit()', + ) + }) + + it('updates the upkeep check data and emits an event', async () => { + const tx = await registry + .connect(admin) + .setUpkeepCheckData(upkeepId, randomBytes) + await expect(tx) + .to.emit(registry, 'UpkeepCheckDataSet') + .withArgs(upkeepId, randomBytes) + + const registration = await registry.getUpkeep(upkeepId) + assert.equal(randomBytes, registration.checkData) + }) + }) + + describe('#setUpkeepGasLimit', () => { + const newGasLimit = BigNumber.from('300000') + + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry.connect(admin).setUpkeepGasLimit(upkeepId.add(1), newGasLimit), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the upkeep is canceled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + await evmRevert( + registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit), + 'UpkeepCancelled()', + ) + }) + + it('reverts if called by anyone but the admin', async () => { + await evmRevert( + registry.connect(owner).setUpkeepGasLimit(upkeepId, newGasLimit), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if new gas limit is out of bounds', async () => { + await evmRevert( + registry + .connect(admin) + .setUpkeepGasLimit(upkeepId, BigNumber.from('100')), + 'GasLimitOutsideRange()', + ) + await evmRevert( + registry + .connect(admin) + .setUpkeepGasLimit(upkeepId, BigNumber.from('6000000')), + 'GasLimitOutsideRange()', + ) + }) + + it('updates the gas limit successfully', async () => { + const initialGasLimit = (await registry.getUpkeep(upkeepId)).performGas + assert.equal(initialGasLimit, performGas.toNumber()) + await registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit) + const updatedGasLimit = (await registry.getUpkeep(upkeepId)).performGas + assert.equal(updatedGasLimit, newGasLimit.toNumber()) + }) + + it('emits a log', async () => { + const tx = await registry + .connect(admin) + .setUpkeepGasLimit(upkeepId, newGasLimit) + await expect(tx) + .to.emit(registry, 'UpkeepGasLimitSet') + .withArgs(upkeepId, newGasLimit) + }) + }) + + describe('#setUpkeepOffchainConfig', () => { + const newConfig = '0xc0ffeec0ffee' + + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry + .connect(admin) + .setUpkeepOffchainConfig(upkeepId.add(1), newConfig), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the upkeep is canceled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + await evmRevert( + registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig), + 'UpkeepCancelled()', + ) + }) + + it('reverts if called by anyone but the admin', async () => { + await evmRevert( + registry.connect(owner).setUpkeepOffchainConfig(upkeepId, newConfig), + 'OnlyCallableByAdmin()', + ) + }) + + it('updates the config successfully', async () => { + const initialConfig = (await registry.getUpkeep(upkeepId)).offchainConfig + assert.equal(initialConfig, '0x') + await registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig) + const updatedConfig = (await registry.getUpkeep(upkeepId)).offchainConfig + assert.equal(newConfig, updatedConfig) + }) + + it('emits a log', async () => { + const tx = await registry + .connect(admin) + .setUpkeepOffchainConfig(upkeepId, newConfig) + await expect(tx) + .to.emit(registry, 'UpkeepOffchainConfigSet') + .withArgs(upkeepId, newConfig) + }) + }) + + describe('#setUpkeepTriggerConfig', () => { + const newConfig = '0xdeadbeef' + + it('reverts if the registration does not exist', async () => { + await evmRevert( + registry + .connect(admin) + .setUpkeepTriggerConfig(upkeepId.add(1), newConfig), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts if the upkeep is canceled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + await evmRevert( + registry.connect(admin).setUpkeepTriggerConfig(upkeepId, newConfig), + 'UpkeepCancelled()', + ) + }) + + it('reverts if called by anyone but the admin', async () => { + await evmRevert( + registry.connect(owner).setUpkeepTriggerConfig(upkeepId, newConfig), + 'OnlyCallableByAdmin()', + ) + }) + + it('emits a log', async () => { + const tx = await registry + .connect(admin) + .setUpkeepTriggerConfig(upkeepId, newConfig) + await expect(tx) + .to.emit(registry, 'UpkeepTriggerConfigSet') + .withArgs(upkeepId, newConfig) + }) + }) + + describe('#transferUpkeepAdmin', () => { + it('reverts when called by anyone but the current upkeep admin', async () => { + await evmRevert( + registry + .connect(payee1) + .transferUpkeepAdmin(upkeepId, await payee2.getAddress()), + 'OnlyCallableByAdmin()', + ) + }) + + it('reverts when transferring to self', async () => { + await evmRevert( + registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await admin.getAddress()), + 'ValueNotChanged()', + ) + }) + + it('reverts when the upkeep is cancelled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + await evmRevert( + registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await keeper1.getAddress()), + 'UpkeepCancelled()', + ) + }) + + it('allows cancelling transfer by reverting to zero address', async () => { + await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + const tx = await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, ethers.constants.AddressZero) + + await expect(tx) + .to.emit(registry, 'UpkeepAdminTransferRequested') + .withArgs( + upkeepId, + await admin.getAddress(), + ethers.constants.AddressZero, + ) + }) + + it('does not change the upkeep admin', async () => { + await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + + const upkeep = await registry.getUpkeep(upkeepId) + assert.equal(await admin.getAddress(), upkeep.admin) + }) + + it('emits an event announcing the new upkeep admin', async () => { + const tx = await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + + await expect(tx) + .to.emit(registry, 'UpkeepAdminTransferRequested') + .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) + }) + + it('does not emit an event when called with the same proposed upkeep admin', async () => { + await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + + const tx = await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + const receipt = await tx.wait() + assert.equal(0, receipt.logs.length) + }) + }) + + describe('#acceptUpkeepAdmin', () => { + beforeEach(async () => { + // Start admin transfer to payee1 + await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + }) + + it('reverts when not called by the proposed upkeep admin', async () => { + await evmRevert( + registry.connect(payee2).acceptUpkeepAdmin(upkeepId), + 'OnlyCallableByProposedAdmin()', + ) + }) + + it('reverts when the upkeep is cancelled', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + await evmRevert( + registry.connect(payee1).acceptUpkeepAdmin(upkeepId), + 'UpkeepCancelled()', + ) + }) + + it('does change the admin', async () => { + await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) + + const upkeep = await registry.getUpkeep(upkeepId) + assert.equal(await payee1.getAddress(), upkeep.admin) + }) + + it('emits an event announcing the new upkeep admin', async () => { + const tx = await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) + await expect(tx) + .to.emit(registry, 'UpkeepAdminTransferred') + .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) + }) + }) + + describe('#withdrawOwnerFunds', () => { + it('can only be called by owner', async () => { + await evmRevert( + registry.connect(keeper1).withdrawOwnerFunds(), + 'Only callable by owner', + ) + }) + + itMaybe('withdraws the collected fees to owner', async () => { + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + // Very high min spend, whole balance as cancellation fees + const minUpkeepSpend = toWei('1000') + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + paymentPremiumPPB, + flatFeeMicroLink, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + }, + offchainVersion, + offchainBytes, + ) + const upkeepBalance = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) + + await registry.connect(owner).cancelUpkeep(upkeepId) + + // Transfered to owner balance on registry + let ownerRegistryBalance = (await registry.getState()).state + .ownerLinkBalance + assert.isTrue(ownerRegistryBalance.eq(upkeepBalance)) + + // Now withdraw + await registry.connect(owner).withdrawOwnerFunds() + + ownerRegistryBalance = (await registry.getState()).state.ownerLinkBalance + const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) + + // Owner registry balance should be changed to 0 + assert.isTrue(ownerRegistryBalance.eq(BigNumber.from('0'))) + + // Owner should be credited with the balance + assert.isTrue(ownerBefore.add(upkeepBalance).eq(ownerAfter)) + }) + }) + + describe('#transferPayeeship', () => { + it('reverts when called by anyone but the current payee', async () => { + await evmRevert( + registry + .connect(payee2) + .transferPayeeship( + await keeper1.getAddress(), + await payee2.getAddress(), + ), + 'OnlyCallableByPayee()', + ) + }) + + it('reverts when transferring to self', async () => { + await evmRevert( + registry + .connect(payee1) + .transferPayeeship( + await keeper1.getAddress(), + await payee1.getAddress(), + ), + 'ValueNotChanged()', + ) + }) + + it('does not change the payee', async () => { + await registry + .connect(payee1) + .transferPayeeship( + await keeper1.getAddress(), + await payee2.getAddress(), + ) + + const info = await registry.getTransmitterInfo(await keeper1.getAddress()) + assert.equal(await payee1.getAddress(), info.payee) + }) + + it('emits an event announcing the new payee', async () => { + const tx = await registry + .connect(payee1) + .transferPayeeship( + await keeper1.getAddress(), + await payee2.getAddress(), + ) + await expect(tx) + .to.emit(registry, 'PayeeshipTransferRequested') + .withArgs( + await keeper1.getAddress(), + await payee1.getAddress(), + await payee2.getAddress(), + ) + }) + + it('does not emit an event when called with the same proposal', async () => { + await registry + .connect(payee1) + .transferPayeeship( + await keeper1.getAddress(), + await payee2.getAddress(), + ) + + const tx = await registry + .connect(payee1) + .transferPayeeship( + await keeper1.getAddress(), + await payee2.getAddress(), + ) + const receipt = await tx.wait() + assert.equal(0, receipt.logs.length) + }) + }) + + describe('#acceptPayeeship', () => { + beforeEach(async () => { + await registry + .connect(payee1) + .transferPayeeship( + await keeper1.getAddress(), + await payee2.getAddress(), + ) + }) + + it('reverts when called by anyone but the proposed payee', async () => { + await evmRevert( + registry.connect(payee1).acceptPayeeship(await keeper1.getAddress()), + 'OnlyCallableByProposedPayee()', + ) + }) + + it('emits an event announcing the new payee', async () => { + const tx = await registry + .connect(payee2) + .acceptPayeeship(await keeper1.getAddress()) + await expect(tx) + .to.emit(registry, 'PayeeshipTransferred') + .withArgs( + await keeper1.getAddress(), + await payee1.getAddress(), + await payee2.getAddress(), + ) + }) + + it('does change the payee', async () => { + await registry.connect(payee2).acceptPayeeship(await keeper1.getAddress()) + + const info = await registry.getTransmitterInfo(await keeper1.getAddress()) + assert.equal(await payee2.getAddress(), info.payee) + }) + }) + + describe('#pause', () => { + it('reverts if called by a non-owner', async () => { + await evmRevert( + registry.connect(keeper1).pause(), + 'Only callable by owner', + ) + }) + + it('marks the contract as paused', async () => { + assert.isFalse((await registry.getState()).state.paused) + + await registry.connect(owner).pause() + + assert.isTrue((await registry.getState()).state.paused) + }) + + it('Does not allow transmits when paused', async () => { + await registry.connect(owner).pause() + + await evmRevert( + getTransmitTx(registry, keeper1, [upkeepId]), + 'RegistryPaused()', + ) + }) + + it('Does not allow creation of new upkeeps when paused', async () => { + await registry.connect(owner).pause() + + await evmRevert( + registry + .connect(owner) + ['registerUpkeep(address,uint32,address,bytes,bytes)']( + mock.address, + performGas, + await admin.getAddress(), + emptyBytes, + '0x', + ), + 'RegistryPaused()', + ) + }) + }) + + describe('#unpause', () => { + beforeEach(async () => { + await registry.connect(owner).pause() + }) + + it('reverts if called by a non-owner', async () => { + await evmRevert( + registry.connect(keeper1).unpause(), + 'Only callable by owner', + ) + }) + + it('marks the contract as not paused', async () => { + assert.isTrue((await registry.getState()).state.paused) + + await registry.connect(owner).unpause() + + assert.isFalse((await registry.getState()).state.paused) + }) + }) + + describe('#migrateUpkeeps() / #receiveUpkeeps()', async () => { + context('when permissions are set', () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1) + await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2) + }) + + it('migrates an upkeep', async () => { + const offchainBytes = '0x987654abcd' + await registry + .connect(admin) + .setUpkeepOffchainConfig(upkeepId, offchainBytes) + const reg1Upkeep = await registry.getUpkeep(upkeepId) + const forwarderAddress = await registry.getForwarder(upkeepId) + expect(reg1Upkeep.balance).to.equal(toWei('100')) + expect(reg1Upkeep.checkData).to.equal(randomBytes) + expect(forwarderAddress).to.not.equal(ethers.constants.AddressZero) + expect(reg1Upkeep.offchainConfig).to.equal(offchainBytes) + expect((await registry.getState()).state.numUpkeeps).to.equal( + numUpkeeps, + ) + const forwarder = await IAutomationForwarderFactory.connect( + forwarderAddress, + owner, + ) + expect(await forwarder.getRegistry()).to.equal(registry.address) + // Set an upkeep admin transfer in progress too + await registry + .connect(admin) + .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) + + // migrate + await registry + .connect(admin) + .migrateUpkeeps([upkeepId], mgRegistry.address) + expect((await registry.getState()).state.numUpkeeps).to.equal( + numUpkeeps - 1, + ) + expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1) + expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0) + expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x') + expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal( + toWei('100'), + ) + expect( + (await mgRegistry.getState()).state.expectedLinkBalance, + ).to.equal(toWei('100')) + expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal( + randomBytes, + ) + expect((await mgRegistry.getUpkeep(upkeepId)).offchainConfig).to.equal( + offchainBytes, + ) + expect(await mgRegistry.getForwarder(upkeepId)).to.equal( + forwarderAddress, + ) + // test that registry is updated on forwarder + expect(await forwarder.getRegistry()).to.equal(mgRegistry.address) + // migration will delete the upkeep and nullify admin transfer + await expect( + registry.connect(payee1).acceptUpkeepAdmin(upkeepId), + ).to.be.revertedWith('UpkeepCancelled()') + await expect( + mgRegistry.connect(payee1).acceptUpkeepAdmin(upkeepId), + ).to.be.revertedWith('OnlyCallableByProposedAdmin()') + }) + + it('migrates a paused upkeep', async () => { + expect((await registry.getUpkeep(upkeepId)).balance).to.equal( + toWei('100'), + ) + expect((await registry.getUpkeep(upkeepId)).checkData).to.equal( + randomBytes, + ) + expect((await registry.getState()).state.numUpkeeps).to.equal( + numUpkeeps, + ) + await registry.connect(admin).pauseUpkeep(upkeepId) + // verify the upkeep is paused + expect((await registry.getUpkeep(upkeepId)).paused).to.equal(true) + // migrate + await registry + .connect(admin) + .migrateUpkeeps([upkeepId], mgRegistry.address) + expect((await registry.getState()).state.numUpkeeps).to.equal( + numUpkeeps - 1, + ) + expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1) + expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0) + expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal( + toWei('100'), + ) + expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x') + expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal( + randomBytes, + ) + expect( + (await mgRegistry.getState()).state.expectedLinkBalance, + ).to.equal(toWei('100')) + // verify the upkeep is still paused after migration + expect((await mgRegistry.getUpkeep(upkeepId)).paused).to.equal(true) + }) + + it('emits an event on both contracts', async () => { + expect((await registry.getUpkeep(upkeepId)).balance).to.equal( + toWei('100'), + ) + expect((await registry.getUpkeep(upkeepId)).checkData).to.equal( + randomBytes, + ) + expect((await registry.getState()).state.numUpkeeps).to.equal( + numUpkeeps, + ) + const tx = registry + .connect(admin) + .migrateUpkeeps([upkeepId], mgRegistry.address) + await expect(tx) + .to.emit(registry, 'UpkeepMigrated') + .withArgs(upkeepId, toWei('100'), mgRegistry.address) + await expect(tx) + .to.emit(mgRegistry, 'UpkeepReceived') + .withArgs(upkeepId, toWei('100'), registry.address) + }) + + it('is only migratable by the admin', async () => { + await expect( + registry + .connect(owner) + .migrateUpkeeps([upkeepId], mgRegistry.address), + ).to.be.revertedWith('OnlyCallableByAdmin()') + await registry + .connect(admin) + .migrateUpkeeps([upkeepId], mgRegistry.address) + }) + }) + + context('when permissions are not set', () => { + it('reverts', async () => { + // no permissions + await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0) + await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0) + await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to + .be.reverted + // only outgoing permissions + await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1) + await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0) + await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to + .be.reverted + // only incoming permissions + await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0) + await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2) + await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to + .be.reverted + // permissions opposite direction + await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 2) + await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 1) + await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to + .be.reverted + }) + }) + }) + + describe('#setPayees', () => { + const IGNORE_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF' + + it('reverts when not called by the owner', async () => { + await evmRevert( + registry.connect(keeper1).setPayees(payees), + 'Only callable by owner', + ) + }) + + it('reverts with different numbers of payees than transmitters', async () => { + await evmRevert( + registry.connect(owner).setPayees([...payees, randomAddress()]), + 'ParameterLengthError()', + ) + }) + + it('reverts if the payee is the zero address', async () => { + await blankRegistry.connect(owner).setConfig(...baseConfig) // used to test initial config + + await evmRevert( + blankRegistry // used to test initial config + .connect(owner) + .setPayees([ethers.constants.AddressZero, ...payees.slice(1)]), + 'InvalidPayee()', + ) + }) + + itMaybe( + 'sets the payees when exisitng payees are zero address', + async () => { + //Initial payees should be zero address + await blankRegistry.connect(owner).setConfig(...baseConfig) // used to test initial config + + for (let i = 0; i < keeperAddresses.length; i++) { + const payee = ( + await blankRegistry.getTransmitterInfo(keeperAddresses[i]) + ).payee // used to test initial config + assert.equal(payee, zeroAddress) + } + + await blankRegistry.connect(owner).setPayees(payees) // used to test initial config + + for (let i = 0; i < keeperAddresses.length; i++) { + const payee = ( + await blankRegistry.getTransmitterInfo(keeperAddresses[i]) + ).payee + assert.equal(payee, payees[i]) + } + }, + ) + + it('does not change the payee if IGNORE_ADDRESS is used as payee', async () => { + const signers = Array.from({ length: 5 }, randomAddress) + const keepers = Array.from({ length: 5 }, randomAddress) + const payees = Array.from({ length: 5 }, randomAddress) + const newTransmitter = randomAddress() + const newPayee = randomAddress() + const ignoreAddresses = new Array(payees.length).fill(IGNORE_ADDRESS) + const newPayees = [...ignoreAddresses, newPayee] + // arbitrum registry + // configure registry with 5 keepers // optimism registry + await blankRegistry // used to test initial configurations + .connect(owner) + .setConfigTypeSafe( + signers, + keepers, + f, + config, + offchainVersion, + offchainBytes, + ) + // arbitrum registry + // set initial payees // optimism registry + await blankRegistry.connect(owner).setPayees(payees) // used to test initial configurations + // arbitrum registry + // add another keeper // optimism registry + await blankRegistry // used to test initial configurations + .connect(owner) + .setConfigTypeSafe( + [...signers, randomAddress()], + [...keepers, newTransmitter], + f, + config, + offchainVersion, + offchainBytes, + ) + // arbitrum registry + // update payee list // optimism registry // arbitrum registry + await blankRegistry.connect(owner).setPayees(newPayees) // used to test initial configurations // optimism registry + const ignored = await blankRegistry.getTransmitterInfo(newTransmitter) // used to test initial configurations + assert.equal(newPayee, ignored.payee) + assert.equal(true, ignored.active) + }) + + it('reverts if payee is non zero and owner tries to change payee', async () => { + const newPayees = [randomAddress(), ...payees.slice(1)] + + await evmRevert( + registry.connect(owner).setPayees(newPayees), + 'InvalidPayee()', + ) + }) + + it('emits events for every payee added and removed', async () => { + const tx = await registry.connect(owner).setPayees(payees) + await expect(tx) + .to.emit(registry, 'PayeesUpdated') + .withArgs(keeperAddresses, payees) + }) + }) + + describe('#cancelUpkeep', () => { + it('reverts if the ID is not valid', async () => { + await evmRevert( + registry.connect(owner).cancelUpkeep(upkeepId.add(1)), + 'CannotCancel()', + ) + }) + + it('reverts if called by a non-owner/non-admin', async () => { + await evmRevert( + registry.connect(keeper1).cancelUpkeep(upkeepId), + 'OnlyCallableByOwnerOrAdmin()', + ) + }) + + describe('when called by the owner', async () => { + it('sets the registration to invalid immediately', async () => { + const tx = await registry.connect(owner).cancelUpkeep(upkeepId) + const receipt = await tx.wait() + const registration = await registry.getUpkeep(upkeepId) + assert.equal( + registration.maxValidBlocknumber.toNumber(), + receipt.blockNumber, + ) + }) + + it('emits an event', async () => { + const tx = await registry.connect(owner).cancelUpkeep(upkeepId) + const receipt = await tx.wait() + await expect(tx) + .to.emit(registry, 'UpkeepCanceled') + .withArgs(upkeepId, BigNumber.from(receipt.blockNumber)) + }) + + it('immediately prevents upkeep', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) + + it('does not revert if reverts if called multiple times', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + await evmRevert( + registry.connect(owner).cancelUpkeep(upkeepId), + 'CannotCancel()', + ) + }) + + describe('when called by the owner when the admin has just canceled', () => { + let oldExpiration: BigNumber + + beforeEach(async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + const registration = await registry.getUpkeep(upkeepId) + oldExpiration = registration.maxValidBlocknumber + }) + + it('allows the owner to cancel it more quickly', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + + const registration = await registry.getUpkeep(upkeepId) + const newExpiration = registration.maxValidBlocknumber + assert.isTrue(newExpiration.lt(oldExpiration)) + }) + }) + }) + + describe('when called by the admin', async () => { + it('reverts if called again by the admin', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + await evmRevert( + registry.connect(admin).cancelUpkeep(upkeepId), + 'CannotCancel()', + ) + }) + + it('reverts if called by the owner after the timeout', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + for (let i = 0; i < cancellationDelay; i++) { + await ethers.provider.send('evm_mine', []) + } + + await evmRevert( + registry.connect(owner).cancelUpkeep(upkeepId), + 'CannotCancel()', + ) + }) + + it('sets the registration to invalid in 50 blocks', async () => { + const tx = await registry.connect(admin).cancelUpkeep(upkeepId) + const receipt = await tx.wait() + const registration = await registry.getUpkeep(upkeepId) + assert.equal( + registration.maxValidBlocknumber.toNumber(), + receipt.blockNumber + 50, + ) + }) + + it('emits an event', async () => { + const tx = await registry.connect(admin).cancelUpkeep(upkeepId) + const receipt = await tx.wait() + await expect(tx) + .to.emit(registry, 'UpkeepCanceled') + .withArgs( + upkeepId, + BigNumber.from(receipt.blockNumber + cancellationDelay), + ) + }) + + it('immediately prevents upkeep', async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await registry.connect(admin).cancelUpkeep(upkeepId) + + await getTransmitTx(registry, keeper1, [upkeepId]) + + for (let i = 0; i < cancellationDelay; i++) { + await ethers.provider.send('evm_mine', []) + } + + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) + + describeMaybe('when an upkeep has been performed', async () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await getTransmitTx(registry, keeper1, [upkeepId]) + }) + + it('deducts a cancellation fee from the upkeep and gives to owner', async () => { + const minUpkeepSpend = toWei('10') + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + paymentPremiumPPB, + flatFeeMicroLink, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + }, + offchainVersion, + offchainBytes, + ) + + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = (await registry.getState()).state.ownerLinkBalance + + const amountSpent = toWei('100').sub(upkeepBefore) + const cancellationFee = minUpkeepSpend.sub(amountSpent) + + await registry.connect(admin).cancelUpkeep(upkeepId) + + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + const ownerAfter = (await registry.getState()).state.ownerLinkBalance + + // post upkeep balance should be previous balance minus cancellation fee + assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter)) + // payee balance should not change + assert.isTrue(payee1Before.eq(payee1After)) + // owner should receive the cancellation fee + assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee)) + }) + + it('deducts up to balance as cancellation fee', async () => { + // Very high min spend, should deduct whole balance as cancellation fees + const minUpkeepSpend = toWei('1000') + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + paymentPremiumPPB, + flatFeeMicroLink, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + }, + offchainVersion, + offchainBytes, + ) + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = (await registry.getState()).state.ownerLinkBalance + + await registry.connect(admin).cancelUpkeep(upkeepId) + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const ownerAfter = (await registry.getState()).state.ownerLinkBalance + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + + // all upkeep balance is deducted for cancellation fee + assert.equal(0, upkeepAfter.toNumber()) + // payee balance should not change + assert.isTrue(payee1After.eq(payee1Before)) + // all upkeep balance is transferred to the owner + assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore)) + }) + + it('does not deduct cancellation fee if more than minUpkeepSpend is spent', async () => { + // Very low min spend, already spent in one perform upkeep + const minUpkeepSpend = BigNumber.from(420) + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + paymentPremiumPPB, + flatFeeMicroLink, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + minUpkeepSpend, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + }, + offchainVersion, + offchainBytes, + ) + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = (await registry.getState()).state.ownerLinkBalance + + await registry.connect(admin).cancelUpkeep(upkeepId) + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const ownerAfter = (await registry.getState()).state.ownerLinkBalance + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + + // upkeep does not pay cancellation fee after cancellation because minimum upkeep spent is met + assert.isTrue(upkeepBefore.eq(upkeepAfter)) + // owner balance does not change + assert.isTrue(ownerAfter.eq(ownerBefore)) + // payee balance does not change + assert.isTrue(payee1Before.eq(payee1After)) + }) + }) + }) + }) + + describe('#withdrawPayment', () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await getTransmitTx(registry, keeper1, [upkeepId]) + }) + + it('reverts if called by anyone but the payee', async () => { + await evmRevert( + registry + .connect(payee2) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ), + 'OnlyCallableByPayee()', + ) + }) + + it('reverts if called with the 0 address', async () => { + await evmRevert( + registry + .connect(payee2) + .withdrawPayment(await keeper1.getAddress(), zeroAddress), + 'InvalidRecipient()', + ) + }) + + it('updates the balances', async () => { + const to = await nonkeeper.getAddress() + const keeperBefore = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationBefore = (await registry.getUpkeep(upkeepId)).balance + const toLinkBefore = await linkToken.balanceOf(to) + const registryLinkBefore = await linkToken.balanceOf(registry.address) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const ownerBefore = (await registry.getState()).state.ownerLinkBalance + + // Withdrawing for first time, last collected = 0 + assert.equal(keeperBefore.lastCollected.toString(), '0') + + //// Do the thing + await registry + .connect(payee1) + .withdrawPayment(await keeper1.getAddress(), to) + + const keeperAfter = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationAfter = (await registry.getUpkeep(upkeepId)).balance + const toLinkAfter = await linkToken.balanceOf(to) + const registryLinkAfter = await linkToken.balanceOf(registry.address) + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const ownerAfter = (await registry.getState()).state.ownerLinkBalance + + // registry total premium should not change + assert.isTrue(registryPremiumBefore.eq(registryPremiumAfter)) + + // Last collected should be updated to premium-change + assert.isTrue( + keeperAfter.lastCollected.eq( + registryPremiumBefore.sub( + registryPremiumBefore.mod(keeperAddresses.length), + ), + ), + ) + + // owner balance should remain unchanged + assert.isTrue(ownerAfter.eq(ownerBefore)) + + assert.isTrue(keeperAfter.balance.eq(BigNumber.from(0))) + assert.isTrue(registrationBefore.eq(registrationAfter)) + assert.isTrue(toLinkBefore.add(keeperBefore.balance).eq(toLinkAfter)) + assert.isTrue( + registryLinkBefore.sub(keeperBefore.balance).eq(registryLinkAfter), + ) + }) + + it('emits a log announcing the withdrawal', async () => { + const balance = ( + await registry.getTransmitterInfo(await keeper1.getAddress()) + ).balance + const tx = await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await expect(tx) + .to.emit(registry, 'PaymentWithdrawn') + .withArgs( + await keeper1.getAddress(), + balance, + await nonkeeper.getAddress(), + await payee1.getAddress(), + ) + }) + }) + + describe('#checkCallback', () => { + it('returns false with appropriate failure reason when target callback reverts', async () => { + await streamsLookupUpkeep.setShouldRevertCallback(true) + + const values: any[] = ['0x1234', '0xabcd'] + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + + assert.isFalse(res.upkeepNeeded) + assert.equal(res.performData, '0x') + assert.equal( + res.upkeepFailureReason, + UpkeepFailureReason.CHECK_CALLBACK_REVERTED, + ) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns false with appropriate failure reason when target callback returns big performData', async () => { + let longBytes = '0x' + for (let i = 0; i <= maxPerformDataSize.toNumber(); i++) { + longBytes += '11' + } + const values: any[] = [longBytes, longBytes] + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + + assert.isFalse(res.upkeepNeeded) + assert.equal(res.performData, '0x') + assert.equal( + res.upkeepFailureReason, + UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, + ) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns false with appropriate failure reason when target callback returns false', async () => { + await streamsLookupUpkeep.setCallbackReturnBool(false) + const values: any[] = ['0x1234', '0xabcd'] + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + + assert.isFalse(res.upkeepNeeded) + assert.equal(res.performData, '0x') + assert.equal( + res.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_NOT_NEEDED, + ) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('succeeds with upkeep needed', async () => { + const values: any[] = ['0x1234', '0xabcd'] + + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + const expectedPerformData = ethers.utils.defaultAbiCoder.encode( + ['bytes[]', 'bytes'], + [values, '0x'], + ) + + assert.isTrue(res.upkeepNeeded) + assert.equal(res.performData, expectedPerformData) + assert.equal(res.upkeepFailureReason, UpkeepFailureReason.NONE) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + }) + + describe('#setUpkeepPrivilegeConfig() / #getUpkeepPrivilegeConfig()', () => { + it('reverts when non manager tries to set privilege config', async () => { + await evmRevert( + registry.connect(payee3).setUpkeepPrivilegeConfig(upkeepId, '0x1234'), + 'OnlyCallableByUpkeepPrivilegeManager()', + ) + }) + + it('returns empty bytes for upkeep privilege config before setting', async () => { + const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) + assert.equal(cfg, '0x') + }) + + it('allows upkeep manager to set privilege config', async () => { + const tx = await registry + .connect(personas.Norbert) + .setUpkeepPrivilegeConfig(upkeepId, '0x1234') + await expect(tx) + .to.emit(registry, 'UpkeepPrivilegeConfigSet') + .withArgs(upkeepId, '0x1234') + + const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) + assert.equal(cfg, '0x1234') + }) + }) + + describe('#setAdminPrivilegeConfig() / #getAdminPrivilegeConfig()', () => { + const admin = randomAddress() + + it('reverts when non manager tries to set privilege config', async () => { + await evmRevert( + registry.connect(payee3).setAdminPrivilegeConfig(admin, '0x1234'), + 'OnlyCallableByUpkeepPrivilegeManager()', + ) + }) + + it('returns empty bytes for upkeep privilege config before setting', async () => { + const cfg = await registry.getAdminPrivilegeConfig(admin) + assert.equal(cfg, '0x') + }) + + it('allows upkeep manager to set privilege config', async () => { + const tx = await registry + .connect(personas.Norbert) + .setAdminPrivilegeConfig(admin, '0x1234') + await expect(tx) + .to.emit(registry, 'AdminPrivilegeConfigSet') + .withArgs(admin, '0x1234') + + const cfg = await registry.getAdminPrivilegeConfig(admin) + assert.equal(cfg, '0x1234') + }) + }) + + describe('transmitterPremiumSplit [ @skip-coverage ]', () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + }) + + it('splits premium evenly across transmitters', async () => { + // Do a transmit from keeper1 + await getTransmitTx(registry, keeper1, [upkeepId]) + + const registryPremium = (await registry.getState()).state.totalPremium + assert.isTrue(registryPremium.gt(BigNumber.from(0))) + + const premiumPerTransmitter = registryPremium.div( + BigNumber.from(keeperAddresses.length), + ) + const k1Balance = ( + await registry.getTransmitterInfo(await keeper1.getAddress()) + ).balance + // transmitter should be reimbursed for gas and get the premium + assert.isTrue(k1Balance.gt(premiumPerTransmitter)) + const k1GasReimbursement = k1Balance.sub(premiumPerTransmitter) + + const k2Balance = ( + await registry.getTransmitterInfo(await keeper2.getAddress()) + ).balance + // non transmitter should get its share of premium + assert.isTrue(k2Balance.eq(premiumPerTransmitter)) + + // Now do a transmit from keeper 2 + await getTransmitTx(registry, keeper2, [upkeepId]) + const registryPremiumNew = (await registry.getState()).state.totalPremium + assert.isTrue(registryPremiumNew.gt(registryPremium)) + const premiumPerTransmitterNew = registryPremiumNew.div( + BigNumber.from(keeperAddresses.length), + ) + const additionalPremium = premiumPerTransmitterNew.sub( + premiumPerTransmitter, + ) + + const k1BalanceNew = ( + await registry.getTransmitterInfo(await keeper1.getAddress()) + ).balance + // k1 should get the new premium + assert.isTrue( + k1BalanceNew.eq(k1GasReimbursement.add(premiumPerTransmitterNew)), + ) + + const k2BalanceNew = ( + await registry.getTransmitterInfo(await keeper2.getAddress()) + ).balance + // k2 should get gas reimbursement in addition to new premium + assert.isTrue(k2BalanceNew.gt(k2Balance.add(additionalPremium))) + }) + + it('updates last collected upon payment withdrawn', async () => { + // Do a transmit from keeper1 + await getTransmitTx(registry, keeper1, [upkeepId]) + + const registryPremium = (await registry.getState()).state.totalPremium + const k1 = await registry.getTransmitterInfo(await keeper1.getAddress()) + const k2 = await registry.getTransmitterInfo(await keeper2.getAddress()) + + // Withdrawing for first time, last collected = 0 + assert.isTrue(k1.lastCollected.eq(BigNumber.from(0))) + assert.isTrue(k2.lastCollected.eq(BigNumber.from(0))) + + //// Do the thing + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + + const k1New = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const k2New = await registry.getTransmitterInfo( + await keeper2.getAddress(), + ) + + // transmitter info lastCollected should be updated for k1, not for k2 + assert.isTrue( + k1New.lastCollected.eq( + registryPremium.sub(registryPremium.mod(keeperAddresses.length)), + ), + ) + assert.isTrue(k2New.lastCollected.eq(BigNumber.from(0))) + }) + + itMaybe( + 'maintains consistent balance information across all parties', + async () => { + // throughout transmits, withdrawals, setConfigs total claim on balances should remain less than expected balance + // some spare change can get lost but it should be less than maxAllowedSpareChange + + let maxAllowedSpareChange = BigNumber.from('0') + await verifyConsistentAccounting(maxAllowedSpareChange) + + await getTransmitTx(registry, keeper1, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee2) + .withdrawPayment( + await keeper2.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await getTransmitTx(registry, keeper1, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses.slice(2, 15), // only use 2-14th index keepers + keeperAddresses.slice(2, 15), + f, + config, + offchainVersion, + offchainBytes, + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await getTransmitTx(registry, keeper3, [upkeepId], { + startingSignerIndex: 2, + }) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('13')) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee3) + .withdrawPayment( + await keeper3.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses.slice(0, 4), // only use 0-3rd index keepers + keeperAddresses.slice(0, 4), + f, + config, + offchainVersion, + offchainBytes, + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + await getTransmitTx(registry, keeper1, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) + await getTransmitTx(registry, keeper3, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) + + await verifyConsistentAccounting(maxAllowedSpareChange) + await registry + .connect(payee5) + .withdrawPayment( + await keeper5.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + }, + ) + }) +}) From 6771c0acdff1a53318802188230bc9782a25c8a9 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 23 Jan 2024 14:42:49 -0600 Subject: [PATCH 192/234] bump operator ui v0.8.0-8da47c3 (#11860) * bump operator ui v0.8.0-8da47c3 * sonar: ignore core/web/assets --- core/web/assets/9f6d832ef97e8493764e.svg.gz | Bin 193 -> 194 bytes core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz | Bin 1739 -> 1705 bytes core/web/assets/index.html | 2 +- core/web/assets/index.html.gz | Bin 418 -> 420 bytes ...a835ee.js => main.74b124ef5d2ef3614139.js} | 6 +++--- .../assets/main.74b124ef5d2ef3614139.js.gz | Bin 0 -> 1196933 bytes .../assets/main.8f602c136d4004a835ee.js.gz | Bin 1190411 -> 0 bytes operator_ui/TAG | 2 +- sonar-project.properties | 2 +- 9 files changed, 6 insertions(+), 6 deletions(-) rename core/web/assets/{main.8f602c136d4004a835ee.js => main.74b124ef5d2ef3614139.js} (90%) create mode 100644 core/web/assets/main.74b124ef5d2ef3614139.js.gz delete mode 100644 core/web/assets/main.8f602c136d4004a835ee.js.gz diff --git a/core/web/assets/9f6d832ef97e8493764e.svg.gz b/core/web/assets/9f6d832ef97e8493764e.svg.gz index 44cd870b0bbe539c0d37376c0006f03325f13e24..94a2aadef9f895c57f32692ec773aa5e7d951e7e 100644 GIT binary patch literal 194 zcmV;z06qU7iwFP!0000212vD!4uU`oMfZM1Q`ggZbQnhiEL`K3z3~AiqYxns;p@$y z?#}7GY14d|8knEqH5Ay6V}Fn`#z>K=dpFW3rQ|R*2-D*hkKJ6L0tLJy50=ScnfK?h zg=>K)?P(7x2n&X|vf#=6q-I0A$^vIWYjDJ{j=_r5WG0`Y)oQJ1AH>Qcv2es+T}~d1 wCtd4|vhdF!LE1TKlMep1`j!`j`y+$`w|uec;!u`(dZ^rd0PhHUEBOEb07YC|=>Px# literal 193 zcmV;y06za8iwFP!0000212vC34}&lag!ldmE1hv34m4DRp);)PRo_xtP(cLo>ze@G z?(?0`a?y_unBMNwTlO4=?#Q|K9z9|EeQ+g&;32_aeBJ!1oh%a|fDULt6K1R|y7Taa zt7Ww`n1gVM28C2rE_o-SHkGRDgyK2+|0?+AM<`2YX_KeSc1 diff --git a/core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz b/core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz index 7e4f235f1b9263b49c03bad9b2924a6355913d21..36ce61e5116cb6d53292b4426d7f19ab00be7e4c 100644 GIT binary patch literal 1705 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 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 4d09643ee1b9aebe70088e318891e5418c6f45b6..afbd1b1a38b50f16f3c36b98a2bcef67890ed40b 100644 GIT binary patch literal 420 zcmV;V0bBkbiwFP!000021C^3nYa1~Th5w2w=u_?0`jTFPw0Q^=QYeMCdFbO9L?pLJ*hLLqxZl8A1}V`!Zqy;i4WNtT{_I zQvRh1MyKn*Mf^b&dCpo51$(Clk+I$yQxg=KO$&HwDDCOF$tcDTKzU{~%Xw3hiR^!N7R4KYW`5CDZw7i^YWx!L%)AX=o)$T zg+@^Udn`_$F0E?uP8+BdrJ4kkn|IsYYQ4q&CakgFyj^Wqn-9&;syl1WgwC4t;_Nsa zJDd3;|KNklh(QS}%&?27KEVXv&bGp;9I}#us)BKBthpB2K$D{2bYYg+C(zVcbHI3w7$2}mGJ54j*|`TFB=wUO#5 zM+m6p=?vuJ z!jQ3)^Q(t0qIfeXM5f)bxkX4xL(F zOm+haiydYy=S7N=4M&2)cVARsOSS24>nn4)zO`KbHEh;5h}Bz>ZKDK@Kos5ljqi|NsoDxlOc+c~KSMyW- M4L7Cb62Jlg09%;KA^-pY diff --git a/core/web/assets/main.8f602c136d4004a835ee.js b/core/web/assets/main.74b124ef5d2ef3614139.js similarity index 90% rename from core/web/assets/main.8f602c136d4004a835ee.js rename to core/web/assets/main.74b124ef5d2ef3614139.js index de75dc2b1c6..74cfe38a5bb 100644 --- a/core/web/assets/main.8f602c136d4004a835ee.js +++ b/core/web/assets/main.74b124ef5d2ef3614139.js @@ -17,7 +17,7 @@ PERFORMANCE OF THIS SOFTWARE. * * Copyright (c) 2014-2017, Jon Schlinkert. * Released under the MIT License. - */ var r=n(47798);function i(e){return!0===r(e)&&"[object Object]"===Object.prototype.toString.call(e)}e.exports=function(e){var t,n;return!1!==i(e)&&"function"==typeof(t=e.constructor)&&!1!==i(n=t.prototype)&&!1!==n.hasOwnProperty("isPrototypeOf")}},72366(e,t,n){"use strict";var r=n(20862),i=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.MuiThemeProviderOld=void 0;var a=i(n(67154)),o=i(n(59713)),s=i(n(34575)),u=i(n(93913)),c=i(n(78585)),l=i(n(29754)),f=i(n(2205)),d=i(n(67294)),h=i(n(45697));i(n(42473));var p=i(n(43890)),b=n(55252),m=r(n(51067)),g=function(e){function t(e,n){var r;return(0,s.default)(this,t),(r=(0,c.default)(this,(0,l.default)(t).call(this))).broadcast=(0,p.default)(),r.outerTheme=m.default.initial(n),r.broadcast.setState(r.mergeOuterLocalTheme(e.theme)),r}return(0,f.default)(t,e),(0,u.default)(t,[{key:"getChildContext",value:function(){var e,t=this.props,n=t.disableStylesGeneration,r=t.sheetsCache,i=t.sheetsManager,a=this.context.muiThemeProviderOptions||{};return void 0!==n&&(a.disableStylesGeneration=n),void 0!==r&&(a.sheetsCache=r),void 0!==i&&(a.sheetsManager=i),e={},(0,o.default)(e,m.CHANNEL,this.broadcast),(0,o.default)(e,"muiThemeProviderOptions",a),e}},{key:"componentDidMount",value:function(){var e=this;this.unsubscribeId=m.default.subscribe(this.context,function(t){e.outerTheme=t,e.broadcast.setState(e.mergeOuterLocalTheme(e.props.theme))})}},{key:"componentDidUpdate",value:function(e){this.props.theme!==e.theme&&this.broadcast.setState(this.mergeOuterLocalTheme(this.props.theme))}},{key:"componentWillUnmount",value:function(){null!==this.unsubscribeId&&m.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"mergeOuterLocalTheme",value:function(e){return"function"==typeof e?e(this.outerTheme):this.outerTheme?(0,a.default)({},this.outerTheme,e):e}},{key:"render",value:function(){return this.props.children}}]),t}(d.default.Component);t.MuiThemeProviderOld=g,g.childContextTypes=(0,a.default)({},m.default.contextTypes,{muiThemeProviderOptions:h.default.object}),g.contextTypes=(0,a.default)({},m.default.contextTypes,{muiThemeProviderOptions:h.default.object}),b.ponyfillGlobal.__MUI_STYLES__||(b.ponyfillGlobal.__MUI_STYLES__={}),b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider||(b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider=g);var v=b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider;t.default=v},59114(e,t,n){"use strict";var r=n(95318);function i(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return en?n:e}function a(e){e=e.substr(1);var t=RegExp(".{1,".concat(e.length/3,"}"),"g"),n=e.match(t);return n&&1===n[0].length&&(n=n.map(function(e){return e+e})),n?"rgb(".concat(n.map(function(e){return parseInt(e,16)}).join(", "),")"):""}function o(e){if(0===e.indexOf("#"))return e;function t(e){var t=e.toString(16);return 1===t.length?"0".concat(t):t}var n=s(e).values;return n=n.map(function(e){return t(e)}),"#".concat(n.join(""))}function s(e){if("#"===e.charAt(0))return s(a(e));var t=e.indexOf("("),n=e.substring(0,t),r=e.substring(t+1,e.length-1).split(",");return r=r.map(function(e){return parseFloat(e)}),{type:n,values:r}}function u(e){var t=e.type,n=e.values;return -1!==t.indexOf("rgb")&&(n=n.map(function(e,t){return t<3?parseInt(e,10):e})),-1!==t.indexOf("hsl")&&(n[1]="".concat(n[1],"%"),n[2]="".concat(n[2],"%")),"".concat(e.type,"(").concat(n.join(", "),")")}function c(e,t){var n=l(e),r=l(t);return(Math.max(n,r)+.05)/(Math.min(n,r)+.05)}function l(e){var t=s(e);if(-1!==t.type.indexOf("rgb")){var n=t.values.map(function(e){return(e/=255)<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)});return Number((.2126*n[0]+.7152*n[1]+.0722*n[2]).toFixed(3))}return t.values[2]/100}function f(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.15;return l(e)>.5?h(e,t):p(e,t)}function d(e,t){return e?(e=s(e),t=i(t),("rgb"===e.type||"hsl"===e.type)&&(e.type+="a"),e.values[3]=t,u(e)):e}function h(e,t){if(!e)return e;if(e=s(e),t=i(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]*=1-t;return u(e)}function p(e,t){if(!e)return e;if(e=s(e),t=i(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]+=(255-e.values[n])*t;return u(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.convertHexToRGB=a,t.rgbToHex=o,t.decomposeColor=s,t.recomposeColor=u,t.getContrastRatio=c,t.getLuminance=l,t.emphasize=f,t.fade=d,t.darken=h,t.lighten=p,r(n(42473))},94811(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=s,t.keys=void 0;var i=r(n(67154)),a=r(n(6479)),o=["xs","sm","md","lg","xl"];function s(e){var t=e.values,n=void 0===t?{xs:0,sm:600,md:960,lg:1280,xl:1920}:t,r=e.unit,s=void 0===r?"px":r,u=e.step,c=void 0===u?5:u,l=(0,a.default)(e,["values","unit","step"]);function f(e){var t="number"==typeof n[e]?n[e]:e;return"@media (min-width:".concat(t).concat(s,")")}function d(e){var t=o.indexOf(e)+1,r=n[o[t]];if(t===o.length)return f("xs");var i="number"==typeof r&&t>0?r:e;return"@media (max-width:".concat(i-c/100).concat(s,")")}function h(e,t){var r=o.indexOf(t)+1;return r===o.length?f(e):"@media (min-width:".concat(n[e]).concat(s,") and ")+"(max-width:".concat(n[o[r]]-c/100).concat(s,")")}function p(e){return h(e,e)}function b(e){return n[e]}return(0,i.default)({keys:o,values:n,up:f,down:d,between:h,only:p,width:b},l)}t.keys=o},20237(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=o,r(n(42473));var i=/([[\].#*$><+~=|^:(),"'`\s])/g;function a(e){var t;return String(e).replace(i,"-")}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.dangerouslyUseGlobalCSS,n=void 0!==t&&t,r=e.productionPrefix,i=void 0===r?"jss":r,o=e.seed,s=void 0===o?"":o,u=0;return function(e,t){return(u+=1,n&&t&&t.options.name)?"".concat(a(t.options.name),"-").concat(e.key):"".concat(i).concat(s).concat(u)}}},40226(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=o;var i=r(n(59713)),a=r(n(67154));function o(e,t,n){var r;return(0,a.default)({gutters:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(0,a.default)({paddingLeft:2*t.unit,paddingRight:2*t.unit},n,(0,i.default)({},e.up("sm"),(0,a.default)({paddingLeft:3*t.unit,paddingRight:3*t.unit},n[e.up("sm")])))},toolbar:(r={minHeight:56},(0,i.default)(r,"".concat(e.up("xs")," and (orientation: landscape)"),{minHeight:48}),(0,i.default)(r,e.up("sm"),{minHeight:64}),r)},n)}},71615(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,r(n(59713));var i=r(n(67154)),a=r(n(6479)),o=r(n(94863)),s=r(n(93078));r(n(42473));var u=r(n(94811)),c=r(n(40226)),l=r(n(21091)),f=r(n(45184)),d=r(n(80743)),h=r(n(59591)),p=r(n(5324)),b=r(n(15406)),m=r(n(88676));function g(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.breakpoints,r=void 0===n?{}:n,g=t.mixins,v=void 0===g?{}:g,y=t.palette,w=void 0===y?{}:y,_=t.shadows,E=t.spacing,S=void 0===E?{}:E,k=t.typography,x=void 0===k?{}:k,T=(0,a.default)(t,["breakpoints","mixins","palette","shadows","spacing","typography"]),M=(0,l.default)(w),O=(0,u.default)(r),A=(0,i.default)({},p.default,S);return(0,i.default)({breakpoints:O,direction:"ltr",mixins:(0,c.default)(O,A,v),overrides:{},palette:M,props:{},shadows:_||d.default,typography:(0,f.default)(M,x)},(0,o.default)({shape:h.default,spacing:A,transitions:b.default,zIndex:m.default},T,{isMergeableObject:s.default}))}var v=g;t.default=v},21091(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=m,t.dark=t.light=void 0;var i=r(n(67154)),a=r(n(6479));r(n(42473));var o=r(n(94863)),s=r(n(78768)),u=r(n(124)),c=r(n(70167)),l=r(n(83165)),f=r(n(515)),d=n(59114),h={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.54)",disabled:"rgba(0, 0, 0, 0.38)",hint:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:f.default.white,default:c.default[50]},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.08)",hoverOpacity:.08,selected:"rgba(0, 0, 0, 0.14)",disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)"}};t.light=h;var p={text:{primary:f.default.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",hint:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:c.default[800],default:"#303030"},action:{active:f.default.white,hover:"rgba(255, 255, 255, 0.1)",hoverOpacity:.1,selected:"rgba(255, 255, 255, 0.2)",disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)"}};function b(e,t,n,r){e[t]||(e.hasOwnProperty(n)?e[t]=e[n]:"light"===t?e.light=(0,d.lighten)(e.main,r):"dark"===t&&(e.dark=(0,d.darken)(e.main,1.5*r)))}function m(e){var t=e.primary,n=void 0===t?{light:s.default[300],main:s.default[500],dark:s.default[700]}:t,r=e.secondary,m=void 0===r?{light:u.default.A200,main:u.default.A400,dark:u.default.A700}:r,g=e.error,v=void 0===g?{light:l.default[300],main:l.default[500],dark:l.default[700]}:g,y=e.type,w=void 0===y?"light":y,_=e.contrastThreshold,E=void 0===_?3:_,S=e.tonalOffset,k=void 0===S?.2:S,x=(0,a.default)(e,["primary","secondary","error","type","contrastThreshold","tonalOffset"]);function T(e){var t;return(0,d.getContrastRatio)(e,p.text.primary)>=E?p.text.primary:h.text.primary}function M(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:500,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:700;return!e.main&&e[t]&&(e.main=e[t]),b(e,"light",n,k),b(e,"dark",r,k),e.contrastText||(e.contrastText=T(e.main)),e}M(n),M(m,"A400","A200","A700"),M(v);var O={dark:p,light:h};return(0,o.default)((0,i.default)({common:f.default,type:w,primary:n,secondary:m,error:v,grey:c.default,contrastThreshold:E,getContrastText:T,augmentColor:M,tonalOffset:k},O[w]),x,{clone:!1})}t.dark=p},16059(e,t){"use strict";function n(e){return e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},45184(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=f;var i=r(n(67154)),a=r(n(6479)),o=r(n(94863));r(n(42473));var s=n(55252);function u(e){return Math.round(1e5*e)/1e5}var c={textTransform:"uppercase"},l='"Roboto", "Helvetica", "Arial", sans-serif';function f(e,t){var n="function"==typeof t?t(e):t,r=n.fontFamily,f=void 0===r?l:r,d=n.fontSize,h=void 0===d?14:d,p=n.fontWeightLight,b=void 0===p?300:p,m=n.fontWeightRegular,g=void 0===m?400:m,v=n.fontWeightMedium,y=void 0===v?500:v,w=n.htmlFontSize,_=void 0===w?16:w,E=n.useNextVariants,S=void 0===E?Boolean(s.ponyfillGlobal.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__):E,k=(n.suppressWarning,n.allVariants),x=(0,a.default)(n,["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","htmlFontSize","useNextVariants","suppressWarning","allVariants"]),T=h/14,M=function(e){return"".concat(e/_*T,"rem")},O=function(t,n,r,a,o){return(0,i.default)({color:e.text.primary,fontFamily:f,fontWeight:t,fontSize:M(n),lineHeight:r},f===l?{letterSpacing:"".concat(u(a/n),"em")}:{},o,k)},A={h1:O(b,96,1,-1.5),h2:O(b,60,1,-.5),h3:O(g,48,1.04,0),h4:O(g,34,1.17,.25),h5:O(g,24,1.33,0),h6:O(y,20,1.6,.15),subtitle1:O(g,16,1.75,.15),subtitle2:O(y,14,1.57,.1),body1Next:O(g,16,1.5,.15),body2Next:O(g,14,1.5,.15),buttonNext:O(y,14,1.75,.4,c),captionNext:O(g,12,1.66,.4),overline:O(g,12,2.66,1,c)},L={display4:(0,i.default)({fontSize:M(112),fontWeight:b,fontFamily:f,letterSpacing:"-.04em",lineHeight:"".concat(u(128/112),"em"),marginLeft:"-.04em",color:e.text.secondary},k),display3:(0,i.default)({fontSize:M(56),fontWeight:g,fontFamily:f,letterSpacing:"-.02em",lineHeight:"".concat(u(73/56),"em"),marginLeft:"-.02em",color:e.text.secondary},k),display2:(0,i.default)({fontSize:M(45),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(51/45),"em"),marginLeft:"-.02em",color:e.text.secondary},k),display1:(0,i.default)({fontSize:M(34),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(41/34),"em"),color:e.text.secondary},k),headline:(0,i.default)({fontSize:M(24),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(32.5/24),"em"),color:e.text.primary},k),title:(0,i.default)({fontSize:M(21),fontWeight:y,fontFamily:f,lineHeight:"".concat(u(24.5/21),"em"),color:e.text.primary},k),subheading:(0,i.default)({fontSize:M(16),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(1.5),"em"),color:e.text.primary},k),body2:(0,i.default)({fontSize:M(14),fontWeight:y,fontFamily:f,lineHeight:"".concat(u(24/14),"em"),color:e.text.primary},k),body1:(0,i.default)({fontSize:M(14),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(20.5/14),"em"),color:e.text.primary},k),caption:(0,i.default)({fontSize:M(12),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(1.375),"em"),color:e.text.secondary},k),button:(0,i.default)({fontSize:M(14),textTransform:"uppercase",fontWeight:y,fontFamily:f,color:e.text.primary},k)};return(0,o.default)((0,i.default)({pxToRem:M,round:u,fontFamily:f,fontSize:h,fontWeightLight:b,fontWeightRegular:g,fontWeightMedium:y},L,A,S?{body1:A.body1Next,body2:A.body2Next,button:A.buttonNext,caption:A.captionNext}:{},{useNextVariants:S}),x,{clone:!1})}},42458(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154));r(n(50008)),r(n(42473));var a=r(n(94863));function o(e,t){return t}function s(e){var t="function"==typeof e;function n(n,r){var s=t?e(n):e;if(!r||!n.overrides||!n.overrides[r])return s;var u=n.overrides[r],c=(0,i.default)({},s);return Object.keys(u).forEach(function(e){c[e]=(0,a.default)(c[e],u[e],{arrayMerge:o})}),c}return{create:n,options:{},themingEnabled:t}}var u=s;t.default=u},58057(e,t){"use strict";function n(e){var t,n=e.theme,r=e.name,i=e.props;if(!n.props||!r||!n.props[r])return i;var a=n.props[r];for(t in a)void 0===i[t]&&(i[t]=a[t]);return i}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},32316(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"createGenerateClassName",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"createMuiTheme",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"jssPreset",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"MuiThemeProvider",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"createStyles",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"withStyles",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"withTheme",{enumerable:!0,get:function(){return l.default}});var i=r(n(20237)),a=r(n(71615)),o=r(n(9399)),s=r(n(72366)),u=r(n(16059)),c=r(n(78252)),l=r(n(82313))},9399(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(29059)),a=r(n(28752)),o=r(n(35828)),s=r(n(50462)),u=r(n(65926)),c=r(n(89347));function l(){return{plugins:[(0,i.default)(),(0,a.default)(),(0,o.default)(),(0,s.default)(),"undefined"==typeof window?null:(0,u.default)(),(0,c.default)()]}}var f=l;t.default=f},35199(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154));function a(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.baseClasses,n=e.newClasses;if(e.Component,!n)return t;var r=(0,i.default)({},t);return Object.keys(n).forEach(function(e){n[e]&&(r[e]="".concat(t[e]," ").concat(n[e]))}),r}r(n(42473)),n(55252);var o=a;t.default=o},88693(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={set:function(e,t,n,r){var i=e.get(t);i||(i=new Map,e.set(t,i)),i.set(n,r)},get:function(e,t,n){var r=e.get(t);return r?r.get(n):void 0},delete:function(e,t,n){e.get(t).delete(n)}};t.default=n},31898(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={jss:"64a55d578f856d258dc345b094a2a2b3",sheetsRegistry:"d4bd0baacbc52bbd48bbb9eb24344ecd",sheetOptions:"6fc570d6bd61383819d0f9e7407c452d"};t.default=n},80743(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=.2,r=.14,i=.12;function a(){return["".concat(arguments.length<=0?void 0:arguments[0],"px ").concat(arguments.length<=1?void 0:arguments[1],"px ").concat(arguments.length<=2?void 0:arguments[2],"px ").concat(arguments.length<=3?void 0:arguments[3],"px rgba(0,0,0,").concat(n,")"),"".concat(arguments.length<=4?void 0:arguments[4],"px ").concat(arguments.length<=5?void 0:arguments[5],"px ").concat(arguments.length<=6?void 0:arguments[6],"px ").concat(arguments.length<=7?void 0:arguments[7],"px rgba(0,0,0,").concat(r,")"),"".concat(arguments.length<=8?void 0:arguments[8],"px ").concat(arguments.length<=9?void 0:arguments[9],"px ").concat(arguments.length<=10?void 0:arguments[10],"px ").concat(arguments.length<=11?void 0:arguments[11],"px rgba(0,0,0,").concat(i,")")].join(",")}var o=["none",a(0,1,3,0,0,1,1,0,0,2,1,-1),a(0,1,5,0,0,2,2,0,0,3,1,-2),a(0,1,8,0,0,3,4,0,0,3,3,-2),a(0,2,4,-1,0,4,5,0,0,1,10,0),a(0,3,5,-1,0,5,8,0,0,1,14,0),a(0,3,5,-1,0,6,10,0,0,1,18,0),a(0,4,5,-2,0,7,10,1,0,2,16,1),a(0,5,5,-3,0,8,10,1,0,3,14,2),a(0,5,6,-3,0,9,12,1,0,3,16,2),a(0,6,6,-3,0,10,14,1,0,4,18,3),a(0,6,7,-4,0,11,15,1,0,4,20,3),a(0,7,8,-4,0,12,17,2,0,5,22,4),a(0,7,8,-4,0,13,19,2,0,5,24,4),a(0,7,9,-4,0,14,21,2,0,5,26,4),a(0,8,9,-5,0,15,22,2,0,6,28,5),a(0,8,10,-5,0,16,24,2,0,6,30,5),a(0,8,11,-5,0,17,26,2,0,6,32,5),a(0,9,11,-5,0,18,28,2,0,7,34,6),a(0,9,12,-6,0,19,29,2,0,7,36,6),a(0,10,13,-6,0,20,31,3,0,8,38,7),a(0,10,13,-6,0,21,33,3,0,8,40,7),a(0,10,14,-6,0,22,35,3,0,8,42,7),a(0,11,14,-7,0,23,36,3,0,9,44,8),a(0,11,15,-7,0,24,38,3,0,9,46,8)];t.default=o},59591(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={borderRadius:4};t.default=n},5324(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={unit:8};t.default=n},51067(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.CHANNEL=void 0;var i=r(n(59713)),a="__THEMING__";t.CHANNEL=a;var o={contextTypes:(0,i.default)({},a,function(){}),initial:function(e){return e[a]?e[a].getState():null},subscribe:function(e,t){return e[a]?e[a].subscribe(t):null},unsubscribe:function(e,t){e[a]&&e[a].unsubscribe(t)}};t.default=o},15406(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.isNumber=t.isString=t.formatMs=t.duration=t.easing=void 0;var i=r(n(6479));r(n(42473));var a={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"};t.easing=a;var o={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};t.duration=o;var s=function(e){return"".concat(Math.round(e),"ms")};t.formatMs=s;var u=function(e){return"string"==typeof e};t.isString=u;var c=function(e){return!isNaN(parseFloat(e))};t.isNumber=c;var l={easing:a,duration:o,create:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["all"],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.duration,r=void 0===n?o.standard:n,u=t.easing,c=void 0===u?a.easeInOut:u,l=t.delay,f=void 0===l?0:l;return(0,i.default)(t,["duration","easing","delay"]),(Array.isArray(e)?e:[e]).map(function(e){return"".concat(e," ").concat("string"==typeof r?r:s(r)," ").concat(c," ").concat("string"==typeof f?f:s(f))}).join(",")},getAutoHeightDuration:function(e){if(!e)return 0;var t=e/36;return Math.round((4+15*Math.pow(t,.25)+t/5)*10)}};t.default=l},78252(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.sheetsManager=void 0;var i=r(n(59713)),a=r(n(67154)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(6479)),d=r(n(67294)),h=r(n(45697));r(n(42473));var p=r(n(8679)),b=n(55252),m=n(55690),g=r(n(31898)),v=r(n(9399)),y=r(n(35199)),w=r(n(88693)),_=r(n(71615)),E=r(n(51067)),S=r(n(20237)),k=r(n(42458)),x=r(n(58057)),T=(0,m.create)((0,v.default)()),M=(0,S.default)(),O=-1e11,A=new Map;t.sheetsManager=A;var L={},C=(0,_.default)({typography:{suppressWarning:!0}}),I=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(n){var r,b=t.withTheme,m=void 0!==b&&b,v=t.flip,_=void 0===v?null:v,S=t.name,I=(0,f.default)(t,["withTheme","flip","name"]),D=(0,k.default)(e),N=D.themingEnabled||"string"==typeof S||m;O+=1,D.options.index=O;var P=function(e){function t(e,n){(0,o.default)(this,t),(r=(0,u.default)(this,(0,c.default)(t).call(this,e,n))).jss=n[g.default.jss]||T,r.sheetsManager=A,r.unsubscribeId=null;var r,i=n.muiThemeProviderOptions;return i&&(i.sheetsManager&&(r.sheetsManager=i.sheetsManager),r.sheetsCache=i.sheetsCache,r.disableStylesGeneration=i.disableStylesGeneration),r.stylesCreatorSaved=D,r.sheetOptions=(0,a.default)({generateClassName:M},n[g.default.sheetOptions]),r.theme=N?E.default.initial(n)||C:L,r.attach(r.theme),r.cacheClasses={value:null,lastProp:null,lastJSS:{}},r}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){var e=this;N&&(this.unsubscribeId=E.default.subscribe(this.context,function(t){var n=e.theme;e.theme=t,e.attach(e.theme),e.setState({},function(){e.detach(n)})}))}},{key:"componentDidUpdate",value:function(){this.stylesCreatorSaved}},{key:"componentWillUnmount",value:function(){this.detach(this.theme),null!==this.unsubscribeId&&E.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"getClasses",value:function(){if(this.disableStylesGeneration)return this.props.classes||{};var e=!1,t=w.default.get(this.sheetsManager,this.stylesCreatorSaved,this.theme);return t.sheet.classes!==this.cacheClasses.lastJSS&&(this.cacheClasses.lastJSS=t.sheet.classes,e=!0),this.props.classes!==this.cacheClasses.lastProp&&(this.cacheClasses.lastProp=this.props.classes,e=!0),e&&(this.cacheClasses.value=(0,y.default)({baseClasses:this.cacheClasses.lastJSS,newClasses:this.props.classes,Component:n})),this.cacheClasses.value}},{key:"attach",value:function(e){if(!this.disableStylesGeneration){var t=this.stylesCreatorSaved,n=w.default.get(this.sheetsManager,t,e);if(n||(n={refs:0,sheet:null},w.default.set(this.sheetsManager,t,e,n)),0===n.refs){this.sheetsCache&&(r=w.default.get(this.sheetsCache,t,e)),!r&&((r=this.createSheet(e)).attach(),this.sheetsCache&&w.default.set(this.sheetsCache,t,e,r)),n.sheet=r;var r,i=this.context[g.default.sheetsRegistry];i&&i.add(r)}n.refs+=1}}},{key:"createSheet",value:function(e){var t=this.stylesCreatorSaved.create(e,S),r=S;return this.jss.createStyleSheet(t,(0,a.default)({meta:r,classNamePrefix:r,flip:"boolean"==typeof _?_:"rtl"===e.direction,link:!1},this.sheetOptions,this.stylesCreatorSaved.options,{name:S||n.displayName},I))}},{key:"detach",value:function(e){if(!this.disableStylesGeneration){var t=w.default.get(this.sheetsManager,this.stylesCreatorSaved,e);if(t.refs-=1,0===t.refs){w.default.delete(this.sheetsManager,this.stylesCreatorSaved,e),this.jss.removeStyleSheet(t.sheet);var n=this.context[g.default.sheetsRegistry];n&&n.remove(t.sheet)}}}},{key:"render",value:function(){var e=this.props,t=(e.classes,e.innerRef),r=(0,f.default)(e,["classes","innerRef"]),i=(0,x.default)({theme:this.theme,name:S,props:r});return m&&!i.theme&&(i.theme=this.theme),d.default.createElement(n,(0,a.default)({},i,{classes:this.getClasses(),ref:t}))}}]),t}(d.default.Component);return P.contextTypes=(0,a.default)((r={muiThemeProviderOptions:h.default.object},(0,i.default)(r,g.default.jss,h.default.object),(0,i.default)(r,g.default.sheetOptions,h.default.object),(0,i.default)(r,g.default.sheetsRegistry,h.default.object),r),N?E.default.contextTypes:{}),(0,p.default)(P,n),P}};b.ponyfillGlobal.__MUI_STYLES__||(b.ponyfillGlobal.__MUI_STYLES__={}),b.ponyfillGlobal.__MUI_STYLES__.withStyles||(b.ponyfillGlobal.__MUI_STYLES__.withStyles=I);var D=function(e,t){return b.ponyfillGlobal.__MUI_STYLES__.withStyles(e,(0,a.default)({defaultTheme:C},t))};t.default=D},82313(e,t,n){"use strict";var r,i=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=i(n(67154)),o=i(n(6479)),s=i(n(34575)),u=i(n(93913)),c=i(n(78585)),l=i(n(29754)),f=i(n(2205)),d=i(n(67294));i(n(45697));var h=i(n(8679)),p=n(55252),b=i(n(71615)),m=i(n(51067));function g(){return r||(r=(0,b.default)({typography:{suppressWarning:!0}}))}var v=function(){return function(e){var t=function(t){function n(e,t){var r;return(0,s.default)(this,n),(r=(0,c.default)(this,(0,l.default)(n).call(this))).state={theme:m.default.initial(t)||g()},r}return(0,f.default)(n,t),(0,u.default)(n,[{key:"componentDidMount",value:function(){var e=this;this.unsubscribeId=m.default.subscribe(this.context,function(t){e.setState({theme:t})})}},{key:"componentWillUnmount",value:function(){null!==this.unsubscribeId&&m.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"render",value:function(){var t=this.props,n=t.innerRef,r=(0,o.default)(t,["innerRef"]);return d.default.createElement(e,(0,a.default)({theme:this.state.theme,ref:n},r))}}]),n}(d.default.Component);return t.contextTypes=m.default.contextTypes,(0,h.default)(t,e),t}};p.ponyfillGlobal.__MUI_STYLES__||(p.ponyfillGlobal.__MUI_STYLES__={}),p.ponyfillGlobal.__MUI_STYLES__.withTheme||(p.ponyfillGlobal.__MUI_STYLES__.withTheme=v);var y=p.ponyfillGlobal.__MUI_STYLES__.withTheme;t.default=y},88676(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={mobileStepper:1e3,appBar:1100,drawer:1200,modal:1300,snackbar:1400,tooltip:1500};t.default=n},41929(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getTransitionProps=r,t.reflow=void 0;var n=function(e){return e.scrollTop};function r(e,t){var n=e.timeout,r=e.style,i=void 0===r?{}:r;return{duration:i.transitionDuration||"number"==typeof n?n:n[t.mode],delay:i.transitionDelay}}t.reflow=n},346(e,t){"use strict";function n(e,t){return function(){return null}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},98741(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.capitalize=a,t.contains=o,t.findIndex=s,t.find=u,t.createChainedFunction=c;var i=r(n(50008));function a(e){return e.charAt(0).toUpperCase()+e.slice(1)}function o(e,t){return Object.keys(t).every(function(n){return e.hasOwnProperty(n)&&e[n]===t[n]})}function s(e,t){for(var n=(0,i.default)(t),r=0;r-1?e[n]:void 0}function c(){for(var e=arguments.length,t=Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:window,n=(0,i.default)(e);return n.defaultView||n.parentView||t}var o=a;t.default=o},44370(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.cloneElementWithClassName=o,t.cloneChildrenWithClassName=s,t.isMuiElement=u,t.setRef=c;var i=r(n(67294)),a=r(n(94184));function o(e,t){return i.default.cloneElement(e,{className:(0,a.default)(e.props.className,t)})}function s(e,t){return i.default.Children.map(e,function(e){return i.default.isValidElement(e)&&o(e,t)})}function u(e,t){return i.default.isValidElement(e)&&-1!==t.indexOf(e.type.muiName)}function c(e,t){"function"==typeof e?e(t):e&&(e.current=t)}},47348(e,t){"use strict";function n(e){return function(){return null}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},21677(e,t){"use strict";function n(e,t,n,r,i){return null}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},78290(e,t,n){"use strict";var r=n(20862);Object.defineProperty(t,"__esModule",{value:!0});var i={};Object.defineProperty(t,"default",{enumerable:!0,get:function(){return a.default}});var a=r(n(88446));Object.keys(a).forEach(function(e){"default"!==e&&"__esModule"!==e&&(Object.prototype.hasOwnProperty.call(i,e)||Object.defineProperty(t,e,{enumerable:!0,get:function(){return a[e]}}))})},88446(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.isWidthDown=t.isWidthUp=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(96421)),h=r(n(20296));n(55252);var p=r(n(8679)),b=r(n(82313)),m=n(94811),g=r(n(58057)),v=function(e,t){var n=!(arguments.length>2)||void 0===arguments[2]||arguments[2];return n?m.keys.indexOf(e)<=m.keys.indexOf(t):m.keys.indexOf(e)2)||void 0===arguments[2]||arguments[2];return n?m.keys.indexOf(t)<=m.keys.indexOf(e):m.keys.indexOf(t)0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=e.withTheme,r=void 0!==n&&n,v=e.noSSR,y=void 0!==v&&v,w=e.initialWidth,_=e.resizeInterval,E=void 0===_?166:_,S=function(e){function n(e){var t;return(0,o.default)(this,n),(t=(0,u.default)(this,(0,c.default)(n).call(this,e))).state={width:y?t.getWidth():void 0},"undefined"!=typeof window&&(t.handleResize=(0,h.default)(function(){var e=t.getWidth();e!==t.state.width&&t.setState({width:e})},E)),t}return(0,l.default)(n,e),(0,s.default)(n,[{key:"componentDidMount",value:function(){var e=this.getWidth();e!==this.state.width&&this.setState({width:e})}},{key:"componentWillUnmount",value:function(){this.handleResize.clear()}},{key:"getWidth",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.innerWidth,t=this.props.theme.breakpoints,n=null,r=1;null===n&&ri.Z,componentPropType:()=>r.Z,exactProp:()=>a.ZP,getDisplayName:()=>o.ZP,ponyfillGlobal:()=>s.Z});var r=n(78728),i=n(5477),a=n(43781),o=n(25189),s=n(34712);/** @license Material-UI v3.0.0-alpha.3 + */ var r=n(47798);function i(e){return!0===r(e)&&"[object Object]"===Object.prototype.toString.call(e)}e.exports=function(e){var t,n;return!1!==i(e)&&"function"==typeof(t=e.constructor)&&!1!==i(n=t.prototype)&&!1!==n.hasOwnProperty("isPrototypeOf")}},72366(e,t,n){"use strict";var r=n(20862),i=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.MuiThemeProviderOld=void 0;var a=i(n(67154)),o=i(n(59713)),s=i(n(34575)),u=i(n(93913)),c=i(n(78585)),l=i(n(29754)),f=i(n(2205)),d=i(n(67294)),h=i(n(45697));i(n(42473));var p=i(n(43890)),b=n(55252),m=r(n(51067)),g=function(e){function t(e,n){var r;return(0,s.default)(this,t),(r=(0,c.default)(this,(0,l.default)(t).call(this))).broadcast=(0,p.default)(),r.outerTheme=m.default.initial(n),r.broadcast.setState(r.mergeOuterLocalTheme(e.theme)),r}return(0,f.default)(t,e),(0,u.default)(t,[{key:"getChildContext",value:function(){var e,t=this.props,n=t.disableStylesGeneration,r=t.sheetsCache,i=t.sheetsManager,a=this.context.muiThemeProviderOptions||{};return void 0!==n&&(a.disableStylesGeneration=n),void 0!==r&&(a.sheetsCache=r),void 0!==i&&(a.sheetsManager=i),e={},(0,o.default)(e,m.CHANNEL,this.broadcast),(0,o.default)(e,"muiThemeProviderOptions",a),e}},{key:"componentDidMount",value:function(){var e=this;this.unsubscribeId=m.default.subscribe(this.context,function(t){e.outerTheme=t,e.broadcast.setState(e.mergeOuterLocalTheme(e.props.theme))})}},{key:"componentDidUpdate",value:function(e){this.props.theme!==e.theme&&this.broadcast.setState(this.mergeOuterLocalTheme(this.props.theme))}},{key:"componentWillUnmount",value:function(){null!==this.unsubscribeId&&m.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"mergeOuterLocalTheme",value:function(e){return"function"==typeof e?e(this.outerTheme):this.outerTheme?(0,a.default)({},this.outerTheme,e):e}},{key:"render",value:function(){return this.props.children}}]),t}(d.default.Component);t.MuiThemeProviderOld=g,g.childContextTypes=(0,a.default)({},m.default.contextTypes,{muiThemeProviderOptions:h.default.object}),g.contextTypes=(0,a.default)({},m.default.contextTypes,{muiThemeProviderOptions:h.default.object}),b.ponyfillGlobal.__MUI_STYLES__||(b.ponyfillGlobal.__MUI_STYLES__={}),b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider||(b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider=g);var v=b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider;t.default=v},59114(e,t,n){"use strict";var r=n(95318);function i(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return en?n:e}function a(e){e=e.substr(1);var t=RegExp(".{1,".concat(e.length/3,"}"),"g"),n=e.match(t);return n&&1===n[0].length&&(n=n.map(function(e){return e+e})),n?"rgb(".concat(n.map(function(e){return parseInt(e,16)}).join(", "),")"):""}function o(e){if(0===e.indexOf("#"))return e;function t(e){var t=e.toString(16);return 1===t.length?"0".concat(t):t}var n=s(e).values;return n=n.map(function(e){return t(e)}),"#".concat(n.join(""))}function s(e){if("#"===e.charAt(0))return s(a(e));var t=e.indexOf("("),n=e.substring(0,t),r=e.substring(t+1,e.length-1).split(",");return r=r.map(function(e){return parseFloat(e)}),{type:n,values:r}}function u(e){var t=e.type,n=e.values;return -1!==t.indexOf("rgb")&&(n=n.map(function(e,t){return t<3?parseInt(e,10):e})),-1!==t.indexOf("hsl")&&(n[1]="".concat(n[1],"%"),n[2]="".concat(n[2],"%")),"".concat(e.type,"(").concat(n.join(", "),")")}function c(e,t){var n=l(e),r=l(t);return(Math.max(n,r)+.05)/(Math.min(n,r)+.05)}function l(e){var t=s(e);if(-1!==t.type.indexOf("rgb")){var n=t.values.map(function(e){return(e/=255)<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)});return Number((.2126*n[0]+.7152*n[1]+.0722*n[2]).toFixed(3))}return t.values[2]/100}function f(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.15;return l(e)>.5?h(e,t):p(e,t)}function d(e,t){return e?(e=s(e),t=i(t),("rgb"===e.type||"hsl"===e.type)&&(e.type+="a"),e.values[3]=t,u(e)):e}function h(e,t){if(!e)return e;if(e=s(e),t=i(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]*=1-t;return u(e)}function p(e,t){if(!e)return e;if(e=s(e),t=i(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]+=(255-e.values[n])*t;return u(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.convertHexToRGB=a,t.rgbToHex=o,t.decomposeColor=s,t.recomposeColor=u,t.getContrastRatio=c,t.getLuminance=l,t.emphasize=f,t.fade=d,t.darken=h,t.lighten=p,r(n(42473))},94811(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=s,t.keys=void 0;var i=r(n(67154)),a=r(n(6479)),o=["xs","sm","md","lg","xl"];function s(e){var t=e.values,n=void 0===t?{xs:0,sm:600,md:960,lg:1280,xl:1920}:t,r=e.unit,s=void 0===r?"px":r,u=e.step,c=void 0===u?5:u,l=(0,a.default)(e,["values","unit","step"]);function f(e){var t="number"==typeof n[e]?n[e]:e;return"@media (min-width:".concat(t).concat(s,")")}function d(e){var t=o.indexOf(e)+1,r=n[o[t]];if(t===o.length)return f("xs");var i="number"==typeof r&&t>0?r:e;return"@media (max-width:".concat(i-c/100).concat(s,")")}function h(e,t){var r=o.indexOf(t)+1;return r===o.length?f(e):"@media (min-width:".concat(n[e]).concat(s,") and ")+"(max-width:".concat(n[o[r]]-c/100).concat(s,")")}function p(e){return h(e,e)}function b(e){return n[e]}return(0,i.default)({keys:o,values:n,up:f,down:d,between:h,only:p,width:b},l)}t.keys=o},20237(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=o,r(n(42473));var i=/([[\].#*$><+~=|^:(),"'`\s])/g;function a(e){var t;return String(e).replace(i,"-")}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.dangerouslyUseGlobalCSS,n=void 0!==t&&t,r=e.productionPrefix,i=void 0===r?"jss":r,o=e.seed,s=void 0===o?"":o,u=0;return function(e,t){return(u+=1,n&&t&&t.options.name)?"".concat(a(t.options.name),"-").concat(e.key):"".concat(i).concat(s).concat(u)}}},40226(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=o;var i=r(n(59713)),a=r(n(67154));function o(e,t,n){var r;return(0,a.default)({gutters:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(0,a.default)({paddingLeft:2*t.unit,paddingRight:2*t.unit},n,(0,i.default)({},e.up("sm"),(0,a.default)({paddingLeft:3*t.unit,paddingRight:3*t.unit},n[e.up("sm")])))},toolbar:(r={minHeight:56},(0,i.default)(r,"".concat(e.up("xs")," and (orientation: landscape)"),{minHeight:48}),(0,i.default)(r,e.up("sm"),{minHeight:64}),r)},n)}},71615(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,r(n(59713));var i=r(n(67154)),a=r(n(6479)),o=r(n(94863)),s=r(n(93078));r(n(42473));var u=r(n(94811)),c=r(n(40226)),l=r(n(21091)),f=r(n(45184)),d=r(n(80743)),h=r(n(59591)),p=r(n(5324)),b=r(n(15406)),m=r(n(88676));function g(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.breakpoints,r=void 0===n?{}:n,g=t.mixins,v=void 0===g?{}:g,y=t.palette,w=void 0===y?{}:y,_=t.shadows,E=t.spacing,S=void 0===E?{}:E,k=t.typography,x=void 0===k?{}:k,T=(0,a.default)(t,["breakpoints","mixins","palette","shadows","spacing","typography"]),M=(0,l.default)(w),O=(0,u.default)(r),A=(0,i.default)({},p.default,S);return(0,i.default)({breakpoints:O,direction:"ltr",mixins:(0,c.default)(O,A,v),overrides:{},palette:M,props:{},shadows:_||d.default,typography:(0,f.default)(M,x)},(0,o.default)({shape:h.default,spacing:A,transitions:b.default,zIndex:m.default},T,{isMergeableObject:s.default}))}var v=g;t.default=v},21091(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=m,t.dark=t.light=void 0;var i=r(n(67154)),a=r(n(6479));r(n(42473));var o=r(n(94863)),s=r(n(78768)),u=r(n(124)),c=r(n(70167)),l=r(n(83165)),f=r(n(515)),d=n(59114),h={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.54)",disabled:"rgba(0, 0, 0, 0.38)",hint:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:f.default.white,default:c.default[50]},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.08)",hoverOpacity:.08,selected:"rgba(0, 0, 0, 0.14)",disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)"}};t.light=h;var p={text:{primary:f.default.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",hint:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:c.default[800],default:"#303030"},action:{active:f.default.white,hover:"rgba(255, 255, 255, 0.1)",hoverOpacity:.1,selected:"rgba(255, 255, 255, 0.2)",disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)"}};function b(e,t,n,r){e[t]||(e.hasOwnProperty(n)?e[t]=e[n]:"light"===t?e.light=(0,d.lighten)(e.main,r):"dark"===t&&(e.dark=(0,d.darken)(e.main,1.5*r)))}function m(e){var t=e.primary,n=void 0===t?{light:s.default[300],main:s.default[500],dark:s.default[700]}:t,r=e.secondary,m=void 0===r?{light:u.default.A200,main:u.default.A400,dark:u.default.A700}:r,g=e.error,v=void 0===g?{light:l.default[300],main:l.default[500],dark:l.default[700]}:g,y=e.type,w=void 0===y?"light":y,_=e.contrastThreshold,E=void 0===_?3:_,S=e.tonalOffset,k=void 0===S?.2:S,x=(0,a.default)(e,["primary","secondary","error","type","contrastThreshold","tonalOffset"]);function T(e){var t;return(0,d.getContrastRatio)(e,p.text.primary)>=E?p.text.primary:h.text.primary}function M(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:500,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:700;return!e.main&&e[t]&&(e.main=e[t]),b(e,"light",n,k),b(e,"dark",r,k),e.contrastText||(e.contrastText=T(e.main)),e}M(n),M(m,"A400","A200","A700"),M(v);var O={dark:p,light:h};return(0,o.default)((0,i.default)({common:f.default,type:w,primary:n,secondary:m,error:v,grey:c.default,contrastThreshold:E,getContrastText:T,augmentColor:M,tonalOffset:k},O[w]),x,{clone:!1})}t.dark=p},16059(e,t){"use strict";function n(e){return e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},45184(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=f;var i=r(n(67154)),a=r(n(6479)),o=r(n(94863));r(n(42473));var s=n(55252);function u(e){return Math.round(1e5*e)/1e5}var c={textTransform:"uppercase"},l='"Roboto", "Helvetica", "Arial", sans-serif';function f(e,t){var n="function"==typeof t?t(e):t,r=n.fontFamily,f=void 0===r?l:r,d=n.fontSize,h=void 0===d?14:d,p=n.fontWeightLight,b=void 0===p?300:p,m=n.fontWeightRegular,g=void 0===m?400:m,v=n.fontWeightMedium,y=void 0===v?500:v,w=n.htmlFontSize,_=void 0===w?16:w,E=n.useNextVariants,S=void 0===E?Boolean(s.ponyfillGlobal.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__):E,k=(n.suppressWarning,n.allVariants),x=(0,a.default)(n,["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","htmlFontSize","useNextVariants","suppressWarning","allVariants"]),T=h/14,M=function(e){return"".concat(e/_*T,"rem")},O=function(t,n,r,a,o){return(0,i.default)({color:e.text.primary,fontFamily:f,fontWeight:t,fontSize:M(n),lineHeight:r},f===l?{letterSpacing:"".concat(u(a/n),"em")}:{},o,k)},A={h1:O(b,96,1,-1.5),h2:O(b,60,1,-.5),h3:O(g,48,1.04,0),h4:O(g,34,1.17,.25),h5:O(g,24,1.33,0),h6:O(y,20,1.6,.15),subtitle1:O(g,16,1.75,.15),subtitle2:O(y,14,1.57,.1),body1Next:O(g,16,1.5,.15),body2Next:O(g,14,1.5,.15),buttonNext:O(y,14,1.75,.4,c),captionNext:O(g,12,1.66,.4),overline:O(g,12,2.66,1,c)},L={display4:(0,i.default)({fontSize:M(112),fontWeight:b,fontFamily:f,letterSpacing:"-.04em",lineHeight:"".concat(u(128/112),"em"),marginLeft:"-.04em",color:e.text.secondary},k),display3:(0,i.default)({fontSize:M(56),fontWeight:g,fontFamily:f,letterSpacing:"-.02em",lineHeight:"".concat(u(73/56),"em"),marginLeft:"-.02em",color:e.text.secondary},k),display2:(0,i.default)({fontSize:M(45),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(51/45),"em"),marginLeft:"-.02em",color:e.text.secondary},k),display1:(0,i.default)({fontSize:M(34),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(41/34),"em"),color:e.text.secondary},k),headline:(0,i.default)({fontSize:M(24),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(32.5/24),"em"),color:e.text.primary},k),title:(0,i.default)({fontSize:M(21),fontWeight:y,fontFamily:f,lineHeight:"".concat(u(24.5/21),"em"),color:e.text.primary},k),subheading:(0,i.default)({fontSize:M(16),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(1.5),"em"),color:e.text.primary},k),body2:(0,i.default)({fontSize:M(14),fontWeight:y,fontFamily:f,lineHeight:"".concat(u(24/14),"em"),color:e.text.primary},k),body1:(0,i.default)({fontSize:M(14),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(20.5/14),"em"),color:e.text.primary},k),caption:(0,i.default)({fontSize:M(12),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(1.375),"em"),color:e.text.secondary},k),button:(0,i.default)({fontSize:M(14),textTransform:"uppercase",fontWeight:y,fontFamily:f,color:e.text.primary},k)};return(0,o.default)((0,i.default)({pxToRem:M,round:u,fontFamily:f,fontSize:h,fontWeightLight:b,fontWeightRegular:g,fontWeightMedium:y},L,A,S?{body1:A.body1Next,body2:A.body2Next,button:A.buttonNext,caption:A.captionNext}:{},{useNextVariants:S}),x,{clone:!1})}},42458(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154));r(n(50008)),r(n(42473));var a=r(n(94863));function o(e,t){return t}function s(e){var t="function"==typeof e;function n(n,r){var s=t?e(n):e;if(!r||!n.overrides||!n.overrides[r])return s;var u=n.overrides[r],c=(0,i.default)({},s);return Object.keys(u).forEach(function(e){c[e]=(0,a.default)(c[e],u[e],{arrayMerge:o})}),c}return{create:n,options:{},themingEnabled:t}}var u=s;t.default=u},58057(e,t){"use strict";function n(e){var t,n=e.theme,r=e.name,i=e.props;if(!n.props||!r||!n.props[r])return i;var a=n.props[r];for(t in a)void 0===i[t]&&(i[t]=a[t]);return i}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},32316(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"createGenerateClassName",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"createMuiTheme",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"jssPreset",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"MuiThemeProvider",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"createStyles",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"withStyles",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"withTheme",{enumerable:!0,get:function(){return l.default}});var i=r(n(20237)),a=r(n(71615)),o=r(n(9399)),s=r(n(72366)),u=r(n(16059)),c=r(n(78252)),l=r(n(82313))},9399(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(29059)),a=r(n(28752)),o=r(n(35828)),s=r(n(50462)),u=r(n(65926)),c=r(n(89347));function l(){return{plugins:[(0,i.default)(),(0,a.default)(),(0,o.default)(),(0,s.default)(),"undefined"==typeof window?null:(0,u.default)(),(0,c.default)()]}}var f=l;t.default=f},35199(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154));function a(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.baseClasses,n=e.newClasses;if(e.Component,!n)return t;var r=(0,i.default)({},t);return Object.keys(n).forEach(function(e){n[e]&&(r[e]="".concat(t[e]," ").concat(n[e]))}),r}r(n(42473)),n(55252);var o=a;t.default=o},88693(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={set:function(e,t,n,r){var i=e.get(t);i||(i=new Map,e.set(t,i)),i.set(n,r)},get:function(e,t,n){var r=e.get(t);return r?r.get(n):void 0},delete:function(e,t,n){e.get(t).delete(n)}};t.default=n},31898(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={jss:"64a55d578f856d258dc345b094a2a2b3",sheetsRegistry:"d4bd0baacbc52bbd48bbb9eb24344ecd",sheetOptions:"6fc570d6bd61383819d0f9e7407c452d"};t.default=n},80743(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=.2,r=.14,i=.12;function a(){return["".concat(arguments.length<=0?void 0:arguments[0],"px ").concat(arguments.length<=1?void 0:arguments[1],"px ").concat(arguments.length<=2?void 0:arguments[2],"px ").concat(arguments.length<=3?void 0:arguments[3],"px rgba(0,0,0,").concat(n,")"),"".concat(arguments.length<=4?void 0:arguments[4],"px ").concat(arguments.length<=5?void 0:arguments[5],"px ").concat(arguments.length<=6?void 0:arguments[6],"px ").concat(arguments.length<=7?void 0:arguments[7],"px rgba(0,0,0,").concat(r,")"),"".concat(arguments.length<=8?void 0:arguments[8],"px ").concat(arguments.length<=9?void 0:arguments[9],"px ").concat(arguments.length<=10?void 0:arguments[10],"px ").concat(arguments.length<=11?void 0:arguments[11],"px rgba(0,0,0,").concat(i,")")].join(",")}var o=["none",a(0,1,3,0,0,1,1,0,0,2,1,-1),a(0,1,5,0,0,2,2,0,0,3,1,-2),a(0,1,8,0,0,3,4,0,0,3,3,-2),a(0,2,4,-1,0,4,5,0,0,1,10,0),a(0,3,5,-1,0,5,8,0,0,1,14,0),a(0,3,5,-1,0,6,10,0,0,1,18,0),a(0,4,5,-2,0,7,10,1,0,2,16,1),a(0,5,5,-3,0,8,10,1,0,3,14,2),a(0,5,6,-3,0,9,12,1,0,3,16,2),a(0,6,6,-3,0,10,14,1,0,4,18,3),a(0,6,7,-4,0,11,15,1,0,4,20,3),a(0,7,8,-4,0,12,17,2,0,5,22,4),a(0,7,8,-4,0,13,19,2,0,5,24,4),a(0,7,9,-4,0,14,21,2,0,5,26,4),a(0,8,9,-5,0,15,22,2,0,6,28,5),a(0,8,10,-5,0,16,24,2,0,6,30,5),a(0,8,11,-5,0,17,26,2,0,6,32,5),a(0,9,11,-5,0,18,28,2,0,7,34,6),a(0,9,12,-6,0,19,29,2,0,7,36,6),a(0,10,13,-6,0,20,31,3,0,8,38,7),a(0,10,13,-6,0,21,33,3,0,8,40,7),a(0,10,14,-6,0,22,35,3,0,8,42,7),a(0,11,14,-7,0,23,36,3,0,9,44,8),a(0,11,15,-7,0,24,38,3,0,9,46,8)];t.default=o},59591(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={borderRadius:4};t.default=n},5324(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={unit:8};t.default=n},51067(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.CHANNEL=void 0;var i=r(n(59713)),a="__THEMING__";t.CHANNEL=a;var o={contextTypes:(0,i.default)({},a,function(){}),initial:function(e){return e[a]?e[a].getState():null},subscribe:function(e,t){return e[a]?e[a].subscribe(t):null},unsubscribe:function(e,t){e[a]&&e[a].unsubscribe(t)}};t.default=o},15406(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.isNumber=t.isString=t.formatMs=t.duration=t.easing=void 0;var i=r(n(6479));r(n(42473));var a={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"};t.easing=a;var o={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};t.duration=o;var s=function(e){return"".concat(Math.round(e),"ms")};t.formatMs=s;var u=function(e){return"string"==typeof e};t.isString=u;var c=function(e){return!isNaN(parseFloat(e))};t.isNumber=c;var l={easing:a,duration:o,create:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["all"],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.duration,r=void 0===n?o.standard:n,u=t.easing,c=void 0===u?a.easeInOut:u,l=t.delay,f=void 0===l?0:l;return(0,i.default)(t,["duration","easing","delay"]),(Array.isArray(e)?e:[e]).map(function(e){return"".concat(e," ").concat("string"==typeof r?r:s(r)," ").concat(c," ").concat("string"==typeof f?f:s(f))}).join(",")},getAutoHeightDuration:function(e){if(!e)return 0;var t=e/36;return Math.round((4+15*Math.pow(t,.25)+t/5)*10)}};t.default=l},78252(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.sheetsManager=void 0;var i=r(n(59713)),a=r(n(67154)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(6479)),d=r(n(67294)),h=r(n(45697));r(n(42473));var p=r(n(8679)),b=n(55252),m=n(55690),g=r(n(31898)),v=r(n(9399)),y=r(n(35199)),w=r(n(88693)),_=r(n(71615)),E=r(n(51067)),S=r(n(20237)),k=r(n(42458)),x=r(n(58057)),T=(0,m.create)((0,v.default)()),M=(0,S.default)(),O=-1e11,A=new Map;t.sheetsManager=A;var L={},C=(0,_.default)({typography:{suppressWarning:!0}}),I=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(n){var r,b=t.withTheme,m=void 0!==b&&b,v=t.flip,_=void 0===v?null:v,S=t.name,I=(0,f.default)(t,["withTheme","flip","name"]),D=(0,k.default)(e),N=D.themingEnabled||"string"==typeof S||m;O+=1,D.options.index=O;var P=function(e){function t(e,n){(0,o.default)(this,t),(r=(0,u.default)(this,(0,c.default)(t).call(this,e,n))).jss=n[g.default.jss]||T,r.sheetsManager=A,r.unsubscribeId=null;var r,i=n.muiThemeProviderOptions;return i&&(i.sheetsManager&&(r.sheetsManager=i.sheetsManager),r.sheetsCache=i.sheetsCache,r.disableStylesGeneration=i.disableStylesGeneration),r.stylesCreatorSaved=D,r.sheetOptions=(0,a.default)({generateClassName:M},n[g.default.sheetOptions]),r.theme=N?E.default.initial(n)||C:L,r.attach(r.theme),r.cacheClasses={value:null,lastProp:null,lastJSS:{}},r}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){var e=this;N&&(this.unsubscribeId=E.default.subscribe(this.context,function(t){var n=e.theme;e.theme=t,e.attach(e.theme),e.setState({},function(){e.detach(n)})}))}},{key:"componentDidUpdate",value:function(){this.stylesCreatorSaved}},{key:"componentWillUnmount",value:function(){this.detach(this.theme),null!==this.unsubscribeId&&E.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"getClasses",value:function(){if(this.disableStylesGeneration)return this.props.classes||{};var e=!1,t=w.default.get(this.sheetsManager,this.stylesCreatorSaved,this.theme);return t.sheet.classes!==this.cacheClasses.lastJSS&&(this.cacheClasses.lastJSS=t.sheet.classes,e=!0),this.props.classes!==this.cacheClasses.lastProp&&(this.cacheClasses.lastProp=this.props.classes,e=!0),e&&(this.cacheClasses.value=(0,y.default)({baseClasses:this.cacheClasses.lastJSS,newClasses:this.props.classes,Component:n})),this.cacheClasses.value}},{key:"attach",value:function(e){if(!this.disableStylesGeneration){var t=this.stylesCreatorSaved,n=w.default.get(this.sheetsManager,t,e);if(n||(n={refs:0,sheet:null},w.default.set(this.sheetsManager,t,e,n)),0===n.refs){this.sheetsCache&&(r=w.default.get(this.sheetsCache,t,e)),!r&&((r=this.createSheet(e)).attach(),this.sheetsCache&&w.default.set(this.sheetsCache,t,e,r)),n.sheet=r;var r,i=this.context[g.default.sheetsRegistry];i&&i.add(r)}n.refs+=1}}},{key:"createSheet",value:function(e){var t=this.stylesCreatorSaved.create(e,S),r=S;return this.jss.createStyleSheet(t,(0,a.default)({meta:r,classNamePrefix:r,flip:"boolean"==typeof _?_:"rtl"===e.direction,link:!1},this.sheetOptions,this.stylesCreatorSaved.options,{name:S||n.displayName},I))}},{key:"detach",value:function(e){if(!this.disableStylesGeneration){var t=w.default.get(this.sheetsManager,this.stylesCreatorSaved,e);if(t.refs-=1,0===t.refs){w.default.delete(this.sheetsManager,this.stylesCreatorSaved,e),this.jss.removeStyleSheet(t.sheet);var n=this.context[g.default.sheetsRegistry];n&&n.remove(t.sheet)}}}},{key:"render",value:function(){var e=this.props,t=(e.classes,e.innerRef),r=(0,f.default)(e,["classes","innerRef"]),i=(0,x.default)({theme:this.theme,name:S,props:r});return m&&!i.theme&&(i.theme=this.theme),d.default.createElement(n,(0,a.default)({},i,{classes:this.getClasses(),ref:t}))}}]),t}(d.default.Component);return P.contextTypes=(0,a.default)((r={muiThemeProviderOptions:h.default.object},(0,i.default)(r,g.default.jss,h.default.object),(0,i.default)(r,g.default.sheetOptions,h.default.object),(0,i.default)(r,g.default.sheetsRegistry,h.default.object),r),N?E.default.contextTypes:{}),(0,p.default)(P,n),P}};b.ponyfillGlobal.__MUI_STYLES__||(b.ponyfillGlobal.__MUI_STYLES__={}),b.ponyfillGlobal.__MUI_STYLES__.withStyles||(b.ponyfillGlobal.__MUI_STYLES__.withStyles=I);var D=function(e,t){return b.ponyfillGlobal.__MUI_STYLES__.withStyles(e,(0,a.default)({defaultTheme:C},t))};t.default=D},82313(e,t,n){"use strict";var r,i=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=i(n(67154)),o=i(n(6479)),s=i(n(34575)),u=i(n(93913)),c=i(n(78585)),l=i(n(29754)),f=i(n(2205)),d=i(n(67294));i(n(45697));var h=i(n(8679)),p=n(55252),b=i(n(71615)),m=i(n(51067));function g(){return r||(r=(0,b.default)({typography:{suppressWarning:!0}}))}var v=function(){return function(e){var t=function(t){function n(e,t){var r;return(0,s.default)(this,n),(r=(0,c.default)(this,(0,l.default)(n).call(this))).state={theme:m.default.initial(t)||g()},r}return(0,f.default)(n,t),(0,u.default)(n,[{key:"componentDidMount",value:function(){var e=this;this.unsubscribeId=m.default.subscribe(this.context,function(t){e.setState({theme:t})})}},{key:"componentWillUnmount",value:function(){null!==this.unsubscribeId&&m.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"render",value:function(){var t=this.props,n=t.innerRef,r=(0,o.default)(t,["innerRef"]);return d.default.createElement(e,(0,a.default)({theme:this.state.theme,ref:n},r))}}]),n}(d.default.Component);return t.contextTypes=m.default.contextTypes,(0,h.default)(t,e),t}};p.ponyfillGlobal.__MUI_STYLES__||(p.ponyfillGlobal.__MUI_STYLES__={}),p.ponyfillGlobal.__MUI_STYLES__.withTheme||(p.ponyfillGlobal.__MUI_STYLES__.withTheme=v);var y=p.ponyfillGlobal.__MUI_STYLES__.withTheme;t.default=y},88676(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={mobileStepper:1e3,appBar:1100,drawer:1200,modal:1300,snackbar:1400,tooltip:1500};t.default=n},41929(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getTransitionProps=r,t.reflow=void 0;var n=function(e){return e.scrollTop};function r(e,t){var n=e.timeout,r=e.style,i=void 0===r?{}:r;return{duration:i.transitionDuration||"number"==typeof n?n:n[t.mode],delay:i.transitionDelay}}t.reflow=n},346(e,t){"use strict";function n(e,t){return function(){return null}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},98741(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.capitalize=a,t.contains=o,t.findIndex=s,t.find=u,t.createChainedFunction=c;var i=r(n(50008));function a(e){return e.charAt(0).toUpperCase()+e.slice(1)}function o(e,t){return Object.keys(t).every(function(n){return e.hasOwnProperty(n)&&e[n]===t[n]})}function s(e,t){for(var n=(0,i.default)(t),r=0;r-1?e[n]:void 0}function c(){for(var e=arguments.length,t=Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:window,n=(0,i.default)(e);return n.defaultView||n.parentView||t}var o=a;t.default=o},44370(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.cloneElementWithClassName=o,t.cloneChildrenWithClassName=s,t.isMuiElement=u,t.setRef=c;var i=r(n(67294)),a=r(n(94184));function o(e,t){return i.default.cloneElement(e,{className:(0,a.default)(e.props.className,t)})}function s(e,t){return i.default.Children.map(e,function(e){return i.default.isValidElement(e)&&o(e,t)})}function u(e,t){return i.default.isValidElement(e)&&-1!==t.indexOf(e.type.muiName)}function c(e,t){"function"==typeof e?e(t):e&&(e.current=t)}},47348(e,t){"use strict";function n(e){return function(){return null}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},21677(e,t){"use strict";function n(e,t,n,r,i){return null}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},78290(e,t,n){"use strict";var r=n(20862);Object.defineProperty(t,"__esModule",{value:!0});var i={};Object.defineProperty(t,"default",{enumerable:!0,get:function(){return a.default}});var a=r(n(88446));Object.keys(a).forEach(function(e){"default"!==e&&"__esModule"!==e&&(Object.prototype.hasOwnProperty.call(i,e)||Object.defineProperty(t,e,{enumerable:!0,get:function(){return a[e]}}))})},88446(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.isWidthDown=t.isWidthUp=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(96421)),h=r(n(20296));n(55252);var p=r(n(8679)),b=r(n(82313)),m=n(94811),g=r(n(58057)),v=function(e,t){var n=!(arguments.length>2)||void 0===arguments[2]||arguments[2];return n?m.keys.indexOf(e)<=m.keys.indexOf(t):m.keys.indexOf(e)2)||void 0===arguments[2]||arguments[2];return n?m.keys.indexOf(t)<=m.keys.indexOf(e):m.keys.indexOf(t)0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=e.withTheme,r=void 0!==n&&n,v=e.noSSR,y=void 0!==v&&v,w=e.initialWidth,_=e.resizeInterval,E=void 0===_?166:_,S=function(e){function n(e){var t;return(0,o.default)(this,n),(t=(0,u.default)(this,(0,c.default)(n).call(this,e))).state={width:y?t.getWidth():void 0},"undefined"!=typeof window&&(t.handleResize=(0,h.default)(function(){var e=t.getWidth();e!==t.state.width&&t.setState({width:e})},E)),t}return(0,l.default)(n,e),(0,s.default)(n,[{key:"componentDidMount",value:function(){var e=this.getWidth();e!==this.state.width&&this.setState({width:e})}},{key:"componentWillUnmount",value:function(){this.handleResize.clear()}},{key:"getWidth",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.innerWidth,t=this.props.theme.breakpoints,n=null,r=1;null===n&&ri.Z,componentPropType:()=>r.Z,exactProp:()=>a.ZP,getDisplayName:()=>o.ZP,ponyfillGlobal:()=>s.Z});var r=n(78728),i=n(5477),a=n(43781),o=n(25189),s=n(34712);/** @license Material-UI v3.0.0-alpha.3 * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -181,7 +181,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 nOB});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(97779),h=n(47886),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(55977),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 e5(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e6(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,n7.kG)(!!n,32),n}var rb=n(10542),rm=n(53712),rg=n(21436),rv=Object.prototype.hasOwnProperty;function ry(e,t){return void 0===t&&(t=Object.create(null)),rw(rp(t.client),e).useQuery(t)}function rw(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new r_(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 r_=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rb.J)({loading:!0,data:void 0,error:void 0,networkStatus:rc.I.loading}),this.skipStandbyResult=(0,rb.J)({loading:!1,data:void 0,error:void 0,networkStatus:rc.I.ready}),this.toQueryResultCache=new(re.mr?WeakMap:Map),rh(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n7.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,rs.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rn((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,ra.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(!rv.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ra.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:rc.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,ra.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,rm.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ro.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,n8._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__&&n7.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rg.O)(e.errors)?new ru.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,n8._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,n8.pi)((0,n8.pi)((0,n8.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,rg.O)(e.errors)&&(t.error=new ru.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:rc.I.refetch}),this.observable.refetch())},e}();function rE(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 ry(i$,e)},iG=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=iz({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(ij,null):o?l.createElement(iN,{error:o}):i?l.createElement(iD,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iW=n(67932),iK=n(8126),iV="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 iq(e){if(iZ())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iZ(){return("undefined"==typeof Intl?"undefined":iV(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iX="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},iJ=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":iX(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"!==iX(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i0=i1;var i2=new i0;function i3(e,t){if(!iZ())return function(e){return e.toString()};var n=i5(e),r=JSON.stringify(t),i=i2.get(String(n),r)||i2.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i4={};function i5(e){var t=e.toString();return i4[t]?i4[t]:i4[t]=iq(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 i9(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i7(e)}function i7(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var ae=n(54087),at=n.n(ae);function an(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)ao(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=at()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){at().cancel(this.scheduledTick)}};function aa(e){var t=ar(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function ao(e,t){aa(e),au(t,e),as(t,e)}function as(e,t){var n=ac(e,t);e.splice(n,0,t)}function au(e,t){var n=e.indexOf(t);e.splice(n,1)}function ac(e,t){var n=t.nextUpdateTime;return an(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var al=(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})]),af=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(al).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),ad=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ab(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=ap(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(iK.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iK.Z(y)},[y]);t=(0,l.useMemo)(function(){return i9(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=ah(u,2),l=c[0],f=c[1];return f=o?av: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=ah(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ah(M,2),A=O[0],L=O[1],C=ah((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ai.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=ah(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i3(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,ad({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,ad({},f,{verboseDate:I?R:void 0}),j):j}ab.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:af,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},ab.defaultProps={locales:[],component:ay,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ab=l.memo(ab);let am=ab;var ag,av=31536e9;function ay(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ap(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",ad({},a,{dateTime:o,title:r?n:void 0}),i)}ay.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var aw=n(30381),a_=n.n(aw),aE=n(31657);function aS(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ak(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 ru.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,ra.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,ra.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,n8.pi)({reset:c},a)]}var ou=n(59067),oc=n(28428),ol=n(11186),of=n(78513);function od(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var oh=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:od({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"}})},op=(0,b.withStyles)(oh)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ia.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),ob=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},om=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},og=(0,b.withStyles)(oh)(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 ov(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 sZ=sq;function sX(e,t){var n=this.__data__,r=s$(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sJ=sX;function sQ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cI}let cN=cD;var cP="[object Arguments]",cR="[object Array]",cj="[object Boolean]",cF="[object Date]",cY="[object Error]",cB="[object Function]",cU="[object Map]",cH="[object Number]",c$="[object Object]",cz="[object RegExp]",cG="[object Set]",cW="[object String]",cK="[object WeakMap]",cV="[object ArrayBuffer]",cq="[object DataView]",cZ="[object Float64Array]",cX="[object Int8Array]",cJ="[object Int16Array]",cQ="[object Int32Array]",c1="[object Uint8Array]",c0="[object Uint8ClampedArray]",c2="[object Uint16Array]",c3="[object Uint32Array]",c4={};function c5(e){return eD(e)&&cN(e.length)&&!!c4[eC(e)]}c4["[object Float32Array]"]=c4[cZ]=c4[cX]=c4[cJ]=c4[cQ]=c4[c1]=c4[c0]=c4[c2]=c4[c3]=!0,c4[cP]=c4[cR]=c4[cV]=c4[cj]=c4[cq]=c4[cF]=c4[cY]=c4[cB]=c4[cU]=c4[cH]=c4[c$]=c4[cz]=c4[cG]=c4[cW]=c4[cK]=!1;let c6=c5;function c9(e){return function(t){return e(t)}}let c8=c9;var c7=n(79730),le=c7.Z&&c7.Z.isTypedArray,lt=le?c8(le):c6;let ln=lt;var lr=Object.prototype.hasOwnProperty;function li(e,t){var n=cT(e),r=!n&&ck(e),i=!n&&!r&&(0,cM.Z)(e),a=!n&&!r&&!i&&ln(e),o=n||r||i||a,s=o?cm(e.length,String):[],u=s.length;for(var c in e)(t||lr.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cC(c,u)))&&s.push(c);return s}let la=li;var lo=Object.prototype;function ls(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||lo)}let lu=ls;var lc=sM(Object.keys,Object);let ll=lc;var lf=Object.prototype.hasOwnProperty;function ld(e){if(!lu(e))return ll(e);var t=[];for(var n in Object(e))lf.call(e,n)&&"constructor"!=n&&t.push(n);return t}let lh=ld;function lp(e){return null!=e&&cN(e.length)&&!ui(e)}let lb=lp;function lm(e){return lb(e)?la(e):lh(e)}let lg=lm;function lv(e,t){return e&&cp(t,lg(t),e)}let ly=lv;function lw(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let l_=lw;var lE=Object.prototype.hasOwnProperty;function lS(e){if(!ed(e))return l_(e);var t=lu(e),n=[];for(var r in e)"constructor"==r&&(t||!lE.call(e,r))||n.push(r);return n}let lk=lS;function lx(e){return lb(e)?la(e,!0):lk(e)}let lT=lx;function lM(e,t){return e&&cp(t,lT(t),e)}let lO=lM;var lA=n(42896);function lL(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hc(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hl=function(e){return Array.isArray(e)&&0===e.length},hf=function(e){return"function"==typeof e},hd=function(e){return null!==e&&"object"==typeof e},hh=function(e){return String(Math.floor(Number(e)))===e},hp=function(e){return"[object String]"===Object.prototype.toString.call(e)},hb=function(e){return 0===l.Children.count(e)},hm=function(e){return hd(e)&&hf(e.then)};function hg(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 hy(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,hg(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=hv(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 sx.all([t,n,r],{arrayMerge:hC})})},[h.validate,h.validationSchema,T,S,k]),O=hP(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}),sh()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sh()(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);hm(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sh()(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&&!sh()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hk,E({type:"SET_ERRORS",payload:h.initialErrors||hk}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hx,E({type:"SET_TOUCHED",payload:h.initialTouched||hx}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hP(function(e){if(y.current[e]&&hf(y.current[e].validate)){var t=hg(_.values,e),n=y.current[e].validate(t);return hm(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=hP(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=hP(function(e,t){var r=hf(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=hP(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hv(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hp(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)?hD(hg(_.values,r),l,c):d?hI(f):c}r&&j(r,i)},[j,_.values]),Y=hP(function(e){if(hp(e))return function(t){return F(t,e)};F(e)}),B=hP(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=hP(function(e){if(hp(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hf(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=hP(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=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(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=hP(function(){return f(_.values,V)}),Z=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hg(_.values,e),error:hg(_.errors,e),touched:!!hg(_.touched,e),initialValue:hg(p.current,e),initialTouched:!!hg(m.current,e),initialError:hg(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=hd(e),n=t?e.name:e,r=hg(_.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!sh()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hf(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ho({},_,{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 hM(e){var t=hT(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(h_,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hf(r)?r(t):hb(r)?null:l.Children.only(r):null)}function hO(e){var t={};if(e.inner){if(0===e.inner.length)return hv(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;hg(t,o.path)||(t=hv(t,o.path,o.message))}}return t}function hA(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hL(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hL(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)||sj(e)?hL(e):""!==e?e:void 0}):sj(e[r])?t[r]=hL(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hC(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?sx(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sx(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hI(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hD(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 hN="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hP(e){var t=(0,l.useRef)(e);return hN(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(ho({},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=hv(n.values,a,e(hg(n.values,a))),u=r?i(hg(n.errors,a)):void 0,c=t?o(hg(n.touched,a)):void 0;return hl(u)&&(u=void 0),hl(c)&&(c=void 0),ho({},n,{values:s,errors:r?hv(n.errors,a,u):n.errors,touched:t?hv(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hH(t),[ha(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hY(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 hF(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 hB(n,e,t)},function(t){return hB(t,e,null)},function(t){return hB(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 hU(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(hc(n)),n.pop=n.pop.bind(hc(n)),n}hs(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sh()(hg(e.formik.values,e.name),hg(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?hH(n):[];return t||(t=r[e]),hf(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=hu(t.formik,["validate","validationSchema"]),s=ho({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hb(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var h$=n(24802),hz=n(71209),hG=n(91750),hW=n(11970),hK=n(4689),hV=n(67598),hq=function(){return(hq=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 hX(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hZ(e,["disabled","field","form","onBlur","helperText"]),d=hg(u,i.name),h=hg(s,i.name)&&!!d;return hq(hq({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 hJ(e){var t=e.children,n=hZ(e,["children"]);return(0,l.createElement)(i_.Z,hq({},hX(n)),t)}function hQ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h1(e){return(0,l.createElement)(h$.Z,hq({},hQ(e)))}function h0(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hZ(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({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 h2(e){return(0,l.createElement)(hz.Z,hq({},h0(e)))}function h3(e){var t=e.Label,n=hZ(e,["Label"]);return(0,l.createElement)(hG.Z,hq({control:(0,l.createElement)(hz.Z,hq({},h0(n)))},t))}function h4(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h5(e){return(0,l.createElement)(hW.default,hq({},h4(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hZ(t,["onBlur"]),i=(e.form,e.onBlur),a=hZ(e,["field","form","onBlur"]);return hq(hq({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h9(e){return(0,l.createElement)(hK.Z,hq({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h7(e){return(0,l.createElement)(hV.default,hq({},h8(e)))}hJ.displayName="FormikMaterialUITextField",h1.displayName="FormikMaterialUISwitch",h2.displayName="FormikMaterialUICheckbox",h3.displayName="FormikMaterialUICheckboxWithLabel",h5.displayName="FormikMaterialUISelect",h9.displayName="FormikMaterialUIRadioGroup",h7.displayName="FormikMaterialUIInputBase";try{a=Map}catch(pe){}try{o=Set}catch(pt){}function pn(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(pr);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]:pn(e[i],t,n)}return r}return e}function pr(e){return pn(e,[],[])}let pi=Object.prototype.toString,pa=Error.prototype.toString,po=RegExp.prototype.toString,ps="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",pu=/^Symbol\((.*)\)(.*)$/;function pc(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pl(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pc(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return ps.call(e).replace(pu,"Symbol($1)");let r=pi.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pa.call(e)+"]":"RegExp"===r?po.call(e):null}function pf(e,t){let n=pl(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pl(this[e],t);return null!==r?r:n},2)}let pd={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: \`${pf(n,!0)}\``+(i?` (cast from the value \`${pf(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"},ph={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"},pp={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"},pb={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pm={isValue:"${path} field must be ${value}"},pg={noUnknown:"${path} field has unspecified keys: ${unknown}"},pv={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:pd,string:ph,number:pp,date:pb,object:pg,array:pv,boolean:pm});var py=n(18721),pw=n.n(py);let p_=e=>e&&e.__isYupSchema__;class pE{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!pw()(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(!p_(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pS=pE;function pk(e){return null==e?[]:[].concat(e)}function px(){return(px=Object.assign||function(e){for(var t=1;tpf(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=[],pk(e).forEach(e=>{pM.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,pM)}}let pO=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pA(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pO(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pM(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pj(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pR(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 pN.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pC()(pP({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pM(pM.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pP({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=>{pM.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}pM.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pN.prototype.__isYupRef=!0;let pF=e=>e.substr(0,e.length-1).substr(1);function pY(e,t,n,r=n){let i,a,o;return t?((0,pI.forEach)(t,(s,u,c)=>{let l=u?pF(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 pB{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){pN.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pN.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 pB;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 pU(){return(pU=Object.assign||function(e){for(var t=1;t{this.typeError(pd.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pU({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=pU({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pr(pU({},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=pU({},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(pU({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pf(e),a=pf(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 nOF});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(97779),h=n(47886),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(55977),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 e5(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e6(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,n7.kG)(!!n,32),n}var rb=n(10542),rm=n(53712),rg=n(21436),rv=Object.prototype.hasOwnProperty;function ry(e,t){return void 0===t&&(t=Object.create(null)),rw(rp(t.client),e).useQuery(t)}function rw(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new r_(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 r_=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rb.J)({loading:!0,data:void 0,error:void 0,networkStatus:rc.I.loading}),this.skipStandbyResult=(0,rb.J)({loading:!1,data:void 0,error:void 0,networkStatus:rc.I.ready}),this.toQueryResultCache=new(re.mr?WeakMap:Map),rh(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n7.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,rs.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rn((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,ra.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(!rv.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ra.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:rc.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,ra.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,rm.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ro.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,n8._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__&&n7.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rg.O)(e.errors)?new ru.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,n8._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,n8.pi)((0,n8.pi)((0,n8.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,rg.O)(e.errors)&&(t.error=new ru.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:rc.I.refetch}),this.observable.refetch())},e}();function rE(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 ry(i$,e)},iG=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=iz({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(ij,null):o?l.createElement(iN,{error:o}):i?l.createElement(iD,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iW=n(67932),iK=n(8126),iV="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 iq(e){if(iZ())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iZ(){return("undefined"==typeof Intl?"undefined":iV(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iX="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},iJ=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":iX(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"!==iX(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i0=i1;var i2=new i0;function i3(e,t){if(!iZ())return function(e){return e.toString()};var n=i5(e),r=JSON.stringify(t),i=i2.get(String(n),r)||i2.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i4={};function i5(e){var t=e.toString();return i4[t]?i4[t]:i4[t]=iq(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 i9(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i7(e)}function i7(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var ae=n(54087),at=n.n(ae);function an(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)ao(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=at()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){at().cancel(this.scheduledTick)}};function aa(e){var t=ar(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function ao(e,t){aa(e),au(t,e),as(t,e)}function as(e,t){var n=ac(e,t);e.splice(n,0,t)}function au(e,t){var n=e.indexOf(t);e.splice(n,1)}function ac(e,t){var n=t.nextUpdateTime;return an(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var al=(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})]),af=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(al).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),ad=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ab(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=ap(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(iK.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iK.Z(y)},[y]);t=(0,l.useMemo)(function(){return i9(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=ah(u,2),l=c[0],f=c[1];return f=o?av: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=ah(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ah(M,2),A=O[0],L=O[1],C=ah((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ai.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=ah(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i3(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,ad({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,ad({},f,{verboseDate:I?R:void 0}),j):j}ab.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:af,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},ab.defaultProps={locales:[],component:ay,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ab=l.memo(ab);let am=ab;var ag,av=31536e9;function ay(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ap(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",ad({},a,{dateTime:o,title:r?n:void 0}),i)}ay.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var aw=n(30381),a_=n.n(aw),aE=n(31657);function aS(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ak(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 ru.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,ra.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,ra.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,n8.pi)({reset:c},a)]}var ou=n(59067),oc=n(28428),ol=n(11186),of=n(78513);function od(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var oh=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:od({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"}})},op=(0,b.withStyles)(oh)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ia.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),ob=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},om=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},og=(0,b.withStyles)(oh)(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 ov(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 sZ=sq;function sX(e,t){var n=this.__data__,r=s$(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sJ=sX;function sQ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cI}let cN=cD;var cP="[object Arguments]",cR="[object Array]",cj="[object Boolean]",cF="[object Date]",cY="[object Error]",cB="[object Function]",cU="[object Map]",cH="[object Number]",c$="[object Object]",cz="[object RegExp]",cG="[object Set]",cW="[object String]",cK="[object WeakMap]",cV="[object ArrayBuffer]",cq="[object DataView]",cZ="[object Float64Array]",cX="[object Int8Array]",cJ="[object Int16Array]",cQ="[object Int32Array]",c1="[object Uint8Array]",c0="[object Uint8ClampedArray]",c2="[object Uint16Array]",c3="[object Uint32Array]",c4={};function c5(e){return eD(e)&&cN(e.length)&&!!c4[eC(e)]}c4["[object Float32Array]"]=c4[cZ]=c4[cX]=c4[cJ]=c4[cQ]=c4[c1]=c4[c0]=c4[c2]=c4[c3]=!0,c4[cP]=c4[cR]=c4[cV]=c4[cj]=c4[cq]=c4[cF]=c4[cY]=c4[cB]=c4[cU]=c4[cH]=c4[c$]=c4[cz]=c4[cG]=c4[cW]=c4[cK]=!1;let c6=c5;function c9(e){return function(t){return e(t)}}let c8=c9;var c7=n(79730),le=c7.Z&&c7.Z.isTypedArray,lt=le?c8(le):c6;let ln=lt;var lr=Object.prototype.hasOwnProperty;function li(e,t){var n=cT(e),r=!n&&ck(e),i=!n&&!r&&(0,cM.Z)(e),a=!n&&!r&&!i&&ln(e),o=n||r||i||a,s=o?cm(e.length,String):[],u=s.length;for(var c in e)(t||lr.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cC(c,u)))&&s.push(c);return s}let la=li;var lo=Object.prototype;function ls(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||lo)}let lu=ls;var lc=sM(Object.keys,Object);let ll=lc;var lf=Object.prototype.hasOwnProperty;function ld(e){if(!lu(e))return ll(e);var t=[];for(var n in Object(e))lf.call(e,n)&&"constructor"!=n&&t.push(n);return t}let lh=ld;function lp(e){return null!=e&&cN(e.length)&&!ui(e)}let lb=lp;function lm(e){return lb(e)?la(e):lh(e)}let lg=lm;function lv(e,t){return e&&cp(t,lg(t),e)}let ly=lv;function lw(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let l_=lw;var lE=Object.prototype.hasOwnProperty;function lS(e){if(!ed(e))return l_(e);var t=lu(e),n=[];for(var r in e)"constructor"==r&&(t||!lE.call(e,r))||n.push(r);return n}let lk=lS;function lx(e){return lb(e)?la(e,!0):lk(e)}let lT=lx;function lM(e,t){return e&&cp(t,lT(t),e)}let lO=lM;var lA=n(42896);function lL(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hc(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hl=function(e){return Array.isArray(e)&&0===e.length},hf=function(e){return"function"==typeof e},hd=function(e){return null!==e&&"object"==typeof e},hh=function(e){return String(Math.floor(Number(e)))===e},hp=function(e){return"[object String]"===Object.prototype.toString.call(e)},hb=function(e){return 0===l.Children.count(e)},hm=function(e){return hd(e)&&hf(e.then)};function hg(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 hy(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,hg(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=hv(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 sx.all([t,n,r],{arrayMerge:hC})})},[h.validate,h.validationSchema,T,S,k]),O=hP(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}),sh()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sh()(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);hm(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sh()(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&&!sh()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hk,E({type:"SET_ERRORS",payload:h.initialErrors||hk}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hx,E({type:"SET_TOUCHED",payload:h.initialTouched||hx}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hP(function(e){if(y.current[e]&&hf(y.current[e].validate)){var t=hg(_.values,e),n=y.current[e].validate(t);return hm(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=hP(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=hP(function(e,t){var r=hf(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=hP(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hv(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hp(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)?hD(hg(_.values,r),l,c):d?hI(f):c}r&&j(r,i)},[j,_.values]),Y=hP(function(e){if(hp(e))return function(t){return F(t,e)};F(e)}),B=hP(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=hP(function(e){if(hp(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hf(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=hP(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=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(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=hP(function(){return f(_.values,V)}),Z=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hg(_.values,e),error:hg(_.errors,e),touched:!!hg(_.touched,e),initialValue:hg(p.current,e),initialTouched:!!hg(m.current,e),initialError:hg(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=hd(e),n=t?e.name:e,r=hg(_.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!sh()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hf(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ho({},_,{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 hM(e){var t=hT(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(h_,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hf(r)?r(t):hb(r)?null:l.Children.only(r):null)}function hO(e){var t={};if(e.inner){if(0===e.inner.length)return hv(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;hg(t,o.path)||(t=hv(t,o.path,o.message))}}return t}function hA(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hL(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hL(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)||sj(e)?hL(e):""!==e?e:void 0}):sj(e[r])?t[r]=hL(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hC(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?sx(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sx(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hI(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hD(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 hN="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hP(e){var t=(0,l.useRef)(e);return hN(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(ho({},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=hv(n.values,a,e(hg(n.values,a))),u=r?i(hg(n.errors,a)):void 0,c=t?o(hg(n.touched,a)):void 0;return hl(u)&&(u=void 0),hl(c)&&(c=void 0),ho({},n,{values:s,errors:r?hv(n.errors,a,u):n.errors,touched:t?hv(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hH(t),[ha(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hY(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 hF(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 hB(n,e,t)},function(t){return hB(t,e,null)},function(t){return hB(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 hU(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(hc(n)),n.pop=n.pop.bind(hc(n)),n}hs(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sh()(hg(e.formik.values,e.name),hg(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?hH(n):[];return t||(t=r[e]),hf(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=hu(t.formik,["validate","validationSchema"]),s=ho({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hb(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var h$=n(24802),hz=n(71209),hG=n(91750),hW=n(11970),hK=n(4689),hV=n(67598),hq=function(){return(hq=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 hX(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hZ(e,["disabled","field","form","onBlur","helperText"]),d=hg(u,i.name),h=hg(s,i.name)&&!!d;return hq(hq({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 hJ(e){var t=e.children,n=hZ(e,["children"]);return(0,l.createElement)(i_.Z,hq({},hX(n)),t)}function hQ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h1(e){return(0,l.createElement)(h$.Z,hq({},hQ(e)))}function h0(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hZ(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({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 h2(e){return(0,l.createElement)(hz.Z,hq({},h0(e)))}function h3(e){var t=e.Label,n=hZ(e,["Label"]);return(0,l.createElement)(hG.Z,hq({control:(0,l.createElement)(hz.Z,hq({},h0(n)))},t))}function h4(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h5(e){return(0,l.createElement)(hW.default,hq({},h4(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hZ(t,["onBlur"]),i=(e.form,e.onBlur),a=hZ(e,["field","form","onBlur"]);return hq(hq({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h9(e){return(0,l.createElement)(hK.Z,hq({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h7(e){return(0,l.createElement)(hV.default,hq({},h8(e)))}hJ.displayName="FormikMaterialUITextField",h1.displayName="FormikMaterialUISwitch",h2.displayName="FormikMaterialUICheckbox",h3.displayName="FormikMaterialUICheckboxWithLabel",h5.displayName="FormikMaterialUISelect",h9.displayName="FormikMaterialUIRadioGroup",h7.displayName="FormikMaterialUIInputBase";try{a=Map}catch(pe){}try{o=Set}catch(pt){}function pn(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(pr);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]:pn(e[i],t,n)}return r}return e}function pr(e){return pn(e,[],[])}let pi=Object.prototype.toString,pa=Error.prototype.toString,po=RegExp.prototype.toString,ps="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",pu=/^Symbol\((.*)\)(.*)$/;function pc(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pl(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pc(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return ps.call(e).replace(pu,"Symbol($1)");let r=pi.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pa.call(e)+"]":"RegExp"===r?po.call(e):null}function pf(e,t){let n=pl(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pl(this[e],t);return null!==r?r:n},2)}let pd={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: \`${pf(n,!0)}\``+(i?` (cast from the value \`${pf(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"},ph={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"},pp={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"},pb={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pm={isValue:"${path} field must be ${value}"},pg={noUnknown:"${path} field has unspecified keys: ${unknown}"},pv={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:pd,string:ph,number:pp,date:pb,object:pg,array:pv,boolean:pm});var py=n(18721),pw=n.n(py);let p_=e=>e&&e.__isYupSchema__;class pE{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!pw()(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(!p_(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pS=pE;function pk(e){return null==e?[]:[].concat(e)}function px(){return(px=Object.assign||function(e){for(var t=1;tpf(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=[],pk(e).forEach(e=>{pM.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,pM)}}let pO=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pA(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pO(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pM(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pj(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pR(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 pN.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pC()(pP({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pM(pM.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pP({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=>{pM.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}pM.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pN.prototype.__isYupRef=!0;let pF=e=>e.substr(0,e.length-1).substr(1);function pY(e,t,n,r=n){let i,a,o;return t?((0,pI.forEach)(t,(s,u,c)=>{let l=u?pF(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 pB{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){pN.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pN.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 pB;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 pU(){return(pU=Object.assign||function(e){for(var t=1;t{this.typeError(pd.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pU({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=pU({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pr(pU({},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=pU({},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(pU({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pf(e),a=pf(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,pU({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),pA({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pA({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pU({},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(pU({},t,{value:e}))._validate(e,pU({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pM.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pM.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pr(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=pd.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pd.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=pd.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pj(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=pk(e).map(e=>new pN(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pS(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pj({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=pd.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pj({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=pd.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pj({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 p$ of(pH.prototype.__isYupSchema__=!0,["validate","validateSync"]))pH.prototype[`${p$}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pY(this,e,t,n.context);return a[p$](r&&r[i],pU({},n,{parent:r,path:e}))};for(let pz of["equals","is"])pH.prototype[pz]=pH.prototype.oneOf;for(let pG of["not","nope"])pH.prototype[pG]=pH.prototype.notOneOf;pH.prototype.optional=pH.prototype.notRequired;let pW=pH;function pK(){return new pW}pK.prototype=pW.prototype;let pV=e=>null==e;function pq(){return new pZ}class pZ extends pH{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=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pV(e)||!0===e})}isFalse(e=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pV(e)||!1===e})}}pq.prototype=pZ.prototype;let pX=/^((([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,pJ=/^((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,pQ=/^(?:[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,p1=e=>pV(e)||e===e.trim(),p0=({}).toString();function p2(){return new p3}class p3 extends pH{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===p0?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=ph.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t=ph.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pV(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||ph.matches,params:{regex:e},test:t=>pV(t)||""===t&&n||-1!==t.search(e)})}email(e=ph.email){return this.matches(pX,{name:"email",message:e,excludeEmptyString:!0})}url(e=ph.url){return this.matches(pJ,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=ph.uuid){return this.matches(pQ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=ph.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:p1})}lowercase(e=ph.lowercase){return this.transform(e=>pV(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toLowerCase()})}uppercase(e=ph.uppercase){return this.transform(e=>pV(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toUpperCase()})}}p2.prototype=p3.prototype;let p4=e=>e!=+e;function p5(){return new p6}class p6 extends pH{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&&!p4(e)}min(e,t=pp.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t>=this.resolve(e)}})}max(e,t=pp.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(t)||t<=this.resolve(e)}})}lessThan(e,t=pp.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pV(t)||tthis.resolve(e)}})}positive(e=pp.positive){return this.moreThan(0,e)}negative(e=pp.negative){return this.lessThan(0,e)}integer(e=pp.integer){return this.test({name:"integer",message:e,test:e=>pV(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pV(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=>pV(t)?t:Math[e](t))}}p5.prototype=p6.prototype;var p9=/^(\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=p9.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 p7=new Date(""),be=e=>"[object Date]"===Object.prototype.toString.call(e);function bt(){return new bn}class bn extends pH{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p7:new Date(e))})})}_typeCheck(e){return be(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pN.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=pb.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pV(e)||e>=this.resolve(n)}})}max(e,t=pb.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pV(e)||e<=this.resolve(n)}})}}bn.INVALID_DATE=p7,bt.prototype=bn.prototype,bt.INVALID_DATE=p7;var br=n(11865),bi=n.n(br),ba=n(68929),bo=n.n(ba),bs=n(67523),bu=n.n(bs),bc=n(94633),bl=n.n(bc);function bf(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pI.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(pw()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pN.isRef(o)&&o.isSibling?i(o.path,a):p_(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bl().array(r,n).reverse()}function bd(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 bh(e){return(t,n)=>bd(e,t)-bd(e,n)}function bp(){return(bp=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bm(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bg=bh([]);class bv extends pH{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bg,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 bb(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=bp({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=pw()(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(!pM.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bb(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],bp({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pA({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=bp({},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 pH&&i instanceof pH&&(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=bh(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=bf(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,pI.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return pw()(i,e)&&(a=bp({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pg.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=bm(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pg.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bu()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(bo())}snakeCase(){return this.transformKeys(bi())}constantCase(){return this.transformKeys(e=>bi()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pC()(this.fields,e=>e.describe()),e}}function by(e){return new bv(e)}function bw(){return(bw=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,bw({},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(!pM.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)}pA({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(!p_(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pf(e));return t.innerType=e,t}length(e,t=pv.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pv.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pv.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(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)}}b_.prototype=bE.prototype;var bS=by().shape({name:p2().required("Required"),url:p2().required("Required")}),bk=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hM,{initialValues:t,validationSchema:bS,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bx=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(iv,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Edit Bridge",action:l.createElement(aL.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aK.Z,null,l.createElement(bk,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bT(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}},ml=n(76023);function mf(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||(i[n]=e[n]);return i}function mB(e,t){if(null==e)return{};var n,r,i=mY(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mU(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 mX={};function mJ(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mX[t]||(mX[t]=mZ(e)),mX[t]}function mQ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mJ(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mV({},e,n[t])},t)}function m1(e){return e.join(" ")}function m0(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m2({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m2(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=m0(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=mV({},s,{className:m1(m)||void 0,style:mQ(s.className,Object.assign({},s.style,i),n)})}else d=mV({},s,{className:m1(s.className)});var g=h(t.children);return l.createElement(c,mq({key:o},d),g)}}let m3=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m4=/\n/g;function m5(e){return e.match(m4)}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 m9(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 m7(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 ge(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mV({},i,"function"==typeof e?e(t):e)}function gt(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=ge(r,n,i);t.unshift(m7(n,h))}return f&l&&(d.style=mV({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gn(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 gt({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=ge(s,t,o);e.unshift(m7(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(m5(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(gt({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=gt({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 gc=n(98695),gl=n.n(gc);let gf=["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 gd=gs(gl(),gu);gd.supportedLanguages=gf;let gh=gd;var gp=n(64566),gb=n(68239);function gm(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gg(){var e=gm(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gg=function(){return e},e}var gv=function(){var e="[[TelemetryIngress.Endpoints]] \nNetwork = '...' # e.g. EVM. Solana, Starknet, Cosmos \nChainID = '...' # e.g. 1, 5, devnet, mainnet-beta URL\nURL = '...'\nServerPubKey = '...'";return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Deprecation warning"}),l.createElement(aK.Z,null,l.createElement(x.default,{variant:"h5",gutterBottom:!0},"Starting in ",l.createElement("code",null,"v2.9.0"),":"),l.createElement(w.default,{dense:!0},l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"TelemetryIngress.URL")," and"," ",l.createElement("code",null,"TelemetryIngress.ServerPubKey")," will no longer be allowed. Please switch to ",l.createElement("code",null,"TelemetryIngress.Endpoints"),":",l.createElement(gh,{language:"toml",style:gu},e))),l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"P2P.V1")," will no longer be supported and must not be set in TOML configuration in order to boot. Use"," ",l.createElement("code",null,"P2P.V2")," instead. If you are using both,"," ",l.createElement("code",null,"V1")," can simply be removed.")))))},gy=n0(gg()),gw=function(e){var t=e.children;return l.createElement(ii.Z,null,l.createElement(ie.default,{component:"th",scope:"row",colSpan:3},t))},g_=function(){return l.createElement(gw,null,"...")},gE=function(e){var t=e.children;return l.createElement(gw,null,t)},gS=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(gE,null,i);if(t)return l.createElement(g_,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mR.Z,{defaultExpanded:o},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},a),l.createElement(mF.Z,{style:s},l.createElement(gh,{language:"toml",style:gu},n))))},gk=function(){var e=ry(gy,{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(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{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(gv,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{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(gS,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gx=n(34823),gT=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gM=(0,b.withStyles)(gT)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gx.N,A.wU);return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r7.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gO=function(){return l.createElement(iv,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(gk,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(gM,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mP,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mS,null))))))},gA=function(){return l.createElement(gO,null)},gL=function(){return l.createElement(gA,null)},gC=n(44431),gI=1e18,gD=function(e){return new gC.BigNumber(e).dividedBy(gI).toFixed(8)},gN=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sf.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aK.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(ob,{title:"Address"}),l.createElement(om,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"Native Token Balance"}),l.createElement(om,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"LINK Balance"}),l.createElement(om,{value:e.linkBalance?gD(e.linkBalance):"--"}))))})),r+1s&&l.createElement(g$.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:r.footer},l.createElement(aL.Z,{href:"/runs",component:tz},"View More"))))))});function vi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function va(){var e=vi(["\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 va=function(){return e},e}var vo=5,vs=n0(va(),vt),vu=function(){var e=ry(vs,{variables:{offset:0,limit:vo},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vr,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vo})},vc=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"}})},vl=(0,b.withStyles)(vc)(function(e){var t=e.classes,n=(0,A.v9)(gx.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ia.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)))}),vf=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"}})},vd=(0,b.withStyles)(vf)(function(e){var t=e.classes,n=e.job;return l.createElement(ii.Z,null,l.createElement(ie.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ip,{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(aA,{tooltip:!0},n.createdAt))))))});function vh(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vp(){var e=vh(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vp=function(){return e},e}var vb=n0(vp()),vm=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vg=(0,b.withStyles)(vm)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gz,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vd,{job:e,key:t})}))))});function vv(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vy(){var e=vv(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vy=function(){return e},e}var vw=5,v_=n0(vy(),vb),vE=function(){var e=ry(v_,{variables:{offset:0,limit:vw},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vg,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vS=function(){return l.createElement(iv,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(vu,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(gH,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vE,null))))),l.createElement(vl,null))},vk=function(){return l.createElement(vS,null)},vx=function(){return l.createElement(vk,null)},vT=n(87239),vM=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vO=n(5022),vA=n(78718),vL=n.n(vA);function vC(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(ii.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(ie.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(ie.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aA,{tooltip:!0},e.createdAt))),l.createElement(ie.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,ym(t,e.status))},e.status.toLowerCase())))})))}),yv=n(16839),yy=n.n(yv);function yw(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=yy().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 y_=n(94164),yE=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)},yS=n(73343),yk=n(3379),yx=n.n(yk);function yT(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(y_.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:yS.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yE,{data:e}))}))};function yD(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyH&&l.createElement("div",{className:t.runDetails},l.createElement(aL.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yU,{observationSource:n.observationSource})))});function yG(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 vO.parse(e),!0}catch(t){return!1}})}),wq=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hM,{initialValues:t,validationSchema:wV,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hj,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wZ=n(50109),wX="persistSpec";function wJ(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wZ.t8(wX,n),{toml:n}):{toml:wZ.U2(wX)||""}}var wQ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wJ({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wZ.t8("".concat(wX),t),n&&n(t)};return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"New Job"}),l.createElement(aK.Z,null,l.createElement(wq,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function w1(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 _L(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 ry(_q,e)},_X=function(){var e=_Z({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_z,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_J=function(e){var t=e.csaKey;return l.createElement(ii.Z,{hover:!0},l.createElement(ie.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_O,{data:t.publicKey}))))};function _Q(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _1(){var e=_Q(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _1=function(){return e},e}var _0=n0(_1()),_2=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r9.Z,null,l.createElement(sf.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ox.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(it.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,null,"Public Key"))),l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gz,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_J,{csaKey:e,key:t})}))))};function _3(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 ry(EL,e)};function EI(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 ry(E0,e)},E6=function(){return os(E2)},E9=function(){return os(E3)},E8=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return ry(E4,e)};function E7(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 ry(SZ,e)};function SJ(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 kX(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 kJ=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kZ(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kq(kK({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r9.Z,null,l.createElement(aK.Z,null,l.createElement(kG,{object:n})))};function kQ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function k1(e){for(var t=1;t0&&l.createElement(ko,{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(kJ,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kj,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k7(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xe(){var e=k7(["\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 xe=function(){return e},e}var xt=n0(xe(),k9),xn=function(){var e=ry(xt,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(ij,null);if(r)return l.createElement(iN,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k8,{run:i});case"NotFoundError":return l.createElement(oo,null);default:return null}};function xr(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xi(){var e=xr(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xi=function(){return e},e}var xa=n0(xi()),xo=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(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iw,null,"Job Runs")),t&&l.createElement(ij,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(yg,{runs:o}),l.createElement(ir.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 xs(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xu(){var e=xs(["\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 xu=function(){return e},e}var xc=n0(xu(),xa),xl=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=ry(xc,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iN,{error:o}):l.createElement(xo,{loading:a,data:i,page:t,pageSize:n})},xf=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xl,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(xn,null)))},xd=by().shape({name:p2().required("Required"),uri:p2().required("Required"),publicKey:p2().required("Required")}),xh=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hM,{initialValues:t,validationSchema:xd,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ox.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xp=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(r9.Z,null,l.createElement(sf.Z,{title:"Edit Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:r,onSubmit:n})))))};function xb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xm(){var e=xb(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xm=function(){return e},e}var xg=n0(xm()),xv=function(){return ry(xg)};function xy(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 ry(xJ,e)};function x1(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);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(Tz,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},TW={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TK=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Register Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:TW,onSubmit:t})))))};function TV(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&&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 Mm(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(ox.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ox.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(ox.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(ox.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(ox.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(mR.Z,{defaultExpanded:0===n,key:n},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(El.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(aA,{tooltip:!0},e.createdAt)))),l.createElement(mF.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(ox.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gh,{language:"toml",style:gu,"data-testid":"codeblock"},e.definition)))}),l.createElement(oI,{open:null!=c,title:c?M_[c.action].title:"",body:c?M_[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(Mo,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MS(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Mk(){var e=MS(["\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 Mk=function(){return e},e}var Mx=n0(Mk(),My),MT=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iw,null,"Job Proposal #",a.id))),l.createElement(Me,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(T$,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ME,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MM(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(55977),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(55977),a=n(47886),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(97779),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(55977),a=n(15857),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;ri,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;nr})},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;nr,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(23564);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(23564),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()},87462(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;tr})},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__(55977),p=__webpack_require__(15857),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 t5(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t9("(",t5(e.variableDefinitions,", "),")"),i=t5(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t5([t,t5([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t9(" = ",r)+t9(" ",t5(i," "))},SelectionSet:function(e){return t6(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t9("",t,": ")+n,s=o+t9("(",t5(r,", "),")");return s.length>t2&&(s=o+t9("(\n",t8(t5(r,"\n")),"\n)")),t5([s,t5(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t9(" ",t5(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t5(["...",t9("on ",t),t5(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(t9("(",t5(r,", "),")")," ")+"on ".concat(n," ").concat(t9("",t5(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"["+t5(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t5(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t9("(",t5(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 t5(["schema",t5(t," "),t6(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t5(["scalar",e.name,t5(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+": "+r+t9(" ",t5(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t5([t+": "+n,t9("= ",r),t5(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t5(["union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t5(["enum",t,t5(n," "),t6(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t5([e.name,t5(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["input",t,t5(n," "),t6(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+(r?" repeatable":"")+" on "+t5(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t5(["extend schema",t5(t," "),t6(n)]," ")},ScalarTypeExtension:function(e){var t;return t5(["extend scalar",e.name,t5(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t5(["extend union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t5(["extend enum",t,t5(n," "),t6(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["extend input",t,t5(n," "),t6(r)]," ")}};function t4(e){return function(t){return t5([t.description,e(t)],"\n")}}function t5(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 t6(e){return t9("{\n",t8(t5(e,"\n")),"\n}")}function t9(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t8(e){return t9(" ",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 n5(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n6(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n9(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n8(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=e9(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)&&n8(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)&&n8(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r5=new(t_.mr?WeakMap:Map);function r6(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r5.set(e,(r5.get(e)+1)%1e15),n.apply(this,arguments)})}function r9(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r8=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;r5.has(n)||(r5.set(n,0),r6(n,"evict"),r6(n,"modify"),r6(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(){r9(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;r9(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===r5.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:r5.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:e8(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 r8(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 r8&&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,{})||n6(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 r8(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)({},e8(e5(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e9(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)({},e8(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=i6(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=i9(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i8(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,i6(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}(),i5=[];function i6(e,t){var n=e.map;return n.has(t)||n.set(t,i5.pop()||{map:new Map}),n.get(t)}function i9(e,t){if(e===t||!t||i8(t))return e;if(!e||i8(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,i9(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i9(t.map.get(n),e.map.get(n)))})}return a}function i8(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i8(r)&&(i5.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","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","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","VRFSpec","WebhookSpec"],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,pU({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),pA({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pA({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pU({},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(pU({},t,{value:e}))._validate(e,pU({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pM.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pM.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pr(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=pd.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pd.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=pd.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pj(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=pk(e).map(e=>new pN(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pS(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pj({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=pd.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pj({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=pd.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pj({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 p$ of(pH.prototype.__isYupSchema__=!0,["validate","validateSync"]))pH.prototype[`${p$}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pY(this,e,t,n.context);return a[p$](r&&r[i],pU({},n,{parent:r,path:e}))};for(let pz of["equals","is"])pH.prototype[pz]=pH.prototype.oneOf;for(let pG of["not","nope"])pH.prototype[pG]=pH.prototype.notOneOf;pH.prototype.optional=pH.prototype.notRequired;let pW=pH;function pK(){return new pW}pK.prototype=pW.prototype;let pV=e=>null==e;function pq(){return new pZ}class pZ extends pH{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=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pV(e)||!0===e})}isFalse(e=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pV(e)||!1===e})}}pq.prototype=pZ.prototype;let pX=/^((([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,pJ=/^((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,pQ=/^(?:[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,p1=e=>pV(e)||e===e.trim(),p0=({}).toString();function p2(){return new p3}class p3 extends pH{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===p0?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=ph.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t=ph.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pV(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||ph.matches,params:{regex:e},test:t=>pV(t)||""===t&&n||-1!==t.search(e)})}email(e=ph.email){return this.matches(pX,{name:"email",message:e,excludeEmptyString:!0})}url(e=ph.url){return this.matches(pJ,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=ph.uuid){return this.matches(pQ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=ph.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:p1})}lowercase(e=ph.lowercase){return this.transform(e=>pV(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toLowerCase()})}uppercase(e=ph.uppercase){return this.transform(e=>pV(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toUpperCase()})}}p2.prototype=p3.prototype;let p4=e=>e!=+e;function p5(){return new p6}class p6 extends pH{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&&!p4(e)}min(e,t=pp.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t>=this.resolve(e)}})}max(e,t=pp.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(t)||t<=this.resolve(e)}})}lessThan(e,t=pp.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pV(t)||tthis.resolve(e)}})}positive(e=pp.positive){return this.moreThan(0,e)}negative(e=pp.negative){return this.lessThan(0,e)}integer(e=pp.integer){return this.test({name:"integer",message:e,test:e=>pV(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pV(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=>pV(t)?t:Math[e](t))}}p5.prototype=p6.prototype;var p9=/^(\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=p9.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 p7=new Date(""),be=e=>"[object Date]"===Object.prototype.toString.call(e);function bt(){return new bn}class bn extends pH{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p7:new Date(e))})})}_typeCheck(e){return be(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pN.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=pb.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pV(e)||e>=this.resolve(n)}})}max(e,t=pb.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pV(e)||e<=this.resolve(n)}})}}bn.INVALID_DATE=p7,bt.prototype=bn.prototype,bt.INVALID_DATE=p7;var br=n(11865),bi=n.n(br),ba=n(68929),bo=n.n(ba),bs=n(67523),bu=n.n(bs),bc=n(94633),bl=n.n(bc);function bf(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pI.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(pw()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pN.isRef(o)&&o.isSibling?i(o.path,a):p_(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bl().array(r,n).reverse()}function bd(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 bh(e){return(t,n)=>bd(e,t)-bd(e,n)}function bp(){return(bp=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bm(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bg=bh([]);class bv extends pH{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bg,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 bb(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=bp({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=pw()(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(!pM.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bb(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],bp({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pA({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=bp({},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 pH&&i instanceof pH&&(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=bh(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=bf(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,pI.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return pw()(i,e)&&(a=bp({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pg.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=bm(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pg.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bu()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(bo())}snakeCase(){return this.transformKeys(bi())}constantCase(){return this.transformKeys(e=>bi()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pC()(this.fields,e=>e.describe()),e}}function by(e){return new bv(e)}function bw(){return(bw=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,bw({},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(!pM.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)}pA({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(!p_(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pf(e));return t.innerType=e,t}length(e,t=pv.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pv.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pv.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(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)}}b_.prototype=bE.prototype;var bS=by().shape({name:p2().required("Required"),url:p2().required("Required")}),bk=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hM,{initialValues:t,validationSchema:bS,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bx=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(iv,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Edit Bridge",action:l.createElement(aL.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aK.Z,null,l.createElement(bk,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bT(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}},ml=n(76023);function mf(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||(i[n]=e[n]);return i}function mB(e,t){if(null==e)return{};var n,r,i=mY(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mU(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 mX={};function mJ(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mX[t]||(mX[t]=mZ(e)),mX[t]}function mQ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mJ(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mV({},e,n[t])},t)}function m1(e){return e.join(" ")}function m0(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m2({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m2(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=m0(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=mV({},s,{className:m1(m)||void 0,style:mQ(s.className,Object.assign({},s.style,i),n)})}else d=mV({},s,{className:m1(s.className)});var g=h(t.children);return l.createElement(c,mq({key:o},d),g)}}let m3=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m4=/\n/g;function m5(e){return e.match(m4)}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 m9(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 m7(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 ge(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mV({},i,"function"==typeof e?e(t):e)}function gt(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=ge(r,n,i);t.unshift(m7(n,h))}return f&l&&(d.style=mV({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gn(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 gt({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=ge(s,t,o);e.unshift(m7(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(m5(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(gt({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=gt({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 gc=n(98695),gl=n.n(gc);let gf=["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 gd=gs(gl(),gu);gd.supportedLanguages=gf;let gh=gd;var gp=n(64566);function gb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gm(){var e=gb(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gm=function(){return e},e}var gg=n0(gm()),gv=function(e){var t=e.children;return l.createElement(ii.Z,null,l.createElement(ie.default,{component:"th",scope:"row",colSpan:3},t))},gy=function(){return l.createElement(gv,null,"...")},gw=function(e){var t=e.children;return l.createElement(gv,null,t)},g_=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(gw,null,i);if(t)return l.createElement(gy,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mR.Z,{defaultExpanded:o},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},a),l.createElement(mF.Z,{style:s},l.createElement(gh,{language:"toml",style:gu},n))))},gE=function(){var e=ry(gg,{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(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(g_,{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(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(g_,{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(g_,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gS=n(34823),gk=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gx=(0,b.withStyles)(gk)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gS.N,A.wU);return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r7.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gT=function(){return l.createElement(iv,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(gE,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(gx,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mP,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mS,null))))))},gM=function(){return l.createElement(gT,null)},gO=function(){return l.createElement(gM,null)},gA=n(44431),gL=1e18,gC=function(e){return new gA.BigNumber(e).dividedBy(gL).toFixed(8)},gI=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sf.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aK.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(ob,{title:"Address"}),l.createElement(om,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"Native Token Balance"}),l.createElement(om,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"LINK Balance"}),l.createElement(om,{value:e.linkBalance?gC(e.linkBalance):"--"}))))})),r+1s&&l.createElement(gU.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:r.footer},l.createElement(aL.Z,{href:"/runs",component:tz},"View More"))))))});function vn(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vr(){var e=vn(["\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 vr=function(){return e},e}var vi=5,va=n0(vr(),g7),vo=function(){var e=ry(va,{variables:{offset:0,limit:vi},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vt,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vi})},vs=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"}})},vu=(0,b.withStyles)(vs)(function(e){var t=e.classes,n=(0,A.v9)(gS.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ia.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)))}),vc=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"}})},vl=(0,b.withStyles)(vc)(function(e){var t=e.classes,n=e.job;return l.createElement(ii.Z,null,l.createElement(ie.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ip,{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(aA,{tooltip:!0},n.createdAt))))))});function vf(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vd(){var e=vf(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vd=function(){return e},e}var vh=n0(vd()),vp=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vb=(0,b.withStyles)(vp)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r7.Z,null,l.createElement(gz,{visible:o}),l.createElement(gG,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gH,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vl,{job:e,key:t})}))))});function vm(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vg(){var e=vm(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vg=function(){return e},e}var vv=5,vy=n0(vg(),vh),vw=function(){var e=ry(vy,{variables:{offset:0,limit:vv},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vb,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},v_=function(){return l.createElement(iv,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(vo,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(gB,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vw,null))))),l.createElement(vu,null))},vE=function(){return l.createElement(v_,null)},vS=function(){return l.createElement(vE,null)},vk=n(87239),vx=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vT=n(5022),vM=n(78718),vO=n.n(vM);function vA(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(ii.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(ie.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(ie.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aA,{tooltip:!0},e.createdAt))),l.createElement(ie.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,yp(t,e.status))},e.status.toLowerCase())))})))}),ym=n(16839),yg=n.n(ym);function yv(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=yg().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 yy=n(94164),yw=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)},y_=n(73343),yE=n(3379),yS=n.n(yE);function yk(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(yy.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:y_.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yw,{data:e}))}))};function yC(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyB&&l.createElement("div",{className:t.runDetails},l.createElement(aL.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yY,{observationSource:n.observationSource})))});function y$(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 vT.parse(e),!0}catch(t){return!1}})}),wK=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hM,{initialValues:t,validationSchema:wW,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hj,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hR,{component:hJ,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(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wV=n(50109),wq="persistSpec";function wZ(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wV.t8(wq,n),{toml:n}):{toml:wV.U2(wq)||""}}var wX=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wZ({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wV.t8("".concat(wq),t),n&&n(t)};return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"New Job"}),l.createElement(aK.Z,null,l.createElement(wK,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function wJ(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 _O(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 ry(_K,e)},_q=function(){var e=_V({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_H,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_Z=function(e){var t=e.csaKey;return l.createElement(ii.Z,{hover:!0},l.createElement(ie.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_T,{data:t.publicKey}))))};function _X(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _J(){var e=_X(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _J=function(){return e},e}var _Q=n0(_J()),_1=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r9.Z,null,l.createElement(sf.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ox.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(it.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,null,"Public Key"))),l.createElement(r7.Z,null,l.createElement(gz,{visible:o}),l.createElement(gG,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gH,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_Z,{csaKey:e,key:t})}))))};function _0(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 ry(EO,e)};function EL(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 ry(EQ,e)},E4=function(){return os(E1)},E5=function(){return os(E0)},E6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return ry(E2,e)};function E9(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 ry(SV,e)};function SZ(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 kq(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 kZ=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kV(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kK(kG({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r9.Z,null,l.createElement(aK.Z,null,l.createElement(k$,{object:n})))};function kX(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function kJ(e){for(var t=1;t0&&l.createElement(ki,{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(kZ,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kP,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k9(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function k8(){var e=k9(["\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 k7=n0(k8(),k5),xe=function(){var e=ry(k7,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(ij,null);if(r)return l.createElement(iN,{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(oo,null);default:return null}};function xt(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xn(){var e=xt(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xn=function(){return e},e}var xr=n0(xn()),xi=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(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iw,null,"Job Runs")),t&&l.createElement(ij,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(yb,{runs:o}),l.createElement(ir.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 xa(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xo(){var e=xa(["\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 xo=function(){return e},e}var xs=n0(xo(),xr),xu=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=ry(xs,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iN,{error:o}):l.createElement(xi,{loading:a,data:i,page:t,pageSize:n})},xc=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xu,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(xe,null)))},xl=by().shape({name:p2().required("Required"),uri:p2().required("Required"),publicKey:p2().required("Required")}),xf=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hM,{initialValues:t,validationSchema:xl,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hj,{"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(hR,{component:hJ,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(hR,{component:hJ,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ox.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xd=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(r9.Z,null,l.createElement(sf.Z,{title:"Edit Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xf,{initialValues:r,onSubmit:n})))))};function xh(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xp(){var e=xh(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xp=function(){return e},e}var xb=n0(xp()),xm=function(){return ry(xb)};function xg(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 ry(xZ,e)};function xJ(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);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(TH,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},Tz={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TG=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Register Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xf,{initialValues:Tz,onSubmit:t})))))};function TW(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&&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 Mp(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(ox.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ox.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(ox.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(ox.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(ox.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(mR.Z,{defaultExpanded:0===n,key:n},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(Eu.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(aA,{tooltip:!0},e.createdAt)))),l.createElement(mF.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(ox.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gh,{language:"toml",style:gu,"data-testid":"codeblock"},e.definition)))}),l.createElement(oI,{open:null!=c,title:c?My[c.action].title:"",body:c?My[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(Mi,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function M_(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function ME(){var e=M_(["\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 ME=function(){return e},e}var MS=n0(ME(),Mg),Mk=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iw,null,"Job Proposal #",a.id))),l.createElement(T8,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(TU,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(Mw,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function Mx(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(55977),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(55977),a=n(47886),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(97779),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(55977),a=n(15857),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;ri,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;nr})},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;nr,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(23564);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(23564),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()},87462(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;tr})},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__(55977),p=__webpack_require__(15857),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 t5(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t9("(",t5(e.variableDefinitions,", "),")"),i=t5(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t5([t,t5([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t9(" = ",r)+t9(" ",t5(i," "))},SelectionSet:function(e){return t6(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t9("",t,": ")+n,s=o+t9("(",t5(r,", "),")");return s.length>t2&&(s=o+t9("(\n",t8(t5(r,"\n")),"\n)")),t5([s,t5(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t9(" ",t5(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t5(["...",t9("on ",t),t5(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(t9("(",t5(r,", "),")")," ")+"on ".concat(n," ").concat(t9("",t5(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"["+t5(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t5(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t9("(",t5(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 t5(["schema",t5(t," "),t6(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t5(["scalar",e.name,t5(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+": "+r+t9(" ",t5(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t5([t+": "+n,t9("= ",r),t5(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t5(["union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t5(["enum",t,t5(n," "),t6(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t5([e.name,t5(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["input",t,t5(n," "),t6(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+(r?" repeatable":"")+" on "+t5(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t5(["extend schema",t5(t," "),t6(n)]," ")},ScalarTypeExtension:function(e){var t;return t5(["extend scalar",e.name,t5(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t5(["extend union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t5(["extend enum",t,t5(n," "),t6(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["extend input",t,t5(n," "),t6(r)]," ")}};function t4(e){return function(t){return t5([t.description,e(t)],"\n")}}function t5(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 t6(e){return t9("{\n",t8(t5(e,"\n")),"\n}")}function t9(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t8(e){return t9(" ",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 n5(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n6(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n9(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n8(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=e9(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)&&n8(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)&&n8(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r5=new(t_.mr?WeakMap:Map);function r6(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r5.set(e,(r5.get(e)+1)%1e15),n.apply(this,arguments)})}function r9(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r8=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;r5.has(n)||(r5.set(n,0),r6(n,"evict"),r6(n,"modify"),r6(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(){r9(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;r9(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===r5.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:r5.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:e8(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 r8(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 r8&&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,{})||n6(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 r8(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)({},e8(e5(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e9(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)({},e8(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=i6(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=i9(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i8(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,i6(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}(),i5=[];function i6(e,t){var n=e.map;return n.has(t)||n.set(t,i5.pop()||{map:new Map}),n.get(t)}function i9(e,t){if(e===t||!t||i8(t))return e;if(!e||i8(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,i9(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i9(t.map.get(n),e.map.get(n)))})}return a}function i8(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i8(r)&&(i5.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","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","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","VRFSpec","WebhookSpec"],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.74b124ef5d2ef3614139.js.gz b/core/web/assets/main.74b124ef5d2ef3614139.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..667a96c1ef3afc61afc8e61da17f2e8de3003008 GIT binary patch literal 1196933 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
  • RTuR!HfB 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⁢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

    11o3l}}W&G6wTLV!|gV6lgBpHpdPTx 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

    <9Z8=x1RK&nHbh? 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%0D|Tn+ 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À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�F`lt+e#9d@;KDo~ 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≤{BfH3)ascy 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%0ATK8*z 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�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�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

    q-69bB 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⋘9WypiZQs1-~Ge>)B|BoKah9Mm~o6rU-4 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�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 z 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

    418T4)hq-uDtSU2Y2G68?FxHI#P6 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 Date: Tue, 23 Jan 2024 23:11:12 +0100 Subject: [PATCH 193/234] Fix LP tests filters check and add to smoke tests in CI (#11649) * fix hasFilters, fix cancelling log emission in case of errors, add some sanity checks, add 3 log poller tests to integration pipeline * fix replay test * fix lints * shorten chaos log poller test * remove debug logs * disable backup poller. DO NOT MERGE THIS * make it configurable which container is paused: cl node or postgres * try Domino's way of stopping log poller * fix smoke test job name * remove unnecessary check * try executing postgress pausing lp test, more debug info about missing logs if any are missing * fix lints * revert disabling of backpolling, simplify some code, less logs * make optional parameters not required in lp on demand test * run lp smoke tests in a separate job * prepare log poller json test list * add missing comma, revert some comments * fix filename for lp matrix job * move log poller scenarios to test file, remove unused load test * reduce code duplication in lp tests * fix lp tests, fix smoke workflow * remove commented out sections, remove cosmossdk.io/errors * removed trailing spaces, added comments to functions --- .github/workflows/integration-tests.yml | 88 +++- .github/workflows/on-demand-log-poller.yml | 9 +- integration-tests/go.mod | 2 +- integration-tests/load/log_poller/config.toml | 22 - .../load/log_poller/log_poller_test.go | 25 - .../reorg/log_poller_maybe_reorg_test.go | 43 -- integration-tests/smoke/log_poller_test.go | 416 ++++++++++++++- .../smoke/log_poller_test.go_test_list.json | 44 ++ .../universal/log_poller/config.go | 8 +- .../universal/log_poller/helpers.go | 438 ++++++++++++---- .../universal/log_poller/scenarios.go | 496 ------------------ 11 files changed, 857 insertions(+), 734 deletions(-) delete mode 100644 integration-tests/load/log_poller/config.toml delete mode 100644 integration-tests/load/log_poller/log_poller_test.go delete mode 100644 integration-tests/reorg/log_poller_maybe_reorg_test.go create mode 100644 integration-tests/smoke/log_poller_test.go_test_list.json delete mode 100644 integration-tests/universal/log_poller/scenarios.go diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8338a097c6c..e53d1a11bbb 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -189,7 +189,8 @@ jobs: runs-on: ubuntu-latest name: Compare/Build Automation Test List outputs: - matrix: ${{ env.MATRIX_JSON }} + automation-matrix: ${{ env.AUTOMATION_JOB_MATRIX_JSON }} + lp-matrix: ${{ env.LP_JOB_MATRIX_JSON }} steps: - name: Check for Skip Tests Label if: contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') @@ -203,6 +204,7 @@ jobs: cd ./integration-tests ./scripts/compareTestList.sh ./smoke/automation_test.go ./scripts/compareTestList.sh ./smoke/keeper_test.go + ./scripts/compareTestList.sh ./smoke/log_poller_test.go - name: Build Test Matrix Lists id: build-test-matrix-list run: | @@ -211,19 +213,22 @@ jobs: MATRIX_JSON_KEEPER=$(./scripts/buildTestMatrixList.sh ./smoke/keeper_test.go keeper ubuntu-latest 1) COMBINED_ARRAY=$(jq -c -n "$MATRIX_JSON_AUTOMATION + $MATRIX_JSON_KEEPER") + LOG_POLLER_MATRIX_JSON=$(./scripts/buildTestMatrixList.sh ./smoke/log_poller_test.go log_poller ubuntu-latest 1) + echo "LP_JOB_MATRIX_JSON=${LOG_POLLER_MATRIX_JSON}" >> $GITHUB_ENV + # if we running a PR against the develop branch we should only run the automation tests unless we are in the merge group event if [[ "$GITHUB_EVENT_NAME" == "merge_group" ]]; then echo "We are in a merge_group event, run both automation and keepers tests" - echo "MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV + echo "AUTOMATION_JOB_MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV else echo "we are not in a merge_group event, if this is a PR to develop run only automation tests, otherwise run everything because we could be running against a release branch" target_branch=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.base.ref) if [[ "$target_branch" == "develop" ]]; then echo "only run automation tests" - echo "MATRIX_JSON=${MATRIX_JSON_AUTOMATION}" >> $GITHUB_ENV + echo "AUTOMATION_JOB_MATRIX_JSON=${MATRIX_JSON_AUTOMATION}" >> $GITHUB_ENV else echo "run both automation and keepers tests" - echo "MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV + echo "AUTOMATION_JOB_MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV fi fi @@ -245,7 +250,7 @@ jobs: strategy: fail-fast: false matrix: - product: ${{fromJson(needs.compare-tests.outputs.matrix)}} + product: ${{fromJson(needs.compare-tests.outputs.automation-matrix)}} runs-on: ${{ matrix.product.os }} name: ETH Smoke Tests ${{ matrix.product.name }} steps: @@ -286,7 +291,7 @@ jobs: LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} GRAFANA_URL: ${{ vars.GRAFANA_URL }} GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} - RUN_ID: ${{ github.run_id }} + RUN_ID: ${{ github.run_id }} with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -306,6 +311,75 @@ jobs: if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + eth-smoke-tests-matrix-log-poller: + if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: + [build-chainlink, changes, compare-tests, build-lint-integration-tests] + env: + SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 + CHAINLINK_COMMIT_SHA: ${{ github.sha }} + CHAINLINK_ENV_USER: ${{ github.actor }} + TEST_LOG_LEVEL: debug + strategy: + fail-fast: false + matrix: + product: ${{fromJson(needs.compare-tests.outputs.lp-matrix)}} + runs-on: ${{ matrix.product.os }} + name: ETH Smoke Tests ${{ matrix.product.name }} + steps: + - name: Collect Metrics + if: needs.changes.outputs.src == 'true' + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: ETH Smoke Tests ${{ matrix.product.name }} + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Build Go Test Command + id: build-go-test-command + run: | + # if the matrix.product.run is set, use it for a different command + if [ "${{ matrix.product.run }}" != "" ]; then + echo "run_command=${{ matrix.product.run }} ./smoke/${{ matrix.product.file }}_test.go" >> "$GITHUB_OUTPUT" + else + echo "run_command=./smoke/${{ matrix.product.name }}_test.go" >> "$GITHUB_OUTPUT" + fi + ## Run this step when changes that require tests to be run are made + - name: Run Tests + if: needs.changes.outputs.src == 'true' + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + env: + PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + artifacts_location: ./integration-tests/smoke/logs/ + publish_check_name: ${{ matrix.product.name }} + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: "" + eth-smoke-tests-matrix: if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} environment: integration @@ -505,7 +579,7 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: trace-data - path: ./integration-tests/smoke/traces/trace-data.json + path: ./integration-tests/smoke/traces/trace-data.json - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 diff --git a/.github/workflows/on-demand-log-poller.yml b/.github/workflows/on-demand-log-poller.yml index 4658e188bac..4b2d67bcf9a 100644 --- a/.github/workflows/on-demand-log-poller.yml +++ b/.github/workflows/on-demand-log-poller.yml @@ -28,21 +28,22 @@ on: required: true selectedNetworks: type: choice + description: Networks to test on options: - "SIMULATED" - "SEPOLIA" - "MUMBAI" fundingPrivateKey: description: Private funding key (Skip for Simulated) - required: true + required: false type: string wsURL: description: WS URL for the network (Skip for Simulated) - required: true + required: false type: string httpURL: description: HTTP URL for the network (Skip for Simulated) - required: true + required: false type: string jobs: @@ -84,4 +85,4 @@ jobs: run: | cd integration-tests go mod download - go test -v -timeout 5h -v -count=1 -run ^TestLogPollerFromEnv$ ./reorg/log_poller_maybe_reorg_test.go + go test -v -timeout 5h -v -count=1 -run ^TestLogPollerFewFiltersFixedDepth$ ./smoke/log_poller_test.go diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 8e47c411123..064a489770b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -6,7 +6,6 @@ go 1.21.4 replace github.com/smartcontractkit/chainlink/v2 => ../ require ( - cosmossdk.io/errors v1.0.0 github.com/K-Phoen/grabana v0.21.17 github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.13.8 @@ -60,6 +59,7 @@ require ( cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.3 // indirect + cosmossdk.io/errors v1.0.0 // indirect cosmossdk.io/math v1.0.1 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect diff --git a/integration-tests/load/log_poller/config.toml b/integration-tests/load/log_poller/config.toml deleted file mode 100644 index 2e328001943..00000000000 --- a/integration-tests/load/log_poller/config.toml +++ /dev/null @@ -1,22 +0,0 @@ -[general] -generator = "looped" -contracts = 10 -events_per_tx = 10 - -[chaos] -experiment_count = 10 - -[looped] -[looped.contract] -execution_count = 300 - -[looped.fuzz] -min_emit_wait_time_ms = 100 -max_emit_wait_time_ms = 500 - -[wasp] -[wasp.load] -call_timeout = "3m" -rate_limit_unit_duration = "2s" -LPS = 30 -duration = "1m" \ No newline at end of file diff --git a/integration-tests/load/log_poller/log_poller_test.go b/integration-tests/load/log_poller/log_poller_test.go deleted file mode 100644 index 04366848f0e..00000000000 --- a/integration-tests/load/log_poller/log_poller_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package logpoller - -import ( - "testing" - - "github.com/ethereum/go-ethereum/accounts/abi" - - "github.com/stretchr/testify/require" - - lp_helpers "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" -) - -func TestLoadTestLogPoller(t *testing.T) { - cfg, err := lp_helpers.ReadConfig(lp_helpers.DefaultConfigFilename) - require.NoError(t, err) - - eventsToEmit := []abi.Event{} - for _, event := range lp_helpers.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - lp_helpers.ExecuteBasicLogPollerTest(t, cfg) -} diff --git a/integration-tests/reorg/log_poller_maybe_reorg_test.go b/integration-tests/reorg/log_poller_maybe_reorg_test.go deleted file mode 100644 index d319e39aa20..00000000000 --- a/integration-tests/reorg/log_poller_maybe_reorg_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package reorg - -import ( - "testing" - - "github.com/ethereum/go-ethereum/accounts/abi" - - logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" -) - -func TestLogPollerFromEnv(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 100, - UseFinalityTag: true, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 400, - MaxEmitWaitTimeMs: 600, - }, - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - err := cfg.OverrideFromEnv() - if err != nil { - t.Errorf("failed to override config from env: %v", err) - t.FailNow() - } - - logpoller.ExecuteCILogPollerTest(t, &cfg) -} diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 03a287ee6b7..4eadf6b3913 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -1,11 +1,25 @@ package smoke import ( + "fmt" + "math/big" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi" - + "github.com/onsi/gomega" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" + + core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" ) // consistency test with no network disruptions with approximate emission of 1500-1600 logs per second for ~110-120 seconds @@ -36,7 +50,11 @@ func TestLogPollerFewFiltersFixedDepth(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit - logpoller.ExecuteBasicLogPollerTest(t, &cfg) + // this was added only to support triggering tests from CI with custom values + err := cfg.OverrideFromEnv() + require.NoError(t, err, fmt.Sprintf("Error overriding config from env: %v", err)) + + executeBasicLogPollerTest(t, &cfg) } func TestLogPollerFewFiltersFinalityTag(t *testing.T) { @@ -65,12 +83,13 @@ func TestLogPollerFewFiltersFinalityTag(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit - logpoller.ExecuteBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t, &cfg) } // consistency test with no network disruptions with approximate emission of 1000-1100 logs per second for ~110-120 seconds // 900 filters are registered -func TestLogManyFiltersPollerFixedDepth(t *testing.T) { +func TestLogPollerManyFiltersFixedDepth(t *testing.T) { + t.Skip("Execute manually, when needed as it runs for a long time") cfg := logpoller.Config{ General: &logpoller.General{ Generator: logpoller.GeneratorType_Looped, @@ -96,10 +115,11 @@ func TestLogManyFiltersPollerFixedDepth(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit - logpoller.ExecuteBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t, &cfg) } -func TestLogManyFiltersPollerFinalityTag(t *testing.T) { +func TestLogPollerManyFiltersFinalityTag(t *testing.T) { + t.Skip("Execute manually, when needed as it runs for a long time") cfg := logpoller.Config{ General: &logpoller.General{ Generator: logpoller.GeneratorType_Looped, @@ -125,7 +145,7 @@ func TestLogManyFiltersPollerFinalityTag(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit - logpoller.ExecuteBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t, &cfg) } // consistency test that introduces random distruptions by pausing either Chainlink or Postgres containers for random interval of 5-20 seconds @@ -141,15 +161,16 @@ func TestLogPollerWithChaosFixedDepth(t *testing.T) { }, LoopedConfig: &logpoller.LoopedConfig{ ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, + ExecutionCount: 70, }, FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, + MinEmitWaitTimeMs: 100, + MaxEmitWaitTimeMs: 300, }, }, ChaosConfig: &logpoller.ChaosConfig{ - ExperimentCount: 10, + ExperimentCount: 4, + TargetComponent: "chainlink", }, } @@ -160,7 +181,7 @@ func TestLogPollerWithChaosFixedDepth(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit - logpoller.ExecuteBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t, &cfg) } func TestLogPollerWithChaosFinalityTag(t *testing.T) { @@ -173,15 +194,16 @@ func TestLogPollerWithChaosFinalityTag(t *testing.T) { }, LoopedConfig: &logpoller.LoopedConfig{ ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, + ExecutionCount: 120, }, FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, + MinEmitWaitTimeMs: 100, + MaxEmitWaitTimeMs: 300, }, }, ChaosConfig: &logpoller.ChaosConfig{ - ExperimentCount: 10, + ExperimentCount: 6, + TargetComponent: "chainlink", }, } @@ -192,7 +214,73 @@ func TestLogPollerWithChaosFinalityTag(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit - logpoller.ExecuteBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t, &cfg) +} + +func TestLogPollerWithChaosPostgresFinalityTag(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 100, + UseFinalityTag: true, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 120, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 100, + MaxEmitWaitTimeMs: 300, + }, + }, + ChaosConfig: &logpoller.ChaosConfig{ + ExperimentCount: 6, + TargetComponent: "postgres", + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + executeBasicLogPollerTest(t, &cfg) +} + +func TestLogPollerWithChaosPostgresFixedDepth(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 100, + UseFinalityTag: false, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 120, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 100, + MaxEmitWaitTimeMs: 300, + }, + }, + ChaosConfig: &logpoller.ChaosConfig{ + ExperimentCount: 6, + TargetComponent: "postgres", + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + executeBasicLogPollerTest(t, &cfg) } // consistency test that registers filters after events were emitted and then triggers replay via API @@ -227,7 +315,7 @@ func TestLogPollerReplayFixedDepth(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit consistencyTimeout := "5m" - logpoller.ExecuteLogPollerReplay(t, &cfg, consistencyTimeout) + executeLogPollerReplay(t, &cfg, consistencyTimeout) } func TestLogPollerReplayFinalityTag(t *testing.T) { @@ -236,7 +324,7 @@ func TestLogPollerReplayFinalityTag(t *testing.T) { Generator: logpoller.GeneratorType_Looped, Contracts: 2, EventsPerTx: 4, - UseFinalityTag: false, + UseFinalityTag: true, }, LoopedConfig: &logpoller.LoopedConfig{ ContractConfig: logpoller.ContractConfig{ @@ -257,5 +345,293 @@ func TestLogPollerReplayFinalityTag(t *testing.T) { cfg.General.EventsToEmit = eventsToEmit consistencyTimeout := "5m" - logpoller.ExecuteLogPollerReplay(t, &cfg, consistencyTimeout) + executeLogPollerReplay(t, &cfg, consistencyTimeout) +} + +func executeBasicLogPollerTest(t *testing.T, cfg *logpoller.Config) { + l := logging.GetTestLogger(t) + coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ + + lpTestEnv := prepareEnvironment(l, t, cfg) + testEnv := lpTestEnv.testEnv + var err error + + // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) + // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) + err = logpoller.RegisterFiltersAndAssertUniquness(l, lpTestEnv.registry, lpTestEnv.upkeepIDs, lpTestEnv.logEmitters, cfg, lpTestEnv.upKeepsNeeded) + require.NoError(t, err, "Error registering filters") + + l.Info().Msg("No duplicate filters found. OK!") + + err = testEnv.EVMClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + + expectedFilters := logpoller.GetExpectedFilters(lpTestEnv.logEmitters, cfg) + waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l, coreLogger, t, testEnv, expectedFilters) + + // Save block number before starting to emit events, so that we can later use it when querying logs + sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest block number") + startBlock := int64(sb) + + l.Info().Int64("Starting Block", startBlock).Msg("STARTING EVENT EMISSION") + startTime := time.Now() + + // Start chaos experimnents by randomly pausing random containers (Chainlink nodes or their DBs) + chaosDoneCh := make(chan error, 1) + go func() { + logpoller.ExecuteChaosExperiment(l, testEnv, cfg, chaosDoneCh) + }() + + totalLogsEmitted, err := logpoller.ExecuteGenerator(t, cfg, lpTestEnv.logEmitters) + endTime := time.Now() + require.NoError(t, err, "Error executing event generator") + + expectedLogsEmitted := logpoller.GetExpectedLogCount(cfg) + duration := int(endTime.Sub(startTime).Seconds()) + + eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest block number") + + l.Info(). + Int("Total logs emitted", totalLogsEmitted). + Uint64("Probable last block with logs", eb). + Int64("Expected total logs emitted", expectedLogsEmitted). + Str("Duration", fmt.Sprintf("%d sec", duration)). + Str("LPS", fmt.Sprintf("~%d/sec", totalLogsEmitted/duration)). + Msg("FINISHED EVENT EMISSION") + + l.Info().Msg("Waiting before proceeding with test until all chaos experiments finish") + chaosError := <-chaosDoneCh + require.NoError(t, chaosError, "Error encountered during chaos experiment") + + // use ridciuously high end block so that we don't have to find out the block number of the last block in which logs were emitted + // as that's not trivial to do (i.e. just because chain was at block X when log emission ended it doesn't mean all events made it to that block) + endBlock := int64(eb) + 10000 + + // logCountWaitDuration, err := time.ParseDuration("5m") + // require.NoError(t, err, "Error parsing log count wait duration") + allNodesLogCountMatches, err := logpoller.FluentlyCheckIfAllNodesHaveLogCount("5m", startBlock, endBlock, totalLogsEmitted, expectedFilters, l, coreLogger, testEnv) + require.NoError(t, err, "Error checking if CL nodes have expected log count") + + conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, cfg, startBlock, endBlock, "5m") +} + +func executeLogPollerReplay(t *testing.T, cfg *logpoller.Config, consistencyTimeout string) { + l := logging.GetTestLogger(t) + coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ + + lpTestEnv := prepareEnvironment(l, t, cfg) + testEnv := lpTestEnv.testEnv + var err error + + // Save block number before starting to emit events, so that we can later use it when querying logs + sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest block number") + startBlock := int64(sb) + + l.Info().Int64("Starting Block", startBlock).Msg("STARTING EVENT EMISSION") + startTime := time.Now() + totalLogsEmitted, err := logpoller.ExecuteGenerator(t, cfg, lpTestEnv.logEmitters) + endTime := time.Now() + require.NoError(t, err, "Error executing event generator") + expectedLogsEmitted := logpoller.GetExpectedLogCount(cfg) + duration := int(endTime.Sub(startTime).Seconds()) + + // Save block number after finishing to emit events, so that we can later use it when querying logs + eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest block number") + + endBlock, err := logpoller.GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) + require.NoError(t, err, "Error getting end block to wait for") + + l.Info().Int64("Ending Block", endBlock).Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") + + // Lets make sure no logs are in DB yet + expectedFilters := logpoller.GetExpectedFilters(lpTestEnv.logEmitters, cfg) + logCountMatches, err := logpoller.ClNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), 0, expectedFilters, l, coreLogger, testEnv.ClCluster) + require.NoError(t, err, "Error checking if CL nodes have expected log count") + require.True(t, logCountMatches, "Some CL nodes already had logs in DB") + l.Info().Msg("No logs were saved by CL nodes yet, as expected. Proceeding.") + + // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) + // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) + err = logpoller.RegisterFiltersAndAssertUniquness(l, lpTestEnv.registry, lpTestEnv.upkeepIDs, lpTestEnv.logEmitters, cfg, lpTestEnv.upKeepsNeeded) + require.NoError(t, err, "Error registering filters") + + err = testEnv.EVMClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + + waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l, coreLogger, t, testEnv, expectedFilters) + + blockFinalisationWaitDuration := "5m" + l.Warn().Str("Duration", blockFinalisationWaitDuration).Msg("Waiting for all CL nodes to have end block finalised") + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + hasFinalised, err := logpoller.LogPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") + } + g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") + }, blockFinalisationWaitDuration, "10s").Should(gomega.Succeed()) + + // Trigger replay + l.Info().Msg("Triggering log poller's replay") + for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { + nodeName := testEnv.ClCluster.Nodes[i].ContainerName + response, _, err := testEnv.ClCluster.Nodes[i].API.ReplayLogPollerFromBlock(startBlock, testEnv.EVMClient.GetChainID().Int64()) + require.NoError(t, err, "Error triggering log poller's replay on node %s", nodeName) + require.Equal(t, "Replay started", response.Data.Attributes.Message, "Unexpected response message from log poller's replay") + } + + // so that we don't have to look for block number of the last block in which logs were emitted as that's not trivial to do + endBlock = endBlock + 10000 + l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for replay logs to be processed by all nodes") + + // logCountWaitDuration, err := time.ParseDuration("5m") + allNodesLogCountMatches, err := logpoller.FluentlyCheckIfAllNodesHaveLogCount("5m", startBlock, endBlock, totalLogsEmitted, expectedFilters, l, coreLogger, testEnv) + require.NoError(t, err, "Error checking if CL nodes have expected log count") + + conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, cfg, startBlock, endBlock, "5m") +} + +type logPollerEnvironment struct { + logEmitters []*contracts.LogEmitter + testEnv *test_env.CLClusterTestEnv + registry contracts.KeeperRegistry + upkeepIDs []*big.Int + upKeepsNeeded int +} + +// prepareEnvironment prepares environment for log poller tests by starting DON, private Ethereum network, +// deploying registry and log emitter contracts and registering log triggered upkeeps +func prepareEnvironment(l zerolog.Logger, t *testing.T, cfg *logpoller.Config) logPollerEnvironment { + if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { + l.Warn().Msg("No events to emit specified, using all events from log emitter contract") + for _, event := range logpoller.EmitterABI.Events { + cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) + } + } + + l.Info().Msg("Starting basic log poller test") + + var ( + err error + upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) + ) + + chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := logpoller.SetupLogPollerTestDocker( + t, ethereum.RegistryVersion_2_1, logpoller.DefaultOCRRegistryConfig, upKeepsNeeded, time.Duration(500*time.Millisecond), cfg.General.UseFinalityTag, + ) + + _, upkeepIDs := actions.DeployConsumers( + t, + registry, + registrar, + linkToken, + contractDeployer, + chainClient, + upKeepsNeeded, + big.NewInt(int64(9e18)), + uint32(2500000), + true, + false, + ) + + err = logpoller.AssertUpkeepIdsUniqueness(upkeepIDs) + require.NoError(t, err, "Error asserting upkeep ids uniqueness") + l.Info().Msg("No duplicate upkeep IDs found. OK!") + + // Deploy Log Emitter contracts + logEmitters := logpoller.UploadLogEmitterContractsAndWaitForFinalisation(l, t, testEnv, cfg) + err = logpoller.AssertContractAddressUniquneness(logEmitters) + require.NoError(t, err, "Error asserting contract addresses uniqueness") + l.Info().Msg("No duplicate contract addresses found. OK!") + + return logPollerEnvironment{ + logEmitters: logEmitters, + registry: registry, + upkeepIDs: upkeepIDs, + upKeepsNeeded: upKeepsNeeded, + testEnv: testEnv, + } +} + +// waitForAllNodesToHaveExpectedFiltersRegisteredOrFail waits until all nodes have expected filters registered until timeout +func waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, testEnv *test_env.CLClusterTestEnv, expectedFilters []logpoller.ExpectedFilter) { + // Make sure that all nodes have expected filters registered before starting to emit events + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + hasFilters := false + for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { + nodeName := testEnv.ClCluster.Nodes[i].ContainerName + l.Info(). + Str("Node name", nodeName). + Msg("Fetching filters from log poller's DB") + var message string + var err error + + hasFilters, message, err = logpoller.NodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) + if !hasFilters || err != nil { + l.Warn(). + Str("Details", message). + Msg("Some filters were missing, but we will retry") + break + } + } + g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") + }, "5m", "10s").Should(gomega.Succeed()) + + l.Info(). + Msg("All nodes have expected filters registered") + l.Info(). + Int("Count", len(expectedFilters)). + Msg("Expected filters count") +} + +// conditionallyWaitUntilNodesHaveTheSameLogsAsEvm checks whether all CL nodes have the same number of logs as EVM node +// if not, then it prints missing logs and wait for some time and checks again +func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, allNodesLogCountMatches bool, lpTestEnv logPollerEnvironment, cfg *logpoller.Config, startBlock, endBlock int64, waitDuration string) { + logCountWaitDuration, err := time.ParseDuration(waitDuration) + require.NoError(t, err, "Error parsing log count wait duration") + + allNodesHaveAllExpectedLogs := false + if !allNodesLogCountMatches { + missingLogs, err := logpoller.GetMissingLogs(startBlock, endBlock, lpTestEnv.logEmitters, lpTestEnv.testEnv.EVMClient, lpTestEnv.testEnv.ClCluster, l, coreLogger, cfg) + if err == nil { + if !missingLogs.IsEmpty() { + logpoller.PrintMissingLogsInfo(missingLogs, l, cfg) + } else { + allNodesHaveAllExpectedLogs = true + l.Info().Msg("All CL nodes have all the logs that EVM node has") + } + } + } + + require.True(t, allNodesLogCountMatches, "Not all CL nodes had expected log count afer %s", logCountWaitDuration) + + // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has + // but only in the rare case that first attempt to do it failed (basically here want to know not only + // if log count matches, but whether details of every single log match) + if !allNodesHaveAllExpectedLogs { + logConsistencyWaitDuration := "5m" + l.Info(). + Str("Duration", logConsistencyWaitDuration). + Msg("Waiting for CL nodes to have all the logs that EVM node has") + + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + missingLogs, err := logpoller.GetMissingLogs(startBlock, endBlock, lpTestEnv.logEmitters, lpTestEnv.testEnv.EVMClient, lpTestEnv.testEnv.ClCluster, l, coreLogger, cfg) + if err != nil { + l.Warn(). + Err(err). + Msg("Error getting missing logs. Retrying...") + } + + if !missingLogs.IsEmpty() { + logpoller.PrintMissingLogsInfo(missingLogs, l, cfg) + } + g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") + }, logConsistencyWaitDuration, "10s").Should(gomega.Succeed()) + } } diff --git a/integration-tests/smoke/log_poller_test.go_test_list.json b/integration-tests/smoke/log_poller_test.go_test_list.json new file mode 100644 index 00000000000..5193c3fd06c --- /dev/null +++ b/integration-tests/smoke/log_poller_test.go_test_list.json @@ -0,0 +1,44 @@ +{ + "tests": [ + { + "name": "TestLogPollerFewFiltersFixedDepth", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerFewFiltersFinalityTag", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerWithChaosFixedDepth", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerWithChaosFinalityTag", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerWithChaosPostgresFinalityTag", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerWithChaosPostgresFixedDepth", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerReplayFixedDepth", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerReplayFinalityTag", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerManyFiltersFixedDepth", + "label": "ubuntu-latest" + }, + { + "name": "TestLogPollerManyFiltersFinalityTag", + "label": "ubuntu-latest" + } + ] +} \ No newline at end of file diff --git a/integration-tests/universal/log_poller/config.go b/integration-tests/universal/log_poller/config.go index d42520397e8..b8773d836f6 100644 --- a/integration-tests/universal/log_poller/config.go +++ b/integration-tests/universal/log_poller/config.go @@ -5,7 +5,6 @@ import ( "os" "strconv" - "cosmossdk.io/errors" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" @@ -57,7 +56,8 @@ type General struct { } type ChaosConfig struct { - ExperimentCount int `toml:"experiment_count"` + ExperimentCount int `toml:"experiment_count"` + TargetComponent string `toml:"target_component"` } type WaspConfig struct { @@ -76,11 +76,11 @@ func ReadConfig(configName string) (*Config, error) { var cfg *Config d, err := os.ReadFile(configName) if err != nil { - return nil, errors.Wrap(err, ErrReadPerfConfig) + return nil, fmt.Errorf("%w: %s", err, ErrReadPerfConfig) } err = toml.Unmarshal(d, &cfg) if err != nil { - return nil, errors.Wrap(err, ErrUnmarshalPerfConfig) + return nil, fmt.Errorf("%w: %s", err, ErrUnmarshalPerfConfig) } if err := cfg.validate(); err != nil { diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 8ad115ecec8..597b82ef6c3 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common" geth_types "github.com/ethereum/go-ethereum/core/types" "github.com/jmoiron/sqlx" + "github.com/onsi/gomega" "github.com/rs/zerolog" "github.com/scylladb/go-reflectx" "github.com/stretchr/testify/require" @@ -29,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -133,6 +135,7 @@ var registerSingleTopicFilter = func(registry contracts.KeeperRegistry, upkeepID // return nil // } +// NewOrm returns a new logpoller.DbORM instance func NewOrm(logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (*logpoller.DbORM, *sqlx.DB, error) { dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", "127.0.0.1", postgresDb.ExternalPort, postgresDb.User, postgresDb.Password, postgresDb.DbName) db, err := sqlx.Open("postgres", dsn) @@ -149,7 +152,8 @@ type ExpectedFilter struct { topic common.Hash } -func getExpectedFilters(logEmitters []*contracts.LogEmitter, cfg *Config) []ExpectedFilter { +// GetExpectedFilters returns a slice of ExpectedFilter structs based on the provided log emitters and config +func GetExpectedFilters(logEmitters []*contracts.LogEmitter, cfg *Config) []ExpectedFilter { expectedFilters := make([]ExpectedFilter, 0) for _, emitter := range logEmitters { for _, event := range cfg.General.EventsToEmit { @@ -163,16 +167,17 @@ func getExpectedFilters(logEmitters []*contracts.LogEmitter, cfg *Config) []Expe return expectedFilters } -var nodeHasExpectedFilters = func(expectedFilters []ExpectedFilter, logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (bool, error) { +// NodeHasExpectedFilters returns true if the provided node has all the expected filters registered +func NodeHasExpectedFilters(expectedFilters []ExpectedFilter, logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (bool, string, error) { orm, db, err := NewOrm(logger, chainID, postgresDb) if err != nil { - return false, err + return false, "", err } defer db.Close() knownFilters, err := orm.LoadFilters() if err != nil { - return false, err + return false, "", err } for _, expectedFilter := range expectedFilters { @@ -185,14 +190,15 @@ var nodeHasExpectedFilters = func(expectedFilters []ExpectedFilter, logger core_ } if !filterFound { - return false, fmt.Errorf("no filter found for emitter %s and topic %s", expectedFilter.emitterAddress.String(), expectedFilter.topic.Hex()) + return false, fmt.Sprintf("no filter found for emitter %s and topic %s", expectedFilter.emitterAddress.String(), expectedFilter.topic.Hex()), nil } } - return true, nil + return true, "", nil } -var randomWait = func(minMilliseconds, maxMilliseconds int) { +// randomWait waits for a random amount of time between minMilliseconds and maxMilliseconds +func randomWait(minMilliseconds, maxMilliseconds int) { rand.New(rand.NewSource(time.Now().UnixNano())) randomMilliseconds := rand.Intn(maxMilliseconds-minMilliseconds+1) + minMilliseconds time.Sleep(time.Duration(randomMilliseconds) * time.Millisecond) @@ -201,10 +207,9 @@ var randomWait = func(minMilliseconds, maxMilliseconds int) { type LogEmitterChannel struct { logsEmitted int err error - // unused - // currentIndex int } +// getIntSlice returns a slice of ints of the provided length func getIntSlice(length int) []int { result := make([]int, length) for i := 0; i < length; i++ { @@ -214,6 +219,7 @@ func getIntSlice(length int) []int { return result } +// getStringSlice returns a slice of strings of the provided length func getStringSlice(length int) []string { result := make([]string, length) for i := 0; i < length; i++ { @@ -223,17 +229,18 @@ func getStringSlice(length int) []string { return result } -var emitEvents = func(ctx context.Context, l zerolog.Logger, logEmitter *contracts.LogEmitter, cfg *Config, wg *sync.WaitGroup, results chan LogEmitterChannel) { +// emitEvents emits events from the provided log emitter concurrently according to the provided config +func emitEvents(ctx context.Context, l zerolog.Logger, logEmitter *contracts.LogEmitter, cfg *Config, wg *sync.WaitGroup, results chan LogEmitterChannel) { address := (*logEmitter).Address().String() localCounter := 0 - select { - case <-ctx.Done(): - l.Warn().Str("Emitter address", address).Msg("Context cancelled, not emitting events") - return - default: - defer wg.Done() - for i := 0; i < cfg.LoopedConfig.ExecutionCount; i++ { - for _, event := range cfg.General.EventsToEmit { + defer wg.Done() + for i := 0; i < cfg.LoopedConfig.ExecutionCount; i++ { + for _, event := range cfg.General.EventsToEmit { + select { + case <-ctx.Done(): + l.Warn().Str("Emitter address", address).Msg("Context cancelled, not emitting events") + return + default: l.Debug().Str("Emitter address", address).Str("Event type", event.Name).Str("index", fmt.Sprintf("%d/%d", (i+1), cfg.LoopedConfig.ExecutionCount)).Msg("Emitting log from emitter") var err error switch event.Name { @@ -243,14 +250,15 @@ var emitEvents = func(ctx context.Context, l zerolog.Logger, logEmitter *contrac _, err = (*logEmitter).EmitLogIntsIndexed(getIntSlice(cfg.General.EventsPerTx)) case "Log3": _, err = (*logEmitter).EmitLogStrings(getStringSlice(cfg.General.EventsPerTx)) + case "Log4": + _, err = (*logEmitter).EmitLogIntMultiIndexed(1, 1, cfg.General.EventsPerTx) default: err = fmt.Errorf("unknown event name: %s", event.Name) } if err != nil { results <- LogEmitterChannel{ - logsEmitted: 0, - err: err, + err: err, } return } @@ -263,35 +271,25 @@ var emitEvents = func(ctx context.Context, l zerolog.Logger, logEmitter *contrac l.Info().Str("Emitter address", address).Str("Index", fmt.Sprintf("%d/%d", i+1, cfg.LoopedConfig.ExecutionCount)).Msg("Emitted all three events") } } - - l.Info().Str("Emitter address", address).Int("Total logs emitted", localCounter).Msg("Finished emitting events") - - results <- LogEmitterChannel{ - logsEmitted: localCounter, - err: nil, - } - } -} - -var chainHasFinalisedEndBlock = func(l zerolog.Logger, evmClient blockchain.EVMClient, endBlock int64) (bool, error) { - effectiveEndBlock := endBlock + 1 - lastFinalisedBlockHeader, err := evmClient.GetLatestFinalizedBlockHeader(context.Background()) - if err != nil { - return false, err } - l.Info().Int64("Last finalised block header", lastFinalisedBlockHeader.Number.Int64()).Int64("End block", effectiveEndBlock).Int64("Blocks left till end block", effectiveEndBlock-lastFinalisedBlockHeader.Number.Int64()).Msg("Waiting for the finalized block to move beyond end block") + l.Info().Str("Emitter address", address).Int("Total logs emitted", localCounter).Msg("Finished emitting events") - return lastFinalisedBlockHeader.Number.Int64() > effectiveEndBlock, nil + results <- LogEmitterChannel{ + logsEmitted: localCounter, + err: nil, + } } -var logPollerHasFinalisedEndBlock = func(endBlock int64, chainID *big.Int, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { +// LogPollerHasFinalisedEndBlock returns true if all CL nodes have finalised processing the provided end block +func LogPollerHasFinalisedEndBlock(endBlock int64, chainID *big.Int, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { wg := &sync.WaitGroup{} type boolQueryResult struct { - nodeName string - hasFinalised bool - err error + nodeName string + hasFinalised bool + finalizedBlock int64 + err error } endBlockCh := make(chan boolQueryResult, len(nodes.Nodes)-1) @@ -327,9 +325,10 @@ var logPollerHasFinalisedEndBlock = func(endBlock int64, chainID *big.Int, l zer } r <- boolQueryResult{ - nodeName: clNode.ContainerName, - hasFinalised: latestBlock.FinalizedBlockNumber > endBlock, - err: nil, + nodeName: clNode.ContainerName, + finalizedBlock: latestBlock.FinalizedBlockNumber, + hasFinalised: latestBlock.FinalizedBlockNumber > endBlock, + err: nil, } } @@ -352,7 +351,7 @@ var logPollerHasFinalisedEndBlock = func(endBlock int64, chainID *big.Int, l zer if r.hasFinalised { l.Info().Str("Node name", r.nodeName).Msg("CL node has finalised end block") } else { - l.Warn().Str("Node name", r.nodeName).Msg("CL node has not finalised end block yet") + l.Warn().Int64("Has", r.finalizedBlock).Int64("Want", endBlock).Str("Node name", r.nodeName).Msg("CL node has not finalised end block yet") } if len(foundMap) == len(nodes.Nodes)-1 { @@ -376,7 +375,8 @@ var logPollerHasFinalisedEndBlock = func(endBlock int64, chainID *big.Int, l zer return <-allFinalisedCh, err } -var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big.Int, expectedLogCount int, expectedFilters []ExpectedFilter, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { +// ClNodesHaveExpectedLogCount returns true if all CL nodes have the expected log count in the provided block range and matching the provided filters +func ClNodesHaveExpectedLogCount(startBlock, endBlock int64, chainID *big.Int, expectedLogCount int, expectedFilters []ExpectedFilter, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { wg := &sync.WaitGroup{} type logQueryResult struct { @@ -386,13 +386,13 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. err error } - queryCh := make(chan logQueryResult, len(nodes.Nodes)-1) + resultChan := make(chan logQueryResult, len(nodes.Nodes)-1) ctx, cancelFn := context.WithCancel(context.Background()) for i := 1; i < len(nodes.Nodes); i++ { wg.Add(1) - go func(clNode *test_env.ClNode, r chan logQueryResult) { + go func(clNode *test_env.ClNode, resultChan chan logQueryResult) { defer wg.Done() select { case <-ctx.Done(): @@ -400,7 +400,7 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. default: orm, db, err := NewOrm(coreLogger, chainID, clNode.PostgresDb) if err != nil { - r <- logQueryResult{ + resultChan <- logQueryResult{ nodeName: clNode.ContainerName, logCount: 0, hasExpectedCount: false, @@ -414,7 +414,7 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. for _, filter := range expectedFilters { logs, err := orm.SelectLogs(startBlock, endBlock, filter.emitterAddress, filter.topic) if err != nil { - r <- logQueryResult{ + resultChan <- logQueryResult{ nodeName: clNode.ContainerName, logCount: 0, hasExpectedCount: false, @@ -425,14 +425,14 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. foundLogsCount += len(logs) } - r <- logQueryResult{ + resultChan <- logQueryResult{ nodeName: clNode.ContainerName, logCount: foundLogsCount, hasExpectedCount: foundLogsCount >= expectedLogCount, - err: err, + err: nil, } } - }(nodes.Nodes[i], queryCh) + }(nodes.Nodes[i], resultChan) } var err error @@ -440,7 +440,7 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. go func() { foundMap := make(map[string]bool, 0) - for r := range queryCh { + for r := range resultChan { if r.err != nil { err = r.err cancelFn() @@ -449,15 +449,22 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. foundMap[r.nodeName] = r.hasExpectedCount if r.hasExpectedCount { - l.Info().Str("Node name", r.nodeName).Int("Logs count", r.logCount).Msg("Expected log count found in CL node") + l.Debug(). + Str("Node name", r.nodeName). + Int("Logs count", r.logCount). + Msg("Expected log count found in CL node") } else { - l.Warn().Str("Node name", r.nodeName).Str("Found/Expected logs", fmt.Sprintf("%d/%d", r.logCount, expectedLogCount)).Int("Missing logs", expectedLogCount-r.logCount).Msg("Too low log count found in CL node") + l.Debug(). + Str("Node name", r.nodeName). + Str("Found/Expected logs", fmt.Sprintf("%d/%d", r.logCount, expectedLogCount)). + Int("Missing logs", expectedLogCount-r.logCount). + Msg("Too low log count found in CL node") } if len(foundMap) == len(nodes.Nodes)-1 { allFound := true - for _, v := range foundMap { - if !v { + for _, hadAllLogs := range foundMap { + if !hadAllLogs { allFound = false break } @@ -470,13 +477,14 @@ var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big. }() wg.Wait() - close(queryCh) + close(resultChan) return <-allFoundCh, err } type MissingLogs map[string][]geth_types.Log +// IsEmpty returns true if there are no missing logs func (m *MissingLogs) IsEmpty() bool { for _, v := range *m { if len(v) > 0 { @@ -487,7 +495,8 @@ func (m *MissingLogs) IsEmpty() bool { return true } -var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *Config) (MissingLogs, error) { +// GetMissingLogs returns a map of CL node name to missing logs in that node compared to EVM node to which the provided evm client is connected +func GetMissingLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *Config) (MissingLogs, error) { wg := &sync.WaitGroup{} type dbQueryResult struct { @@ -511,7 +520,7 @@ var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.L default: nodeName := clnodeCluster.Nodes[i].ContainerName - l.Info().Str("Node name", nodeName).Msg("Fetching log poller logs") + l.Debug().Str("Node name", nodeName).Msg("Fetching log poller logs") orm, db, err := NewOrm(coreLogger, evmClient.GetChainID(), clnodeCluster.Nodes[i].PostgresDb) if err != nil { r <- dbQueryResult{ @@ -528,7 +537,7 @@ var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.L address := (*logEmitters[j]).Address() for _, event := range cfg.General.EventsToEmit { - l.Debug().Str("Event name", event.Name).Str("Emitter address", address.String()).Msg("Fetching single emitter's logs") + l.Trace().Str("Event name", event.Name).Str("Emitter address", address.String()).Msg("Fetching single emitter's logs") result, err := orm.SelectLogs(startBlock, endBlock, address, event.ID) if err != nil { r <- dbQueryResult{ @@ -544,11 +553,11 @@ var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.L logs = append(logs, result...) - l.Debug().Str("Event name", event.Name).Str("Emitter address", address.String()).Int("Log count", len(result)).Msg("Logs found per node") + l.Trace().Str("Event name", event.Name).Str("Emitter address", address.String()).Int("Log count", len(result)).Msg("Logs found per node") } } - l.Warn().Int("Count", len(logs)).Str("Node name", nodeName).Msg("Fetched log poller logs") + l.Info().Int("Count", len(logs)).Str("Node name", nodeName).Msg("Fetched log poller logs") r <- dbQueryResult{ err: nil, @@ -595,16 +604,17 @@ var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.L logs []geth_types.Log } - l.Info().Msg("Started comparison of logs from EVM node and CL nodes. This may take a while if there's a lot of logs") - missingCh := make(chan missingLogResult, len(clnodeCluster.Nodes)-1) evmLogCount := len(allLogsInEVMNode) + l.Info().Int("Log count", evmLogCount).Msg("Started comparison of logs from EVM node and CL nodes. This may take a while if there's a lot of logs") + + missingCh := make(chan missingLogResult, len(clnodeCluster.Nodes)-1) for i := 1; i < len(clnodeCluster.Nodes); i++ { wg.Add(1) go func(i int, result chan missingLogResult) { defer wg.Done() nodeName := clnodeCluster.Nodes[i].ContainerName - l.Info().Str("Node name", nodeName).Str("Progress", fmt.Sprintf("0/%d", evmLogCount)).Msg("Comparing single CL node's logs with EVM logs") + l.Debug().Str("Node name", nodeName).Str("Progress", fmt.Sprintf("0/%d", evmLogCount)).Msg("Comparing single CL node's logs with EVM logs") missingLogs := make([]geth_types.Log, 0) for i, evmLog := range allLogsInEVMNode { @@ -618,7 +628,7 @@ var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.L } if i%10000 == 0 && i != 0 { - l.Info().Str("Node name", nodeName).Str("Progress", fmt.Sprintf("%d/%d", i, evmLogCount)).Msg("Comparing single CL node's logs with EVM logs") + l.Debug().Str("Node name", nodeName).Str("Progress", fmt.Sprintf("%d/%d", i, evmLogCount)).Msg("Comparing single CL node's logs with EVM logs") } if !logFound { @@ -648,15 +658,18 @@ var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.L } } - expectedTotalLogsEmitted := getExpectedLogCount(cfg) + expectedTotalLogsEmitted := GetExpectedLogCount(cfg) if int64(len(allLogsInEVMNode)) != expectedTotalLogsEmitted { - l.Warn().Str("Actual/Expected", fmt.Sprintf("%d/%d", expectedTotalLogsEmitted, len(allLogsInEVMNode))).Msg("Some of the test logs were not found in EVM node. This is a bug in the test") + l.Warn(). + Str("Actual/Expected", fmt.Sprintf("%d/%d", expectedTotalLogsEmitted, len(allLogsInEVMNode))). + Msg("Some of the test logs were not found in EVM node. This is a bug in the test") } return missingLogs, nil } -var printMissingLogsByType = func(missingLogs map[string][]geth_types.Log, l zerolog.Logger, cfg *Config) { +// PrintMissingLogsInfo prints various useful information about the missing logs +func PrintMissingLogsInfo(missingLogs map[string][]geth_types.Log, l zerolog.Logger, cfg *Config) { var findHumanName = func(topic common.Hash) string { for _, event := range cfg.General.EventsToEmit { if event.ID == topic { @@ -675,12 +688,39 @@ var printMissingLogsByType = func(missingLogs map[string][]geth_types.Log, l zer } } + l.Debug().Msg("Missing log by event name") for k, v := range missingByType { - l.Warn().Str("Event name", k).Int("Missing count", v).Msg("Missing logs by type") + l.Debug().Str("Event name", k).Int("Missing count", v).Msg("Missing logs by type") + } + + missingByBlock := make(map[uint64]int) + for _, logs := range missingLogs { + for _, l := range logs { + missingByBlock[l.BlockNumber]++ + } + } + + l.Debug().Msg("Missing logs by block") + for k, v := range missingByBlock { + l.Debug().Uint64("Block number", k).Int("Missing count", v).Msg("Missing logs by block") + } + + missingByEmitter := make(map[string]int) + for _, logs := range missingLogs { + for _, l := range logs { + missingByEmitter[l.Address.String()]++ + } + } + + l.Debug().Msg("Missing logs by emitter") + for k, v := range missingByEmitter { + l.Debug().Str("Emitter address", k).Int("Missing count", v).Msg("Missing logs by emitter") } } -var getEVMLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, l zerolog.Logger, cfg *Config) ([]geth_types.Log, error) { +// getEVMLogs returns a slice of all logs emitted by the provided log emitters in the provided block range, +// which are present in the EVM node to which the provided evm client is connected +func getEVMLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, l zerolog.Logger, cfg *Config) ([]geth_types.Log, error) { allLogsInEVMNode := make([]geth_types.Log, 0) for j := 0; j < len(logEmitters); j++ { address := (*logEmitters[j]).Address() @@ -705,12 +745,13 @@ var getEVMLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEm } } - l.Warn().Int("Count", len(allLogsInEVMNode)).Msg("Logs in EVM node") + l.Info().Int("Count", len(allLogsInEVMNode)).Msg("Logs in EVM node") return allLogsInEVMNode, nil } -func executeGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { +// ExecuteGenerator executes the configured generator and returns the total number of logs emitted +func ExecuteGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { if cfg.General.Generator == GeneratorType_WASP { return runWaspGenerator(t, cfg, logEmitters) } @@ -718,6 +759,7 @@ func executeGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi return runLoopedGenerator(t, cfg, logEmitters) } +// runWaspGenerator runs the wasp generator and returns the total number of logs emitted func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { l := logging.GetTestLogger(t) @@ -775,6 +817,7 @@ func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi return counter.value, nil } +// runLoopedGenerator runs the looped generator and returns the total number of logs emitted func runLoopedGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { l := logging.GetTestLogger(t) @@ -797,31 +840,37 @@ func runLoopedGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogE aggrChan := make(chan int, len(logEmitters)) go func() { - for emitter := range emitterCh { - if emitter.err != nil { - emitErr = emitter.err - cancelFn() + for { + select { + case <-ctx.Done(): return + case emitter := <-emitterCh: + if emitter.err != nil { + emitErr = emitter.err + cancelFn() + return + } + aggrChan <- emitter.logsEmitted } - aggrChan <- emitter.logsEmitted } }() wg.Wait() close(emitterCh) - for i := 0; i < len(logEmitters); i++ { - total += <-aggrChan - } - if emitErr != nil { return 0, emitErr } + for i := 0; i < len(logEmitters); i++ { + total += <-aggrChan + } + return int(total), nil } -func getExpectedLogCount(cfg *Config) int64 { +// GetExpectedLogCount returns the expected number of logs to be emitted based on the provided config +func GetExpectedLogCount(cfg *Config) int64 { if cfg.General.Generator == GeneratorType_WASP { if cfg.Wasp.Load.RPS != 0 { return cfg.Wasp.Load.RPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) * int64(cfg.General.EventsPerTx) @@ -832,40 +881,71 @@ func getExpectedLogCount(cfg *Config) int64 { return int64(len(cfg.General.EventsToEmit) * cfg.LoopedConfig.ExecutionCount * cfg.General.Contracts * cfg.General.EventsPerTx) } -var chaosPauseSyncFn = func(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv) error { +type PauseData struct { + StartBlock uint64 + EndBlock uint64 + TargetComponent string + ContaineName string +} + +var ChaosPauses = []PauseData{} + +// chaosPauseSyncFn pauses ranom container of the provided type for a random amount of time between 5 and 20 seconds +func chaosPauseSyncFn(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, targetComponent string) ChaosPauseData { rand.New(rand.NewSource(time.Now().UnixNano())) - randomBool := rand.Intn(2) == 0 randomNode := testEnv.ClCluster.Nodes[rand.Intn(len(testEnv.ClCluster.Nodes)-1)+1] var component ctf_test_env.EnvComponent - if randomBool { + switch strings.ToLower(targetComponent) { + case "chainlink": component = randomNode.EnvComponent - } else { + case "postgres": component = randomNode.PostgresDb.EnvComponent + default: + return ChaosPauseData{Err: fmt.Errorf("unknown component %s", targetComponent)} } + ctx := context.Background() + pauseStartBlock, err := testEnv.EVMClient.LatestBlockNumber(ctx) + if err != nil { + return ChaosPauseData{Err: err} + } pauseTimeSec := rand.Intn(20-5) + 5 l.Info().Str("Container", component.ContainerName).Int("Pause time", pauseTimeSec).Msg("Pausing component") pauseTimeDur := time.Duration(pauseTimeSec) * time.Second - err := component.ChaosPause(l, pauseTimeDur) + err = component.ChaosPause(l, pauseTimeDur) + if err != nil { + return ChaosPauseData{Err: err} + } l.Info().Str("Container", component.ContainerName).Msg("Component unpaused") + pauseEndBlock, err := testEnv.EVMClient.LatestBlockNumber(ctx) if err != nil { - return err + return ChaosPauseData{Err: err} } - return nil + return ChaosPauseData{PauseData: PauseData{ + StartBlock: pauseStartBlock, + EndBlock: pauseEndBlock, + TargetComponent: targetComponent, + ContaineName: component.ContainerName, + }} +} + +type ChaosPauseData struct { + Err error + PauseData PauseData } -var executeChaosExperiment = func(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, cfg *Config, errorCh chan error) { +// ExecuteChaosExperiment executes the configured chaos experiment, which consist of pausing CL node or Postgres containers +func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, cfg *Config, errorCh chan error) { if cfg.ChaosConfig == nil || cfg.ChaosConfig.ExperimentCount == 0 { errorCh <- nil return } - chaosChan := make(chan error, cfg.ChaosConfig.ExperimentCount) - + chaosChan := make(chan ChaosPauseData, cfg.ChaosConfig.ExperimentCount) wg := &sync.WaitGroup{} go func() { @@ -880,9 +960,11 @@ var executeChaosExperiment = func(l zerolog.Logger, testEnv *test_env.CLClusterT defer func() { <-guardChan wg.Done() - l.Info().Str("Current/Total", fmt.Sprintf("%d/%d", i, cfg.ChaosConfig.ExperimentCount)).Msg("Done with experiment") + current := i + 1 + l.Info().Str("Current/Total", fmt.Sprintf("%d/%d", current, cfg.ChaosConfig.ExperimentCount)).Msg("Done with experiment") }() - chaosChan <- chaosPauseSyncFn(l, testEnv) + chaosChan <- chaosPauseSyncFn(l, testEnv, cfg.ChaosConfig.TargetComponent) + time.Sleep(10 * time.Second) }() } @@ -892,25 +974,28 @@ var executeChaosExperiment = func(l zerolog.Logger, testEnv *test_env.CLClusterT }() go func() { - for err := range chaosChan { - // This will receive errors until chaosChan is closed - if err != nil { - // If an error is encountered, log it, send it to the error channel, and return from the function - l.Err(err).Msg("Error encountered during chaos experiment") - errorCh <- err + var pauseData []PauseData + for result := range chaosChan { + if result.Err != nil { + l.Err(result.Err).Msg("Error encountered during chaos experiment") + errorCh <- result.Err return // Return on actual error } - // No need for an else block here, because if err is nil (which happens when the channel is closed), - // the loop will exit and the following log and nil send will execute. + + pauseData = append(pauseData, result.PauseData) } - // After the loop exits, which it will do when chaosChan is closed, log that all experiments are finished. l.Info().Msg("All chaos experiments finished") errorCh <- nil // Only send nil once, after all errors have been handled and the channel is closed + + for _, p := range pauseData { + l.Debug().Str("Target component", p.TargetComponent).Str("Container", p.ContaineName).Str("Block range", fmt.Sprintf("%d - %d", p.StartBlock, p.EndBlock)).Msgf("Details of executed chaos pause") + } }() } -var GetFinalityDepth = func(chainId int64) (int64, error) { +// GetFinalityDepth returns the finality depth for the provided chain ID +func GetFinalityDepth(chainId int64) (int64, error) { var finalityDepth int64 switch chainId { // Ethereum Sepolia @@ -929,7 +1014,8 @@ var GetFinalityDepth = func(chainId int64) (int64, error) { return finalityDepth, nil } -var GetEndBlockToWaitFor = func(endBlock, chainId int64, cfg *Config) (int64, error) { +// GetEndBlockToWaitFor returns the end block to wait for based on chain id and finality tag provided in config +func GetEndBlockToWaitFor(endBlock, chainId int64, cfg *Config) (int64, error) { if cfg.General.UseFinalityTag { return endBlock + 1, nil } @@ -951,7 +1037,7 @@ const ( ) var ( - defaultOCRRegistryConfig = contracts.KeeperRegistrySettings{ + DefaultOCRRegistryConfig = contracts.KeeperRegistrySettings{ PaymentPremiumPPB: uint32(200000000), FlatFeeMicroLINK: uint32(0), BlockCountPerTurn: big.NewInt(10), @@ -982,7 +1068,8 @@ var ( } ) -func setupLogPollerTestDocker( +// SetupLogPollerTestDocker starts the DON and private Ethereum network +func SetupLogPollerTestDocker( t *testing.T, registryVersion ethereum.KeeperRegistryVersion, registryConfig contracts.KeeperRegistrySettings, @@ -1042,9 +1129,8 @@ func setupLogPollerTestDocker( WithConsensusType(ctf_test_env.ConsensusType_PoS). WithConsensusLayer(ctf_test_env.ConsensusLayer_Prysm). WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). - WithWaitingForFinalization(). WithEthereumChainConfig(ctf_test_env.EthereumChainConfig{ - SecondsPerSlot: 8, + SecondsPerSlot: 4, SlotsPerEpoch: 2, }). Build() @@ -1116,3 +1202,131 @@ func setupLogPollerTestDocker( return env.EVMClient, nodeClients, env.ContractDeployer, linkToken, registry, registrar, env } + +// UploadLogEmitterContractsAndWaitForFinalisation uploads the configured number of log emitter contracts and waits for the upload blocks to be finalised +func UploadLogEmitterContractsAndWaitForFinalisation(l zerolog.Logger, t *testing.T, testEnv *test_env.CLClusterTestEnv, cfg *Config) []*contracts.LogEmitter { + logEmitters := make([]*contracts.LogEmitter, 0) + for i := 0; i < cfg.General.Contracts; i++ { + logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() + logEmitters = append(logEmitters, &logEmitter) + require.NoError(t, err, "Error deploying log emitter contract") + l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") + time.Sleep(200 * time.Millisecond) + } + afterUploadBlock, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest block number") + + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + targetBlockNumber := int64(afterUploadBlock + 1) + finalized, err := testEnv.EVMClient.GetLatestFinalizedBlockHeader(testcontext.Get(t)) + if err != nil { + l.Warn().Err(err).Msg("Error checking if contract were uploaded. Retrying...") + return + } + finalizedBlockNumber := finalized.Number.Int64() + + if finalizedBlockNumber < targetBlockNumber { + l.Debug().Int64("Finalized block", finalized.Number.Int64()).Int64("After upload block", int64(afterUploadBlock+1)).Msg("Waiting for contract upload to finalise") + } + + g.Expect(finalizedBlockNumber >= targetBlockNumber).To(gomega.BeTrue(), "Contract upload did not finalize in time") + }, "2m", "10s").Should(gomega.Succeed()) + + return logEmitters +} + +// AssertUpkeepIdsUniqueness asserts that the provided upkeep IDs are unique +func AssertUpkeepIdsUniqueness(upkeepIDs []*big.Int) error { + upKeepIdSeen := make(map[int64]bool) + for _, upkeepID := range upkeepIDs { + if _, ok := upKeepIdSeen[upkeepID.Int64()]; ok { + return fmt.Errorf("Duplicate upkeep ID %d", upkeepID.Int64()) + } + upKeepIdSeen[upkeepID.Int64()] = true + } + + return nil +} + +// AssertContractAddressUniquneness asserts that the provided contract addresses are unique +func AssertContractAddressUniquneness(logEmitters []*contracts.LogEmitter) error { + contractAddressSeen := make(map[string]bool) + for _, logEmitter := range logEmitters { + address := (*logEmitter).Address().String() + if _, ok := contractAddressSeen[address]; ok { + return fmt.Errorf("Duplicate contract address %s", address) + } + contractAddressSeen[address] = true + } + + return nil +} + +// RegisterFiltersAndAssertUniquness registers the configured log filters and asserts that the filters are unique +// meaning that for each log emitter address and topic there is only one filter +func RegisterFiltersAndAssertUniquness(l zerolog.Logger, registry contracts.KeeperRegistry, upkeepIDs []*big.Int, logEmitters []*contracts.LogEmitter, cfg *Config, upKeepsNeeded int) error { + uniqueFilters := make(map[string]bool) + + upkeepIdIndex := 0 + for i := 0; i < len(logEmitters); i++ { + for j := 0; j < len(cfg.General.EventsToEmit); j++ { + emitterAddress := (*logEmitters[i]).Address() + topicId := cfg.General.EventsToEmit[j].ID + + upkeepID := upkeepIDs[upkeepIdIndex] + l.Debug().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") + err := registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) + randomWait(150, 300) + if err != nil { + return fmt.Errorf("%w: Error registering log trigger for log emitter %s", err, emitterAddress.String()) + } + + if i%10 == 0 { + l.Info().Msgf("Registered log trigger for topic %d for log emitter %d/%d", j, i, len(logEmitters)) + } + + key := fmt.Sprintf("%s-%s", emitterAddress.String(), topicId.Hex()) + if _, ok := uniqueFilters[key]; ok { + return fmt.Errorf("Duplicate filter %s", key) + } + uniqueFilters[key] = true + upkeepIdIndex++ + } + } + + if upKeepsNeeded != len(uniqueFilters) { + return fmt.Errorf("Number of unique filters should be equal to number of upkeeps. Expected %d. Got %d", upKeepsNeeded, len(uniqueFilters)) + } + + return nil +} + +// FluentlyCheckIfAllNodesHaveLogCount checks if all CL nodes have the expected log count for the provided block range and expected filters +// It will retry until the provided duration is reached or until all nodes have the expected log count +func FluentlyCheckIfAllNodesHaveLogCount(duration string, startBlock, endBlock int64, expectedLogCount int, expectedFilters []ExpectedFilter, l zerolog.Logger, coreLogger core_logger.SugaredLogger, testEnv *test_env.CLClusterTestEnv) (bool, error) { + logCountWaitDuration, err := time.ParseDuration(duration) + if err != nil { + return false, err + } + endTime := time.Now().Add(logCountWaitDuration) + + // not using gomega here, because I want to see which logs were missing + allNodesLogCountMatches := false + for time.Now().Before(endTime) { + logCountMatches, clErr := ClNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), expectedLogCount, expectedFilters, l, coreLogger, testEnv.ClCluster) + if clErr != nil { + l.Warn(). + Err(clErr). + Msg("Error checking if CL nodes have expected log count. Retrying...") + } + if logCountMatches { + allNodesLogCountMatches = true + break + } + l.Warn(). + Msg("At least one CL node did not have expected log count. Retrying...") + } + + return allNodesLogCountMatches, nil +} diff --git a/integration-tests/universal/log_poller/scenarios.go b/integration-tests/universal/log_poller/scenarios.go deleted file mode 100644 index 5331c63f752..00000000000 --- a/integration-tests/universal/log_poller/scenarios.go +++ /dev/null @@ -1,496 +0,0 @@ -package logpoller - -import ( - "fmt" - "math/big" - "testing" - "time" - - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" -) - -func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { - l := logging.GetTestLogger(t) - coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ - - if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { - l.Warn().Msg("No events to emit specified, using all events from log emitter contract") - for _, event := range EmitterABI.Events { - cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) - } - } - - l.Info().Msg("Starting basic log poller test") - - var ( - err error - upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) - ) - - chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( - t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(500*time.Millisecond), cfg.General.UseFinalityTag, - ) - - _, upkeepIDs := actions.DeployConsumers( - t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, - upKeepsNeeded, - big.NewInt(automationDefaultLinkFunds), - automationDefaultUpkeepGasLimit, - true, - false, - ) - - // Deploy Log Emitter contracts - logEmitters := make([]*contracts.LogEmitter, 0) - for i := 0; i < cfg.General.Contracts; i++ { - logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() - logEmitters = append(logEmitters, &logEmitter) - require.NoError(t, err, "Error deploying log emitter contract") - l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") - time.Sleep(200 * time.Millisecond) - } - - // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) - // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) - for i := 0; i < len(upkeepIDs); i++ { - emitterAddress := (*logEmitters[i%cfg.General.Contracts]).Address() - upkeepID := upkeepIDs[i] - topicId := cfg.General.EventsToEmit[i%len(cfg.General.EventsToEmit)].ID - - l.Info().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") - err = registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) - randomWait(50, 200) - require.NoError(t, err, "Error registering log trigger for log emitter") - } - - err = chainClient.WaitForEvents() - require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") - - // Make sure that all nodes have expected filters registered before starting to emit events - expectedFilters := getExpectedFilters(logEmitters, cfg) - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { - nodeName := testEnv.ClCluster.Nodes[i].ContainerName - l.Info().Str("Node name", nodeName).Msg("Fetching filters from log poller's DB") - - hasFilters, err := nodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) - if err != nil { - l.Warn().Err(err).Msg("Error checking if node has expected filters. Retrying...") - return - } - - g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") - } - }, "30s", "1s").Should(gomega.Succeed()) - l.Info().Msg("All nodes have expected filters registered") - l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") - - // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest block number") - startBlock := int64(sb) - - l.Info().Msg("STARTING EVENT EMISSION") - startTime := time.Now() - - // Start chaos experimnents by randomly pausing random containers (Chainlink nodes or their DBs) - chaosDoneCh := make(chan error, 1) - go func() { - executeChaosExperiment(l, testEnv, cfg, chaosDoneCh) - }() - - totalLogsEmitted, err := executeGenerator(t, cfg, logEmitters) - endTime := time.Now() - require.NoError(t, err, "Error executing event generator") - - expectedLogsEmitted := getExpectedLogCount(cfg) - duration := int(endTime.Sub(startTime).Seconds()) - l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") - - // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest block number") - - endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) - require.NoError(t, err, "Error getting end block to wait for") - - l.Info().Msg("Waiting before proceeding with test until all chaos experiments finish") - chaosError := <-chaosDoneCh - require.NoError(t, chaosError, "Error encountered during chaos experiment") - - // Wait until last block in which events were emitted has been finalised - // how long should we wait here until all logs are processed? wait for block X to be processed by all nodes? - waitDuration := "15m" - l.Warn().Str("Duration", waitDuration).Msg("Waiting for logs to be processed by all nodes and for chain to advance beyond finality") - - gom.Eventually(func(g gomega.Gomega) { - hasAdvanced, err := chainHasFinalisedEndBlock(l, testEnv.EVMClient, endBlock) - if err != nil { - l.Warn().Err(err).Msg("Error checking if chain has advanced beyond finality. Retrying...") - } - g.Expect(hasAdvanced).To(gomega.BeTrue(), "Chain has not advanced beyond finality") - }, waitDuration, "30s").Should(gomega.Succeed()) - - l.Warn().Str("Duration", "1m").Msg("Waiting for all CL nodes to have end block finalised") - gom.Eventually(func(g gomega.Gomega) { - hasFinalised, err := logPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) - if err != nil { - l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") - } - g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") - }, "1m", "30s").Should(gomega.Succeed()) - - gom.Eventually(func(g gomega.Gomega) { - logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), totalLogsEmitted, expectedFilters, l, coreLogger, testEnv.ClCluster) - if err != nil { - l.Warn().Err(err).Msg("Error checking if CL nodes have expected log count. Retrying...") - } - g.Expect(logCountMatches).To(gomega.BeTrue(), "Not all CL nodes have expected log count") - }, waitDuration, "5s").Should(gomega.Succeed()) - - // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has - logConsistencyWaitDuration := "1m" - l.Warn().Str("Duration", logConsistencyWaitDuration).Msg("Waiting for CL nodes to have all the logs that EVM node has") - - gom.Eventually(func(g gomega.Gomega) { - missingLogs, err := getMissingLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, testEnv.ClCluster, l, coreLogger, cfg) - if err != nil { - l.Warn().Err(err).Msg("Error getting missing logs. Retrying...") - } - - if !missingLogs.IsEmpty() { - printMissingLogsByType(missingLogs, l, cfg) - } - g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") - }, logConsistencyWaitDuration, "5s").Should(gomega.Succeed()) -} - -func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string) { - l := logging.GetTestLogger(t) - coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ - - if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { - l.Warn().Msg("No events to emit specified, using all events from log emitter contract") - for _, event := range EmitterABI.Events { - cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) - } - } - - l.Info().Msg("Starting replay log poller test") - - var ( - err error - upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) - ) - - // we set blockBackfillDepth to 0, to make sure nothing will be backfilled and won't interfere with our test - chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( - t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag) - - _, upkeepIDs := actions.DeployConsumers( - t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, - upKeepsNeeded, - big.NewInt(automationDefaultLinkFunds), - automationDefaultUpkeepGasLimit, - true, - false, - ) - - // Deploy Log Emitter contracts - logEmitters := make([]*contracts.LogEmitter, 0) - for i := 0; i < cfg.General.Contracts; i++ { - logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() - logEmitters = append(logEmitters, &logEmitter) - require.NoError(t, err, "Error deploying log emitter contract") - l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") - time.Sleep(200 * time.Millisecond) - } - - //wait for contracts to be uploaded to chain, TODO: could make this wait fluent - time.Sleep(5 * time.Second) - - // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest block number") - startBlock := int64(sb) - - l.Info().Msg("STARTING EVENT EMISSION") - startTime := time.Now() - totalLogsEmitted, err := executeGenerator(t, cfg, logEmitters) - endTime := time.Now() - require.NoError(t, err, "Error executing event generator") - expectedLogsEmitted := getExpectedLogCount(cfg) - duration := int(endTime.Sub(startTime).Seconds()) - l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") - - // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest block number") - - endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) - require.NoError(t, err, "Error getting end block to wait for") - - // Lets make sure no logs are in DB yet - expectedFilters := getExpectedFilters(logEmitters, cfg) - logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), 0, expectedFilters, l, coreLogger, testEnv.ClCluster) - require.NoError(t, err, "Error checking if CL nodes have expected log count") - require.True(t, logCountMatches, "Some CL nodes already had logs in DB") - l.Info().Msg("No logs were saved by CL nodes yet, as expected. Proceeding.") - - // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) - // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) - for i := 0; i < len(upkeepIDs); i++ { - emitterAddress := (*logEmitters[i%cfg.General.Contracts]).Address() - upkeepID := upkeepIDs[i] - topicId := cfg.General.EventsToEmit[i%len(cfg.General.EventsToEmit)].ID - - l.Info().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") - err = registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) - require.NoError(t, err, "Error registering log trigger for log emitter") - } - - err = chainClient.WaitForEvents() - require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") - - // Make sure that all nodes have expected filters registered before starting to emit events - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { - nodeName := testEnv.ClCluster.Nodes[i].ContainerName - l.Info().Str("Node name", nodeName).Msg("Fetching filters from log poller's DB") - - hasFilters, err := nodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) - if err != nil { - l.Warn().Err(err).Msg("Error checking if node has expected filters. Retrying...") - return - } - - g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") - } - }, "30s", "1s").Should(gomega.Succeed()) - l.Info().Msg("All nodes have expected filters registered") - l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") - - l.Warn().Str("Duration", "1m").Msg("Waiting for all CL nodes to have end block finalised") - gom.Eventually(func(g gomega.Gomega) { - hasFinalised, err := logPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) - if err != nil { - l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") - } - g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") - }, "1m", "30s").Should(gomega.Succeed()) - - // Trigger replay - l.Info().Msg("Triggering log poller's replay") - for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { - nodeName := testEnv.ClCluster.Nodes[i].ContainerName - response, _, err := testEnv.ClCluster.Nodes[i].API.ReplayLogPollerFromBlock(startBlock, testEnv.EVMClient.GetChainID().Int64()) - require.NoError(t, err, "Error triggering log poller's replay on node %s", nodeName) - require.Equal(t, "Replay started", response.Data.Attributes.Message, "Unexpected response message from log poller's replay") - } - - l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for replay logs to be processed by all nodes") - - gom.Eventually(func(g gomega.Gomega) { - logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), totalLogsEmitted, expectedFilters, l, coreLogger, testEnv.ClCluster) - if err != nil { - l.Warn().Err(err).Msg("Error checking if CL nodes have expected log count. Retrying...") - } - g.Expect(logCountMatches).To(gomega.BeTrue(), "Not all CL nodes have expected log count") - }, consistencyTimeout, "30s").Should(gomega.Succeed()) - - // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has - l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for CL nodes to have all the logs that EVM node has") - - gom.Eventually(func(g gomega.Gomega) { - missingLogs, err := getMissingLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, testEnv.ClCluster, l, coreLogger, cfg) - if err != nil { - l.Warn().Err(err).Msg("Error getting missing logs. Retrying...") - } - - if !missingLogs.IsEmpty() { - printMissingLogsByType(missingLogs, l, cfg) - } - g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") - }, consistencyTimeout, "10s").Should(gomega.Succeed()) -} - -type FinalityBlockFn = func(chainId int64, endBlock int64) (int64, error) - -func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { - l := logging.GetTestLogger(t) - coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ - - if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { - l.Warn().Msg("No events to emit specified, using all events from log emitter contract") - for _, event := range EmitterABI.Events { - cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) - } - } - - l.Info().Msg("Starting CI log poller test") - - var ( - err error - upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) - ) - - chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( - t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag, - ) - - _, upkeepIDs := actions.DeployConsumers( - t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, - upKeepsNeeded, - big.NewInt(automationDefaultLinkFunds), - automationDefaultUpkeepGasLimit, - true, - false, - ) - - // Deploy Log Emitter contracts - logEmitters := make([]*contracts.LogEmitter, 0) - for i := 0; i < cfg.General.Contracts; i++ { - logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() - logEmitters = append(logEmitters, &logEmitter) - require.NoError(t, err, "Error deploying log emitter contract") - l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") - time.Sleep(200 * time.Millisecond) - } - - // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) - // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) - for i := 0; i < len(upkeepIDs); i++ { - emitterAddress := (*logEmitters[i%cfg.General.Contracts]).Address() - upkeepID := upkeepIDs[i] - topicId := cfg.General.EventsToEmit[i%len(cfg.General.EventsToEmit)].ID - - l.Info().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") - err = registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) - randomWait(50, 200) - require.NoError(t, err, "Error registering log trigger for log emitter") - } - - err = chainClient.WaitForEvents() - require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") - - // Make sure that all nodes have expected filters registered before starting to emit events - expectedFilters := getExpectedFilters(logEmitters, cfg) - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { - nodeName := testEnv.ClCluster.Nodes[i].ContainerName - l.Info().Str("Node name", nodeName).Msg("Fetching filters from log poller's DB") - - hasFilters, err := nodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) - if err != nil { - l.Warn().Err(err).Msg("Error checking if node has expected filters. Retrying...") - return - } - - g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") - } - }, "1m", "1s").Should(gomega.Succeed()) - l.Info().Msg("All nodes have expected filters registered") - l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") - - // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest block number") - startBlock := int64(sb) - - l.Info().Msg("STARTING EVENT EMISSION") - startTime := time.Now() - - // Start chaos experimnents by randomly pausing random containers (Chainlink nodes or their DBs) - chaosDoneCh := make(chan error, 1) - go func() { - executeChaosExperiment(l, testEnv, cfg, chaosDoneCh) - }() - - totalLogsEmitted, err := executeGenerator(t, cfg, logEmitters) - endTime := time.Now() - require.NoError(t, err, "Error executing event generator") - - expectedLogsEmitted := getExpectedLogCount(cfg) - duration := int(endTime.Sub(startTime).Seconds()) - l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") - - // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest block number") - - endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) - require.NoError(t, err, "Error getting end block to wait for") - - l.Info().Msg("Waiting before proceeding with test until all chaos experiments finish") - chaosError := <-chaosDoneCh - require.NoError(t, chaosError, "Error encountered during chaos experiment") - - // Wait until last block in which events were emitted has been finalised (with buffer) - waitDuration := "45m" - l.Warn().Str("Duration", waitDuration).Msg("Waiting for chain to advance beyond finality") - - gom.Eventually(func(g gomega.Gomega) { - hasAdvanced, err := chainHasFinalisedEndBlock(l, testEnv.EVMClient, endBlock) - if err != nil { - l.Warn().Err(err).Msg("Error checking if chain has advanced beyond finality. Retrying...") - } - g.Expect(hasAdvanced).To(gomega.BeTrue(), "Chain has not advanced beyond finality") - }, waitDuration, "30s").Should(gomega.Succeed()) - - l.Warn().Str("Duration", waitDuration).Msg("Waiting for all CL nodes to have end block finalised") - gom.Eventually(func(g gomega.Gomega) { - hasFinalised, err := logPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) - if err != nil { - l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") - } - g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") - }, waitDuration, "30s").Should(gomega.Succeed()) - - // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has - logConsistencyWaitDuration := "10m" - l.Warn().Str("Duration", logConsistencyWaitDuration).Msg("Waiting for CL nodes to have all the logs that EVM node has") - - gom.Eventually(func(g gomega.Gomega) { - missingLogs, err := getMissingLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, testEnv.ClCluster, l, coreLogger, cfg) - if err != nil { - l.Warn().Err(err).Msg("Error getting missing logs. Retrying...") - } - - if !missingLogs.IsEmpty() { - printMissingLogsByType(missingLogs, l, cfg) - } - g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") - }, logConsistencyWaitDuration, "20s").Should(gomega.Succeed()) - - evmLogs, _ := getEVMLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, l, cfg) - - if totalLogsEmitted != len(evmLogs) { - l.Warn().Int("Total logs emitted", totalLogsEmitted).Int("Total logs in EVM", len(evmLogs)).Msg("Test passed, but total logs emitted does not match total logs in EVM") - } -} From 043f6a43d8d04d27e3ce70f394baf3be9e02991f Mon Sep 17 00:00:00 2001 From: Lei Date: Tue, 23 Jan 2024 16:05:42 -0800 Subject: [PATCH 194/234] exclude debug.go (#11866) --- core/scripts/chaincli/handler/debug.go | 2 +- sonar-project.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index c7b2ad7acbc..21ebfecc30e 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -75,7 +75,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { registryAddress := gethcommon.HexToAddress(k.cfg.RegistryAddress) keeperRegistry21, err := iregistry21.NewIKeeperRegistryMaster(registryAddress, k.client) if err != nil { - failUnknown("failed to connect to registry contract", err) + failUnknown("failed to connect to the registry contract", err) } // verify contract is correct diff --git a/sonar-project.properties b/sonar-project.properties index 111df35ac0c..97ed0a4c7ff 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.sources=. sonar.python.version=3.8 # Full exclusions from the static analysis -sonar.exclusions=**/node_modules/**/*,**/mocks/**/*, **/testdata/**/*, **/contracts/typechain/**/*, **/contracts/artifacts/**/*, **/contracts/cache/**/*, **/contracts/scripts/**/*, **/generated/**/*, **/fixtures/**/*, **/docs/**/*, **/tools/**/*, **/*.pb.go, **/*report.xml, **/*.config.ts, **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go, core/services/relay/evm/types/*_gen.go, core/services/relay/evm/types/gen/main.go, core/services/relay/evm/testfiles/*, **/core/web/assets** +sonar.exclusions=**/node_modules/**/*,**/mocks/**/*, **/testdata/**/*, **/contracts/typechain/**/*, **/contracts/artifacts/**/*, **/contracts/cache/**/*, **/contracts/scripts/**/*, **/generated/**/*, **/fixtures/**/*, **/docs/**/*, **/tools/**/*, **/*.pb.go, **/*report.xml, **/*.config.ts, **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go, core/services/relay/evm/types/*_gen.go, core/services/relay/evm/types/gen/main.go, core/services/relay/evm/testfiles/*, **/core/web/assets**, core/scripts/chaincli/handler/debug.go # Coverage exclusions sonar.coverage.exclusions=**/*.test.ts, **/*_test.go, **/contracts/test/**/*, **/contracts/**/tests/**/*, **/core/**/testutils/**/*, **/core/**/mocks/**/*, **/core/**/cltest/**/*, **/integration-tests/**/*, **/generated/**/*, **/core/scripts**/* , **/*.pb.go, ./plugins/**/*, **/main.go, **/0195_add_not_null_to_evm_chain_id_in_job_specs.go # Duplication exclusions From 2586bc4f2b13b98d090719a254c86c8af691d74f Mon Sep 17 00:00:00 2001 From: Lei Date: Tue, 23 Jan 2024 20:22:40 -0800 Subject: [PATCH 195/234] replace external facing mercury with data streams (#11754) --- core/scripts/chaincli/.env.debugging.example | 10 +++++----- core/scripts/chaincli/DEBUGGING.md | 2 +- core/scripts/chaincli/config/config.go | 12 ++++++------ core/scripts/chaincli/handler/debug.go | 18 +++++++++--------- core/scripts/chaincli/handler/handler.go | 2 +- core/scripts/chaincli/handler/keeper_launch.go | 15 ++++++++------- 6 files changed, 30 insertions(+), 29 deletions(-) diff --git a/core/scripts/chaincli/.env.debugging.example b/core/scripts/chaincli/.env.debugging.example index 6934bb107f5..c2942b99076 100644 --- a/core/scripts/chaincli/.env.debugging.example +++ b/core/scripts/chaincli/.env.debugging.example @@ -8,8 +8,8 @@ KEEPER_REGISTRY_ADDRESS= #TENDERLY_ACCOUNT_NAME= #TENDERLY_PROJECT_NAME= -# [Optional] add mercury info only if your upkeep uses mercury -#MERCURY_ID= -#MERCURY_KEY= -#MERCURY_LEGACY_URL= -#MERCURY_URL= \ No newline at end of file +# [Optional] add data streams (https://docs.chain.link/data-streams) info only if your upkeep uses data streams +#DATA_STREAMS_ID= +#DATA_STREAMS_KEY= +#DATA_STREAMS_URL= +#DATA_STREAMS_LEGACY_URL= \ No newline at end of file diff --git a/core/scripts/chaincli/DEBUGGING.md b/core/scripts/chaincli/DEBUGGING.md index 7c644b1699a..703261fcb54 100644 --- a/core/scripts/chaincli/DEBUGGING.md +++ b/core/scripts/chaincli/DEBUGGING.md @@ -22,7 +22,7 @@ Ensure the following fields are provided in your `.env` file: #### Optional Fields (Streams Lookup) -If your targeted upkeep involves streams lookup, include the following information: +If your targeted upkeep involves streams lookup, please provide the following details. If you are using Data Streams v0.3 (which is likely), only provide the DATA_STREAMS_URL. The DATA_STREAMS_LEGACY_URL is specifically for Data Streams v0.2. - `DATA_STREAMS_ID` - `DATA_STREAMS_KEY` diff --git a/core/scripts/chaincli/config/config.go b/core/scripts/chaincli/config/config.go index d227aa2e298..a17671bf8a8 100644 --- a/core/scripts/chaincli/config/config.go +++ b/core/scripts/chaincli/config/config.go @@ -90,12 +90,12 @@ type Config struct { FeedQuoteAddr string `mapstructure:"FEED_QUOTE_ADDR"` FeedDecimals uint8 `mapstructure:"FEED_DECIMALS"` - // Mercury Config - MercuryURL string `mapstructure:"MERCURY_URL"` - MercuryLegacyURL string `mapstructure:"MERCURY_LEGACY_URL"` - MercuryID string `mapstructure:"MERCURY_ID"` - MercuryKey string `mapstructure:"MERCURY_KEY"` - MercuryCredName string `mapstructure:"MERCURY_CRED_NAME"` + // Data Streams Config + DataStreamsURL string `mapstructure:"DATA_STREAMS_URL"` + DataStreamsLegacyURL string `mapstructure:"DATA_STREAMS_LEGACY_URL"` + DataStreamsID string `mapstructure:"DATA_STREAMS_ID"` + DataStreamsKey string `mapstructure:"DATA_STREAMS_KEY"` + DataStreamsCredName string `mapstructure:"DATA_STREAMS_CRED_NAME"` // Tenderly TenderlyKey string `mapstructure:"TENDERLY_KEY"` diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 21ebfecc30e..2c97adace26 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -257,7 +257,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { - mc := &types2.MercuryCredentials{LegacyURL: k.cfg.MercuryLegacyURL, URL: k.cfg.MercuryURL, Username: k.cfg.MercuryID, Password: k.cfg.MercuryKey} + mc := &types2.MercuryCredentials{LegacyURL: k.cfg.DataStreamsLegacyURL, URL: k.cfg.DataStreamsURL, Username: k.cfg.DataStreamsID, Password: k.cfg.DataStreamsKey} mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) lggr, _ := logger.NewLogger() blockSub := &blockSubscriber{k.client} @@ -282,25 +282,25 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } if streamsLookup.IsMercuryV02() { - message("using mercury lookup v0.2") + message("using data streams lookup v0.2") // check if upkeep is allowed to use mercury v0.2 var allowed bool _, _, _, allowed, err = streams.AllowedToUseMercury(triggerCallOpts, upkeepID) if err != nil { - failUnknown("failed to check if upkeep is allowed to use mercury", err) + failUnknown("failed to check if upkeep is allowed to use data streams", err) } if !allowed { resolveIneligible("upkeep reverted with StreamsLookup but is not allowed to access streams") } } else if streamsLookup.IsMercuryV03() { // handle v0.3 - message("using mercury lookup v0.3") + message("using data streams lookup v0.3") } else { resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") } - if k.cfg.MercuryLegacyURL == "" || k.cfg.MercuryURL == "" || k.cfg.MercuryID == "" || k.cfg.MercuryKey == "" { - failCheckConfig("Mercury configs not set properly, check your MERCURY_LEGACY_URL, MERCURY_URL, MERCURY_ID and MERCURY_KEY", nil) + if k.cfg.DataStreamsLegacyURL == "" || k.cfg.DataStreamsURL == "" || k.cfg.DataStreamsID == "" || k.cfg.DataStreamsKey == "" { + failCheckConfig("Data streams configs not set properly, check your DATA_STREAMS_LEGACY_URL, DATA_STREAMS_URL, DATA_STREAMS_ID and DATA_STREAMS_KEY", nil) } // do mercury request @@ -314,16 +314,16 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { resolveIneligible("upkeep used invalid revert data") } if checkResults[0].PipelineExecutionState == uint8(encoding.InvalidMercuryRequest) { - resolveIneligible("the mercury request data is invalid") + resolveIneligible("the data streams request data is invalid") } if err != nil { - failCheckConfig("failed to do mercury request ", err) + failCheckConfig("failed to do data streams request ", err) } // do checkCallback err = streams.CheckCallback(ctx, values, streamsLookup, checkResults, 0) if err != nil { - failUnknown("failed to execute mercury callback ", err) + failUnknown("failed to execute data streams callback ", err) } if checkResults[0].IneligibilityReason != 0 { message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResults[0].IneligibilityReason)) diff --git a/core/scripts/chaincli/handler/handler.go b/core/scripts/chaincli/handler/handler.go index 591431555a9..a5b6798836a 100644 --- a/core/scripts/chaincli/handler/handler.go +++ b/core/scripts/chaincli/handler/handler.go @@ -333,7 +333,7 @@ func (h *baseHandler) launchChainlinkNode(ctx context.Context, port int, contain if err != nil { return "", nil, fmt.Errorf("failed to create toml file: %w", err) } - var secretTOMLStr = fmt.Sprintf(secretTOML, h.cfg.MercuryURL, h.cfg.MercuryID, h.cfg.MercuryKey) + var secretTOMLStr = fmt.Sprintf(secretTOML, h.cfg.DataStreamsURL, h.cfg.DataStreamsID, h.cfg.DataStreamsKey) secretFile, secretTOMLFileCleanup, err := createTomlFile(secretTOMLStr) if err != nil { return "", nil, fmt.Errorf("failed to create secret toml file: %w", err) diff --git a/core/scripts/chaincli/handler/keeper_launch.go b/core/scripts/chaincli/handler/keeper_launch.go index 25af77f1d5c..1db3884ae4e 100644 --- a/core/scripts/chaincli/handler/keeper_launch.go +++ b/core/scripts/chaincli/handler/keeper_launch.go @@ -19,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" + "github.com/smartcontractkit/chainlink/v2/core/cmd" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" registry12 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2" @@ -377,13 +378,13 @@ func (k *Keeper) createOCR2KeeperJob(ctx context.Context, client cmd.HTTPClient, request, err := json.Marshal(web.CreateJobRequest{ TOML: fmt.Sprintf(ocr2keeperJobTemplate, - contractAddr, // contractID - ocr2KeyConfig.ID, // ocrKeyBundleID - nodeAddr, // transmitterID - node wallet address - k.cfg.BootstrapNodeAddr, // bootstrap node key and address - k.cfg.ChainID, // chainID - contractVersion, // contractVersion - k.cfg.MercuryCredName, // mercury credential name + contractAddr, // contractID + ocr2KeyConfig.ID, // ocrKeyBundleID + nodeAddr, // transmitterID - node wallet address + k.cfg.BootstrapNodeAddr, // bootstrap node key and address + k.cfg.ChainID, // chainID + contractVersion, // contractVersion + k.cfg.DataStreamsCredName, // mercury credential name ), }) if err != nil { From aa05727ca8409e57d5c787c47593d7e3aa9e541c Mon Sep 17 00:00:00 2001 From: chris-de-leon-cll <147140544+chris-de-leon-cll@users.noreply.github.com> Date: Wed, 24 Jan 2024 04:55:18 -0800 Subject: [PATCH 196/234] [DEPLOY-694]: Adds zero-value check to ScrollSequencerUptimeFeed (#11710) * adds zero value address check to ScrollSequencerUptimeFeed _setL1Sender helper function * adds zero address check for l2CrossDomainMessenger * adds more descriptive error messages to custom ZeroAddress error * removing additional msg field from ZeroAddress error * Adds zero address checks for ScrollSequencerUptimeFeed * removes L1 zero address check and removes unnecessary encodeWithSelector calls * updates gas snapshot --- contracts/gas-snapshots/l2ep.gas-snapshot | 24 +++++++++---------- .../dev/scroll/ScrollSequencerUptimeFeed.sol | 6 +++++ .../ArbitrumSequencerUptimeFeed.t.sol | 2 +- .../OptimismSequencerUptimeFeed.t.sol | 10 ++++---- .../scroll/ScrollSequencerUptimeFeed.t.sol | 14 +++++++---- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/contracts/gas-snapshots/l2ep.gas-snapshot b/contracts/gas-snapshots/l2ep.gas-snapshot index 569af2fe661..1f229f7d1d9 100644 --- a/contracts/gas-snapshots/l2ep.gas-snapshot +++ b/contracts/gas-snapshots/l2ep.gas-snapshot @@ -38,7 +38,7 @@ ArbitrumSequencerUptimeFeed_GasCosts:test_GasCosts() (gas: 97495) ArbitrumSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() (gas: 602711) ArbitrumSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() (gas: 573802) ArbitrumSequencerUptimeFeed_UpdateStatus:test_IgnoreOutOfOrderUpdates() (gas: 98976) -ArbitrumSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 15776) +ArbitrumSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 15416) ArbitrumSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndNoTimeChange() (gas: 113269) ArbitrumSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndTimeChange() (gas: 113329) ArbitrumValidator_Validate:test_PostSequencerOffline() (gas: 69068) @@ -76,16 +76,16 @@ OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRo OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRoundData() (gas: 57309) OptimismSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestTimestamp() (gas: 56740) OptimismSequencerUptimeFeed_AggregatorV3Interface:test_AggregatorV3Interface() (gas: 65617) -OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetAnswerWhenRoundDoesNotExistYet() (gas: 18039) -OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetRoundDataWhenRoundDoesNotExistYet() (gas: 18257) -OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetTimestampWhenRoundDoesNotExistYet() (gas: 17963) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetAnswerWhenRoundDoesNotExistYet() (gas: 17679) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetRoundDataWhenRoundDoesNotExistYet() (gas: 17897) +OptimismSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetTimestampWhenRoundDoesNotExistYet() (gas: 17603) OptimismSequencerUptimeFeed_Constructor:test_InitialState() (gas: 21078) OptimismSequencerUptimeFeed_GasCosts:test_GasCosts() (gas: 67197) OptimismSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() (gas: 597640) OptimismSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() (gas: 573807) OptimismSequencerUptimeFeed_UpdateStatus:test_IgnoreOutOfOrderUpdates() (gas: 66532) -OptimismSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 13560) -OptimismSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() (gas: 23967) +OptimismSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 13200) +OptimismSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() (gas: 23607) OptimismSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenNoChange() (gas: 74035) OptimismSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndNoTimeChange() (gas: 96155) OptimismSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndTimeChange() (gas: 96215) @@ -127,16 +127,16 @@ ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRoun ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestRoundData() (gas: 55473) ScrollSequencerUptimeFeed_AggregatorInterfaceGasCosts:test_GasUsageForLatestTimestamp() (gas: 54758) ScrollSequencerUptimeFeed_AggregatorV3Interface:test_AggregatorV3Interface() (gas: 63903) -ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetAnswerWhenRoundDoesNotExistYet() (gas: 18035) -ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetRoundDataWhenRoundDoesNotExistYet() (gas: 18253) -ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetTimestampWhenRoundDoesNotExistYet() (gas: 17959) -ScrollSequencerUptimeFeed_Constructor:test_InitialState() (gas: 21085) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetAnswerWhenRoundDoesNotExistYet() (gas: 17675) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetRoundDataWhenRoundDoesNotExistYet() (gas: 17893) +ScrollSequencerUptimeFeed_AggregatorV3Interface:test_RevertGetTimestampWhenRoundDoesNotExistYet() (gas: 17599) +ScrollSequencerUptimeFeed_Constructor:test_InitialState() (gas: 102485) ScrollSequencerUptimeFeed_GasCosts:test_GasCosts() (gas: 64888) ScrollSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceAllowReadsIfConsumingContractIsWhitelisted() (gas: 597491) ScrollSequencerUptimeFeed_ProtectReadsOnAggregatorV2V3InterfaceFunctions:test_AggregatorV2V3InterfaceDisallowReadsIfConsumingContractIsNotWhitelisted() (gas: 573807) ScrollSequencerUptimeFeed_UpdateStatus:test_IgnoreOutOfOrderUpdates() (gas: 64417) -ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 13560) -ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() (gas: 23967) +ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddr() (gas: 13200) +ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAddrAndNotL1SenderAddr() (gas: 23607) ScrollSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenNoChange() (gas: 71618) ScrollSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndNoTimeChange() (gas: 92018) ScrollSequencerUptimeFeed_UpdateStatus:test_UpdateStatusWhenStatusChangeAndTimeChange() (gas: 92078) diff --git a/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol b/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol index 22b5ed1e2b6..9df2b61238a 100644 --- a/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol +++ b/contracts/src/v0.8/l2ep/dev/scroll/ScrollSequencerUptimeFeed.sol @@ -41,6 +41,8 @@ contract ScrollSequencerUptimeFeed is error InvalidSender(); /// @notice Replacement for AggregatorV3Interface "No data present" error NoDataPresent(); + /// @notice Address must not be the zero address + error ZeroAddress(); event L1SenderTransferred(address indexed from, address indexed to); /// @dev Emitted when an `updateStatus` call is ignored due to unchanged status or stale timestamp @@ -68,6 +70,10 @@ contract ScrollSequencerUptimeFeed is /// @param l2CrossDomainMessengerAddr Address of the L2CrossDomainMessenger contract /// @param initialStatus The initial status of the feed constructor(address l1SenderAddress, address l2CrossDomainMessengerAddr, bool initialStatus) { + if (l2CrossDomainMessengerAddr == address(0)) { + revert ZeroAddress(); + } + _setL1Sender(l1SenderAddress); s_l2CrossDomainMessenger = IL2ScrollMessenger(l2CrossDomainMessengerAddr); diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol index 054c49b1605..3b9df3bf910 100644 --- a/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/arbitrum/ArbitrumSequencerUptimeFeed.t.sol @@ -62,7 +62,7 @@ contract ArbitrumSequencerUptimeFeed_UpdateStatus is ArbitrumSequencerUptimeFeed vm.startPrank(s_strangerAddr, s_strangerAddr); // Tries to update the status from an unauthorized account - vm.expectRevert(abi.encodeWithSelector(ArbitrumSequencerUptimeFeed.InvalidSender.selector)); + vm.expectRevert(ArbitrumSequencerUptimeFeed.InvalidSender.selector); s_arbitrumSequencerUptimeFeed.updateStatus(true, uint64(1)); } diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol index b4aa32ce69b..60598b9f952 100644 --- a/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/optimism/OptimismSequencerUptimeFeed.t.sol @@ -61,7 +61,7 @@ contract OptimismSequencerUptimeFeed_UpdateStatus is OptimismSequencerUptimeFeed vm.startPrank(s_strangerAddr, s_strangerAddr); // Tries to update the status from an unauthorized account - vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.InvalidSender.selector)); + vm.expectRevert(OptimismSequencerUptimeFeed.InvalidSender.selector); s_optimismSequencerUptimeFeed.updateStatus(true, uint64(1)); } @@ -74,7 +74,7 @@ contract OptimismSequencerUptimeFeed_UpdateStatus is OptimismSequencerUptimeFeed s_mockOptimismL2CrossDomainMessenger.setSender(s_strangerAddr); // Tries to update the status from an unauthorized account - vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.InvalidSender.selector)); + vm.expectRevert(OptimismSequencerUptimeFeed.InvalidSender.selector); s_optimismSequencerUptimeFeed.updateStatus(true, uint64(1)); } @@ -257,7 +257,7 @@ contract OptimismSequencerUptimeFeed_AggregatorV3Interface is OptimismSequencerU vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); // Gets data from a round that has not happened yet - vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.NoDataPresent.selector)); + vm.expectRevert(OptimismSequencerUptimeFeed.NoDataPresent.selector); s_optimismSequencerUptimeFeed.getRoundData(2); } @@ -267,7 +267,7 @@ contract OptimismSequencerUptimeFeed_AggregatorV3Interface is OptimismSequencerU vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); // Gets data from a round that has not happened yet - vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.NoDataPresent.selector)); + vm.expectRevert(OptimismSequencerUptimeFeed.NoDataPresent.selector); s_optimismSequencerUptimeFeed.getAnswer(2); } @@ -277,7 +277,7 @@ contract OptimismSequencerUptimeFeed_AggregatorV3Interface is OptimismSequencerU vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); // Gets data from a round that has not happened yet - vm.expectRevert(abi.encodeWithSelector(OptimismSequencerUptimeFeed.NoDataPresent.selector)); + vm.expectRevert(OptimismSequencerUptimeFeed.NoDataPresent.selector); s_optimismSequencerUptimeFeed.getTimestamp(2); } } diff --git a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol index 85195816b90..520fbf6dfdc 100644 --- a/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol +++ b/contracts/src/v0.8/l2ep/test/v1_0_0/scroll/ScrollSequencerUptimeFeed.t.sol @@ -40,6 +40,10 @@ contract ScrollSequencerUptimeFeedTest is L2EPTest { contract ScrollSequencerUptimeFeed_Constructor is ScrollSequencerUptimeFeedTest { /// @notice it should have been deployed with the correct initial state function test_InitialState() public { + // L2 cross domain messenger address must not be the zero address + vm.expectRevert(ScrollSequencerUptimeFeed.ZeroAddress.selector); + new ScrollSequencerUptimeFeed(s_l1OwnerAddr, address(0), false); + // Sets msg.sender and tx.origin to a valid address vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); @@ -61,7 +65,7 @@ contract ScrollSequencerUptimeFeed_UpdateStatus is ScrollSequencerUptimeFeedTest vm.startPrank(s_strangerAddr, s_strangerAddr); // Tries to update the status from an unauthorized account - vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.InvalidSender.selector)); + vm.expectRevert(ScrollSequencerUptimeFeed.InvalidSender.selector); s_scrollSequencerUptimeFeed.updateStatus(true, uint64(1)); } @@ -74,7 +78,7 @@ contract ScrollSequencerUptimeFeed_UpdateStatus is ScrollSequencerUptimeFeedTest s_mockScrollL2CrossDomainMessenger.setSender(s_strangerAddr); // Tries to update the status from an unauthorized account - vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.InvalidSender.selector)); + vm.expectRevert(ScrollSequencerUptimeFeed.InvalidSender.selector); s_scrollSequencerUptimeFeed.updateStatus(true, uint64(1)); } @@ -257,7 +261,7 @@ contract ScrollSequencerUptimeFeed_AggregatorV3Interface is ScrollSequencerUptim vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); // Gets data from a round that has not happened yet - vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.NoDataPresent.selector)); + vm.expectRevert(ScrollSequencerUptimeFeed.NoDataPresent.selector); s_scrollSequencerUptimeFeed.getRoundData(2); } @@ -267,7 +271,7 @@ contract ScrollSequencerUptimeFeed_AggregatorV3Interface is ScrollSequencerUptim vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); // Gets data from a round that has not happened yet - vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.NoDataPresent.selector)); + vm.expectRevert(ScrollSequencerUptimeFeed.NoDataPresent.selector); s_scrollSequencerUptimeFeed.getAnswer(2); } @@ -277,7 +281,7 @@ contract ScrollSequencerUptimeFeed_AggregatorV3Interface is ScrollSequencerUptim vm.startPrank(s_l1OwnerAddr, s_l1OwnerAddr); // Gets data from a round that has not happened yet - vm.expectRevert(abi.encodeWithSelector(ScrollSequencerUptimeFeed.NoDataPresent.selector)); + vm.expectRevert(ScrollSequencerUptimeFeed.NoDataPresent.selector); s_scrollSequencerUptimeFeed.getTimestamp(2); } } From ccb3e99a251866548403350017d761b40c275ead Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 24 Jan 2024 15:21:05 +0100 Subject: [PATCH 197/234] [TT-748] TOML config for integration tests (#11588) * WIP#1 * WIP#2 * WIP#3 * WIP#4 * WIP#5 * WIP#6 * wIP#7 - overrides and validations for vrfv2 * wIP#8 - overrides and validations for vrfv2lus * WIP#9 - adjust vrfv2/plus default configs * fix test config test, add logger to it * smoke tests should work now * fix replace, fix workflow * try to remove newlines from base64 * update ctf version, fix line wrap * use newer version of ctf, fix log poller and workflow issues * expand selected networks to array * fix vrfv2 smoke test, mask baseb4 override * define base64 override before masking it * a couple of masks more * set some content for BASE64_CONFIG_OVERRIDE so that it's masked * remove masks that break toml config * experiment with base64 #1 * fix ocr2 smoke test * fix lints * baseb4 experiment #2 * base64 experiment #3 * hardcode some image * exp#5 * fail config test on purpose * fix lints * run all smoke tests * add missing test config to ocr2 test * add missing loki_url * remove old config files, set config override for remote runner * fix compile errors * fix lint * make the vrfv2plus name in test matrix unique * fix loki env config, adjust vrfv2 and vrfv2plus tests and configs * add TOML-specific overriding of CL image/version in k8s * fixed automation load test * couple small fixes to how helm is created * remove multiple config files, store all config versions in single product-specific toml file * go.mod * restore deleted test, fix vrfv2 smoke tests * fix vrfv2plus config validation * properly fix vrfv2plus smoke test * fix loki basic auth passing * add add-mask again to base64 * remove obsolete configs, use latest ctf, adjust tomls * try another base64 hiding * go.sum * fix loki compile issue * try base64 env var with _SECRET suffix * try yet another variable name * restore old env var name * another obscuring attempt * another attempt * use newer CTF version, check what env vars are forwaded to k8s * go.sum * updated to latest ctf * try on demand autonation test with base64 input * modify all other test workflows so that they use TOML config * update reamdes, example envs and some more workflows * uncomment solana tests * update to latest ctf * update to latest ctf * go.mod * add MustCopy() method to TestConfig, fix failing vrvf2plus test * fix duration usages * fix lints * added examples of full configs for each product * use latest ctf * simplify some examples, correct client compatibility workflow * go.mod * copy TOML configs before executing test binary in compatibility job * use bash not sh to run the script, add missing patterns * chmod script and run it * add debug * print vars before checking their number * another debug attempt * try double quotes * store script and run that * fix script formatting * fix correct script :facepalm: * fix zip command * fix upload artifact path * add some debug * update tracing readme, use latest ctf * latest ctf * lastest ctf * check what is the config missing * add missing pyrscope enabled var * remove debug * don't notify Adam if eth client compatibility test fail * latest ctf, faster eth2 chain by default * latest ctf, slots per epoch = 2 * go.sum * move convert_to_toml_array() bash function to a file and import it in GHA workflows instead of constantly defining before using * provide default configs to live testnet tests that use test binary * pass network config between jobs correctly * set .root_dir for live testnet job * try passing * try with defined output * define correct output for build-tests job * fix value name * debug * 2x base64 * try with shorter variable * add network config to dynamic config * include shared function * embed toml configs when build tag is present * test passing shared functions as outputs * update action version * remove newline from base64 * newer gha * fix log poller path, adjust client compatibility test to not copy tomls * debug * once more * do not use shared function * use correct network toml key for sepolia * fix dynamic toml * fix tomls * fix sepolia endpoint, uncomment slack notifications * remove outputs shared function from clients compatibility tests * fix alfajores once and for all * use newer gha to build tests * don't fail if there's no embedded config * remove tests we don't need anymore * use commit from main version of GHA repository instead of branch commit for build tests action * simplify config overriding * fix go.mod * remove ApplyOverride() and fix compile issue * fix lint * try base64 value extraction * try anothe approach * yet another attempt * yet another attempt 2 * fix base64 from input * one more time with final config * last test * make automation-ondemand test more secure, remove debug from on-demand-vrfv2-performance-test.yml * uncommet test execution in vrfv2 performance workflow * use private eth networks instead of geth in vrf load tests * update TOML keys related to Loki and Grafana * by default use pow, not pos * go.mod * latest ctf * remove references to deleted performance test suite * don't require Loki config when using remote runner * latest ctf * add Load config and figure out overrides * append loki config to ocr soak test on demand job * embed test configs and create .root_dir, when building test image * define an interface for global test config * move interface to testconfig * set pyroscope env for cron * fix check for remote running * bump CTF * use interfaces in public methods instead of TestConfig * small adjustments * fix test env builder * fix automation config type cast * use latest ctf * add special handling for slice override for Automation * update examples * slightly adjust vrfv2 configs, remove default empty values * move creation of base64 config override env var in smoke tests to a GHA action * fix compile error * debug for ocr test, camelcase for base64 action, two new actions to reduce loc * checkout repo before accessing actions * remove unnecessary test config instance from ocr test * mask before using input, more ocr debug * remove debug, fix reading named configurations from base64 override * mask input before using merge base64 action * fix integration tests workflow * update default.toml, add action for live testnet tests * download test binary after creating base64 env var * fix missing log targets * comment out reporting part * reuse live testnets specific action * fix network names * use dynamic network key * fix automation tests (#11815) * fix automation benchmark test action * update default toml * try newer run tests action * another try * look for .root_dir even deeper * Update integration-tests/benchmark/keeper_test.go * Update integration-tests/test.Dockerfile --------- Co-authored-by: Bartek Tofel * fix chain config for eth2 compatibility tests * replace in-workflow toml generation for CL version upgrade tests with GH action * make test timeout configurable in client compatbility tests * prepare base64 config for log poller matrix tests --------- Co-authored-by: Ilja Pavlovs Co-authored-by: skudasov Co-authored-by: Gheorghe Strimtu Co-authored-by: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> --- .github/actions/build-test-image/action.yml | 2 +- .../action.yml | 130 +++++ .../setup-create-base64-config/action.yml | 122 ++++ .../action.yml | 61 ++ .../setup-merge-base64-config/action.yml | 54 ++ .../setup-parse-base64-config/action.yml | 38 ++ .github/scripts/functions.sh | 17 + .github/tracing/README.md | 14 +- .../workflows/automation-benchmark-tests.yml | 107 ++-- .github/workflows/automation-load-tests.yml | 70 +-- .../workflows/automation-nightly-tests.yml | 11 +- .../workflows/automation-ondemand-tests.yml | 63 +- .../workflows/client-compatibility-tests.yml | 87 ++- .github/workflows/integration-chaos-tests.yml | 19 +- .../workflows/integration-staging-tests.yml | 73 ++- .github/workflows/integration-tests.yml | 111 ++-- .github/workflows/live-testnet-tests.yml | 370 ++++++++---- .github/workflows/on-demand-log-poller.yml | 70 +-- .github/workflows/on-demand-ocr-soak-test.yml | 122 +--- .../on-demand-vrfv2-eth2-clients-test.yml | 59 +- .../on-demand-vrfv2-performance-test.yml | 101 +--- .../on-demand-vrfv2plus-eth2-clients-test.yml | 61 +- .../on-demand-vrfv2plus-performance-test.yml | 106 +--- .github/workflows/performance-tests.yml | 87 --- .gitignore | 4 + core/scripts/go.sum | 8 +- go.mod | 2 + go.sum | 17 +- integration-tests/.root_dir | 0 integration-tests/Makefile | 32 +- integration-tests/actions/actions.go | 11 +- integration-tests/actions/actions_local.go | 5 - integration-tests/actions/private_network.go | 29 +- .../vrfv2_actions/vrfv2_config/config.go | 54 -- .../actions/vrfv2_actions/vrfv2_steps.go | 142 +++-- .../vrfv2plus/vrfv2plus_config/config.go | 54 -- .../actions/vrfv2plus/vrfv2plus_steps.go | 184 +++--- integration-tests/benchmark/keeper_test.go | 163 +++--- .../chaos/automation_chaos_test.go | 61 +- integration-tests/chaos/ocr2vrf_chaos_test.go | 31 +- integration-tests/chaos/ocr_chaos_test.go | 34 +- integration-tests/client/chainlink_k8s.go | 9 - .../contracts/ethereum_keeper_contracts.go | 2 +- integration-tests/docker/test_env/cl_node.go | 10 +- integration-tests/docker/test_env/test_env.go | 8 +- .../docker/test_env/test_env_builder.go | 25 +- integration-tests/example.env | 48 -- integration-tests/go.mod | 14 +- integration-tests/go.sum | 76 ++- integration-tests/k8s/connect.go | 2 +- .../automationv2_1/automationv2_1_test.go | 210 +++---- .../load/automationv2_1/helpers.go | 40 +- integration-tests/load/functions/config.go | 125 ---- .../load/functions/functions_test.go | 149 +++-- .../load/functions/gateway_gun.go | 32 +- .../load/functions/gateway_test.go | 27 +- .../load/functions/onchain_monitoring.go | 32 +- integration-tests/load/functions/setup.go | 45 +- integration-tests/load/ocr/config.go | 72 --- integration-tests/load/ocr/config.toml | 20 - integration-tests/load/ocr/ocr_test.go | 30 +- integration-tests/load/vrfv2/cmd/dashboard.go | 1 + integration-tests/load/vrfv2/config.go | 151 ----- integration-tests/load/vrfv2/config.toml | 58 -- integration-tests/load/vrfv2/gun.go | 39 +- integration-tests/load/vrfv2/vrfv2_test.go | 119 ++-- .../load/vrfv2plus/cmd/dashboard.go | 1 + integration-tests/load/vrfv2plus/config.go | 152 ----- integration-tests/load/vrfv2plus/config.toml | 57 -- integration-tests/load/vrfv2plus/gun.go | 44 +- .../load/vrfv2plus/vrfv2plus_test.go | 132 +++-- .../migration/upgrade_version_test.go | 21 +- integration-tests/performance/cron_test.go | 139 ----- .../performance/directrequest_test.go | 160 ------ integration-tests/performance/flux_test.go | 207 ------- integration-tests/performance/keeper_test.go | 194 ------- integration-tests/performance/ocr_test.go | 126 ---- integration-tests/performance/vrf_test.go | 163 ------ .../reorg/automation_reorg_test.go | 45 +- integration-tests/reorg/reorg_test.go | 228 -------- integration-tests/scripts/buildTests | 2 +- integration-tests/smoke/README.md | 24 +- integration-tests/smoke/automation_test.go | 134 +++-- .../smoke/automation_upgrade_test.go | 8 +- integration-tests/smoke/cron_test.go | 13 + integration-tests/smoke/flux_test.go | 7 + integration-tests/smoke/forwarder_ocr_test.go | 7 + .../smoke/forwarders_ocr2_test.go | 8 + integration-tests/smoke/keeper_test.go | 112 +++- integration-tests/smoke/log_poller_test.go | 334 ++--------- .../smoke/log_poller_test.go_test_list.json | 2 +- integration-tests/smoke/ocr2_test.go | 24 +- integration-tests/smoke/ocr2vrf_test.go | 39 +- integration-tests/smoke/ocr_test.go | 16 +- integration-tests/smoke/runlog_test.go | 8 + integration-tests/smoke/vrf_test.go | 12 + integration-tests/smoke/vrfv2_test.go | 172 +++--- integration-tests/smoke/vrfv2plus_test.go | 214 ++++--- integration-tests/soak/forwarder_ocr_test.go | 10 +- integration-tests/soak/ocr_test.go | 10 +- .../testconfig/automation/automation.toml | 49 ++ .../testconfig/automation/config.go | 91 +++ .../testconfig/automation/example.toml | 90 +++ integration-tests/testconfig/configs_embed.go | 21 + .../testconfig/configs_noembed.go | 12 + integration-tests/testconfig/default.toml | 22 + .../testconfig/functions/config.go | 118 ++++ .../testconfig/functions/example.toml | 113 ++++ .../functions/functions.toml} | 100 ++-- integration-tests/testconfig/keeper/config.go | 85 +++ .../testconfig/keeper/example.toml | 88 +++ .../testconfig/keeper/keeper.toml | 17 + .../testconfig/log_poller/config.go | 158 +++++ .../testconfig/log_poller/example.toml | 87 +++ .../testconfig/log_poller/log_poller.toml | 61 ++ .../testconfig/node/example.toml | 79 +++ integration-tests/testconfig/node/node.toml | 5 + integration-tests/testconfig/ocr/example.toml | 96 ++++ integration-tests/testconfig/ocr/ocr.go | 136 +++++ integration-tests/testconfig/ocr/ocr.toml | 43 ++ integration-tests/testconfig/testconfig.go | 540 ++++++++++++++++++ .../testconfig/testconfig_test.go | 88 +++ integration-tests/testconfig/vrf/config.go | 8 + integration-tests/testconfig/vrfv2/config.go | 330 +++++++++++ .../testconfig/vrfv2/example.toml | 136 +++++ integration-tests/testconfig/vrfv2/vrfv2.toml | 143 +++++ .../testconfig/vrfv2plus/config.go | 158 +++++ .../testconfig/vrfv2plus/example.toml | 144 +++++ .../testconfig/vrfv2plus/vrfv2plus.toml | 161 ++++++ .../testreporters/keeper_benchmark.go | 18 +- integration-tests/testreporters/ocr.go | 2 +- integration-tests/testreporters/profile.go | 2 +- integration-tests/testreporters/vrfv2.go | 32 +- integration-tests/testreporters/vrfv2plus.go | 27 +- .../testsetups/keeper_benchmark.go | 24 +- integration-tests/testsetups/ocr.go | 179 +++--- integration-tests/types/testconfigs.go | 44 ++ .../universal/log_poller/config.go | 249 -------- .../universal/log_poller/helpers.go | 90 +-- 139 files changed, 6166 insertions(+), 4635 deletions(-) create mode 100644 .github/actions/setup-create-base64-config-live-testnets/action.yml create mode 100644 .github/actions/setup-create-base64-config/action.yml create mode 100644 .github/actions/setup-create-base64-upgrade-config /action.yml create mode 100644 .github/actions/setup-merge-base64-config/action.yml create mode 100644 .github/actions/setup-parse-base64-config/action.yml create mode 100644 .github/scripts/functions.sh delete mode 100644 .github/workflows/performance-tests.yml create mode 100644 integration-tests/.root_dir delete mode 100644 integration-tests/actions/vrfv2_actions/vrfv2_config/config.go delete mode 100644 integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go delete mode 100644 integration-tests/load/functions/config.go delete mode 100644 integration-tests/load/ocr/config.go delete mode 100644 integration-tests/load/ocr/config.toml delete mode 100644 integration-tests/load/vrfv2/config.go delete mode 100644 integration-tests/load/vrfv2/config.toml delete mode 100644 integration-tests/load/vrfv2plus/config.go delete mode 100644 integration-tests/load/vrfv2plus/config.toml delete mode 100644 integration-tests/performance/cron_test.go delete mode 100644 integration-tests/performance/directrequest_test.go delete mode 100644 integration-tests/performance/flux_test.go delete mode 100644 integration-tests/performance/keeper_test.go delete mode 100644 integration-tests/performance/ocr_test.go delete mode 100644 integration-tests/performance/vrf_test.go delete mode 100644 integration-tests/reorg/reorg_test.go create mode 100644 integration-tests/testconfig/automation/automation.toml create mode 100644 integration-tests/testconfig/automation/config.go create mode 100644 integration-tests/testconfig/automation/example.toml create mode 100644 integration-tests/testconfig/configs_embed.go create mode 100644 integration-tests/testconfig/configs_noembed.go create mode 100644 integration-tests/testconfig/default.toml create mode 100644 integration-tests/testconfig/functions/config.go create mode 100644 integration-tests/testconfig/functions/example.toml rename integration-tests/{load/functions/config.toml => testconfig/functions/functions.toml} (63%) create mode 100644 integration-tests/testconfig/keeper/config.go create mode 100644 integration-tests/testconfig/keeper/example.toml create mode 100644 integration-tests/testconfig/keeper/keeper.toml create mode 100644 integration-tests/testconfig/log_poller/config.go create mode 100644 integration-tests/testconfig/log_poller/example.toml create mode 100644 integration-tests/testconfig/log_poller/log_poller.toml create mode 100644 integration-tests/testconfig/node/example.toml create mode 100644 integration-tests/testconfig/node/node.toml create mode 100644 integration-tests/testconfig/ocr/example.toml create mode 100644 integration-tests/testconfig/ocr/ocr.go create mode 100644 integration-tests/testconfig/ocr/ocr.toml create mode 100644 integration-tests/testconfig/testconfig.go create mode 100644 integration-tests/testconfig/testconfig_test.go create mode 100644 integration-tests/testconfig/vrf/config.go create mode 100644 integration-tests/testconfig/vrfv2/config.go create mode 100644 integration-tests/testconfig/vrfv2/example.toml create mode 100644 integration-tests/testconfig/vrfv2/vrfv2.toml create mode 100644 integration-tests/testconfig/vrfv2plus/config.go create mode 100644 integration-tests/testconfig/vrfv2plus/example.toml create mode 100644 integration-tests/testconfig/vrfv2plus/vrfv2plus.toml create mode 100644 integration-tests/types/testconfigs.go delete mode 100644 integration-tests/universal/log_poller/config.go diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index b7a7948d2cf..1cd1dd8b939 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -15,7 +15,7 @@ inputs: required: false suites: description: The test suites to build into the image - default: chaos migration performance reorg smoke soak benchmark load/automationv2_1 + default: chaos migration reorg smoke soak benchmark load/automationv2_1 required: false QA_AWS_ROLE_TO_ASSUME: description: The AWS role to assume as the CD user, if any. Used in configuring the docker/login-action diff --git a/.github/actions/setup-create-base64-config-live-testnets/action.yml b/.github/actions/setup-create-base64-config-live-testnets/action.yml new file mode 100644 index 00000000000..5ba8150989a --- /dev/null +++ b/.github/actions/setup-create-base64-config-live-testnets/action.yml @@ -0,0 +1,130 @@ +name: Create Base64 Config +description: A composite action that creates a base64-encoded config to be used by integration tests + +inputs: + runId: + description: The run id + testLogCollect: + description: Whether to always collect logs, even for passing tests + default: "false" + chainlinkImage: + description: The chainlink image to use + default: "public.ecr.aws/chainlink/chainlink" + chainlinkVersion: + description: The git commit sha to use for the image tag + pyroscopeServer: + description: URL of Pyroscope server + pyroscopeEnvironment: + description: Name of Pyroscope environment + pyroscopeKey: + description: Pyroscope server key + lokiEndpoint: + description: Loki push endpoint + lokiTenantId: + description: Loki tenant id + lokiBasicAuth: + description: Loki basic auth + logstreamLogTargets: + description: Where to send logs (e.g. file, loki) + grafanaUrl: + description: Grafana URL + grafanaDashboardUrl: + description: Grafana dashboard URL + network: + description: Network to run tests on + httpEndpoints: + description: HTTP endpoints to use for network + wsEndpoints: + description: WS endpoints to use for network + fundingKeys: + description: Funding keys to use for network + +runs: + using: composite + steps: + - name: Prepare Base64 TOML override + shell: bash + id: base64-config-override + env: + RUN_ID: ${{ inputs.runId }} + PYROSCOPE_SERVER: ${{ inputs.pyroscopeServer }} + PYROSCOPE_ENVIRONMENT: ${{ inputs.pyroscopeEnvironment }} + PYROSCOPE_KEY: ${{ inputs.pyroscopeKey }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + LOKI_ENDPOINT: ${{ inputs.lokiEndpoint }} + LOKI_TENANT_ID: ${{ inputs.lokiTenantId }} + LOKI_BASIC_AUTH: ${{ inputs.lokiBasicAuth }} + LOGSTREAM_LOG_TARGETS: ${{ inputs.logstreamLogTargets }} + GRAFANA_URL: ${{ inputs.grafanaUrl }} + GRAFANA_DASHBOARD_URL: ${{ inputs.grafanaDashboardUrl }} + NETWORK: ${{ inputs.network }} + HTTP_ENDPOINTS: ${{ inputs.httpEndpoints }} + WS_ENDPOINTS: ${{ inputs.wsEndpoints }} + FUNDING_KEYS: ${{ inputs.fundingKeys }} + run: | + convert_to_toml_array() { + local IFS=',' + local input_array=($1) + local toml_array_format="[" + + for element in "${input_array[@]}"; do + toml_array_format+="\"$element\"," + done + + toml_array_format="${toml_array_format%,}]" + echo "$toml_array_format" + } + + if [ -n "$PYROSCOPE_SERVER" ]; then + pyroscope_enabled=true + else + pyroscope_enabled=false + fi + + cat << EOF > config.toml + [Common] + chainlink_node_funding=0.5 + + [ChainlinkImage] + image="$CHAINLINK_IMAGE" + version="$CHAINLINK_VERSION" + + [Pyroscope] + enabled=$pyroscope_enabled + server_url="$PYROSCOPE_SERVER" + environment="$PYROSCOPE_ENVIRONMENT" + key="$PYROSCOPE_KEY" + + [Logging] + run_id="$RUN_ID" + + [Logging.LogStream] + log_targets=$(convert_to_toml_array "$LOGSTREAM_LOG_TARGETS") + + [Logging.Loki] + tenant_id="$LOKI_TENANT_ID" + endpoint="$LOKI_URL" + basic_auth="$LOKI_BASIC_AUTH" + + [Logging.Grafana] + base_url="$GRAFANA_URL" + dasboard_url="$GRAFANA_DASHBOARD_URL" + + [Network] + selected_networks=["$NETWORK"] + + [Network.RpcHttpUrls] + "$NETWORK" = $(convert_to_toml_array "$HTTP_ENDPOINTS") + + [Network.RpcWsUrls] + "$NETWORK" = $(convert_to_toml_array "$WS_ENDPOINTS") + + [Network.WalletKeys] + "$NETWORK" = $(convert_to_toml_array "$FUNDING_KEYS") + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + touch .root_dir diff --git a/.github/actions/setup-create-base64-config/action.yml b/.github/actions/setup-create-base64-config/action.yml new file mode 100644 index 00000000000..eafd670226f --- /dev/null +++ b/.github/actions/setup-create-base64-config/action.yml @@ -0,0 +1,122 @@ +name: Create Base64 Config +description: A composite action that creates a base64-encoded config to be used by integration tests + +inputs: + runId: + description: The run id + testLogCollect: + description: Whether to always collect logs, even for passing tests + default: "false" + selectedNetworks: + description: The networks to run tests against + chainlinkImage: + description: The chainlink image to use + default: "public.ecr.aws/chainlink/chainlink" + chainlinkVersion: + description: The git commit sha to use for the image tag + pyroscopeServer: + description: URL of Pyroscope server + pyroscopeEnvironment: + description: Name of Pyroscope environment + pyroscopeKey: + description: Pyroscope server key + lokiEndpoint: + description: Loki push endpoint + lokiTenantId: + description: Loki tenant id + lokiBasicAuth: + description: Loki basic auth + logstreamLogTargets: + description: Where to send logs (e.g. file, loki) + grafanaUrl: + description: Grafana URL + grafanaDashboardUrl: + description: Grafana dashboard URL + +runs: + using: composite + steps: + - name: Prepare Base64 TOML override + shell: bash + id: base64-config-override + env: + RUN_ID: ${{ inputs.runId }} + TEST_LOG_COLLECT: ${{ inputs.testLogCollect }} + SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} + PYROSCOPE_SERVER: ${{ inputs.pyroscopeServer }} + PYROSCOPE_ENVIRONMENT: ${{ inputs.pyroscopeEnvironment }} + PYROSCOPE_KEY: ${{ inputs.pyroscopeKey }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + LOKI_ENDPOINT: ${{ inputs.lokiEndpoint }} + LOKI_TENANT_ID: ${{ inputs.lokiTenantId }} + LOKI_BASIC_AUTH: ${{ inputs.lokiBasicAuth }} + LOGSTREAM_LOG_TARGETS: ${{ inputs.logstreamLogTargets }} + GRAFANA_URL: ${{ inputs.grafanaUrl }} + GRAFANA_DASHBOARD_URL: ${{ inputs.grafanaDashboardUrl }} + run: | + echo ::add-mask::$CHAINLINK_IMAGE + function convert_to_toml_array() { + local IFS=',' + local input_array=($1) + local toml_array_format="[" + + for element in "${input_array[@]}"; do + toml_array_format+="\"$element\"," + done + + toml_array_format="${toml_array_format%,}]" + echo "$toml_array_format" + } + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + log_targets=$(convert_to_toml_array "$LOGSTREAM_LOG_TARGETS") + + if [ -n "$PYROSCOPE_SERVER" ]; then + pyroscope_enabled=true + else + pyroscope_enabled=false + fi + + if [ -n "$TEST_LOG_COLLECT" ]; then + test_log_collect=true + else + test_log_collect=false + fi + + cat << EOF > config.toml + [Network] + selected_networks=$selected_networks + + [ChainlinkImage] + image="$CHAINLINK_IMAGE" + version="$CHAINLINK_VERSION" + + [Pyroscope] + enabled=$pyroscope_enabled + server_url="$PYROSCOPE_SERVER" + environment="$PYROSCOPE_ENVIRONMENT" + key="$PYROSCOPE_KEY" + + [Logging] + test_log_collect=$test_log_collect + run_id="$RUN_ID" + + [Logging.LogStream] + log_targets=$log_targets + + [Logging.Loki] + tenant_id="$LOKI_TENANT_ID" + endpoint="$LOKI_ENDPOINT" + basic_auth="$LOKI_BASIC_AUTH" + # legacy, you only need this to access the cloud version + # bearer_token="bearer_token" + + [Logging.Grafana] + base_url="$GRAFANA_URL" + dasboard_url="$GRAFANA_DASHBOARD_URL" + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV diff --git a/.github/actions/setup-create-base64-upgrade-config /action.yml b/.github/actions/setup-create-base64-upgrade-config /action.yml new file mode 100644 index 00000000000..f3acde2ea97 --- /dev/null +++ b/.github/actions/setup-create-base64-upgrade-config /action.yml @@ -0,0 +1,61 @@ +name: Create Base64 Upgrade Config +description: A composite action that creates a base64-encoded config to be used by Chainlink version upgrade tests + +inputs: + selectedNetworks: + description: The networks to run tests against + chainlinkImage: + description: The chainlink image to upgrade from + default: "public.ecr.aws/chainlink/chainlink" + chainlinkVersion: + description: The git commit sha to use for the image tag + upgradeImage: + description: The chainlink image to upgrade to + default: "public.ecr.aws/chainlink/chainlink" + upgradeVersion: + description: The git commit sha to use for the image tag + +runs: + using: composite + steps: + - name: Prepare Base64 TOML override + shell: bash + id: base64-config-override + env: + SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + UPGRADE_IMAGE: ${{ inputs.upgradeImage }} + UPGRADE_VERSION: ${{ inputs.upgradeVersion }} + run: | + function convert_to_toml_array() { + local IFS=',' + local input_array=($1) + local toml_array_format="[" + + for element in "${input_array[@]}"; do + toml_array_format+="\"$element\"," + done + + toml_array_format="${toml_array_format%,}]" + echo "$toml_array_format" + } + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + + cat << EOF > config.toml + [Network] + selected_networks=$selected_networks + + [ChainlinkImage] + image="$CHAINLINK_IMAGE" + version="$CHAINLINK_VERSION" + + [ChainlinkUpgradeImage] + image="$UPGRADE_IMAGE" + version="$UPGRADE_VERSION" + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV diff --git a/.github/actions/setup-merge-base64-config/action.yml b/.github/actions/setup-merge-base64-config/action.yml new file mode 100644 index 00000000000..e5bf2a7d276 --- /dev/null +++ b/.github/actions/setup-merge-base64-config/action.yml @@ -0,0 +1,54 @@ +name: Merge Base64 Config +description: A composite action that merges user-provided Base64-encoded config with repository's secrets + +inputs: + base64Config: + description: Base64-encoded config to decode + +runs: + using: composite + steps: + - name: Add masks and export base64 config + shell: bash + run: | + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + + decoded_toml=$(echo $BASE64_CONFIG_OVERRIDE | base64 -d) + CHAINLINK_IMAGE=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*image[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + echo ::add-mask::$CHAINLINK_IMAGE + CHAINLINK_VERSION=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*version[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + NETWORKS=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*selected_networks[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + + if [ -n "$CHAINLINK_IMAGE" ]; then + echo "CHAINLINK_IMAGE=$CHAINLINK_IMAGE" >> $GITHUB_ENV + else + echo "No Chainlink Image found in base64-ed config. Exiting" + exit 1 + fi + if [ -n "$CHAINLINK_VERSION" ]; then + echo "CHAINLINK_VERSION=$CHAINLINK_VERSION" >> $GITHUB_ENV + else + echo "No Chainlink Version found in base64-ed config. Exiting" + exit 1 + fi + if [ -n "$NETWORKS" ]; then + echo "NETWORKS=$NETWORKS" >> $GITHUB_ENV + fi + + # use Loki config from GH secrets and merge it with base64 input + cat << EOF > config.toml + [Logging.Loki] + tenant_id="$LOKI_TENANT_ID" + endpoint="$LOKI_URL" + basic_auth="$LOKI_BASIC_AUTH" + # legacy, you only need this to access the cloud version + # bearer_token="bearer_token" + EOF + + echo "$decoded_toml" >> final_config.toml + cat config.toml >> final_config.toml + BASE64_CONFIG_OVERRIDE=$(cat final_config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV \ No newline at end of file diff --git a/.github/actions/setup-parse-base64-config/action.yml b/.github/actions/setup-parse-base64-config/action.yml new file mode 100644 index 00000000000..a744abae9e0 --- /dev/null +++ b/.github/actions/setup-parse-base64-config/action.yml @@ -0,0 +1,38 @@ +name: Parse Base64 Config +description: A composite action that extracts the chainlink image, version and network from a base64-encoded config + +inputs: + base64Config: + description: Base64-encoded config to decode + +runs: + using: composite + steps: + - name: Add masks and export base64 config + shell: bash + run: | + decoded_toml=$(echo $BASE64_CONFIG_OVERRIDE | base64 -d) + CHAINLINK_IMAGE=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*image[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + echo ::add-mask::$CHAINLINK_IMAGE + CHAINLINK_VERSION=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*version[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + NETWORKS=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*selected_networks[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + ETH2_EL_CLIENT=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*execution_layer[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + + if [ -n "$CHAINLINK_IMAGE" ]; then + echo "CHAINLINK_IMAGE=$CHAINLINK_IMAGE" >> $GITHUB_ENV + else + echo "No Chainlink Image found in base64-ed config. Exiting" + exit 1 + fi + if [ -n "$CHAINLINK_VERSION" ]; then + echo "CHAINLINK_VERSION=$CHAINLINK_VERSION" >> $GITHUB_ENV + else + echo "No Chainlink Version found in base64-ed config. Exiting" + exit 1 + fi + if [ -n "$NETWORKS" ]; then + echo "NETWORKS=$NETWORKS" >> $GITHUB_ENV + fi + if [ -n "$ETH2_EL_CLIENT" ]; then + echo "ETH2_EL_CLIENT=$ETH2_EL_CLIENT" >> $GITHUB_ENV + fi \ No newline at end of file diff --git a/.github/scripts/functions.sh b/.github/scripts/functions.sh new file mode 100644 index 00000000000..53b53392269 --- /dev/null +++ b/.github/scripts/functions.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Function to convert a comma-separated list into a TOML array format. +# Usage: convert_to_toml_array "elem1,elem2,elem3" +# Effect: "a,b,c" -> ["a","b","c"] +function convert_to_toml_array() { + local IFS=',' + local input_array=($1) + local toml_array_format="[" + + for element in "${input_array[@]}"; do + toml_array_format+="\"$element\"," + done + + toml_array_format="${toml_array_format%,}]" + echo "$toml_array_format" +} \ No newline at end of file diff --git a/.github/tracing/README.md b/.github/tracing/README.md index 04f0216e25f..06b2eef6652 100644 --- a/.github/tracing/README.md +++ b/.github/tracing/README.md @@ -9,8 +9,18 @@ One way to generate traces locally today is with the OCR2 basic smoke test. 1. navigate to `.github/tracing/` and then run `docker compose --file local-smoke-docker-compose.yaml up` 2. setup a local docker registry at `127.0.0.1:5000` (https://www.docker.com/blog/how-to-use-your-own-registry-2/) 3. run `make build_push_plugin_docker_image` in `chainlink/integration-tests/Makefile` -4. run `SELECTED_NETWORKS=SIMULATED CHAINLINK_IMAGE="127.0.0.1:5000/chainlink" CHAINLINK_VERSION="develop" go test -run TestOCRv2Basic ./smoke/ocr2_test.go` -5. navigate to `localhost:3000/explore` in a web browser to query for traces +4. preapre your `overrides.toml` file with selected network and CL image name and version and place it anywhere +inside `integration-tests` directory. Sample `overrides.toml` file: +```toml +[ChainlinkImage] +image="127.0.0.1:5000/chainlink" +version="develop" + +[Network] +selected_networks=["simulated"] +``` +5. run `go test -run TestOCRv2Basic ./smoke/ocr2_test.go` +6. navigate to `localhost:3000/explore` in a web browser to query for traces Core and the median plugins are instrumented with open telemetry traces, which are sent to the OTEL collector and forwarded to the Tempo backend. The grafana UI can then read the trace data from the Tempo backend. diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index b2e5ad1638e..740975ae5ee 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -2,53 +2,20 @@ name: Automation Benchmark Test on: workflow_dispatch: inputs: - chainlinkVersion: - description: Chainlink image version to use + testType: + description: Type of test to run (benchmark, soak) required: true + default: benchmark type: string - default: 2.6.0 - chainlinkImage: - description: Chainlink image repo to use + base64Config: + description: base64-ed config required: true type: string - default: public.ecr.aws/chainlink/chainlink - network: - description: Network to run tests on - required: true - type: choice - options: - - SIMULATED - - SIMULATED_NONDEV - - GOERLI - - ARBITRUM_GOERLI - - OPTIMISM_GOERLI - - MUMBAI - - SEPOLIA - - BASE_GOERLI - - ARBITRUM_SEPOLIA - - LINEA_GOERLI - TestInputs: - description: TestInputs - required: false - type: string - wsURL: - description: WS URL for the network (Skip for Simulated) - required: false - type: string - httpURL: - description: HTTP URL for the network (Skip for Simulated) - required: false - type: string slackMemberID: description: Notifies test results (Not your @) required: true default: U02Q14G80TY type: string - fundingPrivateKey: - description: Private funding key (Skip for Simulated) - required: false - type: string - jobs: automation_benchmark: environment: integration @@ -57,52 +24,40 @@ jobs: pull-requests: write id-token: write contents: read - name: ${{ inputs.network }} Automation Benchmark Test + name: Automation Benchmark Test runs-on: ubuntu20.04-16cores-64GB env: - SELECTED_NETWORKS: ${{ inputs.network }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: C03KJ5S7KEK - TEST_INPUTS: ${{ inputs.TestInputs }} CHAINLINK_ENV_USER: ${{ github.actor }} REF_NAME: ${{ github.head_ref || github.ref_name }} steps: - - name: Setup Push Tag - shell: bash - run: | - echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY - echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY - echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - - - name: Add mask + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.REF_NAME }} + - name: Get Slack config and mask base64 config run: | - EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) - EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) - EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) SLACK_USER=$(jq -r '.inputs.slackMemberID' $GITHUB_EVENT_PATH) - echo ::add-mask::$EVM_URLS - echo ::add-mask::$EVM_HTTP_URLS - echo ::add-mask::$EVM_KEYS echo ::add-mask::$SLACK_USER - echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV - echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV - echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV - while IFS=',' read -ra EVM_URLS_2; do - for i in "${EVM_URLS_2[@]}"; do - echo ::add-mask::$i - done - done <<< "$EVM_URLS" - while IFS=',' read -ra EVM_HTTP_URLS_2; do - for i in "${EVM_HTTP_URLS_2[@]}"; do - echo ::add-mask::$i - done - done <<< "$EVM_HTTP_URLS" - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Parse base64 config + uses: ./.github/actions/setup-parse-base64-config with: - ref: ${{ env.REF_NAME }} + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} + - name: Send details to Step Summary + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY - name: Build Test Image uses: ./.github/actions/build-test-image with: @@ -118,11 +73,13 @@ jobs: TEST_ARGS: -test.timeout 720h ENV_JOB_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:${{ github.sha }} INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com + TEST_TYPE: ${{ github.event.inputs.testType }} + TEST_TEST_TYPE: ${{ github.event.inputs.testType }} with: test_command_to_run: cd integration-tests && go test -timeout 30m -v -run ^TestAutomationBenchmark$ ./benchmark -count=1 test_download_vendor_packages_command: make gomod - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} token: ${{ secrets.GITHUB_TOKEN }} should_cleanup: false go_mod_path: ./integration-tests/go.mod @@ -136,6 +93,6 @@ jobs: with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ${{ inputs.network }} Automation Benchmark Test + this-job-name: Automation Benchmark Test test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index 96dffa01db9..583314f438e 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -2,30 +2,10 @@ name: Automation Load Test on: workflow_dispatch: inputs: - chainlinkVersion: - description: Chainlink image version to use + base64Config: + description: base64-ed config required: true - type: string - default: 2.6.0 - chainlinkImage: - description: Chainlink image repo to use - required: true - type: string - default: public.ecr.aws/chainlink/chainlink - network: - description: Network to run tests on - required: true - type: choice - options: - - SIMULATED - TestInputs: - description: TestInputs - required: false - type: string - ConfigOverride: - description: ConfigOverride - required: false - type: string + type: string slackMemberID: description: Notifies test results (Not your @) required: true @@ -40,34 +20,40 @@ jobs: pull-requests: write id-token: write contents: read - name: ${{ inputs.network }} Automation Load Test + name: Automation Load Test runs-on: ubuntu20.04-16cores-64GB env: - SELECTED_NETWORKS: ${{ inputs.network }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: C03KJ5S7KEK - TEST_INPUTS: ${{ inputs.TestInputs }} - CONFIG_OVERRIDE: ${{ inputs.ConfigOverride }} CHAINLINK_ENV_USER: ${{ github.actor }} REF_NAME: ${{ github.head_ref || github.ref_name }} steps: - - name: Setup Push Tag - shell: bash - run: | - echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY - echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY - echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - - - name: Add mask + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.REF_NAME }} + - name: Get Slack config and mask base64 config run: | SLACK_USER=$(jq -r '.inputs.slackMemberID' $GITHUB_EVENT_PATH) echo ::add-mask::$SLACK_USER echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Parse base64 config + uses: ./.github/actions/setup-parse-base64-config with: - ref: ${{ env.REF_NAME }} + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} + - name: Send details to Step Summary + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY - name: Build Test Image uses: ./.github/actions/build-test-image with: @@ -90,8 +76,8 @@ jobs: with: test_command_to_run: cd integration-tests && go test -timeout 1h -v -run TestLogTrigger ./load/automationv2_1 -count=1 test_download_vendor_packages_command: make gomod - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} token: ${{ secrets.GITHUB_TOKEN }} should_cleanup: false go_mod_path: ./integration-tests/go.mod @@ -105,6 +91,6 @@ jobs: with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ${{ inputs.network }} Automation Load Test + this-job-name: Automation Load Test test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true diff --git a/.github/workflows/automation-nightly-tests.yml b/.github/workflows/automation-nightly-tests.yml index 9fa3746f3df..9d45ec99e74 100644 --- a/.github/workflows/automation-nightly-tests.yml +++ b/.github/workflows/automation-nightly-tests.yml @@ -71,13 +71,18 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.head_ref || github.ref_name }} + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-upgrade-config + with: + selectedNetworks: ${{ env.SELECTED_NETWORKS }} + chainlinkImage: "public.ecr.aws/chainlink/chainlink" + chainlinkVersion: "latest" + upgradeImage: ${{ env.UPGRADE_IMAGE }} + upgradeVersion: ${{ env.UPGRADE_VERSION }} - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: - SELECTED_NETWORKS: ${{ matrix.tests.network }} TEST_SUITE: ${{ matrix.tests.suite }} - UPGRADE_VERSION: ${{ github.sha }} - UPGRADE_IMAGE: ${{ env.CHAINLINK_IMAGE }} with: test_command_to_run: cd ./integration-tests && go test -timeout 60m -count=1 -json -test.parallel=${{ matrix.tests.nodes }} ${{ matrix.tests.command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index e023006d58b..ddf26d5607a 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -162,25 +162,70 @@ jobs: echo "upgrade_version=${{ github.sha }}" >>$GITHUB_OUTPUT echo "upgrade_image=${{ env.CHAINLINK_IMAGE }}" >>$GITHUB_OUTPUT else - echo "image=${{ inputs.chainlinkImage }}" >>$GITHUB_OUTPUT + READ_CL_IMAGE=$(jq -r '.inputs.chainlinkImage' $GITHUB_EVENT_PATH) + echo ::add-mask::$READ_CL_IMAGE + echo "image=$READ_CL_IMAGE" >>$GITHUB_OUTPUT echo "version=${{ inputs.chainlinkVersion }}" >>$GITHUB_OUTPUT echo "upgrade_version=${{ inputs.chainlinkVersion }}" >>$GITHUB_OUTPUT - echo "upgrade_image=${{ inputs.chainlinkImage }}" >>$GITHUB_OUTPUT + echo "upgrade_image=$READ_CL_IMAGE" >>$GITHUB_OUTPUT fi if [[ "${{ matrix.tests.name }}" == "upgrade" ]]; then - echo "image=${{ inputs.chainlinkImageUpdate }}" >>$GITHUB_OUTPUT + READ_CL_UPGR_IMAGE=$(jq -r '.inputs.chainlinkImageUpdate' $GITHUB_EVENT_PATH) + echo ::add-mask::$READ_CL_UPGR_IMAGE + echo "image=$READ_CL_UPGR_IMAGE" >>$GITHUB_OUTPUT echo "version=${{ inputs.chainlinkVersionUpdate }}" >>$GITHUB_OUTPUT fi - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + - name: Prepare Base64 TOML config env: - PYROSCOPE_SERVER: ${{ matrix.tests.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 - PYROSCOPE_ENVIRONMENT: ${{ matrix.tests.pyroscope_env }} - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} SELECTED_NETWORKS: ${{ matrix.tests.network }} - TEST_SUITE: ${{ matrix.tests.suite }} + OLD_IMAGE: ${{ steps.determine-build.outputs.image }} + OLD_VERSION: ${{ steps.determine-build.outputs.version }} UPGRADE_VERSION: ${{ steps.determine-build.outputs.upgrade_version }} UPGRADE_IMAGE: ${{ steps.determine-build.outputs.upgrade_image }} + PYROSCOPE_SERVER: ${{ matrix.tests.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + PYROSCOPE_ENVIRONMENT: ${{ matrix.tests.pyroscope_env }} + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + run: | + echo ::add-mask::$UPGRADE_IMAGE + echo ::add-mask::$OLD_IMAGE + + # load reusable functions + source ./.github/scripts/functions.sh + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + + if [ -n "$PYROSCOPE_SERVER" ]; then + pyroscope_enabled=true + else + pyroscope_enabled=false + fi + + cat << EOF > config.toml + [Network] + selected_networks=$selected_networks + + [ChainlinkImage] + image="$OLD_IMAGE" + version="$OLD_VERSION" + + [ChainlinkUpgradeImage] + image="$UPGRADE_IMAGE" + version="$UPGRADE_VERSION" + + [Pyroscope] + enabled=$pyroscope_enabled + server_url="$PYROSCOPE_SERVER" + environment="$PYROSCOPE_ENVIRONMENT" + key="$PYROSCOPE_KEY" + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + env: + TEST_SUITE: ${{ matrix.tests.suite }} with: test_command_to_run: cd ./integration-tests && go test -timeout 60m -count=1 -json -test.parallel=${{ matrix.tests.nodes }} ${{ matrix.tests.command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index 865c72258cd..0334d32cae8 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -67,11 +67,12 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@bce4caa154b1e0e652d042788e14c8870832acd2 # v2.3.0 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod + go_tags: embed cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} cache_restore_only: "true" binary_name: tests @@ -100,43 +101,51 @@ jobs: test: TestOCRBasic file: ocr client: geth + timeout: 30m pyroscope_env: ci-smoke-ocr-geth-simulated # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE # - name: ocr-nethermind # test: TestOCRBasic # file: ocr # client: nethermind + # timeout: 30m # pyroscope_env: ci-smoke-ocr-nethermind-simulated - name: ocr-besu test: TestOCRBasic file: ocr client: besu + timeout: 30m pyroscope_env: ci-smoke-ocr-besu-simulated - name: ocr-erigon test: TestOCRBasic file: ocr client: erigon + timeout: 30m pyroscope_env: ci-smoke-ocr-erigon-simulated - name: ocr2-geth test: TestOCRv2Basic file: ocr2 client: geth + timeout: 30m pyroscope_env: ci-smoke-ocr2-geth-simulated # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE # - name: ocr2-nethermind # test: TestOCRv2Basic # file: ocr2 # client: nethermind + # timeout: 30m # pyroscope_env: ci-smoke-nethermind-evm-simulated - name: ocr2-besu test: TestOCRv2Basic file: ocr2 - client: besu + client: besu + timeout: 30m pyroscope_env: ci-smoke-ocr2-besu-simulated - name: ocr2-erigon test: TestOCRv2Basic file: ocr2 - client: erigon + client: erigon + timeout: 60m pyroscope_env: ci-smoke-ocr2-erigon-simulated runs-on: ubuntu-latest name: Client Compatibility Test ${{ matrix.name }} @@ -145,15 +154,79 @@ jobs: uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 + - name: Prepare Base64 TOML config env: + SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-client-compatability-${{ matrix.client }}-testnet PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} ETH2_EL_CLIENT: ${{matrix.client}} + CHAINLINK_VERSION: ${{ github.sha }} + run: | + convert_to_toml_array() { + local IFS=',' + local input_array=($1) + local toml_array_format="[" + + for element in "${input_array[@]}"; do + toml_array_format+="\"$element\"," + done + + toml_array_format="${toml_array_format%,}]" + echo "$toml_array_format" + } + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + + if [ -n "$ETH2_EL_CLIENT" ]; then + execution_layer="$ETH2_EL_CLIENT" + else + execution_layer="geth" + fi + + if [ -n "$PYROSCOPE_SERVER" ]; then + pyroscope_enabled=true + else + pyroscope_enabled=false + fi + + cat << EOF > config.toml + [Network] + selected_networks=$selected_networks + + [ChainlinkImage] + image="$CHAINLINK_IMAGE" + version="$CHAINLINK_VERSION" + + [Pyroscope] + enabled=$pyroscope_enabled + server_url="$PYROSCOPE_SERVER" + environment="$PYROSCOPE_ENVIRONMENT" + key="$PYROSCOPE_KEY" + + [PrivateEthereumNetwork] + consensus_type="pos" + consensus_layer="prysm" + execution_layer="$execution_layer" + wait_for_finalization=false + + [PrivateEthereumNetwork.EthereumChainConfig] + chain_id=1337 + genesis_delay=15 + seconds_per_slot=3 + validator_count=8 + slots_per_epoch=2 + addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + touch .root_dir + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@912bed7e07a1df4d06ea53a031e9773bb65dc7bd # v2.3.0 with: - test_command_to_run: ./tests -test.timeout 30m -test.run ${{ matrix.test }} + test_command_to_run: ./tests -test.timeout ${{ matrix.timeout }} -test.run ${{ matrix.test }} binary_name: tests cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} @@ -207,7 +280,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": "${{ contains(join(needs.*.result, ','), 'failure') && 'Some tests failed, notifying <@U01Q4N37KFG> and <@U060CGGPY8H>' || 'All Good!' }}" + "text": "${{ contains(join(needs.*.result, ','), 'failure') && 'Some tests failed, notifying <@U060CGGPY8H>' || 'All Good!' }}" } }, { diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index 10c62810996..82af083543e 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -12,7 +12,6 @@ env: ENV_JOB_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:${{ github.sha }} TEST_SUITE: chaos TEST_ARGS: -test.timeout 1h - SELECTED_NETWORKS: SIMULATED CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} TEST_LOG_LEVEL: debug @@ -108,6 +107,24 @@ jobs: continue-on-error: true - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Prepare Base64 TOML config + env: + CHAINLINK_VERSION: ${{ github.sha }} + run: | + echo ::add-mask::$CHAINLINK_IMAGE + + cat << EOF > config.toml + [Network] + selected_networks=["SIMULATED"] + + [ChainlinkImage] + image="$CHAINLINK_IMAGE" + version="$CHAINLINK_VERSION" + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: diff --git a/.github/workflows/integration-staging-tests.yml b/.github/workflows/integration-staging-tests.yml index 2abb9a3cfae..24e4acfe0fb 100644 --- a/.github/workflows/integration-staging-tests.yml +++ b/.github/workflows/integration-staging-tests.yml @@ -1,3 +1,4 @@ +# NEEDS ADJUSTING TO TOML CONFIG BEFORE USING!! name: E2E Functions staging tests on: @@ -41,18 +42,86 @@ jobs: env: LOKI_URL: ${{ secrets.LOKI_URL }} LOKI_TOKEN: ${{ secrets.LOKI_TOKEN }} - SELECTED_NETWORKS: ${{ inputs.network }} SELECTED_TEST: ${{ inputs.test_type }} MUMBAI_URLS: ${{ secrets.FUNCTIONS_STAGING_MUMBAI_URLS }} MUMBAI_KEYS: ${{ secrets.FUNCTIONS_STAGING_MUMBAI_KEYS }} - WASP_LOG_LEVEL: info steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 + - name: Prepare Base64 TOML override + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + run: | + convert_to_toml_array() { + local IFS=',' + local input_array=($1) + local toml_array_format="[" + + for element in "${input_array[@]}"; do + toml_array_format+="\"$element\"," + done + + toml_array_format="${toml_array_format%,}]" + echo "$toml_array_format" + } + + if [ -n "$PYROSCOPE_SERVER" ]; then + pyroscope_enabled=true + else + pyroscope_enabled=false + fi + + cat << EOF > config.toml + [Common] + chainlink_node_funding=0.5 + + [ChainlinkImage] + image="$CHAINLINK_IMAGE" + version="${{ github.sha }}" + + [Pyroscope] + enabled=$pyroscope_enabled + server_url="$PYROSCOPE_SERVER" + environment="$PYROSCOPE_ENVIRONMENT" + key="$PYROSCOPE_KEY" + + [Logging] + run_id="$RUN_ID" + + [Logging.LogStream] + log_targets=$log_targets + + [Logging.Loki] + tenant_id="$LOKI_TENANT_ID" + endpoint="$LOKI_URL" + basic_auth="$LOKI_BASIC_AUTH" + + [Logging.Grafana] + base_url="$GRAFANA_URL" + dasboard_url="/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + + [Network] + selected_networks=["sepolia"] + + [Network.RpcHttpUrls] + sepolia = $(convert_to_toml_array "$SEPOLIA_HTTP_URLS") + + [Network.RpcWsUrls] + sepolia = $(convert_to_toml_array "$SEPOLIA_URLS") + + [Network.WalletKeys] + sepolia = $(convert_to_toml_array "$EVM_KEYS") + EOF + + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Run E2E soak tests run: | cd integration-tests/load/functions diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e53d1a11bbb..3ea98ddd8ce 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -97,7 +97,7 @@ jobs: run: | cd ./integration-tests go build ./... - SELECTED_NETWORKS=SIMULATED go test -run=^# ./... + go test -run=^# ./... - name: Lint Go uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 with: @@ -121,9 +121,9 @@ jobs: - name: "" dockerfile: core/chainlink.Dockerfile tag-suffix: "" - - name: (plugins) - dockerfile: plugins/chainlink.Dockerfile - tag-suffix: -plugins + # - name: (plugins) + # dockerfile: plugins/chainlink.Dockerfile + # tag-suffix: -plugins name: Build Chainlink Image ${{ matrix.image.name }} runs-on: ubuntu20.04-16cores-64GB needs: [changes, enforce-ctf-version] @@ -277,21 +277,28 @@ jobs: else echo "run_command=./smoke/${{ matrix.product.name }}_test.go" >> "$GITHUB_OUTPUT" fi + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + selectedNetworks: ${{ env.SELECTED_NETWORKS }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ${{ matrix.product.pyroscope_env }} + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 - env: - PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 - PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} - LOKI_URL: ${{ secrets.LOKI_URL }} - LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} - LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} - GRAFANA_URL: ${{ vars.GRAFANA_URL }} - GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} - RUN_ID: ${{ github.run_id }} with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -356,14 +363,27 @@ jobs: else echo "run_command=./smoke/${{ matrix.product.name }}_test.go" >> "$GITHUB_OUTPUT" fi + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + selectedNetworks: ${{ env.SELECTED_NETWORKS }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ${{ matrix.product.pyroscope_env }} + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 - env: - PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 - PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -401,19 +421,21 @@ jobs: - name: cron nodes: 2 os: ubuntu-latest - pyroscope_env: "" + pyroscope_env: "ci-smoke-cron-evm-simulated" - name: flux nodes: 1 os: ubuntu-latest - pyroscope_env: "" + pyroscope_env: "ci-smoke-flux-evm-simulated" - name: ocr nodes: 2 os: ubuntu-latest + run: -run TestOCRJobReplacement file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 nodes: 6 os: ubuntu-latest + run: -run TestOCRv2JobReplacement file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 @@ -421,10 +443,6 @@ jobs: os: ubuntu-latest pyroscope_env: ci-smoke-ocr2-plugins-evm-simulated tag_suffix: "-plugins" - - name: runlog - nodes: 1 - os: ubuntu-latest - pyroscope_env: "" - name: vrf nodes: 2 os: ubuntu-latest @@ -523,21 +541,27 @@ jobs: if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | docker logs otel-collector + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + selectedNetworks: ${{ env.SELECTED_NETWORKS }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ${{ matrix.product.pyroscope_env }} + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 - env: - PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 - PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - LOKI_TENANT_ID: ${{ vars.LOKI_TENANT_ID }} - LOKI_URL: ${{ secrets.LOKI_URL }} - LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} - LOGSTREAM_LOG_TARGETS: ${{ vars.LOGSTREAM_LOG_TARGETS }} - GRAFANA_URL: ${{ vars.GRAFANA_URL }} - GRAFANA_DATASOURCE: ${{ vars.GRAFANA_DATASOURCE }} - RUN_ID: ${{ github.run_id }} + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -554,7 +578,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" - ## Run this step when changes that do not need the test to run are made + # Run this step when changes that do not need the test to run are made - name: Run Setup if: needs.changes.outputs.src == 'false' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 @@ -592,6 +616,7 @@ jobs: runs-on: ubuntu-latest name: ETH Smoke Tests needs: [eth-smoke-tests-matrix, eth-smoke-tests-matrix-automation] + # needs: [eth-smoke-tests-matrix] steps: - name: Check smoke test matrix status if: needs.eth-smoke-tests-matrix.result != 'success' || needs.eth-smoke-tests-matrix-automation.result != 'success' @@ -697,6 +722,14 @@ jobs: - name: Name Versions run: | echo "Running migration tests from version '${{ steps.get_latest_version.outputs.latest_version }}' to: '${{ github.sha }}'" + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-upgrade-config + with: + selectedNetworks: ${{ env.SELECTED_NETWORKS }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ steps.get_latest_version.outputs.latest_version }} + upgradeImage: ${{ env.UPGRADE_IMAGE }} + upgradeVersion: ${{ env.UPGRADE_VERSION }} - name: Run Migration Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: @@ -732,7 +765,7 @@ jobs: test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true - ### Solana Section + ## Solana Section get_solana_sha: name: Get Solana Sha From Go Mod environment: Integration diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index a4c0c244f03..9e12089531d 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -32,40 +32,6 @@ env: CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} TEST_LOG_LEVEL: debug - EVM_KEYS: ${{ secrets.QA_EVM_KEYS }} - - SEPOLIA_URLS: ${{ secrets.QA_SEPOLIA_URLS }} - SEPOLIA_HTTP_URLS: ${{ secrets.QA_SEPOLIA_HTTP_URLS }} - - BSC_TESTNET_URLS: ${{ secrets.QA_BSC_TESTNET_URLS }} - BSC_TESTNET_HTTP_URLS: ${{ secrets.QA_BSC_TESTNET_HTTP_URLS }} - - OPTIMISM_SEPOLIA_URLS: ${{ secrets.QA_OPTIMISM_SEPOLIA_URLS }} - OPTIMISM_SEPOLIA_HTTP_URLS: ${{ secrets.QA_OPTIMISM_SEPOLIA_HTTP_URLS }} - - ARBITRUM_SEPOLIA_URLS: ${{ secrets.QA_ARBITRUM_SEPOLIA_URLS }} - ARBITRUM_SEPOLIA_HTTP_URLS: ${{ secrets.QA_ARBITRUM_SEPOLIA_HTTP_URLS }} - - BASE_SEPOLIA_URLS: ${{ secrets.QA_BASE_SEPOLIA_URLS }} - BASE_SEPOLIA_HTTP_URLS: ${{ secrets.QA_BASE_SEPOLIA_HTTP_URLS }} - - POLYGON_MUMBAI_URLS: ${{ secrets.QA_POLYGON_MUMBAI_URLS }} - POLYGON_MUMBAI_HTTP_URLS: ${{ secrets.QA_POLYGON_MUMBAI_HTTP_URLS }} - - AVALANCHE_FUJI_URLS: ${{ secrets.QA_AVALANCHE_FUJI_URLS }} - AVALANCHE_FUJI_HTTP_URLS: ${{ secrets.QA_AVALANCHE_FUJI_HTTP_URLS }} - - FANTOM_TESTNET_URLS: ${{ secrets.QA_FANTOM_TESTNET_URLS }} - FANTOM_TESTNET_HTTP_URLS: ${{ secrets.QA_FANTOM_TESTNET_HTTP_URLS }} - - CELO_ALFAJORES_URLS: ${{ secrets.QA_CELO_ALFAJORES_URLS }} - CELO_ALFAJORES_HTTP_URLS: ${{ secrets.QA_CELO_ALFAJORES_HTTP_URLS }} - - SCROLL_SEPOLIA_URLS: ${{ secrets.QA_SCROLL_SEPOLIA_URLS }} - SCROLL_SEPOLIA_HTTP_URLS: ${{ secrets.QA_SCROLL_SEPOLIA_HTTP_URLS }} - - LINEA_GOERLI_URLS: ${{ secrets.QA_LINEA_GOERLI_URLS }} - LINEA_GOERLI_HTTP_URLS: ${{ secrets.QA_LINEA_GOERLI_HTTP_URLS }} jobs: @@ -107,7 +73,7 @@ jobs: permissions: id-token: write contents: read - name: Build Tests Binary + name: Build Tests Binary runs-on: ubuntu-latest steps: - name: Collect Metrics @@ -128,6 +94,7 @@ jobs: test_download_vendor_packages_command: cd ./integration-tests && go mod download token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod + go_tags: embed cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} cache_restore_only: "true" binary_name: tests @@ -237,8 +204,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: SEPOLIA strategy: max-parallel: 1 fail-fast: false @@ -253,16 +218,35 @@ jobs: name: Sepolia ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-sepolia + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "sepolia" + httpEndpoints: ${{ secrets.QA_SEPOLIA_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_SEPOLIA_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -292,8 +276,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: BSC_TESTNET strategy: max-parallel: 1 fail-fast: false @@ -307,17 +289,36 @@ jobs: test: TestAutomationBasic/registry_2_1_logtrigger name: BSC Testnet ${{ matrix.product }} Tests runs-on: ubuntu-latest - steps: + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-bsc-testnet + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "bsc_testnet" + httpEndpoints: ${{ secrets.QA_BSC_TESTNET_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_BSC_TESTNET_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests - - name: Run Tests + name: tests + - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-bsc-testnet - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -345,8 +346,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: OPTIMISM_SEPOLIA strategy: max-parallel: 1 fail-fast: false @@ -361,16 +360,35 @@ jobs: name: Optimism Sepolia ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-optimism-sepolia + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "optimism_sepolia" + httpEndpoints: ${{ secrets.QA_OPTIMISM_SEPOLIA_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_OPTIMISM_SEPOLIA_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -398,8 +416,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: ARBITRUM_SEPOLIA strategy: max-parallel: 1 fail-fast: false @@ -414,16 +430,35 @@ jobs: name: Arbitrum Sepolia ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-arbitrum-sepolia + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "arbitrum_sepolia" + httpEndpoints: ${{ secrets.QA_ARBITRUM_SEPOLIA_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_ARBITRUM_SEPOLIA_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -451,8 +486,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: BASE_SEPOLIA strategy: max-parallel: 1 fail-fast: false @@ -463,16 +496,35 @@ jobs: name: Base Sepolia ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-base-sepolia + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "base_sepolia" + httpEndpoints: ${{ secrets.QA_BASE_SEPOLIA_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_BASE_SEPOLIA_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-base-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -500,8 +552,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: POLYGON_MUMBAI strategy: max-parallel: 1 fail-fast: false @@ -516,16 +566,35 @@ jobs: name: Polygon Mumbai ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-polygon-mumbai + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "polygon_mumbai" + httpEndpoints: ${{ secrets.QA_POLYGON_MUMBAI_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_POLYGON_MUMBAI_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-polygon-mumbai - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -553,8 +622,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: AVALANCHE_FUJI strategy: max-parallel: 1 fail-fast: false @@ -569,16 +636,35 @@ jobs: name: Avalanche Fuji ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-avalanche-fuji + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "avalanche_fuji" + httpEndpoints: ${{ secrets.QA_AVALANCHE_FUJI_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_AVALANCHE_FUJI_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-avalanche-fuji - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -606,8 +692,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: FANTOM_TESTNET strategy: max-parallel: 1 fail-fast: false @@ -622,16 +706,35 @@ jobs: name: Fantom Testnet ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-fantom-testnet + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "fantom_testnet" + httpEndpoints: ${{ secrets.QA_FANTOM_TESTNET_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_FANTOM_TESTNET_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-fantom-testnet - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -659,8 +762,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: CELO_ALFAJORES strategy: max-parallel: 1 fail-fast: false @@ -671,16 +772,35 @@ jobs: name: Celo Alfajores ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-celo-alfajores + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "celo_alfajores" + httpEndpoints: ${{ secrets.QA_CELO_ALFAJORES_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_CELO_ALFAJORES_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-celo-alfajores - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -710,8 +830,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: SCROLL_SEPOLIA strategy: max-parallel: 1 fail-fast: false @@ -722,16 +840,35 @@ jobs: name: Scroll Sepolia ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-scroll-sepolia + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "scroll_sepolia" + httpEndpoints: ${{ secrets.QA_SCROLL_SEPOLIA_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_SCROLL_SEPOLIA_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-scroll-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -759,8 +896,6 @@ jobs: id-token: write contents: read needs: [build-chainlink, build-tests] - env: - SELECTED_NETWORKS: LINEA_GOERLI strategy: max-parallel: 1 fail-fast: false @@ -771,16 +906,35 @@ jobs: name: Linea Goerli ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Prepare Base64 TOML override + uses: ./.github/actions/setup-create-base64-config-live-testnets + with: + runId: ${{ github.run_id }} + testLogCollect: ${{ vars.TEST_LOG_COLLECT }} + chainlinkImage: ${{ env.CHAINLINK_IMAGE }} + chainlinkVersion: ${{ github.sha }} + pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 + pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-linea-goerli + pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} + lokiEndpoint: ${{ secrets.LOKI_URL }} + lokiTenantId: ${{ vars.LOKI_TENANT_ID }} + lokiBasicAuth: ${{ secrets.LOKI_BASIC_AUTH }} + logstreamLogTargets: ${{ vars.LOGSTREAM_LOG_TARGETS }} + grafanaUrl: ${{ vars.GRAFANA_URL }} + grafanaDashboardUrl: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + network: "linea_goerli" + httpEndpoints: ${{ secrets.QA_LINEA_GOERLI_HTTP_URLS }} + wsEndpoints: ${{ secrets.QA_LINEA_GOERLI_URLS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-linea-goerli - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests diff --git a/.github/workflows/on-demand-log-poller.yml b/.github/workflows/on-demand-log-poller.yml index 4b2d67bcf9a..ad3617841d3 100644 --- a/.github/workflows/on-demand-log-poller.yml +++ b/.github/workflows/on-demand-log-poller.yml @@ -2,80 +2,26 @@ name: On Demand Log Poller Consistency Test on: workflow_dispatch: inputs: - contracts: - description: Number of test contracts - default: "2" + base64Config: + description: base64-ed config required: true - eventsPerTx: - description: Number of events to emit per transaction - default: "10" - required: true - useFinalityTag: - description: Use finality tag - default: "false" - required: true - loadDuration: - description: Load duration (e.g. 10s, 10m, 1h) - default: "10m" - required: true - chainlinkImage: - description: Chainlink image to use - default: "public.ecr.aws/chainlink/chainlink" - required: true - chainlinkVersion: - description: Chainlink version to use - default: "2.7.0-beta1" - required: true - selectedNetworks: - type: choice - description: Networks to test on - options: - - "SIMULATED" - - "SEPOLIA" - - "MUMBAI" - fundingPrivateKey: - description: Private funding key (Skip for Simulated) - required: false - type: string - wsURL: - description: WS URL for the network (Skip for Simulated) - required: false type: string - httpURL: - description: HTTP URL for the network (Skip for Simulated) - required: false - type: string jobs: test: env: - CONTRACTS: ${{ inputs.contracts }} - EVENTS_PER_TX: ${{ inputs.eventsPerTx }} - LOAD_DURATION: ${{ inputs.loadDuration }} - USE_FINALITY_TAG: ${{ inputs.useFinalityTag }} - CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} - CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} - SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} REF_NAME: ${{ github.head_ref || github.ref_name }} runs-on: ubuntu20.04-8cores-32GB steps: - - name: Get Inputs + - name: Add masks and export base64 config run: | - EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) - EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) - EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) - - echo ::add-mask::$EVM_URLS - echo ::add-mask::$EVM_HTTP_URLS - echo ::add-mask::$EVM_KEYS - - echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV - echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV - echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: - ref: ${{ env.REF_NAME }} + ref: ${{ env.REF_NAME }} - name: Setup Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 32e080aafca..b02e44db117 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -2,74 +2,19 @@ name: On Demand OCR Soak Test on: workflow_dispatch: inputs: - network: - description: Network to run tests on - type: choice - options: - - "SIMULATED" - - "GOERLI" - - "OPTIMISM_GOERLI" - - "ARBITRUM_GOERLI" - - "ARBITRUM_SEPOLIA" - - "ARBITRUM_MAINNET" - - "CELO_ALFAJORES" - - "CELO_MAINNET" - - "BASE_GOERLI" - - "BASE_SEPOLIA" - - "BASE_MAINNET" - - "BSC_MAINNET" - - "BSC_TESTNET" - - "SCROLL_SEPOLIA" - - "SCROLL_MAINNET" - - "POLYGON_MUMBAI" - - "POLYGON_MAINNET" - - "LINEA_GOERLI" - - "LINEA_MAINNET" - - "FANTOM_TESTNET" - - "FANTOM_MAINNET" - - "KROMA_MAINNET" - - "KROMA_SEPOLIA" - - "WEMIX_TESTNET" - - "WEMIX_MAINNET" - fundingPrivateKey: - description: Private funding key (Skip for Simulated) - required: false - type: string - wsURL: - description: WS URL for the network (Skip for Simulated) - required: false - type: string - httpURL: - description: HTTP URL for the network (Skip for Simulated) - required: false - type: string + base64Config: + description: base64-ed config + required: true + type: string slackMemberID: description: Slack Member ID (Not your @) required: true default: U01A2B2C3D4 type: string - chainlinkImage: - description: Container image location for the Chainlink nodes - required: true - default: public.ecr.aws/chainlink/chainlink - chainlinkVersion: - description: Container image version for the Chainlink nodes - required: true - default: "2.7.0" - ocrVersion: - description: Version of OCR to Use - type: choice - options: - - 1 - - 2 - testInputs: - description: Duration;Funding;TimeBetweenRounds - required: false - default: "10m;.1;1m" jobs: ocr_soak_test: - name: ${{ inputs.network }} OCR Soak Test + name: OCR Soak Test environment: integration runs-on: ubuntu-latest permissions: @@ -78,12 +23,9 @@ jobs: id-token: write contents: read env: - CHAINLINK_COMMIT_SHA: ${{ inputs.chainlinkVersion }} CHAINLINK_ENV_USER: ${{ github.actor }} - SELECTED_NETWORKS: ${{ inputs.network }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: ${{ secrets.QA_SLACK_CHANNEL }} - OCR_VERSION: ${{ inputs.ocrVersion }} TEST_LOG_LEVEL: debug REF_NAME: ${{ github.head_ref || github.ref_name }} ENV_JOB_IMAGE_BASE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests @@ -96,54 +38,32 @@ jobs: hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} this-job-name: ${{ inputs.network }} OCR Soak Test continue-on-error: true - - name: Get Inputs + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.REF_NAME }} + - name: Get Slack config and mask base64 config run: | - # Mask the sensitive inputs - EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) - EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) - EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) SLACK_USER=$(jq -r '.inputs.slackMemberID' $GITHUB_EVENT_PATH) - - echo ::add-mask::$EVM_URLS - echo ::add-mask::$EVM_HTTP_URLS - echo ::add-mask::$EVM_KEYS echo ::add-mask::$SLACK_USER - - echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV - echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV - echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV - # Read in our test inputs - IFS=';' read -ra parts <<< "${{ inputs.testInputs }}" - index=0 - for part in "${parts[@]}"; do - # A little hacky, but good enough for this - if [ $index -eq 0 ]; then - echo "OCR_TEST_DURATION=$part" - echo "OCR_TEST_DURATION=$part" >> $GITHUB_ENV - elif [ $index -eq 1 ]; then - echo "OCR_CHAINLINK_NODE_FUNDING=$part" - echo "OCR_CHAINLINK_NODE_FUNDING=$part" >> $GITHUB_ENV - elif [ $index -eq 2 ]; then - echo "OCR_TIME_BETWEEN_ROUNDS=$part" - echo "OCR_TIME_BETWEEN_ROUNDS=$part" >> $GITHUB_ENV - else - echo "Additional Unregistered Input: $part" - fi - ((index+=1)) - done - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Parse base64 config + uses: ./.github/actions/setup-parse-base64-config with: - ref: ${{ env.REF_NAME }} + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} - name: Setup Push Tag shell: bash run: | echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY - name: Build Image uses: ./.github/actions/build-test-image with: @@ -163,8 +83,8 @@ jobs: with: test_command_to_run: cd ./integration-tests && go test -v -count=1 -run ^TestOCRSoak$ ./soak test_download_vendor_packages_command: make gomod - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} token: ${{ secrets.GITHUB_TOKEN }} should_cleanup: false go_mod_path: ./integration-tests/go.mod diff --git a/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml b/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml index 3b27e4519c8..de53b493a5f 100644 --- a/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml +++ b/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml @@ -2,28 +2,14 @@ name: On Demand VRFV2 Smoke Test (Ethereum clients) on: workflow_dispatch: inputs: - client: - description: Execution client to use - type: choice - options: - - "geth" - - "nethermind" - - "besu" - - "erigon" - chainlinkImage: - description: Container image location for the Chainlink nodes + base64Config: + description: base64-ed config required: true - default: public.ecr.aws/chainlink/chainlink - chainlinkVersion: - description: Container image version for the Chainlink nodes - required: true - default: "2.6.0" - configBase64: - description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) - required: false + type: string + jobs: vrfv2_smoke_test: - name: VRFV2 Smoke Test with ${{ inputs.client }} client + name: VRFV2 Smoke Test with custom EL client client environment: integration runs-on: ubuntu20.04-8cores-32GB permissions: @@ -32,31 +18,40 @@ jobs: id-token: write contents: read env: - SELECTED_NETWORKS: "SIMULATED" - CONFIG: ${{ inputs.configBase64 }} TEST_LOG_LEVEL: debug REF_NAME: ${{ github.head_ref || github.ref_name }} - CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} - CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} steps: - - name: Setup Push Tag + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Mask base64 config + run: | + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Parse base64 config + uses: ./.github/actions/setup-parse-base64-config + with: + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} + - name: Send details to Step Summary shell: bash run: | echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY + echo "### Execution client used" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.ETH2_EL_CLIENT }}\`" >>$GITHUB_STEP_SUMMARY - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -run TestVRFv2Basic ./smoke/vrfv2_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} artifacts_name: vrf-test-logs artifacts_location: ./integration-tests/smoke/logs/ @@ -66,5 +61,3 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" - env: - ETH2_EL_CLIENT: ${{ inputs.client }} diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml index 3c505ed24ff..7fd0d2b83b1 100644 --- a/.github/workflows/on-demand-vrfv2-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -2,45 +2,10 @@ name: On Demand VRFV2 Performance Test on: workflow_dispatch: inputs: - network: - description: Network to run tests on - type: choice - options: - - "ETHEREUM_MAINNET" - - "SIMULATED" - - "SEPOLIA" - - "OPTIMISM_MAINNET" - - "OPTIMISM_GOERLI" - - "ARBITRUM_MAINNET" - - "ARBITRUM_GOERLI" - - "ARBITRUM_SEPOLIA" - - "BSC_MAINNET" - - "BSC_TESTNET" - - "POLYGON_MAINNET" - - "POLYGON_MUMBAI" - - "AVALANCHE_FUJI" - - "AVALANCHE_MAINNET" - - "NEXON_DEV" - fundingPrivateKey: - description: Private funding key (Skip for Simulated) - required: false - type: string - wsURL: - description: WS URL for the network (Skip for Simulated) - required: false - type: string - httpURL: - description: HTTP URL for the network (Skip for Simulated) - required: false - type: string - chainlinkImage: - description: Container image location for the Chainlink nodes + base64Config: + description: base64-ed config required: true - default: public.ecr.aws/chainlink/chainlink - chainlinkVersion: - description: Container image version for the Chainlink nodes - required: true - default: "2.6.0" + type: string performanceTestType: description: Performance Test Type of test to run type: choice @@ -49,20 +14,9 @@ on: - "Load" - "Stress" - "Spike" - testDuration: - description: Duration of the test (time string) - required: true - default: 5m - useExistingEnv: - description: Set `true` to use existing environment or `false` to deploy CL node and all contracts - required: false - default: false - configBase64: - description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) - required: false jobs: vrfv2_performance_test: - name: ${{ inputs.network }} VRFV2 Performance Test + name: VRFV2 Performance Test environment: integration runs-on: ubuntu20.04-8cores-32GB permissions: @@ -74,15 +28,9 @@ jobs: LOKI_URL: ${{ secrets.LOKI_URL }} LOKI_TENANT_ID: ${{ secrets.LOKI_TENANT_ID }} LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} - SELECTED_NETWORKS: ${{ inputs.network }} TEST_TYPE: ${{ inputs.performanceTestType }} - VRFV2_TEST_DURATION: ${{ inputs.testDuration }} - VRFV2_USE_EXISTING_ENV: ${{ inputs.useExistingEnv }} - CONFIG: ${{ inputs.configBase64 }} TEST_LOG_LEVEL: debug REF_NAME: ${{ github.head_ref || github.ref_name }} - CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} - CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: ${{ secrets.QA_VRF_SLACK_CHANNEL }} WASP_LOG_LEVEL: info @@ -95,38 +43,35 @@ jobs: hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} this-job-name: ${{ inputs.network }} VRFV2 Performance Test continue-on-error: true - - name: Setup Push Tag + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Mask base64 config + run: | + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Merge and export base64 config + uses: ./.github/actions/setup-merge-base64-config + with: + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} + - name: Send details to Step Summary shell: bash run: | echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - - name: Get Inputs - run: | - EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) - EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) - EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) - - echo ::add-mask::$EVM_URLS - echo ::add-mask::$EVM_HTTP_URLS - echo ::add-mask::$EVM_KEYS - - echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV - echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV - echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV - - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 24h -run TestVRFV2Performance/vrfv2_performance_test ./load/vrfv2 test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} artifacts_name: vrf-test-logs artifacts_location: ./integration-tests/load/vrfv2/logs/ diff --git a/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml b/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml index 331c626ba9c..1772730075e 100644 --- a/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml @@ -2,28 +2,14 @@ name: On Demand VRFV2Plus Smoke Test (Ethereum clients) on: workflow_dispatch: inputs: - client: - description: Execution client to use - type: choice - options: - - "geth" - - "nethermind" - - "besu" - - "erigon" - chainlinkImage: - description: Container image location for the Chainlink nodes + base64Config: + description: base64-ed config required: true - default: public.ecr.aws/chainlink/chainlink - chainlinkVersion: - description: Container image version for the Chainlink nodes - required: true - default: "2.6.0" - configBase64: - description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) - required: false + type: string + jobs: vrfv2plus_smoke_test: - name: VRFV2Plus Smoke Test with ${{ inputs.client }} client + name: VRFV2Plus Smoke Test with custom EL client environment: integration runs-on: ubuntu20.04-8cores-32GB permissions: @@ -32,31 +18,40 @@ jobs: id-token: write contents: read env: - SELECTED_NETWORKS: "SIMULATED" - CONFIG: ${{ inputs.configBase64 }} TEST_LOG_LEVEL: debug REF_NAME: ${{ github.head_ref || github.ref_name }} - CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} - CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} - steps: - - name: Setup Push Tag + steps: + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Mask base64 config + run: | + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Parse base64 config + uses: ./.github/actions/setup-parse-base64-config + with: + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} + - name: Send details to Step Summary shell: bash run: | echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY + echo "### Execution client used" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.ETH2_EL_CLIENT }}\`" >>$GITHUB_STEP_SUMMARY - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -run ^TestVRFv2Plus$/^Link_Billing$ ./smoke/vrfv2plus_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} artifacts_name: vrfplus-test-logs artifacts_location: ./integration-tests/smoke/logs/ @@ -66,5 +61,3 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" - env: - ETH2_EL_CLIENT: ${{ inputs.client }} diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 86c35d8e076..858eecff761 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -2,45 +2,10 @@ name: On Demand VRFV2 Plus Performance Test on: workflow_dispatch: inputs: - network: - description: Network to run tests on - type: choice - options: - - "ETHEREUM_MAINNET" - - "SIMULATED" - - "SEPOLIA" - - "OPTIMISM_MAINNET" - - "OPTIMISM_GOERLI" - - "ARBITRUM_MAINNET" - - "ARBITRUM_GOERLI" - - "ARBITRUM_SEPOLIA" - - "BSC_MAINNET" - - "BSC_TESTNET" - - "POLYGON_MAINNET" - - "POLYGON_MUMBAI" - - "AVALANCHE_FUJI" - - "AVALANCHE_MAINNET" - - "NEXON_DEV" - fundingPrivateKey: - description: Private funding key (Skip for Simulated) - required: false - type: string - wsURL: - description: WS URL for the network (Skip for Simulated) - required: false - type: string - httpURL: - description: HTTP URL for the network (Skip for Simulated) - required: false - type: string - chainlinkImage: - description: Container image location for the Chainlink nodes - required: true - default: public.ecr.aws/chainlink/chainlink - chainlinkVersion: - description: Container image version for the Chainlink nodes + base64Config: + description: base64-ed config required: true - default: "2.6.0" + type: string performanceTestType: description: Performance Test Type of test to run type: choice @@ -48,21 +13,11 @@ on: - "Soak" - "Load" - "Stress" - - "Spike" - testDuration: - description: Duration of the test (time string) - required: true - default: 5m - useExistingEnv: - description: Set `true` to use existing environment or `false` to deploy CL node and all contracts - required: false - default: "false" - configBase64: - description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) - required: false + - "Spike" + jobs: vrfv2plus_performance_test: - name: ${{ inputs.network }} VRFV2 Plus Performance Test + name: VRFV2 Plus Performance Test environment: integration runs-on: ubuntu20.04-8cores-32GB permissions: @@ -74,19 +29,13 @@ jobs: LOKI_URL: ${{ secrets.LOKI_URL }} LOKI_TENANT_ID: ${{ secrets.LOKI_TENANT_ID }} LOKI_BASIC_AUTH: ${{ secrets.LOKI_BASIC_AUTH }} - SELECTED_NETWORKS: ${{ inputs.network }} TEST_TYPE: ${{ inputs.performanceTestType }} - VRFV2PLUS_TEST_DURATION: ${{ inputs.testDuration }} - VRFV2PLUS_USE_EXISTING_ENV: ${{ inputs.useExistingEnv }} - CONFIG: ${{ inputs.configBase64 }} TEST_LOG_LEVEL: debug REF_NAME: ${{ github.head_ref || github.ref_name }} - CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} - CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} SLACK_CHANNEL: ${{ secrets.QA_VRF_SLACK_CHANNEL }} WASP_LOG_LEVEL: info - steps: + steps: - name: Collect Metrics id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 @@ -95,38 +44,35 @@ jobs: hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} this-job-name: ${{ inputs.network }} VRFV2 Plus Performance Test continue-on-error: true - - name: Setup Push Tag + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Mask base64 config + run: | + BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Merge and export base64 config + uses: ./.github/actions/setup-merge-base64-config + with: + base64Config: ${{ env.BASE64_CONFIG_OVERRIDE }} + - name: Send details to Step Summary shell: bash run: | echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.CHAINLINK_IMAGE }}\`" >>$GITHUB_STEP_SUMMARY echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - - name: Get Inputs - run: | - EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) - EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) - EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) - - echo ::add-mask::$EVM_URLS - echo ::add-mask::$EVM_HTTP_URLS - echo ::add-mask::$EVM_KEYS - - echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV - echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV - echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV - - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 + echo "### Networks on which test was run" >>$GITHUB_STEP_SUMMARY + echo "\`${{ env.NETWORKS }}\`" >>$GITHUB_STEP_SUMMARY - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ inputs.chainlinkImage }} - cl_image_tag: ${{ inputs.chainlinkVersion }} + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ env.CHAINLINK_VERSION }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} artifacts_name: vrf-test-logs artifacts_location: ./integration-tests/load/vrfv2plus/logs/ diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml deleted file mode 100644 index 7b9eef7fccc..00000000000 --- a/.github/workflows/performance-tests.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Performance Tests -on: - workflow_dispatch: - inputs: - focus: - description: cron|directrequest|flux|keeper|ocr|vrf|suite - required: true - default: suite - type: string - -jobs: - build-chainlink: - environment: integration - permissions: - id-token: write - contents: read - name: Build Chainlink Image - runs-on: ubuntu-latest - steps: - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 - with: - aws-region: ${{ secrets.QA_AWS_REGION }} - role-to-assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - role-duration-seconds: 3600 - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - - name: Build and Push - uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 - with: - context: . - file: core/chainlink.Dockerfile - # comma separated like: KEY1=VAL1,KEY2=VAL2,... - build-args: COMMIT_SHA=${{ github.sha }} - tags: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink:latest.${{ github.sha }} - push: true - - name: Collect Metrics - if: always() - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - with: - basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: Build Chainlink Image - continue-on-error: true - run_tests: - environment: integration - name: run core evm ${{ github.event.inputs.focus }} performance tests - runs-on: ubuntu-latest - needs: build-chainlink - steps: - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 - with: - test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 10 ./performance 2>&1 | tee /tmp/gotest.log | gotestfmt - test_download_vendor_packages_command: make gomod - cl_repo: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink - cl_image_tag: latest.${{ github.sha }} - artifacts_location: ./integration-tests/performance/logs - publish_report_paths: ./tests-perf-report.xml - publish_check_name: Core Performance Test Results - go_mod_path: ./integration-tests/go.mod - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - - name: Publish pprof artifacts - if: ${{ success() }} - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - with: - name: pprof_results - path: ./integration-tests/performance/logs - - name: Collect Metrics - if: always() - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 - with: - basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: run core evm ${{ github.event.inputs.focus }} performance tests - continue-on-error: true diff --git a/.gitignore b/.gitignore index 45ec5eb2bd4..ae69535d352 100644 --- a/.gitignore +++ b/.gitignore @@ -83,8 +83,12 @@ go.work* # This sometimes shows up for some reason tools/flakeytests/coverage.txt +# Integration tests create these files .test_summary/ .run.id # Fuzz tests can create these files **/testdata/fuzz/* + +# Runtime test configuration that might contain secrets +overrides.toml \ No newline at end of file diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 4ecbf283746..bd13587c679 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1127,8 +1127,8 @@ github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= -github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -1967,8 +1967,8 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= -nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/go.mod b/go.mod index ab63dbd6475..0bc08adc3fc 100644 --- a/go.mod +++ b/go.mod @@ -266,6 +266,7 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rs/zerolog v1.30.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect @@ -321,6 +322,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + nhooyr.io/websocket v1.8.7 // indirect pgregory.net/rapid v0.5.5 // indirect rsc.io/tmplfunc v0.0.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index b71fcd79bc2..ba26cb877d8 100644 --- a/go.sum +++ b/go.sum @@ -261,6 +261,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= @@ -430,6 +431,7 @@ github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NB github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= @@ -473,12 +475,15 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= @@ -501,6 +506,7 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -846,6 +852,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -876,6 +883,7 @@ github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awS github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a h1:dHCfT5W7gghzPtfsW488uPmEOm85wewI+ypUwibyTdU= github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= @@ -1111,10 +1119,11 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= -github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -1953,8 +1962,8 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= -nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/integration-tests/.root_dir b/integration-tests/.root_dir new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 62d9b281d66..8415c00f9cd 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -60,12 +60,12 @@ lint: golangci-lint --color=always run ./... --fix -v build: - @go build ./... && SELECTED_NETWORKS=SIMULATED go test -run=^# ./... + @go build ./... go test -run=^# ./... # Builds the test image # tag: the tag for the test image being built, example: tag=tate # base_tag: the tag for the base-test-image to use, example: base_tag=latest -# suite: the test suites to build into the image, example: suite="chaos soak smoke reorg migration performance" +# suite: the test suites to build into the image, example: suite="chaos soak smoke reorg migration" # push: set to true if you want the image pushed or leave blank if not, example: push=true .PHONY: build_test_image build_test_image: @@ -89,10 +89,11 @@ run_tracing: ## Test Runner .PHONY: run -run: # Need to set network first in case it's unset. Doesn't matter for the runner - SELECTED_NETWORKS="SIMULATED" go run . +run: + go run . ## All commands will use 16 threads to run tests in parallel. To change this, use -test.parallel n +## Remember to set selected_networks and CL image in the TOML file (e.g. overrides.toml) # Smoke .PHONY: test_smoke_product @@ -122,23 +123,15 @@ test_chaos_network: install_gotestfmt ## Run all smoke tests test_chaos_verbose: ## Run all smoke tests with verbose logging go test -timeout 24h -count=1 -v $(args) ./chaos -# Performance -.PHONY: test_perf -test_perf: ## Run core node performance tests. - TEST_LOG_LEVEL="disabled" \ - SELECTED_NETWORKS="SIMULATED,SIMULATED_1,SIMULATED_2" \ - go test -timeout 1h -count=1 -json $(args) ./performance 2>&1 | tee /tmp/gotest.log | gotestfmt - # Migrations .PHONY: test_node_migrations -test_node_migrations: install_gotestfmt ## Run all node migration tests +test_node_migrations: install_gotestfmt ## Run all node migration tests. TEST_LOG_LEVEL="disabled" \ go test -timeout 1h -count=1 -json $(args) ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt .PHONY: test_node_migrations_simulated -test_node_migrations_simulated: install_gotestfmt +test_node_migrations_simulated: install_gotestfmt TEST_LOG_LEVEL="disabled" \ - SELECTED_NETWORKS="SIMULATED,SIMULATED_1,SIMULATED_2" \ go test -timeout 1h -count=1 -json $(args) ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt .PHONY: test_node_migrations_verbose @@ -147,7 +140,6 @@ test_node_migrations_verbose: .PHONY: test_node_migrations_simulated_verbose test_node_migrations_simulated_verbose: - SELECTED_NETWORKS="SIMULATED,SIMULATED_1,SIMULATED_2" \ go test -timeout 1h -count=1 -v $(args) ./migration # Soak @@ -157,7 +149,7 @@ test_soak_ocr: .PHONY: test_soak_ocr_simulated test_soak_ocr_simulated: - SELECTED_NETWORKS="SIMULATED" go test -v -count=1 -run TestOCRSoak ./soak + go test -v -count=1 -run TestOCRSoak ./soak .PHONY: test_soak_forwarder_ocr test_soak_forwarder_ocr: @@ -165,7 +157,7 @@ test_soak_forwarder_ocr: .PHONY: test_soak_forwarder_ocr_simulated test_soak_forwarder_ocr_simulated: - SELECTED_NETWORKS="SIMULATED" go test -v -count=1 -run TestForwarderOCRSoak ./soak + go test -v -count=1 -run TestForwarderOCRSoak ./soak .PHONY: test_soak_automation test_soak_automation: @@ -173,8 +165,6 @@ test_soak_automation: .PHONY: test_soak_automation_simulated test_soak_automation_simulated: - SELECTED_NETWORKS="SIMULATED" \ - TEST_INPUTS="TEST_TYPE=SOAK,NUMBEROFCONTRACTS=50,BLOCKRANGE=1000,BLOCKINTERVAL=50,GRAFANA_DASHBOARD_URL=https://chainlinklabs.grafana.net/d/Q8n6m1unz/chainlink-keepers-qa?orgId=1" \ go test -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 .PHONY: test_benchmark_automation @@ -204,11 +194,9 @@ build_plugin_docker_image: # args: the args to pass to the test runner, example: args="--focus @cron -p" # product: the product to run tests for, example: product=cron # example usage: make run_test_with_local_image image=chainlink tag=latest-dev product=cron +# remember to put the case CL image name and tag in the TOML config (and don't forget about selected network configuration) .PHONY: run_test_with_local_image run_test_with_local_image: build_docker_image - CHAINLINK_IMAGE=$(image) \ - CHAINLINK_VERSION=$(tag) \ - SELECTED_NETWORKS="SIMULATED,SIMULATED_1,SIMULATED_2" \ ARGS="$(args)" \ PRODUCT=$(product) \ ./scripts/run_product_tests diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 624df39c479..95b538129c7 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -4,6 +4,7 @@ package actions import ( "crypto/ecdsa" "encoding/json" + "errors" "fmt" "math/big" "strings" @@ -258,10 +259,11 @@ func TeardownSuite( chainlinkNodes []*client.ChainlinkK8sClient, optionalTestReporter testreporters.TestReporter, // Optionally pass in a test reporter to log further metrics failingLogLevel zapcore.Level, // Examines logs after the test, and fails the test if any Chainlink logs are found at or above provided level + grafnaUrlProvider testreporters.GrafanaURLProvider, clients ...blockchain.EVMClient, ) error { l := logging.GetTestLogger(t) - if err := testreporters.WriteTeardownLogs(t, env, optionalTestReporter, failingLogLevel); err != nil { + if err := testreporters.WriteTeardownLogs(t, env, optionalTestReporter, failingLogLevel, grafnaUrlProvider); err != nil { return fmt.Errorf("Error dumping environment logs, leaving environment running for manual retrieval, err: %w", err) } // Delete all jobs to stop depleting the funds @@ -302,11 +304,12 @@ func TeardownRemoteSuite( namespace string, chainlinkNodes []*client.ChainlinkK8sClient, optionalTestReporter testreporters.TestReporter, // Optionally pass in a test reporter to log further metrics + grafnaUrlProvider testreporters.GrafanaURLProvider, client blockchain.EVMClient, ) error { l := logging.GetTestLogger(t) var err error - if err = testreporters.SendReport(t, namespace, "./", optionalTestReporter); err != nil { + if err = testreporters.SendReport(t, namespace, "./", optionalTestReporter, grafnaUrlProvider); err != nil { l.Warn().Err(err).Msg("Error writing test report") } // Delete all jobs to stop depleting the funds @@ -415,8 +418,8 @@ func UpgradeChainlinkNodeVersions( newImage, newVersion string, nodes ...*client.ChainlinkK8sClient, ) error { - if newImage == "" && newVersion == "" { - return fmt.Errorf("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") + if newImage == "" || newVersion == "" { + return errors.New("New image and new version is needed to upgrade the node") } for _, node := range nodes { if err := node.UpgradeVersion(testEnvironment, newImage, newVersion); err != nil { diff --git a/integration-tests/actions/actions_local.go b/integration-tests/actions/actions_local.go index d4913cabd8a..8ac623d8841 100644 --- a/integration-tests/actions/actions_local.go +++ b/integration-tests/actions/actions_local.go @@ -2,8 +2,6 @@ package actions import ( - "fmt" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -13,9 +11,6 @@ func UpgradeChainlinkNodeVersionsLocal( newImage, newVersion string, nodes ...*test_env.ClNode, ) error { - if newImage == "" && newVersion == "" { - return fmt.Errorf("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") - } for _, node := range nodes { if err := node.UpgradeVersion(newImage, newVersion); err != nil { return err diff --git a/integration-tests/actions/private_network.go b/integration-tests/actions/private_network.go index c7556f8061b..7f8bfe8bb2c 100644 --- a/integration-tests/actions/private_network.go +++ b/integration-tests/actions/private_network.go @@ -1,33 +1,28 @@ package actions import ( - "errors" - "github.com/rs/zerolog" ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) -func EthereumNetworkConfigFromEnvOrDefault(l zerolog.Logger) (network ctf_test_env.EthereumNetwork, err error) { - chainConfig := ctf_test_env.EthereumChainConfig{ - SecondsPerSlot: 8, - SlotsPerEpoch: 4, - } - - ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() - network, err = ethBuilder. - WithExecClientFromEnvVar(). - WithEthereumChainConfig(chainConfig). - Build() - - if errors.Is(err, ctf_test_env.ErrMissingExecClientEnvVar) { - l.Warn().Msg("No exec client env var set, will use old geth") - ethBuilder = ctf_test_env.NewEthereumNetworkBuilder() +func EthereumNetworkConfigFromConfig(l zerolog.Logger, config tc.GlobalTestConfig) (network ctf_test_env.EthereumNetwork, err error) { + if config.GetPrivateEthereumNetworkConfig() == nil { + l.Warn().Msg("No TOML private ethereum network config found, will use old geth") + ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() network, err = ethBuilder. WithConsensusType(ctf_test_env.ConsensusType_PoW). WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). Build() + + return } + ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() + network, err = ethBuilder. + WithExistingConfig(*config.GetPrivateEthereumNetworkConfig()). + Build() + return } diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go b/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go deleted file mode 100644 index 36d266fec2d..00000000000 --- a/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go +++ /dev/null @@ -1,54 +0,0 @@ -package vrfv2_config - -import "time" - -type VRFV2Config struct { - ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with - CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"10"` // Max gas price in GWei for the chainlink node - IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token - LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed - MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator - SubscriptionFundingAmountLink float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"5"` // Amount of LINK to fund the subscription with - NumberOfWords uint32 `envconfig:"NUMBER_OF_WORDS" default:"3"` // Number of words to request - CallbackGasLimit uint32 `envconfig:"CALLBACK_GAS_LIMIT" default:"1000000"` // Gas limit for the callback - MaxGasLimitCoordinatorConfig uint32 `envconfig:"MAX_GAS_LIMIT_COORDINATOR_CONFIG" default:"2500000"` // Max gas limit for the VRF Coordinator config - FallbackWeiPerUnitLink int64 `envconfig:"FALLBACK_WEI_PER_UNIT_LINK" default:"60000000000000000"` // Fallback wei per unit LINK for the VRF Coordinator config - StalenessSeconds uint32 `envconfig:"STALENESS_SECONDS" default:"86400"` // Staleness in seconds for the VRF Coordinator config - GasAfterPaymentCalculation uint32 `envconfig:"GAS_AFTER_PAYMENT_CALCULATION" default:"33825"` // Gas after payment calculation for the VRF Coordinator config - FulfillmentFlatFeeLinkPPMTier1 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_1" default:"500"` - FulfillmentFlatFeeLinkPPMTier2 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_2" default:"500"` - FulfillmentFlatFeeLinkPPMTier3 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_3" default:"500"` - FulfillmentFlatFeeLinkPPMTier4 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_4" default:"500"` - FulfillmentFlatFeeLinkPPMTier5 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_5" default:"500"` - ReqsForTier2 int64 `envconfig:"REQS_FOR_TIER_2" default:"0"` - ReqsForTier3 int64 `envconfig:"REQS_FOR_TIER_3" default:"0"` - ReqsForTier4 int64 `envconfig:"REQS_FOR_TIER_4" default:"0"` - ReqsForTier5 int64 `envconfig:"REQS_FOR_TIER_5" default:"0"` - - NumberOfSubToCreate int `envconfig:"NUMBER_OF_SUB_TO_CREATE" default:"1"` // Number of subscriptions to create - - RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request - RandomnessRequestCountPerRequestDeviation uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST_DEVIATION" default:"0"` // How many randomness requests to send per request - - RandomWordsFulfilledEventTimeout time.Duration `envconfig:"RANDOM_WORDS_FULFILLED_EVENT_TIMEOUT" default:"2m"` // How long to wait for the RandomWordsFulfilled event to be emitted - - //Wrapper Config - WrapperGasOverhead uint32 `envconfig:"WRAPPER_GAS_OVERHEAD" default:"50000"` - CoordinatorGasOverhead uint32 `envconfig:"COORDINATOR_GAS_OVERHEAD" default:"52000"` - WrapperPremiumPercentage uint8 `envconfig:"WRAPPER_PREMIUM_PERCENTAGE" default:"25"` - WrapperMaxNumberOfWords uint8 `envconfig:"WRAPPER_MAX_NUMBER_OF_WORDS" default:"10"` - WrapperConsumerFundingAmountNativeToken float64 `envconfig:"WRAPPER_CONSUMER_FUNDING_AMOUNT_NATIVE_TOKEN" default:"1"` - WrapperConsumerFundingAmountLink int64 `envconfig:"WRAPPER_CONSUMER_FUNDING_AMOUNT_LINK" default:"10"` - - //LOAD/SOAK Test Config - TestDuration time.Duration `envconfig:"TEST_DURATION" default:"3m"` // How long to run the test for - RPS int64 `envconfig:"RPS" default:"1"` // How many requests per second to send - RateLimitUnitDuration time.Duration `envconfig:"RATE_LIMIT_UNIT_DURATION" default:"1m"` - //Using existing environment and contracts - UseExistingEnv bool `envconfig:"USE_EXISTING_ENV" default:"false"` // Whether to use an existing environment or create a new one - CoordinatorAddress string `envconfig:"COORDINATOR_ADDRESS" default:""` // Coordinator address - ConsumerAddress string `envconfig:"CONSUMER_ADDRESS" default:""` // Consumer address - LinkAddress string `envconfig:"LINK_ADDRESS" default:""` // Link address - SubID uint64 `envconfig:"SUB_ID" default:""` // Subscription ID - KeyHash string `envconfig:"KEY_HASH" default:""` -} diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index 15148ce9682..1a958548965 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -12,7 +12,6 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" @@ -26,6 +25,9 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types" ) var ( @@ -283,7 +285,7 @@ func FundVRFCoordinatorV2Subscription( // SetupVRFV2Environment will create specified number of subscriptions and add the same conumer/s to each of them func SetupVRFV2Environment( env *test_env.CLClusterTestEnv, - vrfv2Config vrfv2_config.VRFV2Config, + vrfv2TestConfig types.VRFv2TestConfig, useVRFOwner bool, useTestCoordinator bool, linkToken contracts.LinkToken, @@ -307,25 +309,25 @@ func SetupVRFV2Environment( if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) } - + vrfv2Config := vrfv2TestConfig.GetVRFv2Config().General vrfCoordinatorV2FeeConfig := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ - FulfillmentFlatFeeLinkPPMTier1: vrfv2Config.FulfillmentFlatFeeLinkPPMTier1, - FulfillmentFlatFeeLinkPPMTier2: vrfv2Config.FulfillmentFlatFeeLinkPPMTier2, - FulfillmentFlatFeeLinkPPMTier3: vrfv2Config.FulfillmentFlatFeeLinkPPMTier3, - FulfillmentFlatFeeLinkPPMTier4: vrfv2Config.FulfillmentFlatFeeLinkPPMTier4, - FulfillmentFlatFeeLinkPPMTier5: vrfv2Config.FulfillmentFlatFeeLinkPPMTier5, - ReqsForTier2: big.NewInt(vrfv2Config.ReqsForTier2), - ReqsForTier3: big.NewInt(vrfv2Config.ReqsForTier3), - ReqsForTier4: big.NewInt(vrfv2Config.ReqsForTier4), - ReqsForTier5: big.NewInt(vrfv2Config.ReqsForTier5)} + FulfillmentFlatFeeLinkPPMTier1: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier1, + FulfillmentFlatFeeLinkPPMTier2: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier2, + FulfillmentFlatFeeLinkPPMTier3: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier3, + FulfillmentFlatFeeLinkPPMTier4: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier4, + FulfillmentFlatFeeLinkPPMTier5: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier5, + ReqsForTier2: big.NewInt(*vrfv2Config.ReqsForTier2), + ReqsForTier3: big.NewInt(*vrfv2Config.ReqsForTier3), + ReqsForTier4: big.NewInt(*vrfv2Config.ReqsForTier4), + ReqsForTier5: big.NewInt(*vrfv2Config.ReqsForTier5)} l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") err = vrfv2Contracts.Coordinator.SetConfig( - vrfv2Config.MinimumConfirmations, - vrfv2Config.MaxGasLimitCoordinatorConfig, - vrfv2Config.StalenessSeconds, - vrfv2Config.GasAfterPaymentCalculation, - big.NewInt(vrfv2Config.FallbackWeiPerUnitLink), + *vrfv2Config.MinimumConfirmations, + *vrfv2Config.MaxGasLimitCoordinatorConfig, + *vrfv2Config.StalenessSeconds, + *vrfv2Config.GasAfterPaymentCalculation, + big.NewInt(*vrfv2Config.FallbackWeiPerUnitLink), vrfCoordinatorV2FeeConfig, ) if err != nil { @@ -341,7 +343,7 @@ func SetupVRFV2Environment( Msg("Creating and funding subscriptions, adding consumers") subIDs, err := CreateFundSubsAndAddConsumers( env, - vrfv2Config, + big.NewFloat(*vrfv2Config.SubscriptionFundingAmountLink), linkToken, vrfv2Contracts.Coordinator, vrfv2Contracts.LoadTestConsumers, numberOfSubToCreate) if err != nil { @@ -365,7 +367,7 @@ func SetupVRFV2Environment( } chainID := env.EVMClient.GetChainID() - newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2Config, numberOfTxKeysToCreate, chainID) + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2TestConfig, numberOfTxKeysToCreate, chainID) if err != nil { return nil, nil, nil, err } @@ -402,7 +404,7 @@ func SetupVRFV2Environment( CoordinatorAddress: vrfv2Contracts.Coordinator.Address(), FromAddresses: allNativeTokenKeyAddressStrings, EVMChainID: chainID.String(), - MinIncomingConfirmations: int(vrfv2Config.MinimumConfirmations), + MinIncomingConfirmations: int(*vrfv2Config.MinimumConfirmations), PublicKey: pubKeyCompressed, EstimateGasMultiplier: 1, BatchFulfillmentEnabled: false, @@ -425,7 +427,7 @@ func SetupVRFV2Environment( // [[EVM.KeySpecific]] // Key = '...' nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(allNativeTokenKeyAddressStrings, vrfv2Config.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddressStrings, *vrfv2Config.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -492,7 +494,7 @@ func setupVRFOwnerContract(env *test_env.CLClusterTestEnv, vrfv2Contracts *VRFV2 func SetupVRFV2WrapperEnvironment( env *test_env.CLClusterTestEnv, - vrfv2Config vrfv2_config.VRFV2Config, + vrfv2TestConfig tc.VRFv2TestConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, coordinator contracts.VRFCoordinatorV2, @@ -516,13 +518,15 @@ func SetupVRFV2WrapperEnvironment( return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } + vrfv2Config := vrfv2TestConfig.GetVRFv2Config() + // Configure VRF v2 wrapper contract err = wrapperContracts.VRFV2Wrapper.SetConfig( - vrfv2Config.WrapperGasOverhead, - vrfv2Config.CoordinatorGasOverhead, - vrfv2Config.WrapperPremiumPercentage, + *vrfv2Config.General.WrapperGasOverhead, + *vrfv2Config.General.CoordinatorGasOverhead, + *vrfv2Config.General.WrapperPremiumPercentage, keyHash, - vrfv2Config.WrapperMaxNumberOfWords, + *vrfv2Config.General.WrapperMaxNumberOfWords, ) if err != nil { return nil, nil, err @@ -543,7 +547,7 @@ func SetupVRFV2WrapperEnvironment( } // Fund wrapper subscription - err = FundSubscriptions(env, vrfv2Config.SubscriptionFundingAmountLink, linkToken, coordinator, []uint64{wrapperSubID}) + err = FundSubscriptions(env, big.NewFloat(*vrfv2Config.General.SubscriptionFundingAmountLink), linkToken, coordinator, []uint64{wrapperSubID}) if err != nil { return nil, nil, err } @@ -551,7 +555,7 @@ func SetupVRFV2WrapperEnvironment( // Fund consumer with LINK err = linkToken.Transfer( wrapperContracts.LoadTestConsumers[0].Address(), - big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(vrfv2Config.WrapperConsumerFundingAmountLink)), + big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2Config.General.WrapperConsumerFundingAmountLink)), ) if err != nil { return nil, nil, err @@ -564,7 +568,7 @@ func SetupVRFV2WrapperEnvironment( return wrapperContracts, &wrapperSubID, nil } -func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { +func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, testConfig tc.CommonTestConfig, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { var newNativeTokenKeyAddresses []string for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { newTxKey, response, err := env.ClCluster.NodeAPIs()[0].CreateTxKey("evm", chainID.String()) @@ -575,7 +579,7 @@ func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_ return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) } newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.ID) - err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(vrfv2Config.ChainlinkNodeFunding)) + err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(*testConfig.GetCommonConfig().ChainlinkNodeFunding)) if err != nil { return nil, err } @@ -585,13 +589,13 @@ func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_ func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, - vrfv2Config vrfv2_config.VRFV2Config, + subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, consumers []contracts.VRFv2LoadTestConsumer, numberOfSubToCreate int, ) ([]uint64, error) { - subIDs, err := CreateSubsAndFund(env, vrfv2Config, linkToken, coordinator, numberOfSubToCreate) + subIDs, err := CreateSubsAndFund(env, subscriptionFundingAmountLink, linkToken, coordinator, numberOfSubToCreate) if err != nil { return nil, err } @@ -619,7 +623,7 @@ func CreateFundSubsAndAddConsumers( func CreateSubsAndFund( env *test_env.CLClusterTestEnv, - vrfv2Config vrfv2_config.VRFV2Config, + subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, subAmountToCreate int, @@ -632,7 +636,7 @@ func CreateSubsAndFund( if err != nil { return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = FundSubscriptions(env, vrfv2Config.SubscriptionFundingAmountLink, linkToken, coordinator, subs) + err = FundSubscriptions(env, subscriptionFundingAmountLink, linkToken, coordinator, subs) if err != nil { return nil, err } @@ -694,14 +698,14 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts func FundSubscriptions( env *test_env.CLClusterTestEnv, - fundingAmountLink float64, + subscriptionFundingAmountLink *big.Float, linkAddress contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, subIDs []uint64, ) error { for _, subID := range subIDs { //Link Billing - amountJuels := conversions.EtherToWei(big.NewFloat(fundingAmountLink)) + amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) err := FundVRFCoordinatorV2Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) @@ -715,20 +719,23 @@ func FundSubscriptions( } func DirectFundingRequestRandomnessAndWaitForFulfillment( + l zerolog.Logger, consumer contracts.VRFv2WrapperLoadTestConsumer, coordinator contracts.VRFCoordinatorV2, - vrfv2Data *VRFV2Data, subID uint64, + vrfv2Data *VRFV2Data, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, randomnessRequestCountPerRequest uint16, - vrfv2Config vrfv2_config.VRFV2Config, + randomnessRequestCountPerRequestDeviation uint16, randomWordsFulfilledEventTimeout time.Duration, - l zerolog.Logger, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { - logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l) + logRandRequest(l, consumer.Address(), coordinator.Address(), subID, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomness( - vrfv2Config.MinimumConfirmations, - vrfv2Config.CallbackGasLimit, - vrfv2Config.NumberOfWords, + minimumConfirmations, + callbackGasLimit, + numberOfWords, randomnessRequestCountPerRequest, ) if err != nil { @@ -750,22 +757,25 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( } func RequestRandomnessAndWaitForFulfillment( + l zerolog.Logger, consumer contracts.VRFv2LoadTestConsumer, coordinator contracts.VRFCoordinatorV2, - vrfv2Data *VRFV2Data, subID uint64, + vrfv2Data *VRFV2Data, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, randomnessRequestCountPerRequest uint16, - vrfv2Config vrfv2_config.VRFV2Config, + randomnessRequestCountPerRequestDeviation uint16, randomWordsFulfilledEventTimeout time.Duration, - l zerolog.Logger, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { - logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l) + logRandRequest(l, consumer.Address(), coordinator.Address(), subID, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomness( vrfv2Data.KeyHash, subID, - vrfv2Config.MinimumConfirmations, - vrfv2Config.CallbackGasLimit, - vrfv2Config.NumberOfWords, + minimumConfirmations, + callbackGasLimit, + numberOfWords, randomnessRequestCountPerRequest, ) if err != nil { @@ -784,23 +794,26 @@ func RequestRandomnessAndWaitForFulfillment( } func RequestRandomnessWithForceFulfillAndWaitForFulfillment( + l zerolog.Logger, consumer contracts.VRFv2LoadTestConsumer, coordinator contracts.VRFCoordinatorV2, vrfOwner contracts.VRFOwner, vrfv2Data *VRFV2Data, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, randomnessRequestCountPerRequest uint16, - vrfv2Config vrfv2_config.VRFV2Config, + randomnessRequestCountPerRequestDeviation uint16, subTopUpAmount *big.Int, linkAddress common.Address, randomWordsFulfilledEventTimeout time.Duration, - l zerolog.Logger, ) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, *vrf_owner.VRFOwnerRandomWordsForced, error) { - logRandRequest(consumer.Address(), coordinator.Address(), 0, vrfv2Config, l) + logRandRequest(l, consumer.Address(), coordinator.Address(), 0, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomWordsWithForceFulfill( vrfv2Data.KeyHash, - vrfv2Config.MinimumConfirmations, - vrfv2Config.CallbackGasLimit, - vrfv2Config.NumberOfWords, + minimumConfirmations, + callbackGasLimit, + numberOfWords, randomnessRequestCountPerRequest, subTopUpAmount, linkAddress, @@ -1013,19 +1026,24 @@ func LogRandomWordsForcedEvent( } func logRandRequest( + l zerolog.Logger, consumer string, coordinator string, subID uint64, - vrfv2Config vrfv2_config.VRFV2Config, - l zerolog.Logger, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, ) { l.Debug(). Str("Consumer", consumer). Str("Coordinator", coordinator). Uint64("SubID", subID). - Uint16("MinimumConfirmations", vrfv2Config.MinimumConfirmations). - Uint32("CallbackGasLimit", vrfv2Config.CallbackGasLimit). - Uint16("RandomnessRequestCountPerRequest", vrfv2Config.RandomnessRequestCountPerRequest). - Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2Config.RandomnessRequestCountPerRequestDeviation). + Uint16("MinimumConfirmations", minimumConfirmations). + Uint32("CallbackGasLimit", callbackGasLimit). + Uint32("NumberOfWords", numberOfWords). + Uint16("RandomnessRequestCountPerRequest", randomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", randomnessRequestCountPerRequestDeviation). Msg("Requesting randomness") } diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go deleted file mode 100644 index 233b35a2383..00000000000 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go +++ /dev/null @@ -1,54 +0,0 @@ -package vrfv2plus_config - -import "time" - -const ( - BILLING_TYPE_LINK = "LINK" - BILLING_TYPE_NATIVE = "NATIVE" - BILLING_TYPE_LINK_AND_NATIVE = "LINK_AND_NATIVE" -) - -type VRFV2PlusConfig struct { - ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with - CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"10"` // Max gas price in GWei for the chainlink node - SubscriptionBillingType string `envconfig:"SUBSCRIPTION_BILLING_TYPE" default:"LINK_AND_NATIVE"` // Whether to use native payment or LINK token, or both - LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed - MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator - SubscriptionFundingAmountLink float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"5"` // Amount of LINK to fund the subscription with - SubscriptionFundingAmountNative float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_NATIVE" default:"1"` // Amount of native currency to fund the subscription with - NumberOfWords uint32 `envconfig:"NUMBER_OF_WORDS" default:"3"` // Number of words to request - CallbackGasLimit uint32 `envconfig:"CALLBACK_GAS_LIMIT" default:"1000000"` // Gas limit for the callback - MaxGasLimitCoordinatorConfig uint32 `envconfig:"MAX_GAS_LIMIT_COORDINATOR_CONFIG" default:"2500000"` // Max gas limit for the VRF Coordinator config - FallbackWeiPerUnitLink int64 `envconfig:"FALLBACK_WEI_PER_UNIT_LINK" default:"60000000000000000"` // Fallback wei per unit LINK for the VRF Coordinator config - StalenessSeconds uint32 `envconfig:"STALENESS_SECONDS" default:"86400"` // Staleness in seconds for the VRF Coordinator config - GasAfterPaymentCalculation uint32 `envconfig:"GAS_AFTER_PAYMENT_CALCULATION" default:"33825"` // Gas after payment calculation for the VRF Coordinator config - FulfillmentFlatFeeLinkPPM uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM" default:"500"` // Flat fee in ppm for LINK for the VRF Coordinator config - FulfillmentFlatFeeNativePPM uint32 `envconfig:"FULFILLMENT_FLAT_FEE_NATIVE_PPM" default:"500"` // Flat fee in ppm for native currency for the VRF Coordinator config - - NumberOfSubToCreate int `envconfig:"NUMBER_OF_SUB_TO_CREATE" default:"1"` // Number of subscriptions to create - - RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request - RandomnessRequestCountPerRequestDeviation uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST_DEVIATION" default:"0"` // How many randomness requests to send per request - - RandomWordsFulfilledEventTimeout time.Duration `envconfig:"RANDOM_WORDS_FULFILLED_EVENT_TIMEOUT" default:"2m"` // How long to wait for the RandomWordsFulfilled event to be emitted - - //Wrapper Config - WrapperGasOverhead uint32 `envconfig:"WRAPPER_GAS_OVERHEAD" default:"50000"` - CoordinatorGasOverhead uint32 `envconfig:"COORDINATOR_GAS_OVERHEAD" default:"52000"` - WrapperPremiumPercentage uint8 `envconfig:"WRAPPER_PREMIUM_PERCENTAGE" default:"25"` - WrapperMaxNumberOfWords uint8 `envconfig:"WRAPPER_MAX_NUMBER_OF_WORDS" default:"10"` - WrapperConsumerFundingAmountNativeToken float64 `envconfig:"WRAPPER_CONSUMER_FUNDING_AMOUNT_NATIVE_TOKEN" default:"1"` - WrapperConsumerFundingAmountLink int64 `envconfig:"WRAPPER_CONSUMER_FUNDING_AMOUNT_LINK" default:"10"` - - //LOAD/SOAK Test Config - TestDuration time.Duration `envconfig:"TEST_DURATION" default:"3m"` // How long to run the test for - RPS int64 `envconfig:"RPS" default:"1"` // How many requests per second to send - RateLimitUnitDuration time.Duration `envconfig:"RATE_LIMIT_UNIT_DURATION" default:"1m"` - //Using existing environment and contracts - UseExistingEnv bool `envconfig:"USE_EXISTING_ENV" default:"false"` // Whether to use an existing environment or create a new one - CoordinatorAddress string `envconfig:"COORDINATOR_ADDRESS" default:""` // Coordinator address - ConsumerAddress string `envconfig:"CONSUMER_ADDRESS" default:""` // Consumer address - LinkAddress string `envconfig:"LINK_ADDRESS" default:""` // Link address - SubID string `envconfig:"SUB_ID" default:""` // Subscription ID - KeyHash string `envconfig:"KEY_HASH" default:""` -} diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index b19efd1d677..fc2a47f53ef 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -18,10 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/types" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" @@ -214,7 +216,7 @@ func FundVRFCoordinatorV2_5Subscription( // SetupVRFV2_5Environment will create specified number of subscriptions and add the same conumer/s to each of them func SetupVRFV2_5Environment( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusTestConfig types.VRFv2PlusTestConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, numberOfTxKeysToCreate int, @@ -230,15 +232,16 @@ func SetupVRFV2_5Environment( } l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") + vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General err = vrfv2_5Contracts.Coordinator.SetConfig( - vrfv2PlusConfig.MinimumConfirmations, - vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, - vrfv2PlusConfig.StalenessSeconds, - vrfv2PlusConfig.GasAfterPaymentCalculation, - big.NewInt(vrfv2PlusConfig.FallbackWeiPerUnitLink), + *vrfv2PlusConfig.MinimumConfirmations, + *vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, + *vrfv2PlusConfig.StalenessSeconds, + *vrfv2PlusConfig.GasAfterPaymentCalculation, + big.NewInt(*vrfv2PlusConfig.FallbackWeiPerUnitLink), vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig{ - FulfillmentFlatFeeLinkPPM: vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, - FulfillmentFlatFeeNativePPM: vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + FulfillmentFlatFeeLinkPPM: *vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, + FulfillmentFlatFeeNativePPM: *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, }, ) if err != nil { @@ -260,9 +263,12 @@ func SetupVRFV2_5Environment( Msg("Creating and funding subscriptions, adding consumers") subIDs, err := CreateFundSubsAndAddConsumers( env, - vrfv2PlusConfig, + big.NewFloat(*vrfv2PlusConfig.SubscriptionFundingAmountNative), + big.NewFloat(*vrfv2PlusConfig.SubscriptionFundingAmountLink), linkToken, - vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, numberOfSubToCreate) + vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, + numberOfSubToCreate, + vrfv2plus_config.BillingType(*vrfv2PlusConfig.SubscriptionBillingType)) if err != nil { return nil, nil, nil, err } @@ -284,7 +290,7 @@ func SetupVRFV2_5Environment( } chainID := env.EVMClient.GetChainID() - newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2PlusConfig, numberOfTxKeysToCreate, chainID) + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2PlusTestConfig, numberOfTxKeysToCreate, chainID) if err != nil { return nil, nil, nil, err } @@ -299,7 +305,7 @@ func SetupVRFV2_5Environment( CoordinatorAddress: vrfv2_5Contracts.Coordinator.Address(), FromAddresses: allNativeTokenKeyAddresses, EVMChainID: chainID.String(), - MinIncomingConfirmations: int(vrfv2PlusConfig.MinimumConfirmations), + MinIncomingConfirmations: int(*vrfv2PlusConfig.MinimumConfirmations), PublicKey: pubKeyCompressed, EstimateGasMultiplier: 1, BatchFulfillmentEnabled: false, @@ -322,7 +328,7 @@ func SetupVRFV2_5Environment( // Key = '...' nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, node.WithLogPollInterval(1*time.Second), - node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, *vrfv2PlusConfig.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration and log poll period configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -347,7 +353,7 @@ func SetupVRFV2_5Environment( return vrfv2_5Contracts, subIDs, &data, nil } -func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { +func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, commonTestConfig tc.CommonTestConfig, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { var newNativeTokenKeyAddresses []string for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { newTxKey, response, err := env.ClCluster.NodeAPIs()[0].CreateTxKey("evm", chainID.String()) @@ -358,7 +364,7 @@ func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2PlusConfig vr return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) } newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.ID) - err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)) + err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(*commonTestConfig.GetCommonConfig().ChainlinkNodeFunding)) if err != nil { return nil, err } @@ -368,13 +374,23 @@ func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2PlusConfig vr func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + subscriptionFundingAmountNative *big.Float, + subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, consumers []contracts.VRFv2PlusLoadTestConsumer, numberOfSubToCreate int, + subscriptionBillingType vrfv2plus_config.BillingType, ) ([]*big.Int, error) { - subIDs, err := CreateSubsAndFund(env, vrfv2PlusConfig, linkToken, coordinator, numberOfSubToCreate) + subIDs, err := CreateSubsAndFund( + env, + subscriptionFundingAmountNative, + subscriptionFundingAmountLink, + linkToken, + coordinator, + numberOfSubToCreate, + subscriptionBillingType, + ) if err != nil { return nil, err } @@ -402,10 +418,12 @@ func CreateFundSubsAndAddConsumers( func CreateSubsAndFund( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + subscriptionFundingAmountNative *big.Float, + subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, subAmountToCreate int, + subscriptionBillingType vrfv2plus_config.BillingType, ) ([]*big.Int, error) { subs, err := CreateSubs(env, coordinator, subAmountToCreate) if err != nil { @@ -415,7 +433,15 @@ func CreateSubsAndFund( if err != nil { return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, subs) + err = FundSubscriptions( + env, + subscriptionFundingAmountNative, + subscriptionFundingAmountLink, + linkToken, + coordinator, + subs, + subscriptionBillingType, + ) if err != nil { return nil, err } @@ -477,16 +503,18 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts func FundSubscriptions( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + subscriptionFundingAmountNative *big.Float, + subscriptionFundingAmountLink *big.Float, linkAddress contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, subIDs []*big.Int, + subscriptionBillingType vrfv2plus_config.BillingType, ) error { for _, subID := range subIDs { - switch vrfv2PlusConfig.SubscriptionBillingType { - case vrfv2plus_config.BILLING_TYPE_NATIVE: + switch subscriptionBillingType { + case vrfv2plus_config.BillingType_Native: //Native Billing - amountWei := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) + amountWei := conversions.EtherToWei(subscriptionFundingAmountNative) err := coordinator.FundSubscriptionWithNative( subID, amountWei, @@ -494,16 +522,16 @@ func FundSubscriptions( if err != nil { return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) } - case vrfv2plus_config.BILLING_TYPE_LINK: + case vrfv2plus_config.BillingType_Link: //Link Billing - amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) err := FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) } - case vrfv2plus_config.BILLING_TYPE_LINK_AND_NATIVE: + case vrfv2plus_config.BillingType_Link_and_Native: //Native Billing - amountWei := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) + amountWei := conversions.EtherToWei(subscriptionFundingAmountNative) err := coordinator.FundSubscriptionWithNative( subID, amountWei, @@ -512,13 +540,13 @@ func FundSubscriptions( return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) } //Link Billing - amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) } default: - return fmt.Errorf("invalid billing type: %s", vrfv2PlusConfig.SubscriptionBillingType) + return fmt.Errorf("invalid billing type: %s", subscriptionBillingType) } } err := env.EVMClient.WaitForEvents() @@ -558,19 +586,22 @@ func RequestRandomnessAndWaitForFulfillment( vrfv2PlusData *VRFV2PlusData, subID *big.Int, isNativeBilling bool, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, randomnessRequestCountPerRequest uint16, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + randomnessRequestCountPerRequestDeviation uint16, randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { - logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l) + logRandRequest(l, consumer.Address(), coordinator.Address(), subID, isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomness( vrfv2PlusData.KeyHash, subID, - vrfv2PlusConfig.MinimumConfirmations, - vrfv2PlusConfig.CallbackGasLimit, + minimumConfirmations, + callbackGasLimit, isNativeBilling, - vrfv2PlusConfig.NumberOfWords, + numberOfWords, randomnessRequestCountPerRequest, ) if err != nil { @@ -594,18 +625,22 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( vrfv2PlusData *VRFV2PlusData, subID *big.Int, isNativeBilling bool, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, l zerolog.Logger, ) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { - logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l) + logRandRequest(l, consumer.Address(), coordinator.Address(), subID, isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomness( vrfv2PlusData.KeyHash, subID, - vrfv2PlusConfig.MinimumConfirmations, - vrfv2PlusConfig.CallbackGasLimit, + minimumConfirmations, + callbackGasLimit, isNativeBilling, - vrfv2PlusConfig.NumberOfWords, - vrfv2PlusConfig.RandomnessRequestCountPerRequest, + numberOfWords, + randomnessRequestCountPerRequest, ) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) @@ -638,14 +673,14 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( func SetupVRFV2PlusWrapperEnvironment( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusTestConfig types.VRFv2PlusTestConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, coordinator contracts.VRFCoordinatorV2_5, keyHash [32]byte, wrapperConsumerContractsAmount int, ) (*VRFV2PlusWrapperContracts, *big.Int, error) { - + vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( env.ContractDeployer, env.EVMClient, @@ -664,15 +699,15 @@ func SetupVRFV2PlusWrapperEnvironment( return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } err = wrapperContracts.VRFV2PlusWrapper.SetConfig( - vrfv2PlusConfig.WrapperGasOverhead, - vrfv2PlusConfig.CoordinatorGasOverhead, - vrfv2PlusConfig.WrapperPremiumPercentage, + *vrfv2PlusConfig.WrapperGasOverhead, + *vrfv2PlusConfig.CoordinatorGasOverhead, + *vrfv2PlusConfig.WrapperPremiumPercentage, keyHash, - vrfv2PlusConfig.WrapperMaxNumberOfWords, - vrfv2PlusConfig.StalenessSeconds, - big.NewInt(vrfv2PlusConfig.FallbackWeiPerUnitLink), - vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, - vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + *vrfv2PlusConfig.WrapperMaxNumberOfWords, + *vrfv2PlusConfig.StalenessSeconds, + big.NewInt(*vrfv2PlusConfig.FallbackWeiPerUnitLink), + *vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, + *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, ) if err != nil { return nil, nil, err @@ -694,7 +729,7 @@ func SetupVRFV2PlusWrapperEnvironment( return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, []*big.Int{wrapperSubID}) + err = FundSubscriptions(env, big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, coordinator, []*big.Int{wrapperSubID}, vrfv2plus_config.BillingType(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionBillingType)) if err != nil { return nil, nil, err } @@ -702,7 +737,7 @@ func SetupVRFV2PlusWrapperEnvironment( //fund consumer with Link err = linkToken.Transfer( wrapperContracts.LoadTestConsumers[0].Address(), - big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), + big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), ) if err != nil { return nil, nil, err @@ -713,7 +748,7 @@ func SetupVRFV2PlusWrapperEnvironment( } //fund consumer with Eth - err = wrapperContracts.LoadTestConsumers[0].Fund(big.NewFloat(vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)) + err = wrapperContracts.LoadTestConsumers[0].Fund(big.NewFloat(*vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)) if err != nil { return nil, nil, err } @@ -771,27 +806,31 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( vrfv2PlusData *VRFV2PlusData, subID *big.Int, isNativeBilling bool, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { - logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l) + logRandRequest(l, consumer.Address(), coordinator.Address(), subID, isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) if isNativeBilling { _, err := consumer.RequestRandomnessNative( - vrfv2PlusConfig.MinimumConfirmations, - vrfv2PlusConfig.CallbackGasLimit, - vrfv2PlusConfig.NumberOfWords, - vrfv2PlusConfig.RandomnessRequestCountPerRequest, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + randomnessRequestCountPerRequest, ) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingNativePayment, err) } } else { _, err := consumer.RequestRandomness( - vrfv2PlusConfig.MinimumConfirmations, - vrfv2PlusConfig.CallbackGasLimit, - vrfv2PlusConfig.NumberOfWords, - vrfv2PlusConfig.RandomnessRequestCountPerRequest, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + randomnessRequestCountPerRequest, ) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingLinkPayment, err) @@ -1065,20 +1104,25 @@ func LogFulfillmentDetailsNativeBilling( } func logRandRequest( + l zerolog.Logger, consumer string, coordinator string, subID *big.Int, isNativeBilling bool, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, - l zerolog.Logger) { + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16) { l.Debug(). Str("Consumer", consumer). Str("Coordinator", coordinator). Str("SubID", subID.String()). Bool("IsNativePayment", isNativeBilling). - Uint16("MinimumConfirmations", vrfv2PlusConfig.MinimumConfirmations). - Uint32("CallbackGasLimit", vrfv2PlusConfig.CallbackGasLimit). - Uint16("RandomnessRequestCountPerRequest", vrfv2PlusConfig.RandomnessRequestCountPerRequest). - Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation). + Uint16("MinimumConfirmations", minimumConfirmations). + Uint32("CallbackGasLimit", callbackGasLimit). + Uint32("NumberOfWords", numberOfWords). + Uint16("RandomnessRequestCountPerRequest", randomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", randomnessRequestCountPerRequestDeviation). Msg("Requesting randomness") } diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 015dff1126c..12a7e4c75aa 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -3,8 +3,6 @@ package benchmark import ( "fmt" "math/big" - "os" - "strconv" "strings" "testing" "time" @@ -12,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" env_client "github.com/smartcontractkit/chainlink-testing-framework/k8s/client" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" @@ -25,6 +24,9 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" + "github.com/smartcontractkit/chainlink/integration-tests/types" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( @@ -111,27 +113,6 @@ LimitDefault = 5_000_000` } ) -var ( - NumberOfNodes, _ = strconv.Atoi(getEnv("NUMBEROFNODES", "6")) - RegistryToTest = getEnv("REGISTRY", "2_1") - NumberOfUpkeeps, _ = strconv.Atoi(getEnv("NUMBEROFUPKEEPS", "500")) - CheckGasToBurn, _ = strconv.ParseInt(getEnv("CHECKGASTOBURN", "100000"), 0, 64) - PerformGasToBurn, _ = strconv.ParseInt(getEnv("PERFORMGASTOBURN", "50000"), 0, 64) - BlockRange, _ = strconv.ParseInt(getEnv("BLOCKRANGE", "3600"), 0, 64) - BlockInterval, _ = strconv.ParseInt(getEnv("BLOCKINTERVAL", "20"), 0, 64) - ChainlinkNodeFunding, _ = strconv.ParseFloat(getEnv("CHAINLINKNODEFUNDING", "0.5"), 64) - MaxPerformGas, _ = strconv.ParseInt(getEnv("MAXPERFORMGAS", "5000000"), 0, 32) - UpkeepGasLimit, _ = strconv.ParseInt(getEnv("UPKEEPGASLIMIT", fmt.Sprint(PerformGasToBurn+100000)), 0, 64) - NumberOfRegistries, _ = strconv.Atoi(getEnv("NUMBEROFREGISTRIES", "1")) - ForceSingleTxnKey, _ = strconv.ParseBool(getEnv("FORCESINGLETXNKEY", "false")) - DeleteJobsOnEnd, _ = strconv.ParseBool(getEnv("DELETEJOBSONEND", "true")) - RegistryAddress = getEnv("REGISTRYADDRESS", "") - RegistrarAddress = getEnv("REGISTRARADDRESS", "") - LinkTokenAddress = getEnv("LINKTOKENADDRESS", "") - EthFeedAddress = getEnv("ETHFEEDADDRESS", "") - GasFeedAddress = getEnv("GASFEEDADDRESS", "") -) - type NetworkConfig struct { upkeepSLA int64 blockTime time.Duration @@ -141,20 +122,26 @@ type NetworkConfig struct { func TestAutomationBenchmark(t *testing.T) { l := logging.GetTestLogger(t) - testEnvironment, benchmarkNetwork := SetupAutomationBenchmarkEnv(t) + testType, err := tc.GetConfigurationNameFromEnv() + require.NoError(t, err, "Error getting test type") + + config, err := tc.GetConfig(testType, tc.Keeper) + require.NoError(t, err, "Error getting test config") + + testEnvironment, benchmarkNetwork := SetupAutomationBenchmarkEnv(t, &config) if testEnvironment.WillUseRemoteRunner() { return } networkName := strings.ReplaceAll(benchmarkNetwork.Name, " ", "") - testName := fmt.Sprintf("%s%s", networkName, RegistryToTest) - l.Info().Str("Test Name", testName).Str("Test Inputs", os.Getenv("TEST_INPUTS")).Msg("Running Benchmark Test") - benchmarkTestNetwork := networkConfig[networkName] + testName := fmt.Sprintf("%s%s", networkName, *config.Keeper.Common.RegistryToTest) + l.Info().Str("Test Name", testName).Msg("Running Benchmark Test") + benchmarkTestNetwork := getNetworkConfig(networkName, &config) l.Info().Str("Namespace", testEnvironment.Cfg.Namespace).Msg("Connected to Keepers Benchmark Environment") chainClient, err := blockchain.NewEVMClient(benchmarkNetwork, testEnvironment, l) require.NoError(t, err, "Error connecting to blockchain") - registryVersions := addRegistry(RegistryToTest) + registryVersions := addRegistry(&config) keeperBenchmarkTest := testsetups.NewKeeperBenchmarkTest(t, testsetups.KeeperBenchmarkTestInputs{ BlockchainClient: chainClient, @@ -166,7 +153,7 @@ func TestAutomationBenchmark(t *testing.T) { CheckGasLimit: uint32(45_000_000), //45M StalenessSeconds: big.NewInt(90_000), GasCeilingMultiplier: uint16(2), - MaxPerformGas: uint32(MaxPerformGas), + MaxPerformGas: uint32(*config.Keeper.Common.MaxPerformGas), MinUpkeepSpend: big.NewInt(0), FallbackGasPrice: big.NewInt(2e11), FallbackLinkPrice: big.NewInt(2e18), @@ -174,27 +161,27 @@ func TestAutomationBenchmark(t *testing.T) { MaxPerformDataSize: uint32(5_000), }, Upkeeps: &testsetups.UpkeepConfig{ - NumberOfUpkeeps: NumberOfUpkeeps, - CheckGasToBurn: CheckGasToBurn, - PerformGasToBurn: PerformGasToBurn, - BlockRange: BlockRange, - BlockInterval: BlockInterval, - UpkeepGasLimit: UpkeepGasLimit, + NumberOfUpkeeps: *config.Keeper.Common.NumberOfUpkeeps, + CheckGasToBurn: *config.Keeper.Common.CheckGasToBurn, + PerformGasToBurn: *config.Keeper.Common.PerformGasToBurn, + BlockRange: *config.Keeper.Common.BlockRange, + BlockInterval: *config.Keeper.Common.BlockInterval, + UpkeepGasLimit: *config.Keeper.Common.UpkeepGasLimit, FirstEligibleBuffer: 1, }, Contracts: &testsetups.PreDeployedContracts{ - RegistrarAddress: RegistrarAddress, - RegistryAddress: RegistryAddress, - LinkTokenAddress: LinkTokenAddress, - EthFeedAddress: EthFeedAddress, - GasFeedAddress: GasFeedAddress, + RegistrarAddress: *config.Keeper.Common.RegistrarAddress, + RegistryAddress: *config.Keeper.Common.RegistryAddress, + LinkTokenAddress: *config.Keeper.Common.LinkTokenAddress, + EthFeedAddress: *config.Keeper.Common.EthFeedAddress, + GasFeedAddress: *config.Keeper.Common.GasFeedAddress, }, ChainlinkNodeFunding: benchmarkTestNetwork.funding, UpkeepSLA: benchmarkTestNetwork.upkeepSLA, BlockTime: benchmarkTestNetwork.blockTime, DeltaStage: benchmarkTestNetwork.deltaStage, - ForceSingleTxnKey: ForceSingleTxnKey, - DeleteJobsOnEnd: DeleteJobsOnEnd, + ForceSingleTxnKey: *config.Keeper.Common.ForceSingleTxKey, + DeleteJobsOnEnd: *config.Keeper.Common.DeleteJobsOnEnd, }, ) t.Cleanup(func() { @@ -202,12 +189,12 @@ func TestAutomationBenchmark(t *testing.T) { l.Error().Err(err).Msg("Error when tearing down remote suite") } }) - keeperBenchmarkTest.Setup(testEnvironment) + keeperBenchmarkTest.Setup(testEnvironment, &config) keeperBenchmarkTest.Run() } -func addRegistry(registryToTest string) []eth_contracts.KeeperRegistryVersion { - switch registryToTest { +func addRegistry(config *tc.TestConfig) []eth_contracts.KeeperRegistryVersion { + switch *config.Keeper.Common.RegistryToTest { case "1_1": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_1} case "1_2": @@ -224,9 +211,9 @@ func addRegistry(registryToTest string) []eth_contracts.KeeperRegistryVersion { return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1, eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} case "2_0-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_0, NumberOfRegistries) + return repeatRegistries(eth_contracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) case "2_1-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_1, NumberOfRegistries) + return repeatRegistries(eth_contracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) default: return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} } @@ -240,6 +227,22 @@ func repeatRegistries(registryVersion eth_contracts.KeeperRegistryVersion, numbe return repeatedRegistries } +func getNetworkConfig(networkName string, config *tc.TestConfig) NetworkConfig { + var nc NetworkConfig + var ok bool + if nc, ok = networkConfig[networkName]; !ok { + return NetworkConfig{} + } + + if networkName == "SimulatedGeth" || networkName == "geth" { + return nc + } + + nc.funding = big.NewFloat(*config.Common.ChainlinkNodeFunding) + + return nc +} + var networkConfig = map[string]NetworkConfig{ "SimulatedGeth": { upkeepSLA: int64(120), //2 minutes @@ -257,99 +260,71 @@ var networkConfig = map[string]NetworkConfig{ upkeepSLA: int64(4), blockTime: 12 * time.Second, deltaStage: time.Duration(0), - funding: big.NewFloat(ChainlinkNodeFunding), }, "ArbitrumGoerli": { upkeepSLA: int64(20), blockTime: time.Second, deltaStage: time.Duration(0), - funding: big.NewFloat(ChainlinkNodeFunding), }, "OptimismGoerli": { upkeepSLA: int64(20), blockTime: time.Second, deltaStage: time.Duration(0), - funding: big.NewFloat(ChainlinkNodeFunding), }, "SepoliaTestnet": { upkeepSLA: int64(4), blockTime: 12 * time.Second, deltaStage: time.Duration(0), - funding: big.NewFloat(ChainlinkNodeFunding), }, "PolygonMumbai": { upkeepSLA: int64(4), blockTime: 12 * time.Second, deltaStage: time.Duration(0), - funding: big.NewFloat(ChainlinkNodeFunding), }, "BaseGoerli": { upkeepSLA: int64(60), blockTime: 2 * time.Second, deltaStage: 20 * time.Second, - funding: big.NewFloat(ChainlinkNodeFunding), }, "ArbitrumSepolia": { upkeepSLA: int64(120), blockTime: time.Second, deltaStage: 20 * time.Second, - funding: big.NewFloat(ChainlinkNodeFunding), }, "LineaGoerli": { upkeepSLA: int64(120), blockTime: time.Second, deltaStage: 20 * time.Second, - funding: big.NewFloat(ChainlinkNodeFunding), }, } -func getEnv(key, fallback string) string { - if inputs, ok := os.LookupEnv("TEST_INPUTS"); ok { - values := strings.Split(inputs, ",") - for _, value := range values { - if strings.Contains(value, key) { - return strings.Split(value, "=")[1] - } - } - } - return fallback -} - -func SetupAutomationBenchmarkEnv(t *testing.T) (*environment.Environment, blockchain.EVMNetwork) { +func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenchmarkTestConfig) (*environment.Environment, blockchain.EVMNetwork) { l := logging.GetTestLogger(t) - testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] // Environment currently being used to run benchmark test on + testNetwork := networks.MustGetSelectedNetworkConfig(keeperTestConfig.GetNetworkConfig())[0] // Environment currently being used to run benchmark test on blockTime := "1" networkDetailTOML := `MinIncomingConfirmations = 1` - if strings.Contains(RegistryToTest, "2_") { - NumberOfNodes++ + numberOfNodes := *keeperTestConfig.GetKeeperConfig().Common.NumberOfNodes + + if strings.Contains(*keeperTestConfig.GetKeeperConfig().Common.RegistryToTest, "2_") { + numberOfNodes++ } - testType := strings.ToLower(getEnv("TEST_TYPE", "benchmark")) testEnvironment := environment.New(&environment.Config{ TTL: time.Hour * 720, // 30 days, NamespacePrefix: fmt.Sprintf( "automation-%s-%s-%s", - testType, + strings.ToLower(keeperTestConfig.GetConfigurationName()), strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-"), - strings.ReplaceAll(strings.ToLower(RegistryToTest), "_", "-"), + strings.ReplaceAll(strings.ToLower(*keeperTestConfig.GetKeeperConfig().Common.RegistryToTest), "_", "-"), ), Test: t, PreventPodEviction: true, }) - // propagate TEST_INPUTS to remote runner - if testEnvironment.WillUseRemoteRunner() { - key := "TEST_INPUTS" - err := os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) - require.NoError(t, err, "failed to set the environment variable TEST_INPUTS for remote runner") - key = "GRAFANA_DASHBOARD_URL" - err = os.Setenv(fmt.Sprintf("TEST_%s", key), getEnv(key, "")) - require.NoError(t, err, "failed to set the environment variable GRAFANA_DASHBOARD_URL for remote runner") - } dbResources := performanceDbResources chainlinkResources := performanceChainlinkResources - if testType == "soak" { + if strings.ToLower(keeperTestConfig.GetConfigurationName()) == "soak" { chainlinkResources = soakChainlinkResources dbResources = soakDbResources } @@ -363,7 +338,7 @@ func SetupAutomationBenchmarkEnv(t *testing.T) (*environment.Environment, blockc Values: map[string]interface{}{ "geth": map[string]interface{}{ "tx": map[string]interface{}{ - "replicas": NumberOfNodes, + "replicas": numberOfNodes, }, "miner": map[string]interface{}{ "replicas": 2, @@ -413,7 +388,7 @@ func SetupAutomationBenchmarkEnv(t *testing.T) (*environment.Environment, blockc // separate RPC urls per CL node internalWsURLs := make([]string, 0) internalHttpURLs := make([]string, 0) - for i := 0; i < NumberOfNodes; i++ { + for i := 0; i < numberOfNodes; i++ { // for simulated-nod-dev each CL node gets its own RPC node if testNetwork.Name == networks.SimulatedEVMNonDev.Name { podName := fmt.Sprintf("%s-ethereum-geth:%d", testNetwork.Name, i) @@ -435,14 +410,22 @@ func SetupAutomationBenchmarkEnv(t *testing.T) (*environment.Environment, blockc } l.Debug().Strs("internalWsURLs", internalWsURLs).Strs("internalHttpURLs", internalHttpURLs).Msg("internalURLs") - for i := 0; i < NumberOfNodes; i++ { + for i := 0; i < numberOfNodes; i++ { testNetwork.HTTPURLs = []string{internalHttpURLs[i]} testNetwork.URLs = []string{internalWsURLs[i]} - testEnvironment.AddHelm(chainlink.New(i, map[string]any{ - "toml": networks.AddNetworkDetailedConfig(keeperBenchmarkBaseTOML, networkDetailTOML, testNetwork), + + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(keeperTestConfig.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(keeperTestConfig.GetPyroscopeConfig(), target) + } + + cd := chainlink.NewWithOverride(i, map[string]any{ + "toml": networks.AddNetworkDetailedConfig(keeperBenchmarkBaseTOML, keeperTestConfig.GetPyroscopeConfig(), networkDetailTOML, testNetwork), "chainlink": chainlinkResources, "db": dbResources, - })) + }, keeperTestConfig.GetChainlinkImageConfig(), overrideFn) + + testEnvironment.AddHelm(cd) } err = testEnvironment.Run() require.NoError(t, err, "Error launching test environment") diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 06352a93d72..711a3557307 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" @@ -25,6 +26,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( @@ -41,7 +44,7 @@ ListenAddresses = ["0.0.0.0:6690"]` defaultAutomationSettings = map[string]interface{}{ "replicas": 6, - "toml": networks.AddNetworksConfig(baseTOML, networks.MustGetSelectedNetworksFromEnv()[0]), + "toml": "", "db": map[string]interface{}{ "stateful": true, "capacity": "1Gi", @@ -58,11 +61,7 @@ ListenAddresses = ["0.0.0.0:6690"]` }, } - defaultEthereumSettings = ðereum.Props{ - // utils.MustGetSelectedNetworksFromEnv() - NetworkName: networks.MustGetSelectedNetworksFromEnv()[0].Name, - Simulated: networks.MustGetSelectedNetworksFromEnv()[0].Simulated, - WsURLs: networks.MustGetSelectedNetworksFromEnv()[0].URLs, + defaultEthereumSettings = ethereum.Props{ Values: map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ @@ -96,6 +95,20 @@ ListenAddresses = ["0.0.0.0:6690"]` } ) +func getDefaultAutomationSettings(config *tc.TestConfig) map[string]interface{} { + defaultAutomationSettings["toml"] = networks.AddNetworksConfig(baseTOML, config.Pyroscope, networks.MustGetSelectedNetworkConfig(config.Network)[0]) + return defaultAutomationSettings +} + +func getDefaultEthereumSettings(config *tc.TestConfig) *ethereum.Props { + network := networks.MustGetSelectedNetworkConfig(config.Network)[0] + defaultEthereumSettings.NetworkName = network.Name + defaultEthereumSettings.Simulated = network.Simulated + defaultEthereumSettings.WsURLs = network.URLs + + return &defaultEthereumSettings +} + type KeeperConsumerContracts int32 const ( @@ -120,6 +133,18 @@ func TestAutomationChaos(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() + config, err := tc.GetConfig("Chaos", tc.Automation) + if err != nil { + t.Fatal(err) + } + + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) + ctf_config.MightConfigOverridePyroscopeKey(config.Pyroscope, target) + } + + chainlinkCfg := chainlink.NewWithOverride(0, getDefaultAutomationSettings(&config), config.ChainlinkImage, overrideFn) + testCases := map[string]struct { networkChart environment.ConnectedChart clChart environment.ConnectedChart @@ -128,8 +153,8 @@ func TestAutomationChaos(t *testing.T) { }{ // see ocr_chaos.test.go for comments PodChaosFailMinorityNodes: { - ethereum.New(defaultEthereumSettings), - chainlink.New(0, defaultAutomationSettings), + ethereum.New(getDefaultEthereumSettings(&config)), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, @@ -137,8 +162,8 @@ func TestAutomationChaos(t *testing.T) { }, }, PodChaosFailMajorityNodes: { - ethereum.New(defaultEthereumSettings), - chainlink.New(0, defaultAutomationSettings), + ethereum.New(getDefaultEthereumSettings(&config)), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -146,8 +171,8 @@ func TestAutomationChaos(t *testing.T) { }, }, PodChaosFailMajorityDB: { - ethereum.New(defaultEthereumSettings), - chainlink.New(0, defaultAutomationSettings), + ethereum.New(getDefaultEthereumSettings(&config)), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -156,8 +181,8 @@ func TestAutomationChaos(t *testing.T) { }, }, NetworkChaosFailMajorityNetwork: { - ethereum.New(defaultEthereumSettings), - chainlink.New(0, defaultAutomationSettings), + ethereum.New(getDefaultEthereumSettings(&config)), + chainlinkCfg, chaos.NewNetworkPartition, &chaos.Props{ FromLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -166,8 +191,8 @@ func TestAutomationChaos(t *testing.T) { }, }, NetworkChaosFailBlockchainNode: { - ethereum.New(defaultEthereumSettings), - chainlink.New(0, defaultAutomationSettings), + ethereum.New(getDefaultEthereumSettings(&config)), + chainlinkCfg, chaos.NewNetworkPartition, &chaos.Props{ FromLabels: &map[string]*string{"app": ptr.Ptr("geth")}, @@ -182,7 +207,7 @@ func TestAutomationChaos(t *testing.T) { testCase := testCase t.Run(fmt.Sprintf("Automation_%s", name), func(t *testing.T) { t.Parallel() - network := networks.MustGetSelectedNetworksFromEnv()[0] // Need a new copy of the network for each test + network := networks.MustGetSelectedNetworkConfig(config.Network)[0] // Need a new copy of the network for each test testEnvironment := environment. New(&environment.Config{ @@ -224,7 +249,7 @@ func TestAutomationChaos(t *testing.T) { if chainClient != nil { chainClient.GasStats().PrintStats() } - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, &config, chainClient) require.NoError(t, err, "Error tearing down environment") }) diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index 200114ddafb..eda60a37f3c 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" @@ -26,17 +27,24 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestOCR2VRFChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - loadedNetwork := networks.MustGetSelectedNetworksFromEnv()[0] + testconfig, err := tc.GetConfig("Chaos", tc.OCR2VRF) + if err != nil { + t.Fatal(err) + } + + loadedNetwork := networks.MustGetSelectedNetworkConfig(testconfig.Network)[0] defaultOCR2VRFSettings := map[string]interface{}{ "replicas": 6, "toml": networks.AddNetworkDetailedConfig( config.BaseOCR2Config, + testconfig.Pyroscope, config.DefaultOCR2VRFNetworkDetailTomlConfig, loadedNetwork, ), @@ -48,6 +56,13 @@ func TestOCR2VRFChaos(t *testing.T) { WsURLs: loadedNetwork.URLs, } + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(testconfig.ChainlinkImage, target) + ctf_config.MightConfigOverridePyroscopeKey(testconfig.Pyroscope, target) + } + + chainlinkCfg := chainlink.NewWithOverride(0, defaultOCR2VRFSettings, testconfig.ChainlinkImage, overrideFn) + testCases := map[string]struct { networkChart environment.ConnectedChart clChart environment.ConnectedChart @@ -65,7 +80,7 @@ func TestOCR2VRFChaos(t *testing.T) { //4. verify VRF request gets fulfilled PodChaosFailMinorityNodes: { ethereum.New(defaultOCR2VRFEthereumSettings), - chainlink.New(0, defaultOCR2VRFSettings), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, @@ -75,7 +90,7 @@ func TestOCR2VRFChaos(t *testing.T) { //todo - currently failing, need to investigate deeper //PodChaosFailMajorityNodes: { // ethereum.New(defaultOCR2VRFEthereumSettings), - // chainlink.New(0, defaultOCR2VRFSettings), + // chainlinkCfg, // chaos.NewFailPods, // &chaos.Props{ // LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -85,7 +100,7 @@ func TestOCR2VRFChaos(t *testing.T) { //todo - do we need these chaos tests? //PodChaosFailMajorityDB: { // ethereum.New(defaultOCR2VRFEthereumSettings), - // chainlink.New(0, defaultOCR2VRFSettings), + // chainlinkCfg, // chaos.NewFailPods, // &chaos.Props{ // LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -95,7 +110,7 @@ func TestOCR2VRFChaos(t *testing.T) { //}, //NetworkChaosFailMajorityNetwork: { // ethereum.New(defaultOCR2VRFEthereumSettings), - // chainlink.New(0, defaultOCR2VRFSettings), + // chainlinkCfg, // chaos.NewNetworkPartition, // &chaos.Props{ // FromLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -105,7 +120,7 @@ func TestOCR2VRFChaos(t *testing.T) { //}, //NetworkChaosFailBlockchainNode: { // ethereum.New(defaultOCR2VRFEthereumSettings), - // chainlink.New(0, defaultOCR2VRFSettings), + // chainlinkCfg, // chaos.NewNetworkPartition, // &chaos.Props{ // FromLabels: &map[string]*string{"app": ptr.Ptr("geth")}, @@ -119,7 +134,7 @@ func TestOCR2VRFChaos(t *testing.T) { testCase := tc t.Run(fmt.Sprintf("OCR2VRF_%s", testCaseName), func(t *testing.T) { t.Parallel() - testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] // Need a new copy of the network for each test + testNetwork := networks.MustGetSelectedNetworkConfig(testconfig.Network)[0] // Need a new copy of the network for each test testEnvironment := environment. New(&environment.Config{ NamespacePrefix: fmt.Sprintf( @@ -150,7 +165,7 @@ func TestOCR2VRFChaos(t *testing.T) { require.NoError(t, err, "Retrieving on-chain wallet addresses for chainlink nodes shouldn't fail") t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, &testconfig, chainClient) require.NoError(t, err, "Error tearing down environment") }) diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index a59a0a028c2..97f7c67d1b9 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -3,7 +3,6 @@ package chaos import ( "fmt" "math/big" - "os" "testing" "github.com/onsi/gomega" @@ -12,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" @@ -25,8 +25,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( @@ -51,14 +51,26 @@ var ( chaosEndRound int64 = 4 ) -func TestMain(m *testing.M) { - defaultOCRSettings["toml"] = networks.AddNetworksConfig(config.BaseOCR1Config, networks.MustGetSelectedNetworksFromEnv()[0]) - os.Exit(m.Run()) +func getDefaultOcrSettings(config *tc.TestConfig) map[string]interface{} { + defaultOCRSettings["toml"] = networks.AddNetworksConfig(baseTOML, config.Pyroscope, networks.MustGetSelectedNetworkConfig(config.Network)[0]) + return defaultAutomationSettings } func TestOCRChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Chaos", tc.OCR) + if err != nil { + t.Fatal(err) + } + + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) + ctf_config.MightConfigOverridePyroscopeKey(config.Pyroscope, target) + } + + chainlinkCfg := chainlink.NewWithOverride(0, getDefaultOcrSettings(&config), config.ChainlinkImage, overrideFn) + testCases := map[string]struct { networkChart environment.ConnectedChart clChart environment.ConnectedChart @@ -77,7 +89,7 @@ func TestOCRChaos(t *testing.T) { // https://github.com/smartcontractkit/chainlink-testing-framework/k8s/blob/master/README.md NetworkChaosFailMajorityNetwork: { ethereum.New(nil), - chainlink.New(0, defaultOCRSettings), + chainlinkCfg, chaos.NewNetworkPartition, &chaos.Props{ FromLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -87,7 +99,7 @@ func TestOCRChaos(t *testing.T) { }, NetworkChaosFailBlockchainNode: { ethereum.New(nil), - chainlink.New(0, defaultOCRSettings), + chainlinkCfg, chaos.NewNetworkPartition, &chaos.Props{ FromLabels: &map[string]*string{"app": ptr.Ptr("geth")}, @@ -97,7 +109,7 @@ func TestOCRChaos(t *testing.T) { }, PodChaosFailMinorityNodes: { ethereum.New(nil), - chainlink.New(0, defaultOCRSettings), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, @@ -106,7 +118,7 @@ func TestOCRChaos(t *testing.T) { }, PodChaosFailMajorityNodes: { ethereum.New(nil), - chainlink.New(0, defaultOCRSettings), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -115,7 +127,7 @@ func TestOCRChaos(t *testing.T) { }, PodChaosFailMajorityDB: { ethereum.New(nil), - chainlink.New(0, defaultOCRSettings), + chainlinkCfg, chaos.NewFailPods, &chaos.Props{ LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, @@ -164,7 +176,7 @@ func TestOCRChaos(t *testing.T) { if chainClient != nil { chainClient.GasStats().PrintStats() } - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, &config, chainClient) require.NoError(t, err, "Error tearing down environment") }) diff --git a/integration-tests/client/chainlink_k8s.go b/integration-tests/client/chainlink_k8s.go index 27fd956103e..ab4ce341584 100644 --- a/integration-tests/client/chainlink_k8s.go +++ b/integration-tests/client/chainlink_k8s.go @@ -2,7 +2,6 @@ package client import ( - "fmt" "os" "regexp" @@ -42,16 +41,8 @@ func NewChainlinkK8sClient(c *ChainlinkConfig, podName, chartName string) (*Chai // Note: You need to call Run() on the test environment for changes to take effect // Note: This function is not thread safe, call from a single thread func (c *ChainlinkK8sClient) UpgradeVersion(testEnvironment *environment.Environment, newImage, newVersion string) error { - if newVersion == "" { - return fmt.Errorf("new version is empty") - } - if newImage == "" { - newImage = os.Getenv("CHAINLINK_IMAGE") - } log.Info(). Str("Chart Name", c.ChartName). - Str("Old Image", os.Getenv("CHAINLINK_IMAGE")). - Str("Old Version", os.Getenv("CHAINLINK_VERSION")). Str("New Image", newImage). Str("New Version", newVersion). Msg("Upgrading Chainlink Node") diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index ad191d65d16..a15fcfc7aea 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -19,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink/integration-tests/testreporters" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_consumer_benchmark" @@ -42,6 +41,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_transcoder" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_consumer_performance_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_counter_wrapper" diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 1cf3bc05416..2ffd49b8776 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -163,16 +163,10 @@ func (n *ClNode) Restart(cfg *chainlink.Config) error { // UpgradeVersion restarts the cl node with new image and version func (n *ClNode) UpgradeVersion(newImage, newVersion string) error { - if newVersion == "" { - return fmt.Errorf("new version is empty") - } - if newImage == "" { - newImage = os.Getenv("CHAINLINK_IMAGE") - } n.l.Info(). Str("Name", n.ContainerName). - Str("Old Image", os.Getenv("CHAINLINK_IMAGE")). - Str("Old Version", os.Getenv("CHAINLINK_VERSION")). + Str("Old Image", newImage). + Str("Old Version", newVersion). Str("New Image", newImage). Str("New Version", newVersion). Msg("Upgrading Chainlink Node") diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 4ffa8cb3a88..cbe2ed2d462 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "math/big" - "os" "runtime/debug" "testing" @@ -23,6 +22,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + + core_testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( @@ -149,14 +150,15 @@ func (te *CLClusterTestEnv) StartMockAdapter() error { return te.MockAdapter.StartContainer() } -func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count int, secretsConfig string, opts ...ClNodeOption) error { +// pass config here +func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count int, secretsConfig string, testconfig core_testconfig.GlobalTestConfig, opts ...ClNodeOption) error { if te.Cfg != nil && te.Cfg.ClCluster != nil { te.ClCluster = te.Cfg.ClCluster } else { opts = append(opts, WithSecrets(secretsConfig), WithLogStream(te.LogStream)) te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { - ocrNode, err := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, opts...) + ocrNode, err := NewClNode([]string{te.Network.Name}, *testconfig.GetChainlinkImageConfig().Image, *testconfig.GetChainlinkImageConfig().Version, nodeConfig, opts...) if err != nil { return err } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 336a70ded2b..6195ce22bca 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -22,6 +22,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) type CleanUpType string @@ -52,6 +54,7 @@ type CLTestEnvBuilder struct { chainOptionsFn []ChainOption evmClientNetworkOption []EVMClientNetworkOption ethereumNetwork *test_env.EthereumNetwork + testConfig tc.GlobalTestConfig /* funding */ ETHFunds *big.Float @@ -113,6 +116,11 @@ func (b *CLTestEnvBuilder) WithCLNodes(clNodesCount int) *CLTestEnvBuilder { return b } +func (b *CLTestEnvBuilder) WithTestConfig(cfg tc.GlobalTestConfig) *CLTestEnvBuilder { + b.testConfig = cfg + return b +} + func (b *CLTestEnvBuilder) WithCLNodeOptions(opt ...ClNodeOption) *CLTestEnvBuilder { b.clNodesOpts = append(b.clNodesOpts, opt...) return b @@ -213,6 +221,10 @@ func (b *CLTestEnvBuilder) EVMClientNetworkOptions(opts ...EVMClientNetworkOptio } func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { + if b.testConfig == nil { + return nil, fmt.Errorf("test config must be set") + } + if b.te == nil { var err error b, err = b.WithTestEnv(nil) @@ -227,7 +239,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } if b.hasLogStream { - b.te.LogStream, err = logstream.NewLogStream(b.te.t, nil) + b.te.LogStream, err = logstream.NewLogStream(b.te.t, b.testConfig.GetLoggingConfig()) if err != nil { return nil, err } @@ -273,7 +285,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.l.Info().Str("Absolute path", logPath).Msg("LogStream logs folder location") } - if b.t.Failed() || os.Getenv("TEST_LOG_COLLECT") == "true" { + if b.t.Failed() || *b.testConfig.GetLoggingConfig().TestLogCollect { // we can't do much if this fails, so we just log the error in logstream _ = b.te.LogStream.FlushAndShutdown() b.te.LogStream.PrintLogTargetsLocations() @@ -303,27 +315,26 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { return nil, fmt.Errorf("cannot create nodes with custom config without nonDevNetworks") } - err = b.te.StartClCluster(b.clNodeConfig, b.clNodesCount, b.secretsConfig, b.clNodesOpts...) + err = b.te.StartClCluster(b.clNodeConfig, b.clNodesCount, b.secretsConfig, b.testConfig, b.clNodesOpts...) if err != nil { return nil, err } return b.te, nil } - networkConfig := networks.MustGetSelectedNetworksFromEnv()[0] + networkConfig := networks.MustGetSelectedNetworkConfig(b.testConfig.GetNetworkConfig())[0] var rpcProvider test_env.RpcProvider if b.ethereumNetwork != nil && networkConfig.Simulated { // TODO here we should save the ethereum network config to te.Cfg, but it doesn't exist at this point // in general it seems we have no methods for saving config to file and we only load it from file // but I don't know how that config file is to be created or whether anyone ever done that - var enCfg test_env.EthereumNetwork b.ethereumNetwork.DockerNetworkNames = []string{b.te.Network.Name} networkConfig, rpcProvider, err = b.te.StartEthereumNetwork(b.ethereumNetwork) if err != nil { return nil, err } b.te.RpcProvider = rpcProvider - b.te.PrivateEthereumConfig = &enCfg + b.te.PrivateEthereumConfig = b.ethereumNetwork } if !b.isNonEVM { @@ -387,7 +398,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } } - err := b.te.StartClCluster(cfg, b.clNodesCount, b.secretsConfig, b.clNodesOpts...) + err := b.te.StartClCluster(cfg, b.clNodesCount, b.secretsConfig, b.testConfig, b.clNodesOpts...) if err != nil { return nil, err } diff --git a/integration-tests/example.env b/integration-tests/example.env index bbc76bb0c09..35db6263644 100644 --- a/integration-tests/example.env +++ b/integration-tests/example.env @@ -2,11 +2,8 @@ # `source ./integration-tests/.env` ########## General Test Settings ########## -export CHAINLINK_IMAGE="public.ecr.aws/chainlink/chainlink" # Image repo to pull the Chainlink image from -export CHAINLINK_VERSION="2.4.0" # Version of the Chainlink image to pull export CHAINLINK_ENV_USER="Satoshi-Nakamoto" # Name of the person running the tests (change to your own) export TEST_LOG_LEVEL="info" # info | debug | trace -export TEST_LOG_COLLECT="false" # true | false, whether to collect the test logs after the test is complete, this will always be done if the test fails, regardless of this setting ########## Soak/Chaos/Load Test Specific Settings ########## # Remote Runner @@ -19,52 +16,7 @@ export SLACK_API_KEY="xoxb-example-key" # API key used to report soak test resul export SLACK_CHANNEL="C000000000" # Channel ID for the slack bot to post test results export SLACK_USER="U000000000" # User ID of the person running the soak tests to properly notify them -# OCR Soak Test Settings -export OCR_TEST_DURATION="15m" # For an OCRv1 soak test, how long to run the test for -export OCR_CHAINLINK_NODE_FUNDING=".01" # For an OCRv1 soak test, how much ETH to send to each node -export OCR_TIME_BETWEEN_ROUNDS="1m" # For an OCRv1 soak test, how long to wait between starting new rounds - -# Node Version Upgrade Settings -export TEST_UPGRADE_IMAGE="" # Image path to use for the node version upgrade test, likely empty -export TEST_UPGRADE_VERSION="2.0.0" # Version of the Chainlink image to upgrade to for upgrade tests. Should be newer than the current version - ########## Network Settings ########## - -# If running on a live network, you must set the following values -# Select a pre-defined network(s) -export SELECTED_NETWORKS="SIMULATED" -# General private values that will be retrieved when running on non-simulated networks -export EVM_URLS="wss://evm.url,wss://other.url" # Comma-sparated list of websocket URLs to use when running on live networks -export EVM_HTTP_URLS="https://evm.url,https://other.url" # Comma-sparated list of HTTP URLs to use when running on live networks -export EVM_KEYS="private,funding,keys" # Comma-separated list of private keys to use when running on live networks - -# Specific private values retrieved when running on specified chains -# Will override the general values above if the SELECTED_NETWORKS contains the network name -# Goerli -export GOERLI_URLS="wss://goerli.io/ws" -export GOERLI_HTTP_URLS="http://goerli.io/ws" -export GOERLI_KEYS="goerli,funding,keys" -# Sepolia -export SEPOLIA_URLS="wss://sepolia.io/ws" -export SEPOLIA_HTTP_URLS="http://sepolia.io/ws" -export SEPOLIA_KEYS="sepolia,funding,keys" -# Klaytn Baobab -export KLAYTN_BAOBAB_URLS="wss://klaytn.io/ws" -export KLAYTN_BAOBAB_HTTP_URLS="http://klaytn.io/ws" -export KLAYTN_BAOBAB_KEYS="klaytn,funding,keys" -# Metis Stardust -export METIS_STARDUST_URLS="wss://metis.io/ws" -export METIS_STARDUST_HTTP_URLS="http://metis.io/ws" -export METIS_STARDUST_KEYS="metis,funding,keys" -# Arbitrum Goerli -export ARBITRUM_GOERLI_URLS="wss://arbitrum.io/ws" -export ARBITRUM_GOERLI_HTTP_URLS="http://arbitrum.io/ws" -export ARBITRUM_GOERLI_KEYS="arbitrum,funding,keys" -# Optimism Goerli -export OPTIMISM_GOERLI_URLS="wss://optimism.io/ws" -export OPTIMISM_GOERLI_HTTP_URLS="http://optimism.io/ws" -export OPTIMISM_GOERLI_KEYS="optimism,funding,keys" - # General EVM Settings, used only for quick prototyping when using GENERAL as the SELECTED_NETWORK export EVM_NAME="General EVM" export EVM_CHAIN_ID="1" diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 064a489770b..9fe026cdd97 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -7,13 +7,13 @@ replace github.com/smartcontractkit/chainlink/v2 => ../ require ( github.com/K-Phoen/grabana v0.21.17 + github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.13.8 github.com/go-resty/resty/v2 v2.7.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.4.0 github.com/jmoiron/sqlx v1.3.5 - github.com/kelseyhightower/envconfig v1.4.0 github.com/lib/pq v1.10.9 github.com/manifoldco/promptui v0.9.0 github.com/onsi/gomega v1.30.0 @@ -25,20 +25,22 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 - github.com/smartcontractkit/chainlink-testing-framework v1.22.6 + github.com/smartcontractkit/chainlink-testing-framework v1.23.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 - github.com/smartcontractkit/wasp v0.4.0 + github.com/smartcontractkit/wasp v0.4.1 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.4 + github.com/test-go/testify v1.1.4 github.com/testcontainers/testcontainers-go v0.23.0 github.com/umbracle/ethgo v0.1.3 go.dedis.ch/kyber/v3 v3.1.0 go.uber.org/ratelimit v0.2.0 go.uber.org/zap v1.26.0 golang.org/x/sync v0.5.0 + golang.org/x/text v0.14.0 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -46,7 +48,6 @@ require ( exclude github.com/hashicorp/consul v1.2.1 replace ( - // Required until https://github.com/testcontainers/testcontainers-go/pull/1971 is merged github.com/testcontainers/testcontainers-go => github.com/Tofel/testcontainers-go v0.0.0-20231130110817-e6fbf9498b56 // Pin K8s versions as their updates are highly disruptive and go mod keeps wanting to update them k8s.io/api => k8s.io/api v0.25.11 @@ -284,6 +285,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -436,7 +438,6 @@ require ( golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.16.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect @@ -485,4 +486,7 @@ replace ( // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f + + // type func(a Label, b Label) bool of func(a, b Label) bool {…} does not match inferred type func(a Label, b Label) int for func(a E, b E) int + github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 ) diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 10671c432a3..95d294e611f 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -81,19 +81,31 @@ github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 h1:9kDVnTz3vbfweTqAUmk/a/pH5pWFCHtvRpHYC0G/dcA= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 h1:bWh0Z2rOEDfB/ywv/l0iHN1JgyazE6kW/aIA89+CEK0= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= @@ -204,6 +216,8 @@ github.com/aws/jsii-runtime-go v1.75.0/go.mod h1:TKCyrtM0pygEPo4rDZzbMSDNCDNTSYS github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -418,8 +432,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/godo v1.104.1 h1:SZNxjAsskM/su0YW9P8Wx3gU0W1Z13b6tZlYNpl5BnA= -github.com/digitalocean/godo v1.104.1/go.mod h1:VAI/L5YDzMuPRU01lEEUSQ/sp5Z//1HnnFv/RBTEdbg= +github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= +github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -794,8 +808,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -805,15 +819,15 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= -github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo= +github.com/gophercloud/gophercloud v1.5.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -939,8 +953,8 @@ github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c h1:Nc3Mt2BAnq0/VoLEntF/nipX+K1S7pG+RgwiitSv6v0= -github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e h1:sr4lujmn9heD030xx/Pd4B/JSmvRhFzuotNXaaV0WLs= +github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -950,8 +964,8 @@ github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7H github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= -github.com/hetznercloud/hcloud-go/v2 v2.4.0 h1:MqlAE+w125PLvJRCpAJmEwrIxoVdUdOyuFUhE/Ukbok= -github.com/hetznercloud/hcloud-go/v2 v2.4.0/go.mod h1:l7fA5xsncFBzQTyw29/dw5Yr88yEGKKdc6BHf24ONS0= +github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g= +github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -981,8 +995,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ionos-cloud/sdk-go/v6 v6.1.9 h1:Iq3VIXzeEbc8EbButuACgfLMiY5TPVWUPNrF+Vsddo4= -github.com/ionos-cloud/sdk-go/v6 v6.1.9/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0= +github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= @@ -1133,8 +1147,8 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/linode/linodego v1.23.0 h1:s0ReCZtuN9Z1IoUN9w1RLeYO1dMZUGPwOQ/IBFsBHtU= -github.com/linode/linodego v1.23.0/go.mod h1:0U7wj/UQOqBNbKv1FYTXiBUXueR8DY4HvIotwE0ENgg= +github.com/linode/linodego v1.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw= +github.com/linode/linodego v1.19.0/go.mod h1:XZFR+yJ9mm2kwf6itZ6SCpu+6w3KnIevV0Uu5HNWJgQ= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -1317,8 +1331,8 @@ github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= -github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= +github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= +github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1395,8 +1409,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.48.1 h1:CTszphSNTXkuCG6O0IfpKdHcJkvvnAAE1GbELKS+NFk= -github.com/prometheus/prometheus v0.48.1/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= +github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 h1:6ksZ7t1hNOzGPPs8DK7SvXQf6UfWzi+W5Z7PCBl8gx4= +github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510/go.mod h1:UC0TwJiF90m2T3iYPQBKnGu8gv3s55dF/EgpTq8gyvo= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw= github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= @@ -1444,8 +1458,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 h1:yWfiTPwYxB0l5fGMhl/G+liULugVIHD9AU77iNLrURQ= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20 h1:a9hSJdJcd16e0HoMsnFvaHvxB3pxSD+SC7+CISp7xY0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= @@ -1498,8 +1512,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8b github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= -github.com/smartcontractkit/chainlink-testing-framework v1.22.6 h1:5kWMlo99RY/ys4EWGMPsEg1sfY67f5YQogI8PohdRvw= -github.com/smartcontractkit/chainlink-testing-framework v1.22.6/go.mod h1:FBRC6elqaqO8jiMZMfa3UKrvwzZhMGUtYqVTGYmfFrA= +github.com/smartcontractkit/chainlink-testing-framework v1.23.0 h1:x9xDkPCgukjU0GStjIb2x7qlLytycYujWKVLcG1YQow= +github.com/smartcontractkit/chainlink-testing-framework v1.23.0/go.mod h1:StIOdxvwd8AMO6xuBtmD6FQfJXktEn/mJJEr7728BTc= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= @@ -1512,8 +1526,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wasp v0.4.0 h1:N8yPxlBvoJiyE6HaDkTkwRbuOHkGgQFGEHbw36oh4jA= -github.com/smartcontractkit/wasp v0.4.0/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4= +github.com/smartcontractkit/wasp v0.4.1 h1:qgIx2s+eCwH0OaBKaHEAHUQ1Z47bAgDu+ICS9IOqvGQ= +github.com/smartcontractkit/wasp v0.4.1/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4= github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb6vPbtRQ= github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= diff --git a/integration-tests/k8s/connect.go b/integration-tests/k8s/connect.go index 34eafc42e24..db9ccecb174 100644 --- a/integration-tests/k8s/connect.go +++ b/integration-tests/k8s/connect.go @@ -54,7 +54,7 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC URLs: []string{cfg.NetworkWSURL}, HTTPURLs: []string{cfg.NetworkHTTPURL}, ChainlinkTransactionLimit: 500000, - Timeout: blockchain.JSONStrDuration{Duration: 2 * time.Minute}, + Timeout: blockchain.StrDuration{Duration: 2 * time.Minute}, MinimumConfirmations: 1, GasEstimationBuffer: 10000, } diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index d2bca2e670c..49758477767 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -2,11 +2,9 @@ package automationv2_1 import ( "context" - "encoding/base64" "fmt" "math" "math/big" - "os" "strconv" "strings" "testing" @@ -26,18 +24,21 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + a_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" @@ -117,105 +118,50 @@ ListenAddresses = ["0.0.0.0:6690"]` } ) -var ( - numberofNodes, _ = strconv.Atoi(getEnv("NUMBEROFNODES", "6")) // Number of nodes in the DON - duration, _ = strconv.Atoi(getEnv("DURATION", "900")) // Test duration in seconds - blockTime, _ = strconv.Atoi(getEnv("BLOCKTIME", "1")) // Block time in seconds for geth simulated dev network - nodeFunding, _ = strconv.ParseFloat(getEnv("NODEFUNDING", "100"), 64) // Amount of native to fund each node with - - specType = getEnv("SPECTYPE", "minimum") // minimum, recommended, local specs for the test - logLevel = getEnv("LOGLEVEL", "info") // log level for the chainlink nodes - pyroscope, _ = strconv.ParseBool(getEnv("PYROSCOPE", "false")) // enable pyroscope for the chainlink nodes - prometheus, _ = strconv.ParseBool(getEnv("PROMETHEUS", "false")) // enable prometheus for the chainlink nodes - configOverride = os.Getenv("CONFIG_OVERRIDE") // config overrides for the load config -) - -type Load struct { - NumberOfEvents int64 `toml:",omitempty"` - NumberOfSpamMatchingEvents int64 `toml:",omitempty"` - NumberOfSpamNonMatchingEvents int64 `toml:",omitempty"` - CheckBurnAmount *big.Int `toml:",omitempty"` - PerformBurnAmount *big.Int `toml:",omitempty"` - UpkeepGasLimit uint32 `toml:",omitempty"` - NumberOfUpkeeps int `toml:",omitempty"` - SharedTrigger bool `toml:",omitempty"` -} - -type LoadConfig struct { - Load []Load `toml:",omitempty"` -} - -var defaultLoadConfig = LoadConfig{ - Load: []Load{ - { - NumberOfEvents: 1, - NumberOfSpamMatchingEvents: 1, - NumberOfSpamNonMatchingEvents: 0, - CheckBurnAmount: big.NewInt(0), - PerformBurnAmount: big.NewInt(0), - UpkeepGasLimit: 1_000_000, - NumberOfUpkeeps: 5, - SharedTrigger: false, - }, - { - NumberOfEvents: 1, - NumberOfSpamMatchingEvents: 0, - NumberOfSpamNonMatchingEvents: 1, - CheckBurnAmount: big.NewInt(0), - PerformBurnAmount: big.NewInt(0), - UpkeepGasLimit: 1_000_000, - NumberOfUpkeeps: 5, - SharedTrigger: true, - }}, -} - func TestLogTrigger(t *testing.T) { ctx := tests.Context(t) l := logging.GetTestLogger(t) - loadConfig := &LoadConfig{} - if configOverride != "" { - d, err := base64.StdEncoding.DecodeString(configOverride) - require.NoError(t, err, "Error decoding config override") - l.Info().Str("CONFIG_OVERRIDE", configOverride).Bytes("Decoded value", d).Msg("Decoding config override") - err = toml.Unmarshal(d, &loadConfig) - require.NoError(t, err, "Error unmarshalling config override") - } else { - loadConfig = &defaultLoadConfig + loadedTestConfig, err := tc.GetConfig("Load", tc.Automation) + if err != nil { + t.Fatal(err) } - loadConfigBytes, err := toml.Marshal(loadConfig) - require.NoError(t, err, "Error marshalling load config") + version := *loadedTestConfig.ChainlinkImage.Version + image := *loadedTestConfig.ChainlinkImage.Image l.Info().Msg("Starting automation v2.1 log trigger load test") - l.Info().Str("TEST_INPUTS", os.Getenv("TEST_INPUTS")).Int("Number of Nodes", numberofNodes). - Int("Duration", duration). - Int("Block Time", blockTime). - Str("Spec Type", specType). - Str("Log Level", logLevel). - Str("Image", os.Getenv(config.EnvVarCLImage)). - Str("Tag", os.Getenv(config.EnvVarCLTag)). - Bytes("Load Config", loadConfigBytes). + l.Info(). + Int("Number of Nodes", *loadedTestConfig.Automation.General.NumberOfNodes). + Int("Duration", *loadedTestConfig.Automation.General.Duration). + Int("Block Time", *loadedTestConfig.Automation.General.BlockTime). + Str("Spec Type", *loadedTestConfig.Automation.General.SpecType). + Str("Log Level", *loadedTestConfig.Automation.General.ChainlinkNodeLogLevel). + Str("Image", image). + Str("Tag", version). Msg("Test Config") testConfigFormat := `Number of Nodes: %d -Duration: %d -Block Time: %d -Spec Type: %s -Log Level: %s -Image: %s -Tag: %s - -Load Config: -%s` - - testConfig := fmt.Sprintf(testConfigFormat, numberofNodes, duration, - blockTime, specType, logLevel, os.Getenv(config.EnvVarCLImage), os.Getenv(config.EnvVarCLTag), string(loadConfigBytes)) + Duration: %d + Block Time: %d + Spec Type: %s + Log Level: %s + Image: %s + Tag: %s + + Load Config: + %s` + + prettyLoadConfig, err := toml.Marshal(loadedTestConfig.Automation.Load) + require.NoError(t, err, "Error marshalling load config") + + testConfig := fmt.Sprintf(testConfigFormat, *loadedTestConfig.Automation.General.NumberOfNodes, *loadedTestConfig.Automation.General.Duration, + *loadedTestConfig.Automation.General.BlockTime, *loadedTestConfig.Automation.General.SpecType, *loadedTestConfig.Automation.General.ChainlinkNodeLogLevel, image, version, string(prettyLoadConfig)) l.Info().Str("testConfig", testConfig).Msg("Test Config") - testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] + testNetwork := networks.MustGetSelectedNetworkConfig(loadedTestConfig.Network)[0] testType := "load" - loadDuration := time.Duration(duration) * time.Second + loadDuration := time.Duration(*loadedTestConfig.Automation.General.Duration) * time.Second automationDefaultLinkFunds := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(10000))) //10000 LINK registrySettings := &contracts.KeeperRegistrySettings{ @@ -245,28 +191,6 @@ Load Config: PreventPodEviction: true, }) - if testEnvironment.WillUseRemoteRunner() { - key := "TEST_INPUTS" - err := os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) - require.NoError(t, err, "failed to set the environment variable TEST_INPUTS for remote runner") - - key = config.EnvVarPyroscopeServer - err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) - require.NoError(t, err, "failed to set the environment variable PYROSCOPE_SERVER for remote runner") - - key = config.EnvVarPyroscopeKey - err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) - require.NoError(t, err, "failed to set the environment variable PYROSCOPE_KEY for remote runner") - - key = "GRAFANA_DASHBOARD_URL" - err = os.Setenv(fmt.Sprintf("TEST_%s", key), getEnv(key, "")) - require.NoError(t, err, "failed to set the environment variable GRAFANA_DASHBOARD_URL for remote runner") - - key = "CONFIG_OVERRIDE" - err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) - require.NoError(t, err, "failed to set the environment variable CONFIG_OVERRIDE for remote runner") - } - testEnvironment. AddHelm(ethereum.New(ðereum.Props{ NetworkName: testNetwork.Name, @@ -275,7 +199,7 @@ Load Config: Values: map[string]interface{}{ "resources": gethNodeSpec, "geth": map[string]interface{}{ - "blocktime": blockTime, + "blocktime": *loadedTestConfig.Automation.General.BlockTime, "capacity": "20Gi", }, }, @@ -293,7 +217,7 @@ Load Config: dbSpec = minimumDbSpec ) - switch specType { + switch *loadedTestConfig.Automation.General.SpecType { case "recommended": nodeSpec = recNodeSpec dbSpec = recDbSpec @@ -305,28 +229,34 @@ Load Config: } - if !pyroscope { - err = os.Setenv(config.EnvVarPyroscopeServer, "") - require.NoError(t, err, "Error setting pyroscope server env var") + if *loadedTestConfig.Pyroscope.Enabled { + loadedTestConfig.Pyroscope.Environment = &testEnvironment.Cfg.Namespace } - err = os.Setenv(config.EnvVarPyroscopeEnvironment, testEnvironment.Cfg.Namespace) - require.NoError(t, err, "Error setting pyroscope environment env var") + numberOfUpkeeps := *loadedTestConfig.Automation.General.NumberOfNodes - for i := 0; i < numberofNodes+1; i++ { // +1 for the OCR boot node + for i := 0; i < numberOfUpkeeps+1; i++ { // +1 for the OCR boot node var nodeTOML string if i == 1 || i == 3 { - nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"%s\"", baseTOML, logLevel) + nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"%s\"", baseTOML, *loadedTestConfig.Automation.General.ChainlinkNodeLogLevel) } else { nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"info\"", baseTOML) } - nodeTOML = networks.AddNetworksConfig(nodeTOML, testNetwork) - testEnvironment.AddHelm(chainlink.New(i, map[string]any{ + nodeTOML = networks.AddNetworksConfig(nodeTOML, loadedTestConfig.Pyroscope, testNetwork) + + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(loadedTestConfig.ChainlinkImage, target) + ctf_config.MightConfigOverridePyroscopeKey(loadedTestConfig.Pyroscope, target) + } + + cd := chainlink.NewWithOverride(i, map[string]any{ "toml": nodeTOML, "chainlink": nodeSpec, "db": dbSpec, - "prometheus": prometheus, - })) + "prometheus": *loadedTestConfig.Automation.General.UsePrometheus, + }, loadedTestConfig.ChainlinkImage, overrideFn) + + testEnvironment.AddHelm(cd) } err = testEnvironment.Run() @@ -383,7 +313,7 @@ Load Config: a.SetupAutomationDeployment(t) - err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(nodeFunding), 0) + err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(*loadedTestConfig.Common.ChainlinkNodeFunding), 0) require.NoError(t, err, "Error funding chainlink nodes") consumerContracts := make([]contracts.KeeperConsumer, 0) @@ -406,21 +336,22 @@ Load Config: } upkeepConfigs := make([]automationv2.UpkeepConfig, 0) - loadConfigs := make([]Load, 0) + loadConfigs := make([]a_config.Load, 0) cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) require.NoError(t, err, "Error building concurrent chain client") - for _, u := range loadConfig.Load { - for i := 0; i < u.NumberOfUpkeeps; i++ { + for _, u := range loadedTestConfig.Automation.Load { + for i := 0; i < *u.NumberOfUpkeeps; i++ { consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() require.NoError(t, err, "Error deploying automation consumer contract") consumerContracts = append(consumerContracts, consumerContract) l.Debug(). Str("Contract Address", consumerContract.Address()). Int("Number", i+1). - Int("Out Of", u.NumberOfUpkeeps). + Int("Out Of", *u.NumberOfUpkeeps). Msg("Deployed Automation Log Trigger Consumer Contract") - loadCfg := Load{ + + loadCfg := a_config.Load{ NumberOfEvents: u.NumberOfEvents, NumberOfSpamMatchingEvents: u.NumberOfSpamMatchingEvents, NumberOfSpamNonMatchingEvents: u.NumberOfSpamNonMatchingEvents, @@ -429,9 +360,10 @@ Load Config: UpkeepGasLimit: u.UpkeepGasLimit, SharedTrigger: u.SharedTrigger, } + loadConfigs = append(loadConfigs, loadCfg) - if u.SharedTrigger && i > 0 { + if *u.SharedTrigger && i > 0 { triggerAddresses = append(triggerAddresses, triggerAddresses[len(triggerAddresses)-1]) continue } @@ -442,7 +374,7 @@ Load Config: l.Debug(). Str("Contract Address", triggerContract.Address().Hex()). Int("Number", i+1). - Int("Out Of", u.NumberOfUpkeeps). + Int("Out Of", *u.NumberOfUpkeeps). Msg("Deployed Automation Log Trigger Emitter Contract") } err = chainClient.WaitForEvents() @@ -476,7 +408,7 @@ Load Config: UpkeepName: fmt.Sprintf("LogTriggerUpkeep-%d", i), EncryptedEmail: []byte("test@mail.com"), UpkeepContract: common.HexToAddress(consumerContract.Address()), - GasLimit: loadConfigs[i].UpkeepGasLimit, + GasLimit: *loadConfigs[i].UpkeepGasLimit, AdminAddress: common.HexToAddress(chainClient.GetDefaultWallet().Address()), TriggerType: uint8(1), CheckData: encodedCheckDataStruct, @@ -514,11 +446,11 @@ Load Config: for i, triggerContract := range triggerContracts { c := LogTriggerConfig{ Address: triggerContract.Address().String(), - NumberOfEvents: loadConfigs[i].NumberOfEvents, - NumberOfSpamMatchingEvents: loadConfigs[i].NumberOfSpamMatchingEvents, - NumberOfSpamNonMatchingEvents: loadConfigs[i].NumberOfSpamNonMatchingEvents, + NumberOfEvents: int64(*loadConfigs[i].NumberOfEvents), + NumberOfSpamMatchingEvents: int64(*loadConfigs[i].NumberOfSpamMatchingEvents), + NumberOfSpamNonMatchingEvents: int64(*loadConfigs[i].NumberOfSpamNonMatchingEvents), } - numberOfEventsEmittedPerSec = numberOfEventsEmittedPerSec + loadConfigs[i].NumberOfEvents + numberOfEventsEmittedPerSec = numberOfEventsEmittedPerSec + int64(*loadConfigs[i].NumberOfEvents) configs = append(configs, c) } @@ -529,7 +461,7 @@ Load Config: Str("Duration", testSetupDuration.String()). Msg("Test setup ended") - ts, err := sendSlackNotification("Started", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + ts, err := sendSlackNotification("Started", l, &loadedTestConfig, testEnvironment.Cfg.Namespace, strconv.Itoa(*loadedTestConfig.Automation.General.NumberOfNodes), strconv.FormatInt(startTimeTestSetup.UnixMilli(), 10), "now", []slack.Block{extraBlockWithText("\bTest Config\b\n```" + testConfig + "```")}, slack.MsgOptionBlocks()) if err != nil { @@ -770,7 +702,7 @@ Test Duration: %s` avgR, medianR, ninetyPctR, ninetyNinePctR, maximumR, len(allUpkeepDelays), len(allUpkeepDelaysFast), len(allUpkeepDelaysRecovery), numberOfEventsEmitted, eventsMissed, percentMissed, testExDuration.String()) - _, err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + _, err = sendSlackNotification("Finished", l, &loadedTestConfig, testEnvironment.Cfg.Namespace, strconv.Itoa(*loadedTestConfig.Automation.General.NumberOfNodes), strconv.FormatInt(startTimeTestSetup.UnixMilli(), 10), strconv.FormatInt(time.Now().UnixMilli(), 10), []slack.Block{extraBlockWithText("\bTest Report\b\n```" + testReport + "```")}, slack.MsgOptionTS(ts)) if err != nil { @@ -778,7 +710,7 @@ Test Duration: %s` } t.Cleanup(func() { - if err = actions.TeardownRemoteSuite(t, testEnvironment.Cfg.Namespace, chainlinkNodes, nil, chainClient); err != nil { + if err = actions.TeardownRemoteSuite(t, testEnvironment.Cfg.Namespace, chainlinkNodes, nil, &loadedTestConfig, chainClient); err != nil { l.Error().Err(err).Msg("Error when tearing down remote suite") } }) diff --git a/integration-tests/load/automationv2_1/helpers.go b/integration-tests/load/automationv2_1/helpers.go index 68ac909af90..bd5bfe58667 100644 --- a/integration-tests/load/automationv2_1/helpers.go +++ b/integration-tests/load/automationv2_1/helpers.go @@ -2,47 +2,37 @@ package automationv2_1 import ( "fmt" - "os" - "strings" "github.com/rs/zerolog" "github.com/slack-go/slack" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) -func getEnv(key, fallback string) string { - if inputs, ok := os.LookupEnv("TEST_INPUTS"); ok { - values := strings.Split(inputs, ",") - for _, value := range values { - if strings.Contains(value, key) { - return strings.Split(value, "=")[1] - } - } - } - return fallback -} - func extraBlockWithText(text string) slack.Block { return slack.NewSectionBlock(slack.NewTextBlockObject( "mrkdwn", text, false, false), nil, nil) } -func sendSlackNotification(header string, l zerolog.Logger, namespace string, numberOfNodes, +func sendSlackNotification(header string, l zerolog.Logger, config *tc.TestConfig, namespace string, numberOfNodes, startingTime string, endingTime string, extraBlocks []slack.Block, msgOption slack.MsgOption) (string, error) { slackClient := slack.New(reportModel.SlackAPIKey) headerText := ":chainlink-keepers: Automation Load Test " + header + " :white_check_mark:" - formattedDashboardUrl := fmt.Sprintf("%s?orgId=1&from=%s&to=%s&var-namespace=%s&var-number_of_nodes=%s", testreporters.DashboardUrl, startingTime, endingTime, namespace, numberOfNodes) - l.Info().Str("Dashboard", formattedDashboardUrl).Msg("Dashboard URL") + grafanaUrl, err := config.GetGrafanaBaseURL() + if err != nil { + return "", err + } - pyroscopeServer := os.Getenv(config.EnvVarPyroscopeServer) - pyroscopeEnvironment := os.Getenv(config.EnvVarPyroscopeEnvironment) + dashboardUrl, err := config.GetGrafanaDashboardURL() + if err != nil { + return "", err + } - formattedPyroscopeUrl := fmt.Sprintf("%s/?query=chainlink-node.cpu{Environment=\"%s\"}&from=%s&to=%s", pyroscopeServer, pyroscopeEnvironment, startingTime, endingTime) + formattedDashboardUrl := fmt.Sprintf("%s%s?orgId=1&from=%s&to=%s&var-namespace=%s&var-number_of_nodes=%s", grafanaUrl, dashboardUrl, startingTime, endingTime, namespace, numberOfNodes) + l.Info().Str("Dashboard", formattedDashboardUrl).Msg("Dashboard URL") var notificationBlocks []slack.Block @@ -51,7 +41,11 @@ func sendSlackNotification(header string, l zerolog.Logger, namespace string, nu notificationBlocks = append(notificationBlocks, slack.NewContextBlock("context_block", slack.NewTextBlockObject("plain_text", namespace, false, false))) notificationBlocks = append(notificationBlocks, slack.NewDividerBlock()) - if pyroscopeServer != "" { + if *config.Pyroscope.Enabled { + pyroscopeServer := *config.Pyroscope.ServerUrl + pyroscopeEnvironment := *config.Pyroscope.Environment + + formattedPyroscopeUrl := fmt.Sprintf("%s/?query=chainlink-node.cpu{Environment=\"%s\"}&from=%s&to=%s", pyroscopeServer, pyroscopeEnvironment, startingTime, endingTime) l.Info().Str("Pyroscope", formattedPyroscopeUrl).Msg("Dashboard URL") notificationBlocks = append(notificationBlocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("<%s|Pyroscope>", diff --git a/integration-tests/load/functions/config.go b/integration-tests/load/functions/config.go deleted file mode 100644 index d34e8a9f1eb..00000000000 --- a/integration-tests/load/functions/config.go +++ /dev/null @@ -1,125 +0,0 @@ -package loadfunctions - -import ( - "fmt" - "math/big" - "os" - - "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog/log" - - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" -) - -const ( - DefaultConfigFilename = "config.toml" - - ErrReadPerfConfig = "failed to read TOML config for performance tests" - ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" -) - -type PerformanceConfig struct { - Soak *Soak `toml:"Soak"` - SecretsSoak *SecretsSoak `toml:"SecretsSoak"` - RealSoak *RealSoak `toml:"RealSoak"` - Stress *Stress `toml:"Stress"` - SecretsStress *SecretsStress `toml:"SecretsStress"` - RealStress *RealStress `toml:"RealStress"` - GatewayListSoak *GatewayListSoak `toml:"GatewayListSoak"` - GatewaySetSoak *GatewaySetSoak `toml:"GatewaySetSoak"` - Common *Common `toml:"Common"` - MumbaiPrivateKey string -} - -type Common struct { - Funding - LINKTokenAddr string `toml:"link_token_addr"` - Coordinator string `toml:"coordinator_addr"` - Router string `toml:"router_addr"` - LoadTestClient string `toml:"client_addr"` - SubscriptionID uint64 `toml:"subscription_id"` - DONID string `toml:"don_id"` - GatewayURL string `toml:"gateway_url"` - Receiver string `toml:"receiver"` - FunctionsCallPayloadHTTP string `toml:"functions_call_payload_http"` - FunctionsCallPayloadWithSecrets string `toml:"functions_call_payload_with_secrets"` - FunctionsCallPayloadReal string `toml:"functions_call_payload_real"` - SecretsSlotID uint8 `toml:"secrets_slot_id"` - SecretsVersionID uint64 `toml:"secrets_version_id"` - // Secrets these are for CI secrets - Secrets string `toml:"secrets"` -} - -type Funding struct { - NodeFunds *big.Float `toml:"node_funds"` - SubFunds *big.Int `toml:"sub_funds"` -} - -type Soak struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type SecretsSoak struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type RealSoak struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type Stress struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type SecretsStress struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type RealStress struct { - RPS int64 `toml:"rps"` - RequestsPerCall uint32 `toml:"requests_per_call"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type GatewayListSoak struct { - RPS int64 `toml:"rps"` - Duration *commonconfig.Duration `toml:"duration"` -} - -type GatewaySetSoak struct { - RPS int64 `toml:"rps"` - Duration *commonconfig.Duration `toml:"duration"` -} - -func ReadConfig() (*PerformanceConfig, error) { - var cfg *PerformanceConfig - d, err := os.ReadFile(DefaultConfigFilename) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - err = toml.Unmarshal(d, &cfg) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) - } - log.Debug().Interface("PerformanceConfig", cfg).Msg("Parsed performance config") - mpk := os.Getenv("MUMBAI_KEYS") - murls := os.Getenv("MUMBAI_URLS") - snet := os.Getenv("SELECTED_NETWORKS") - if mpk == "" || murls == "" || snet == "" { - return nil, fmt.Errorf( - "ensure variables are set:\nMUMBAI_KEYS variable, private keys, comma separated\nSELECTED_NETWORKS=MUMBAI\nMUMBAI_URLS variable, websocket urls, comma separated", - ) - } - cfg.MumbaiPrivateKey = mpk - return cfg, nil -} diff --git a/integration-tests/load/functions/functions_test.go b/integration-tests/load/functions/functions_test.go index dc52846d3c9..d3b82cde33b 100644 --- a/integration-tests/load/functions/functions_test.go +++ b/integration-tests/load/functions/functions_test.go @@ -6,12 +6,15 @@ import ( "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestFunctionsLoad(t *testing.T) { - cfg, err := ReadConfig() - require.NoError(t, err) - ft, err := SetupLocalLoadTestEnv(cfg) + generalConfig, err := tc.GetConfig(tc.NoKey, tc.Functions) + require.NoError(t, err, "failed to get config") + + ft, err := SetupLocalLoadTestEnv(&generalConfig, &generalConfig) require.NoError(t, err) ft.EVMClient.ParallelTransactions(false) @@ -20,10 +23,14 @@ func TestFunctionsLoad(t *testing.T) { "commit": "functions_healthcheck", } - MonitorLoadStats(t, ft, labels) + MonitorLoadStats(t, ft, labels, &generalConfig) t.Run("mumbai functions soak test http", func(t *testing.T) { - _, err := wasp.NewProfile(). + config, err := tc.GetConfig("Soak", tc.Functions) + require.NoError(t, err, "failed to get config") + cfg := config.Functions + cfgl := config.Logging.Loki + _, err = wasp.NewProfile(). Add(wasp.NewGenerator(&wasp.Config{ T: t, LoadType: wasp.RPS, @@ -31,28 +38,32 @@ func TestFunctionsLoad(t *testing.T) { RateLimitUnitDuration: 5 * time.Second, CallTimeout: 3 * time.Minute, Schedule: wasp.Plain( - cfg.Soak.RPS, - cfg.Soak.Duration.Duration(), + *cfg.Performance.RPS, + cfg.Performance.Duration.Duration, ), Gun: NewSingleFunctionCallGun( ft, ModeHTTPPayload, - cfg.Soak.RequestsPerCall, - cfg.Common.FunctionsCallPayloadHTTP, - cfg.Common.SecretsSlotID, - cfg.Common.SecretsVersionID, + *cfg.Performance.RequestsPerCall, + *cfg.Common.FunctionsCallPayloadHTTP, + *cfg.Common.SecretsSlotID, + *cfg.Common.SecretsVersionID, []string{}, - cfg.Common.SubscriptionID, - StringToByte32(cfg.Common.DONID), + *cfg.Common.SubscriptionID, + StringToByte32(*cfg.Common.DONID), ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })). Run(true) require.NoError(t, err) }) t.Run("mumbai functions stress test http", func(t *testing.T) { + config, err := tc.GetConfig("Stress", tc.Functions) + require.NoError(t, err, "failed to get config") + cfg := config.Functions + cfgl := config.Logging.Loki _, err = wasp.NewProfile(). Add(wasp.NewGenerator(&wasp.Config{ T: t, @@ -61,29 +72,33 @@ func TestFunctionsLoad(t *testing.T) { RateLimitUnitDuration: 5 * time.Second, CallTimeout: 3 * time.Minute, Schedule: wasp.Plain( - cfg.Stress.RPS, - cfg.Stress.Duration.Duration(), + *cfg.Performance.RPS, + cfg.Performance.Duration.Duration, ), Gun: NewSingleFunctionCallGun( ft, ModeHTTPPayload, - cfg.Stress.RequestsPerCall, - cfg.Common.FunctionsCallPayloadHTTP, - cfg.Common.SecretsSlotID, - cfg.Common.SecretsVersionID, + *cfg.Performance.RequestsPerCall, + *cfg.Common.FunctionsCallPayloadHTTP, + *cfg.Common.SecretsSlotID, + *cfg.Common.SecretsVersionID, []string{}, - cfg.Common.SubscriptionID, - StringToByte32(cfg.Common.DONID), + *cfg.Common.SubscriptionID, + StringToByte32(*cfg.Common.DONID), ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })). Run(true) require.NoError(t, err) }) t.Run("mumbai functions soak test only secrets", func(t *testing.T) { - _, err := wasp.NewProfile(). + config, err := tc.GetConfig("SecretsSoak", tc.Functions) + require.NoError(t, err, "failed to get config") + cfg := config.Functions + cfgl := config.Logging.Loki + _, err = wasp.NewProfile(). Add(wasp.NewGenerator(&wasp.Config{ T: t, LoadType: wasp.RPS, @@ -91,28 +106,32 @@ func TestFunctionsLoad(t *testing.T) { RateLimitUnitDuration: 5 * time.Second, CallTimeout: 3 * time.Minute, Schedule: wasp.Plain( - cfg.SecretsSoak.RPS, - cfg.SecretsSoak.Duration.Duration(), + *cfg.Performance.RPS, + cfg.Performance.Duration.Duration, ), Gun: NewSingleFunctionCallGun( ft, ModeSecretsOnlyPayload, - cfg.SecretsSoak.RequestsPerCall, - cfg.Common.FunctionsCallPayloadWithSecrets, - cfg.Common.SecretsSlotID, - cfg.Common.SecretsVersionID, + *cfg.Performance.RequestsPerCall, + *cfg.Common.FunctionsCallPayloadWithSecrets, + *cfg.Common.SecretsSlotID, + *cfg.Common.SecretsVersionID, []string{}, - cfg.Common.SubscriptionID, - StringToByte32(cfg.Common.DONID), + *cfg.Common.SubscriptionID, + StringToByte32(*cfg.Common.DONID), ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })). Run(true) require.NoError(t, err) }) t.Run("mumbai functions stress test only secrets", func(t *testing.T) { + config, err := tc.GetConfig("SecretsStress", tc.Functions) + require.NoError(t, err, "failed to get config") + cfg := config.Functions + cfgl := config.Logging.Loki _, err = wasp.NewProfile(). Add(wasp.NewGenerator(&wasp.Config{ T: t, @@ -121,29 +140,33 @@ func TestFunctionsLoad(t *testing.T) { RateLimitUnitDuration: 5 * time.Second, CallTimeout: 3 * time.Minute, Schedule: wasp.Plain( - cfg.SecretsStress.RPS, - cfg.SecretsStress.Duration.Duration(), + *cfg.Performance.RPS, + cfg.Performance.Duration.Duration, ), Gun: NewSingleFunctionCallGun( ft, ModeSecretsOnlyPayload, - cfg.SecretsStress.RequestsPerCall, - cfg.Common.FunctionsCallPayloadWithSecrets, - cfg.Common.SecretsSlotID, - cfg.Common.SecretsVersionID, + *cfg.Performance.RequestsPerCall, + *cfg.Common.FunctionsCallPayloadWithSecrets, + *cfg.Common.SecretsSlotID, + *cfg.Common.SecretsVersionID, []string{}, - cfg.Common.SubscriptionID, - StringToByte32(cfg.Common.DONID), + *cfg.Common.SubscriptionID, + StringToByte32(*cfg.Common.DONID), ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })). Run(true) require.NoError(t, err) }) t.Run("mumbai functions soak test real", func(t *testing.T) { - _, err := wasp.NewProfile(). + config, err := tc.GetConfig("RealSoak", tc.Functions) + require.NoError(t, err, "failed to get config") + cfg := config.Functions + cfgl := config.Logging.Loki + _, err = wasp.NewProfile(). Add(wasp.NewGenerator(&wasp.Config{ T: t, LoadType: wasp.RPS, @@ -151,28 +174,32 @@ func TestFunctionsLoad(t *testing.T) { RateLimitUnitDuration: 5 * time.Second, CallTimeout: 3 * time.Minute, Schedule: wasp.Plain( - cfg.RealSoak.RPS, - cfg.RealSoak.Duration.Duration(), + *cfg.Performance.RPS, + cfg.Performance.Duration.Duration, ), Gun: NewSingleFunctionCallGun( ft, ModeReal, - cfg.RealSoak.RequestsPerCall, - cfg.Common.FunctionsCallPayloadReal, - cfg.Common.SecretsSlotID, - cfg.Common.SecretsVersionID, + *cfg.Performance.RequestsPerCall, + *cfg.Common.FunctionsCallPayloadReal, + *cfg.Common.SecretsSlotID, + *cfg.Common.SecretsVersionID, []string{"1", "2", "3", "4"}, - cfg.Common.SubscriptionID, - StringToByte32(cfg.Common.DONID), + *cfg.Common.SubscriptionID, + StringToByte32(*cfg.Common.DONID), ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })). Run(true) require.NoError(t, err) }) t.Run("mumbai functions stress test real", func(t *testing.T) { + config, err := tc.GetConfig("RealStress", tc.Functions) + require.NoError(t, err, "failed to get config") + cfg := config.Functions + cfgl := config.Logging.Loki _, err = wasp.NewProfile(). Add(wasp.NewGenerator(&wasp.Config{ T: t, @@ -181,22 +208,22 @@ func TestFunctionsLoad(t *testing.T) { RateLimitUnitDuration: 5 * time.Second, CallTimeout: 3 * time.Minute, Schedule: wasp.Plain( - cfg.RealStress.RPS, - cfg.RealStress.Duration.Duration(), + *cfg.Performance.RPS, + cfg.Performance.Duration.Duration, ), Gun: NewSingleFunctionCallGun( ft, ModeReal, - cfg.RealStress.RequestsPerCall, - cfg.Common.FunctionsCallPayloadReal, - cfg.Common.SecretsSlotID, - cfg.Common.SecretsVersionID, + *cfg.Performance.RequestsPerCall, + *cfg.Common.FunctionsCallPayloadReal, + *cfg.Common.SecretsSlotID, + *cfg.Common.SecretsVersionID, []string{"1", "2", "3", "4"}, - cfg.Common.SubscriptionID, - StringToByte32(cfg.Common.DONID), + *cfg.Common.SubscriptionID, + StringToByte32(*cfg.Common.DONID), ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })). Run(true) require.NoError(t, err) diff --git a/integration-tests/load/functions/gateway_gun.go b/integration-tests/load/functions/gateway_gun.go index 62fe80ed02c..cc6132e94e7 100644 --- a/integration-tests/load/functions/gateway_gun.go +++ b/integration-tests/load/functions/gateway_gun.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "fmt" "math/rand" - "os" "strconv" "time" @@ -12,12 +11,14 @@ import ( "github.com/rs/zerolog/log" "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/integration-tests/types" ) /* SingleFunctionCallGun is a gun that constantly requests randomness for one feed */ type GatewaySecretsSetGun struct { - Cfg *PerformanceConfig + Cfg types.FunctionsTestConfig Resty *resty.Client SlotID uint Method string @@ -26,7 +27,7 @@ type GatewaySecretsSetGun struct { DONPublicKey []byte } -func NewGatewaySecretsSetGun(cfg *PerformanceConfig, method string, pKey *ecdsa.PrivateKey, tdh2PubKey *tdh2easy.PublicKey, donPubKey []byte) *GatewaySecretsSetGun { +func NewGatewaySecretsSetGun(cfg types.FunctionsTestConfig, method string, pKey *ecdsa.PrivateKey, tdh2PubKey *tdh2easy.PublicKey, donPubKey []byte) *GatewaySecretsSetGun { return &GatewaySecretsSetGun{ Cfg: cfg, Resty: resty.New(), @@ -59,12 +60,18 @@ func callSecretsSet(m *GatewaySecretsSetGun) *wasp.Response { if err != nil { return &wasp.Response{Error: err.Error(), Failed: true} } + network := m.Cfg.GetNetworkConfig().SelectedNetworks[0] + if len(m.Cfg.GetNetworkConfig().WalletKeys[network]) < 1 { + panic(fmt.Sprintf("no wallet keys found for %s", network)) + } + + cfg := m.Cfg.GetFunctionsConfig() _, _, err = UploadS4Secrets(m.Resty, &S4SecretsCfg{ - GatewayURL: m.Cfg.Common.GatewayURL, - PrivateKey: os.Getenv("MUMBAI_KEYS"), + GatewayURL: *cfg.Common.GatewayURL, + PrivateKey: m.Cfg.GetNetworkConfig().WalletKeys[network][0], MessageID: randNum, Method: "secrets_set", - DonID: m.Cfg.Common.DONID, + DonID: *cfg.Common.DONID, S4SetSlotID: randSlot, S4SetVersion: version, S4SetExpirationPeriod: expiration, @@ -81,13 +88,18 @@ func callSecretsList(m *GatewaySecretsSetGun) *wasp.Response { randSlot := uint(rand.Intn(5)) version := uint64(time.Now().UnixNano()) expiration := int64(60 * 60 * 1000) + network := m.Cfg.GetNetworkConfig().SelectedNetworks[0] + if len(m.Cfg.GetNetworkConfig().WalletKeys[network]) < 1 { + panic(fmt.Sprintf("no wallet keys found for %s", network)) + } + cfg := m.Cfg.GetFunctionsConfig() if err := ListS4Secrets(m.Resty, &S4SecretsCfg{ - GatewayURL: fmt.Sprintf(m.Cfg.Common.GatewayURL), - RecieverAddr: m.Cfg.Common.Receiver, - PrivateKey: os.Getenv("MUMBAI_KEYS"), + GatewayURL: *cfg.Common.GatewayURL, + RecieverAddr: *cfg.Common.Receiver, + PrivateKey: m.Cfg.GetNetworkConfig().WalletKeys[network][0], MessageID: randNum, Method: m.Method, - DonID: m.Cfg.Common.DONID, + DonID: *cfg.Common.DONID, S4SetSlotID: randSlot, S4SetVersion: version, S4SetExpirationPeriod: expiration, diff --git a/integration-tests/load/functions/gateway_test.go b/integration-tests/load/functions/gateway_test.go index c8e63f92f2b..be5d148386c 100644 --- a/integration-tests/load/functions/gateway_test.go +++ b/integration-tests/load/functions/gateway_test.go @@ -6,13 +6,17 @@ import ( "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" ) func TestGatewayLoad(t *testing.T) { - cfg, err := ReadConfig() + listConfig, err := tc.GetConfig("GatewayList", tc.Functions) require.NoError(t, err) - ft, err := SetupLocalLoadTestEnv(cfg) + cfgl := listConfig.Logging.Loki + + require.NoError(t, err) + ft, err := SetupLocalLoadTestEnv(&listConfig, &listConfig) require.NoError(t, err) ft.EVMClient.ParallelTransactions(false) @@ -25,36 +29,39 @@ func TestGatewayLoad(t *testing.T) { LoadType: wasp.RPS, GenName: functions.MethodSecretsList, Schedule: wasp.Plain( - cfg.GatewayListSoak.RPS, - cfg.GatewayListSoak.Duration.Duration(), + *listConfig.Functions.Performance.RPS, + listConfig.Functions.Performance.Duration.Duration, ), Gun: NewGatewaySecretsSetGun( - cfg, + &listConfig, functions.MethodSecretsList, ft.EthereumPrivateKey, ft.ThresholdPublicKey, ft.DONPublicKey, ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), } + setConfig, err := tc.GetConfig("GatewaySet", tc.Functions) + require.NoError(t, err) + secretsSetCfg := &wasp.Config{ LoadType: wasp.RPS, GenName: functions.MethodSecretsSet, Schedule: wasp.Plain( - cfg.GatewaySetSoak.RPS, - cfg.GatewaySetSoak.Duration.Duration(), + *setConfig.Functions.Performance.RPS, + setConfig.Functions.Performance.Duration.Duration, ), Gun: NewGatewaySecretsSetGun( - cfg, + &setConfig, functions.MethodSecretsSet, ft.EthereumPrivateKey, ft.ThresholdPublicKey, ft.DONPublicKey, ), Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), } t.Run("gateway secrets list soak test", func(t *testing.T) { diff --git a/integration-tests/load/functions/onchain_monitoring.go b/integration-tests/load/functions/onchain_monitoring.go index c4b4bdb78c0..12a10ce0042 100644 --- a/integration-tests/load/functions/onchain_monitoring.go +++ b/integration-tests/load/functions/onchain_monitoring.go @@ -6,6 +6,8 @@ import ( "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ @@ -23,7 +25,7 @@ type LoadStats struct { Empty uint32 } -func MonitorLoadStats(t *testing.T, ft *FunctionsTest, labels map[string]string) { +func MonitorLoadStats(t *testing.T, ft *FunctionsTest, labels map[string]string, config tc.GlobalTestConfig) { go func() { updatedLabels := make(map[string]string) for k, v := range labels { @@ -32,7 +34,9 @@ func MonitorLoadStats(t *testing.T, ft *FunctionsTest, labels map[string]string) updatedLabels["type"] = LokiTypeLabel updatedLabels["go_test_name"] = t.Name() updatedLabels["gen_name"] = "performance" - lc, err := wasp.NewLokiClient(wasp.NewEnvLokiConfig()) + cfgl := config.GetLoggingConfig().Loki + lokiConfig := wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken) + lc, err := wasp.NewLokiClient(lokiConfig) if err != nil { log.Error().Err(err).Msg(ErrLokiClient) return @@ -46,16 +50,20 @@ func MonitorLoadStats(t *testing.T, ft *FunctionsTest, labels map[string]string) if err != nil { log.Error().Err(err).Msg(ErrMetrics) } - log.Info(). - Hex("LastReqID", []byte(stats.LastRequestID)). - Str("LastResponse", stats.LastResponse). - Str("LastError", stats.LastError). - Uint32("Total", stats.Total). - Uint32("Succeeded", stats.Succeeded). - Uint32("Errored", stats.Errored). - Uint32("Empty", stats.Empty).Msg("On-chain stats for load test client") - if err := lc.HandleStruct(wasp.LabelsMapToModel(updatedLabels), time.Now(), stats); err != nil { - log.Error().Err(err).Msg(ErrLokiPush) + if stats != nil { + log.Info(). + Hex("LastReqID", []byte(stats.LastRequestID)). + Str("LastResponse", stats.LastResponse). + Str("LastError", stats.LastError). + Uint32("Total", stats.Total). + Uint32("Succeeded", stats.Succeeded). + Uint32("Errored", stats.Errored). + Uint32("Empty", stats.Empty).Msg("On-chain stats for load test client") + if err := lc.HandleStruct(wasp.LabelsMapToModel(updatedLabels), time.Now(), stats); err != nil { + log.Error().Err(err).Msg(ErrLokiPush) + } + } else { + log.Warn().Msg("No stats to push to Loki") } } }() diff --git a/integration-tests/load/functions/setup.go b/integration-tests/load/functions/setup.go index e96eac3cf82..e6711907592 100644 --- a/integration-tests/load/functions/setup.go +++ b/integration-tests/load/functions/setup.go @@ -5,7 +5,6 @@ import ( "fmt" "math/big" mrand "math/rand" - "os" "strconv" "time" @@ -18,6 +17,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) @@ -50,8 +51,9 @@ type S4SecretsCfg struct { S4SetPayload string } -func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { - bc, err := blockchain.NewEVMClientFromNetwork(networks.MustGetSelectedNetworksFromEnv()[0], log.Logger) +func SetupLocalLoadTestEnv(globalConfig tc.GlobalTestConfig, functionsConfig types.FunctionsTestConfig) (*FunctionsTest, error) { + selectedNetwork := networks.MustGetSelectedNetworkConfig(globalConfig.GetNetworkConfig())[0] + bc, err := blockchain.NewEVMClientFromNetwork(selectedNetwork, log.Logger) if err != nil { return nil, err } @@ -67,28 +69,31 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { if err != nil { return nil, err } - lt, err := cl.LoadLINKToken(cfg.Common.LINKTokenAddr) + + cfg := functionsConfig.GetFunctionsConfig() + + lt, err := cl.LoadLINKToken(*cfg.Common.LINKTokenAddr) if err != nil { return nil, err } - coord, err := cl.LoadFunctionsCoordinator(cfg.Common.Coordinator) + coord, err := cl.LoadFunctionsCoordinator(*cfg.Common.Coordinator) if err != nil { return nil, err } - router, err := cl.LoadFunctionsRouter(cfg.Common.Router) + router, err := cl.LoadFunctionsRouter(*cfg.Common.Router) if err != nil { return nil, err } var loadTestClient contracts.FunctionsLoadTestClient - if cfg.Common.LoadTestClient != "" { - loadTestClient, err = cl.LoadFunctionsLoadTestClient(cfg.Common.LoadTestClient) + if cfg.Common.LoadTestClient != nil && *cfg.Common.LoadTestClient != "" { + loadTestClient, err = cl.LoadFunctionsLoadTestClient(*cfg.Common.LoadTestClient) } else { - loadTestClient, err = cd.DeployFunctionsLoadTestClient(cfg.Common.Router) + loadTestClient, err = cd.DeployFunctionsLoadTestClient(*cfg.Common.Router) } if err != nil { return nil, err } - if cfg.Common.SubscriptionID == 0 { + if cfg.Common.SubscriptionID == nil { log.Info().Msg("Creating new subscription") subID, err := router.CreateSubscriptionWithConsumer(loadTestClient.Address()) if err != nil { @@ -98,13 +103,13 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { if err != nil { return nil, fmt.Errorf("failed to encode subscription ID for funding: %w", err) } - _, err = lt.TransferAndCall(router.Address(), big.NewInt(0).Mul(cfg.Common.Funding.SubFunds, big.NewInt(1e18)), encodedSubId) + _, err = lt.TransferAndCall(router.Address(), big.NewInt(0).Mul(cfg.Common.SubFunds, big.NewInt(1e18)), encodedSubId) if err != nil { return nil, fmt.Errorf("failed to transferAndCall router, LINK funding: %w", err) } - cfg.Common.SubscriptionID = subID + cfg.Common.SubscriptionID = &subID } - pKey, pubKey, err := parseEthereumPrivateKey(os.Getenv("MUMBAI_KEYS")) + pKey, pubKey, err := parseEthereumPrivateKey(selectedNetwork.PrivateKeys[0]) if err != nil { return nil, fmt.Errorf("failed to load Ethereum private key: %w", err) } @@ -123,17 +128,17 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { return nil, fmt.Errorf("failed to unmarshal tdh2 public key: %w", err) } var encryptedSecrets string - if cfg.Common.Secrets != "" { - encryptedSecrets, err = EncryptS4Secrets(pKey, tdh2pk, donPubKey, cfg.Common.Secrets) + if cfg.Common.Secrets != nil && *cfg.Common.Secrets != "" { + encryptedSecrets, err = EncryptS4Secrets(pKey, tdh2pk, donPubKey, *cfg.Common.Secrets) if err != nil { return nil, fmt.Errorf("failed to generate tdh2 secrets: %w", err) } slotID, slotVersion, err := UploadS4Secrets(resty.New(), &S4SecretsCfg{ - GatewayURL: cfg.Common.GatewayURL, - PrivateKey: cfg.MumbaiPrivateKey, + GatewayURL: *cfg.Common.GatewayURL, + PrivateKey: selectedNetwork.PrivateKeys[0], MessageID: strconv.Itoa(mrand.Intn(100000-1) + 1), Method: "secrets_set", - DonID: cfg.Common.DONID, + DonID: *cfg.Common.DONID, S4SetSlotID: uint(mrand.Intn(5)), S4SetVersion: uint64(time.Now().UnixNano()), S4SetExpirationPeriod: 60 * 60 * 1000, @@ -142,8 +147,8 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { if err != nil { return nil, fmt.Errorf("failed to upload secrets to S4: %w", err) } - cfg.Common.SecretsSlotID = slotID - cfg.Common.SecretsVersionID = slotVersion + cfg.Common.SecretsSlotID = &slotID + cfg.Common.SecretsVersionID = &slotVersion log.Info(). Uint8("SlotID", slotID). Uint64("SlotVersion", slotVersion). diff --git a/integration-tests/load/ocr/config.go b/integration-tests/load/ocr/config.go deleted file mode 100644 index 4b324210b09..00000000000 --- a/integration-tests/load/ocr/config.go +++ /dev/null @@ -1,72 +0,0 @@ -package ocr - -import ( - "encoding/base64" - "fmt" - "os" - - "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog/log" - - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" -) - -const ( - DefaultConfigFilename = "config.toml" - ErrReadPerfConfig = "failed to read TOML config for performance tests" - ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" -) - -type PerformanceConfig struct { - Load *Load `toml:"Load"` - Volume *Volume `toml:"Volume"` - Common *Common `toml:"Common"` -} - -type Common struct { - ETHFunds int `toml:"eth_funds"` -} - -type Load struct { - TestDuration *commonconfig.Duration `toml:"test_duration"` - Rate int64 `toml:"rate"` - RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` - VerificationInterval *commonconfig.Duration `toml:"verification_interval"` - VerificationTimeout *commonconfig.Duration `toml:"verification_timeout"` - EAChangeInterval *commonconfig.Duration `toml:"ea_change_interval"` -} - -type Volume struct { - TestDuration *commonconfig.Duration `toml:"test_duration"` - Rate int64 `toml:"rate"` - VURequestsPerUnit int `toml:"vu_requests_per_unit"` - RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` - VerificationInterval *commonconfig.Duration `toml:"verification_interval"` - VerificationTimeout *commonconfig.Duration `toml:"verification_timeout"` - EAChangeInterval *commonconfig.Duration `toml:"ea_change_interval"` -} - -func ReadConfig() (*PerformanceConfig, error) { - var cfg *PerformanceConfig - rawConfig := os.Getenv("CONFIG") - var d []byte - var err error - if rawConfig == "" { - d, err = os.ReadFile(DefaultConfigFilename) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - } else { - d, err = base64.StdEncoding.DecodeString(rawConfig) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - } - err = toml.Unmarshal(d, &cfg) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) - } - - log.Debug().Interface("Config", cfg).Msg("Parsed config") - return cfg, nil -} diff --git a/integration-tests/load/ocr/config.toml b/integration-tests/load/ocr/config.toml deleted file mode 100644 index df8364b3ee4..00000000000 --- a/integration-tests/load/ocr/config.toml +++ /dev/null @@ -1,20 +0,0 @@ -[Load] -test_duration = "3m" -rate_limit_unit_duration = "1m" -rate = 3 -verification_interval = "5s" -verification_timeout = "3m" -ea_change_interval = "5s" - -[Volume] -test_duration = "3m" -rate_limit_unit_duration = "1m" -vu_requests_per_unit = 10 -rate = 1 -verification_interval = "5s" -verification_timeout = "3m" - -ea_change_interval = "5s" - -[Common] -eth_funds = 3 \ No newline at end of file diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index 49e624ecd89..c6edf9122b9 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink-testing-framework/logging" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/k8s" ) @@ -27,21 +28,25 @@ func TestOCRLoad(t *testing.T) { require.NoError(t, err) ocrInstances, err := SetupFeed(cc, msClient, cd, bootstrapNode, workerNodes, lt) require.NoError(t, err) - cfg, err := ReadConfig() + + config, err := tc.GetConfig("Load", tc.OCR) require.NoError(t, err) - SimulateEAActivity(l, cfg.Load.EAChangeInterval.Duration(), ocrInstances, workerNodes, msClient) + + cfg := config.OCR + cfgl := config.Logging.Loki + SimulateEAActivity(l, cfg.Load.EAChangeInterval.Duration, ocrInstances, workerNodes, msClient) p := wasp.NewProfile() p.Add(wasp.NewGenerator(&wasp.Config{ T: t, GenName: "ocr", LoadType: wasp.RPS, - CallTimeout: cfg.Load.VerificationTimeout.Duration(), - RateLimitUnitDuration: cfg.Load.RateLimitUnitDuration.Duration(), - Schedule: wasp.Plain(cfg.Load.Rate, cfg.Load.TestDuration.Duration()), + CallTimeout: cfg.Load.VerificationTimeout.Duration, + RateLimitUnitDuration: cfg.Load.RateLimitUnitDuration.Duration, + Schedule: wasp.Plain(*cfg.Load.Rate, cfg.Load.TestDuration.Duration), Gun: NewGun(l, cc, ocrInstances), Labels: CommonTestLabels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })) _, err = p.Run(true) require.NoError(t, err) @@ -53,19 +58,22 @@ func TestOCRVolume(t *testing.T) { require.NoError(t, err) lt, err := SetupCluster(cc, cd, workerNodes) require.NoError(t, err) - cfg, err := ReadConfig() + config, err := tc.GetConfig("Volume", tc.OCR) require.NoError(t, err) + cfg := config.OCR + cfgl := config.Logging.Loki + p := wasp.NewProfile() p.Add(wasp.NewGenerator(&wasp.Config{ T: t, GenName: "ocr", LoadType: wasp.VU, - CallTimeout: cfg.Volume.VerificationTimeout.Duration(), - Schedule: wasp.Plain(cfg.Volume.Rate, cfg.Volume.TestDuration.Duration()), - VU: NewVU(l, cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration(), cc, lt, cd, bootstrapNode, workerNodes, msClient), + CallTimeout: cfg.Volume.VerificationTimeout.Duration, + Schedule: wasp.Plain(*cfg.Volume.Rate, cfg.Volume.TestDuration.Duration), + VU: NewVU(l, *cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration, cc, lt, cd, bootstrapNode, workerNodes, msClient), Labels: CommonTestLabels, - LokiConfig: wasp.NewEnvLokiConfig(), + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })) _, err = p.Run(true) require.NoError(t, err) diff --git a/integration-tests/load/vrfv2/cmd/dashboard.go b/integration-tests/load/vrfv2/cmd/dashboard.go index 0fb7be2b78b..2536b4caed2 100644 --- a/integration-tests/load/vrfv2/cmd/dashboard.go +++ b/integration-tests/load/vrfv2/cmd/dashboard.go @@ -13,6 +13,7 @@ import ( ) func main() { + //TODO switch to TOML too? lokiDS := os.Getenv("DATA_SOURCE_NAME") d, err := wasp.NewDashboard(nil, []dashboard.Option{ diff --git a/integration-tests/load/vrfv2/config.go b/integration-tests/load/vrfv2/config.go deleted file mode 100644 index e97ac6a964e..00000000000 --- a/integration-tests/load/vrfv2/config.go +++ /dev/null @@ -1,151 +0,0 @@ -package loadvrfv2 - -import ( - "encoding/base64" - "fmt" - "os" - - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" - - "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog/log" - - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" -) - -const ( - DefaultConfigFilename = "config.toml" - SoakTestType = "Soak" - LoadTestType = "Load" - StressTestType = "Stress" - SpikeTestType = "Spike" - - ErrReadPerfConfig = "failed to read TOML config for performance tests" - ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" - ErrDeviationShouldBeLessThanOriginal = "`RandomnessRequestCountPerRequestDeviation` should be less than `RandomnessRequestCountPerRequest`" -) - -type PerformanceConfig struct { - Soak *Soak `toml:"Soak"` - Load *Load `toml:"Load"` - Stress *Stress `toml:"Stress"` - Spike *Spike `toml:"Spike"` - - Common *Common `toml:"Common"` - ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnvConfig"` - NewEnvConfig *NewEnvConfig `toml:"NewEnvConfig"` -} - -type ExistingEnvConfig struct { - CoordinatorAddress string `toml:"coordinator_address"` - ConsumerAddress string `toml:"consumer_address"` - LinkAddress string `toml:"link_address"` - SubID uint64 `toml:"sub_id"` - KeyHash string `toml:"key_hash"` - Funding - CreateFundSubsAndAddConsumers bool `toml:"create_fund_subs_and_add_consumers"` - NodeSendingKeys []string `toml:"node_sending_keys"` -} - -type NewEnvConfig struct { - Funding -} - -type Common struct { - MinimumConfirmations uint16 `toml:"minimum_confirmations"` - CancelSubsAfterTestRun bool `toml:"cancel_subs_after_test_run"` -} - -type Funding struct { - SubFunding - NodeSendingKeyFunding float64 `toml:"node_sending_key_funding"` - NodeSendingKeyFundingMin float64 `toml:"node_sending_key_funding_min"` -} - -type SubFunding struct { - SubFundsLink float64 `toml:"sub_funds_link"` -} - -type Soak struct { - PerformanceTestConfig -} - -type Load struct { - PerformanceTestConfig -} - -type Stress struct { - PerformanceTestConfig -} - -type Spike struct { - PerformanceTestConfig -} - -type PerformanceTestConfig struct { - NumberOfSubToCreate int `toml:"number_of_sub_to_create"` - - RPS int64 `toml:"rps"` - //Duration *commonconfig.Duration `toml:"duration"` - RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` - RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` - RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` -} - -func ReadConfig() (*PerformanceConfig, error) { - var cfg *PerformanceConfig - rawConfig := os.Getenv("CONFIG") - var d []byte - var err error - if rawConfig == "" { - d, err = os.ReadFile(DefaultConfigFilename) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - } else { - d, err = base64.StdEncoding.DecodeString(rawConfig) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - } - err = toml.Unmarshal(d, &cfg) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) - } - - if cfg.Soak.RandomnessRequestCountPerRequest <= cfg.Soak.RandomnessRequestCountPerRequestDeviation { - return nil, fmt.Errorf("%s, err: %w", ErrDeviationShouldBeLessThanOriginal, err) - } - - log.Debug().Interface("Config", cfg).Msg("Parsed config") - return cfg, nil -} - -func SetPerformanceTestConfig(testType string, vrfv2Config *vrfv2_config.VRFV2Config, cfg *PerformanceConfig) { - switch testType { - case SoakTestType: - vrfv2Config.NumberOfSubToCreate = cfg.Soak.NumberOfSubToCreate - vrfv2Config.RPS = cfg.Soak.RPS - vrfv2Config.RateLimitUnitDuration = cfg.Soak.RateLimitUnitDuration.Duration() - vrfv2Config.RandomnessRequestCountPerRequest = cfg.Soak.RandomnessRequestCountPerRequest - vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Soak.RandomnessRequestCountPerRequestDeviation - case LoadTestType: - vrfv2Config.NumberOfSubToCreate = cfg.Load.NumberOfSubToCreate - vrfv2Config.RPS = cfg.Load.RPS - vrfv2Config.RateLimitUnitDuration = cfg.Load.RateLimitUnitDuration.Duration() - vrfv2Config.RandomnessRequestCountPerRequest = cfg.Load.RandomnessRequestCountPerRequest - vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Load.RandomnessRequestCountPerRequestDeviation - case StressTestType: - vrfv2Config.NumberOfSubToCreate = cfg.Stress.NumberOfSubToCreate - vrfv2Config.RPS = cfg.Stress.RPS - vrfv2Config.RateLimitUnitDuration = cfg.Stress.RateLimitUnitDuration.Duration() - vrfv2Config.RandomnessRequestCountPerRequest = cfg.Stress.RandomnessRequestCountPerRequest - vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Stress.RandomnessRequestCountPerRequestDeviation - case SpikeTestType: - vrfv2Config.NumberOfSubToCreate = cfg.Spike.NumberOfSubToCreate - vrfv2Config.RPS = cfg.Spike.RPS - vrfv2Config.RateLimitUnitDuration = cfg.Spike.RateLimitUnitDuration.Duration() - vrfv2Config.RandomnessRequestCountPerRequest = cfg.Spike.RandomnessRequestCountPerRequest - vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Spike.RandomnessRequestCountPerRequestDeviation - } -} diff --git a/integration-tests/load/vrfv2/config.toml b/integration-tests/load/vrfv2/config.toml deleted file mode 100644 index 6f54d1ec998..00000000000 --- a/integration-tests/load/vrfv2/config.toml +++ /dev/null @@ -1,58 +0,0 @@ - -[Common] -minimum_confirmations = 3 -cancel_subs_after_test_run = true - -[NewEnvConfig] -sub_funds_link = 1000 -node_sending_key_funding = 1000 - -[ExistingEnvConfig] -coordinator_address = "" -consumer_address = "" -sub_id = 1 -key_hash = "" -create_fund_subs_and_add_consumers = true -link_address = "" -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [ - "", - "", - "", - "", - "", - "", -] - -# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds -[Soak] -rate_limit_unit_duration = "6s" -rps = 1 -randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds -[Load] -rate_limit_unit_duration = "3s" -rps = 1 -randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx -[Stress] -rate_limit_unit_duration = "1s" -rps = 3 -randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds -[Spike] -rate_limit_unit_duration = "1m" -rps = 1 -randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index fd8cb6195e8..bd1dc96fadf 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -4,36 +4,35 @@ import ( "math/rand" "github.com/rs/zerolog" - "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/smartcontractkit/chainlink/integration-tests/types" ) /* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { - contracts *vrfv2_actions.VRFV2Contracts - keyHash [32]byte - subIDs []uint64 - vrfv2Config vrfv2_config.VRFV2Config - logger zerolog.Logger + contracts *vrfv2_actions.VRFV2Contracts + keyHash [32]byte + subIDs []uint64 + testConfig types.VRFv2TestConfig + logger zerolog.Logger } func NewSingleHashGun( contracts *vrfv2_actions.VRFV2Contracts, keyHash [32]byte, subIDs []uint64, - vrfv2Config vrfv2_config.VRFV2Config, + testConfig types.VRFv2TestConfig, logger zerolog.Logger, ) *SingleHashGun { return &SingleHashGun{ - contracts: contracts, - keyHash: keyHash, - subIDs: subIDs, - vrfv2Config: vrfv2Config, - logger: logger, + contracts: contracts, + keyHash: keyHash, + subIDs: subIDs, + testConfig: testConfig, + logger: logger, } } @@ -41,19 +40,23 @@ func NewSingleHashGun( func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets + vrfv2Config := m.testConfig.GetVRFv2Config().General //randomly increase/decrease randomness request count per TX - randomnessRequestCountPerRequest := deviateValue(m.vrfv2Config.RandomnessRequestCountPerRequest, m.vrfv2Config.RandomnessRequestCountPerRequestDeviation) + randomnessRequestCountPerRequest := deviateValue(*vrfv2Config.RandomnessRequestCountPerRequest, *vrfv2Config.RandomnessRequestCountPerRequestDeviation) _, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + m.logger, //the same consumer is used for all requests and in all subs m.contracts.LoadTestConsumers[0], m.contracts.Coordinator, - &vrfv2_actions.VRFV2Data{VRFV2KeyData: vrfv2_actions.VRFV2KeyData{KeyHash: m.keyHash}}, //randomly pick a subID from pool of subIDs m.subIDs[randInRange(0, len(m.subIDs)-1)], + &vrfv2_actions.VRFV2Data{VRFV2KeyData: vrfv2_actions.VRFV2KeyData{KeyHash: m.keyHash}}, + *vrfv2Config.MinimumConfirmations, + *vrfv2Config.CallbackGasLimit, + *vrfv2Config.NumberOfWords, randomnessRequestCountPerRequest, - m.vrfv2Config, - m.vrfv2Config.RandomWordsFulfilledEventTimeout, - m.logger, + *vrfv2Config.RandomnessRequestCountPerRequestDeviation, + vrfv2Config.RandomWordsFulfilledEventTimeout.Duration, ) if err != nil { return &wasp.Response{Error: err.Error(), Failed: true} diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index b31f02ab46f..142764b1719 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -3,13 +3,11 @@ package loadvrfv2 import ( "context" "math/big" - "os" "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/kelseyhightower/envconfig" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" @@ -18,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" @@ -26,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( @@ -39,26 +38,21 @@ var ( "branch": "vrfv2_healthcheck", "commit": "vrfv2_healthcheck", } - - testType = os.Getenv("TEST_TYPE") ) func TestVRFV2Performance(t *testing.T) { - cfg, err := ReadConfig() + l := logging.GetTestLogger(t) + + testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err) - var vrfv2Config vrfv2_config.VRFV2Config - err = envconfig.Process("VRFV2", &vrfv2Config) + testConfig, err := tc.GetConfig(testType, tc.VRFv2) require.NoError(t, err) testReporter := &testreporters.VRFV2TestReporter{} + vrfv2Config := testConfig.VRFv2 - SetPerformanceTestConfig(testType, &vrfv2Config, cfg) - - l := logging.GetTestLogger(t) - //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2Config.MinimumConfirmations = cfg.Common.MinimumConfirmations - - lokiConfig := wasp.NewEnvLokiConfig() + cfgl := testConfig.Logging.Loki + lokiConfig := wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken) lc, err := wasp.NewLokiClient(lokiConfig) if err != nil { l.Error().Err(err).Msg(ErrLokiClient) @@ -68,35 +62,38 @@ func TestVRFV2Performance(t *testing.T) { updatedLabels := UpdateLabels(labels, t) l.Info(). - Str("Test Type", testType). - Str("Test Duration", vrfv2Config.TestDuration.Truncate(time.Second).String()). - Int64("RPS", vrfv2Config.RPS). - Str("RateLimitUnitDuration", vrfv2Config.RateLimitUnitDuration.String()). - Uint16("RandomnessRequestCountPerRequest", vrfv2Config.RandomnessRequestCountPerRequest). - Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2Config.RandomnessRequestCountPerRequestDeviation). - Bool("UseExistingEnv", vrfv2Config.UseExistingEnv). + Str("Test Type", string(testType)). + Str("Test Duration", vrfv2Config.Performance.TestDuration.Duration.Truncate(time.Second).String()). + Int64("RPS", *vrfv2Config.Performance.RPS). + Str("RateLimitUnitDuration", vrfv2Config.Performance.RateLimitUnitDuration.String()). + Uint16("RandomnessRequestCountPerRequest", *vrfv2Config.General.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2Config.General.RandomnessRequestCountPerRequestDeviation). + Bool("UseExistingEnv", *vrfv2Config.Performance.UseExistingEnv). Msg("Performance Test Configuration") - if vrfv2Config.UseExistingEnv { + if *vrfv2Config.Performance.UseExistingEnv { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2Config.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress - vrfv2Config.ConsumerAddress = cfg.ExistingEnvConfig.ConsumerAddress - vrfv2Config.LinkAddress = cfg.ExistingEnvConfig.LinkAddress - vrfv2Config.SubscriptionFundingAmountLink = cfg.ExistingEnvConfig.SubFunding.SubFundsLink - vrfv2Config.SubID = cfg.ExistingEnvConfig.SubID - vrfv2Config.KeyHash = cfg.ExistingEnvConfig.KeyHash + cfg := testConfig.VRFv2 + + vrfv2Config.Performance.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress + vrfv2Config.Performance.ConsumerAddress = cfg.ExistingEnvConfig.ConsumerAddress + vrfv2Config.Performance.LinkAddress = cfg.ExistingEnvConfig.LinkAddress + vrfv2Config.General.SubscriptionFundingAmountLink = cfg.ExistingEnvConfig.SubFunding.SubFundsLink + vrfv2Config.Performance.SubID = cfg.ExistingEnvConfig.SubID + vrfv2Config.Performance.KeyHash = cfg.ExistingEnvConfig.KeyHash env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&testConfig). WithCustomCleanup( func() { - teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2Config) + teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) if env.EVMClient.NetworkSimulated() { l.Info(). Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if cfg.Common.CancelSubsAfterTestRun { + if *vrfv2Config.Common.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner cancelSubsAndReturnFunds(subIDs, l) } @@ -106,38 +103,38 @@ func TestVRFV2Performance(t *testing.T) { require.NoError(t, err, "error creating test env") - coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(vrfv2Config.CoordinatorAddress) + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(*vrfv2Config.Performance.CoordinatorAddress) require.NoError(t, err) var consumers []contracts.VRFv2LoadTestConsumer - if cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { - linkToken, err := env.ContractLoader.LoadLINKToken(vrfv2Config.LinkAddress) + if *cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { + linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2Config.Performance.LinkAddress) require.NoError(t, err) consumers, err = vrfv2_actions.DeployVRFV2Consumers(env.ContractDeployer, coordinator.Address(), 1) require.NoError(t, err) err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) l.Info(). - Str("Coordinator", cfg.ExistingEnvConfig.CoordinatorAddress). - Int("Number of Subs to create", vrfv2Config.NumberOfSubToCreate). + Str("Coordinator", *cfg.ExistingEnvConfig.CoordinatorAddress). + Int("Number of Subs to create", *vrfv2Config.General.NumberOfSubToCreate). Msg("Creating and funding subscriptions, deploying and adding consumers to subs") subIDs, err = vrfv2_actions.CreateFundSubsAndAddConsumers( env, - vrfv2Config, + big.NewFloat(*cfg.General.SubscriptionFundingAmountLink), linkToken, coordinator, consumers, - vrfv2Config.NumberOfSubToCreate, + *vrfv2Config.General.NumberOfSubToCreate, ) require.NoError(t, err) } else { - consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(vrfv2Config.ConsumerAddress) + consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(*vrfv2Config.Performance.ConsumerAddress) require.NoError(t, err) consumers = append(consumers, consumer) - subIDs = append(subIDs, vrfv2Config.SubID) + subIDs = append(subIDs, *vrfv2Config.Performance.SubID) } - err = FundNodesIfNeeded(cfg, env.EVMClient, l) + err = FundNodesIfNeeded(&testConfig, env.EVMClient, l) require.NoError(t, err) vrfv2Contracts = &vrfv2_actions.VRFV2Contracts{ @@ -150,7 +147,7 @@ func TestVRFV2Performance(t *testing.T) { VRFV2KeyData: vrfv2_actions.VRFV2KeyData{ VRFKey: nil, EncodedProvingKey: [2]*big.Int{}, - KeyHash: common.HexToHash(vrfv2Config.KeyHash), + KeyHash: common.HexToHash(*vrfv2Config.Performance.KeyHash), }, VRFJob: nil, PrimaryEthAddress: "", @@ -159,26 +156,27 @@ func TestVRFV2Performance(t *testing.T) { } else { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2Config.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding - vrfv2Config.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink + testConfig.Common.ChainlinkNodeFunding = testConfig.VRFv2.NewEnvConfig.NodeSendingKeyFunding + vrfv2Config.General.SubscriptionFundingAmountLink = testConfig.VRFv2.NewEnvConfig.Funding.SubFundsLink - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&testConfig). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). WithCustomCleanup( func() { - teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2Config) + teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) if env.EVMClient.NetworkSimulated() { l.Info(). Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if cfg.Common.CancelSubsAfterTestRun { + if *testConfig.VRFv2.Common.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner cancelSubsAndReturnFunds(subIDs, l) } @@ -193,7 +191,7 @@ func TestVRFV2Performance(t *testing.T) { env.ParallelTransactions(true) - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*vrfv2Config.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") linkToken, err := actions.DeployLINKToken(env.ContractDeployer) @@ -204,7 +202,7 @@ func TestVRFV2Performance(t *testing.T) { vrfv2Contracts, subIDs, vrfv2Data, err = vrfv2_actions.SetupVRFV2Environment( env, - vrfv2Config, + &testConfig, useVRFOwner, useTestCoordinator, linkToken, @@ -213,7 +211,7 @@ func TestVRFV2Performance(t *testing.T) { env.EVMClient.GetDefaultWallet().Address(), 0, 1, - vrfv2Config.NumberOfSubToCreate, + *vrfv2Config.General.NumberOfSubToCreate, l, ) require.NoError(t, err, "error setting up VRF v2 env") @@ -231,12 +229,12 @@ func TestVRFV2Performance(t *testing.T) { T: t, LoadType: wasp.RPS, GenName: "gun", - RateLimitUnitDuration: vrfv2Config.RateLimitUnitDuration, + RateLimitUnitDuration: vrfv2Config.Performance.RateLimitUnitDuration.Duration, Gun: NewSingleHashGun( vrfv2Contracts, vrfv2Data.KeyHash, subIDs, - vrfv2Config, + &testConfig, l, ), Labels: labels, @@ -253,8 +251,8 @@ func TestVRFV2Performance(t *testing.T) { t.Run("vrfv2 performance test", func(t *testing.T) { singleFeedConfig.Schedule = wasp.Plain( - vrfv2Config.RPS, - vrfv2Config.TestDuration, + *vrfv2Config.Performance.RPS, + vrfv2Config.Performance.TestDuration.Duration, ) _, err = wasp.NewProfile(). Add(wasp.NewGenerator(singleFeedConfig)). @@ -297,15 +295,16 @@ func cancelSubsAndReturnFunds(subIDs []uint64, l zerolog.Logger) { } } -func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l zerolog.Logger) error { - if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { +func FundNodesIfNeeded(vrfv2TestConfig tc.VRFv2TestConfig, client blockchain.EVMClient, l zerolog.Logger) error { + cfg := vrfv2TestConfig.GetVRFv2Config() + if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin != nil && *cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { address := common.HexToAddress(sendingKey) sendingKeyBalance, err := client.BalanceAt(context.Background(), address) if err != nil { return err } - fundingAtLeast := conversions.EtherToWei(big.NewFloat(cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) + fundingAtLeast := conversions.EtherToWei(big.NewFloat(*cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) fundingToSendEth := conversions.WeiToEther(fundingToSendWei) if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { @@ -338,7 +337,7 @@ func teardown( updatedLabels map[string]string, testReporter *testreporters.VRFV2TestReporter, testType string, - vrfv2Config vrfv2_config.VRFV2Config, + testConfig *tc.TestConfig, ) { //send final results to Loki metrics := GetLoadTestMetrics(consumer) @@ -351,7 +350,7 @@ func teardown( metrics.AverageFulfillmentInMillions, metrics.SlowestFulfillment, metrics.FastestFulfillment, - vrfv2Config, + testConfig, ) // send Slack notification diff --git a/integration-tests/load/vrfv2plus/cmd/dashboard.go b/integration-tests/load/vrfv2plus/cmd/dashboard.go index 049ee9ff2e9..2be0e265041 100644 --- a/integration-tests/load/vrfv2plus/cmd/dashboard.go +++ b/integration-tests/load/vrfv2plus/cmd/dashboard.go @@ -13,6 +13,7 @@ import ( ) func main() { + //TODO switch to TOML too? lokiDS := os.Getenv("DATA_SOURCE_NAME") d, err := wasp.NewDashboard(nil, []dashboard.Option{ diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go deleted file mode 100644 index 25f295a4039..00000000000 --- a/integration-tests/load/vrfv2plus/config.go +++ /dev/null @@ -1,152 +0,0 @@ -package loadvrfv2plus - -import ( - "encoding/base64" - "fmt" - "os" - - "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog/log" - - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" -) - -const ( - DefaultConfigFilename = "config.toml" - SoakTestType = "Soak" - LoadTestType = "Load" - StressTestType = "Stress" - SpikeTestType = "Spike" - - ErrReadPerfConfig = "failed to read TOML config for performance tests" - ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" - ErrDeviationShouldBeLessThanOriginal = "`RandomnessRequestCountPerRequestDeviation` should be less than `RandomnessRequestCountPerRequest`" -) - -type PerformanceConfig struct { - Soak *Soak `toml:"Soak"` - Load *Load `toml:"Load"` - Stress *Stress `toml:"Stress"` - Spike *Spike `toml:"Spike"` - - Common *Common `toml:"Common"` - ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnvConfig"` - NewEnvConfig *NewEnvConfig `toml:"NewEnvConfig"` -} - -type ExistingEnvConfig struct { - CoordinatorAddress string `toml:"coordinator_address"` - ConsumerAddress string `toml:"consumer_address"` - LinkAddress string `toml:"link_address"` - SubID string `toml:"sub_id"` - KeyHash string `toml:"key_hash"` - Funding - CreateFundSubsAndAddConsumers bool `toml:"create_fund_subs_and_add_consumers"` - NodeSendingKeys []string `toml:"node_sending_keys"` -} - -type NewEnvConfig struct { - Funding -} - -type Common struct { - MinimumConfirmations uint16 `toml:"minimum_confirmations"` - CancelSubsAfterTestRun bool `toml:"cancel_subs_after_test_run"` - SubBillingType string `toml:"sub_billing_type"` -} - -type Funding struct { - SubFunding - NodeSendingKeyFunding float64 `toml:"node_sending_key_funding"` - NodeSendingKeyFundingMin float64 `toml:"node_sending_key_funding_min"` -} - -type SubFunding struct { - SubFundsLink float64 `toml:"sub_funds_link"` - SubFundsNative float64 `toml:"sub_funds_native"` -} - -type Soak struct { - PerformanceTestConfig -} - -type Load struct { - PerformanceTestConfig -} - -type Stress struct { - PerformanceTestConfig -} - -type Spike struct { - PerformanceTestConfig -} - -type PerformanceTestConfig struct { - NumberOfSubToCreate int `toml:"number_of_sub_to_create"` - - RPS int64 `toml:"rps"` - //Duration *commonconfig.Duration `toml:"duration"` - RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` - RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` - RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` -} - -func ReadConfig() (*PerformanceConfig, error) { - var cfg *PerformanceConfig - rawConfig := os.Getenv("CONFIG") - var d []byte - var err error - if rawConfig == "" { - d, err = os.ReadFile(DefaultConfigFilename) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - } else { - d, err = base64.StdEncoding.DecodeString(rawConfig) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) - } - } - err = toml.Unmarshal(d, &cfg) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) - } - - if cfg.Soak.RandomnessRequestCountPerRequest <= cfg.Soak.RandomnessRequestCountPerRequestDeviation { - return nil, fmt.Errorf("%s, err: %w", ErrDeviationShouldBeLessThanOriginal, err) - } - - log.Debug().Interface("Config", cfg).Msg("Parsed config") - return cfg, nil -} - -func SetPerformanceTestConfig(testType string, vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, cfg *PerformanceConfig) { - switch testType { - case SoakTestType: - vrfv2PlusConfig.NumberOfSubToCreate = cfg.Soak.NumberOfSubToCreate - vrfv2PlusConfig.RPS = cfg.Soak.RPS - vrfv2PlusConfig.RateLimitUnitDuration = cfg.Soak.RateLimitUnitDuration.Duration() - vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Soak.RandomnessRequestCountPerRequest - vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Soak.RandomnessRequestCountPerRequestDeviation - case LoadTestType: - vrfv2PlusConfig.NumberOfSubToCreate = cfg.Load.NumberOfSubToCreate - vrfv2PlusConfig.RPS = cfg.Load.RPS - vrfv2PlusConfig.RateLimitUnitDuration = cfg.Load.RateLimitUnitDuration.Duration() - vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Load.RandomnessRequestCountPerRequest - vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Load.RandomnessRequestCountPerRequestDeviation - case StressTestType: - vrfv2PlusConfig.NumberOfSubToCreate = cfg.Stress.NumberOfSubToCreate - vrfv2PlusConfig.RPS = cfg.Stress.RPS - vrfv2PlusConfig.RateLimitUnitDuration = cfg.Stress.RateLimitUnitDuration.Duration() - vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Stress.RandomnessRequestCountPerRequest - vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Stress.RandomnessRequestCountPerRequestDeviation - case SpikeTestType: - vrfv2PlusConfig.NumberOfSubToCreate = cfg.Spike.NumberOfSubToCreate - vrfv2PlusConfig.RPS = cfg.Spike.RPS - vrfv2PlusConfig.RateLimitUnitDuration = cfg.Spike.RateLimitUnitDuration.Duration() - vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Spike.RandomnessRequestCountPerRequest - vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Spike.RandomnessRequestCountPerRequestDeviation - } -} diff --git a/integration-tests/load/vrfv2plus/config.toml b/integration-tests/load/vrfv2plus/config.toml deleted file mode 100644 index 9572fb9c45f..00000000000 --- a/integration-tests/load/vrfv2plus/config.toml +++ /dev/null @@ -1,57 +0,0 @@ -[Common] -minimum_confirmations = 1 -cancel_subs_after_test_run = true -sub_billing_type = "LINK_AND_NATIVE" # LINK_AND_NATIVE, LINK, NATIVE - -[NewEnvConfig] -sub_funds_link = 1 -sub_funds_native = 1 -node_funds = 10 -node_sending_key_funding = 1000 - -[ExistingEnvConfig] -coordinator_address = "" -consumer_address = "" -sub_id = "" -key_hash = "" -create_fund_subs_and_add_consumers = true -node_sending_key_funding_min = 10 -node_sending_keys = [ - "", - "" -] -sub_funds_native = 10 -link_address = "" -sub_funds_link = 10 - -# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds -[Soak] -rate_limit_unit_duration = "6s" -rps = 1 -randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds -[Load] -rate_limit_unit_duration = "1s" -rps = 1 -randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx -[Stress] -rate_limit_unit_duration = "1s" -rps = 3 -randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds -[Spike] -rate_limit_unit_duration = "1m" -rps = 1 -randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index 03603e7bb05..bfa56d5f3dc 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -9,32 +9,33 @@ import ( "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/types" ) /* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { - contracts *vrfv2plus.VRFV2_5Contracts - keyHash [32]byte - subIDs []*big.Int - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - logger zerolog.Logger + contracts *vrfv2plus.VRFV2_5Contracts + keyHash [32]byte + subIDs []*big.Int + testConfig types.VRFv2PlusTestConfig + logger zerolog.Logger } func NewSingleHashGun( contracts *vrfv2plus.VRFV2_5Contracts, keyHash [32]byte, subIDs []*big.Int, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + testConfig types.VRFv2PlusTestConfig, logger zerolog.Logger, ) *SingleHashGun { return &SingleHashGun{ - contracts: contracts, - keyHash: keyHash, - subIDs: subIDs, - vrfv2PlusConfig: vrfv2PlusConfig, - logger: logger, + contracts: contracts, + keyHash: keyHash, + subIDs: subIDs, + testConfig: testConfig, + logger: logger, } } @@ -42,13 +43,13 @@ func NewSingleHashGun( func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets - billingType, err := selectBillingType(m.vrfv2PlusConfig.SubscriptionBillingType) + billingType, err := selectBillingType(*m.testConfig.GetVRFv2PlusConfig().General.SubscriptionBillingType) if err != nil { return &wasp.Response{Error: err.Error(), Failed: true} } //randomly increase/decrease randomness request count per TX - randomnessRequestCountPerRequest := deviateValue(m.vrfv2PlusConfig.RandomnessRequestCountPerRequest, m.vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation) + randomnessRequestCountPerRequest := deviateValue(*m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequest, *m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequestDeviation) _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( //the same consumer is used for all requests and in all subs m.contracts.LoadTestConsumers[0], @@ -58,9 +59,12 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { m.subIDs[randInRange(0, len(m.subIDs)-1)], //randomly pick payment type billingType, + *m.testConfig.GetVRFv2PlusConfig().General.MinimumConfirmations, + *m.testConfig.GetVRFv2PlusConfig().General.CallbackGasLimit, + *m.testConfig.GetVRFv2PlusConfig().General.NumberOfWords, randomnessRequestCountPerRequest, - m.vrfv2PlusConfig, - m.vrfv2PlusConfig.RandomWordsFulfilledEventTimeout, + *m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequestDeviation, + m.testConfig.GetVRFv2PlusConfig().General.RandomWordsFulfilledEventTimeout.Duration, m.logger, ) if err != nil { @@ -86,12 +90,12 @@ func randInRange(min int, max int) int { } func selectBillingType(billingType string) (bool, error) { - switch billingType { - case vrfv2plus_config.BILLING_TYPE_LINK: + switch vrfv2plus_config.BillingType(billingType) { + case vrfv2plus_config.BillingType_Link: return false, nil - case vrfv2plus_config.BILLING_TYPE_NATIVE: + case vrfv2plus_config.BillingType_Native: return true, nil - case vrfv2plus_config.BILLING_TYPE_LINK_AND_NATIVE: + case vrfv2plus_config.BillingType_Link_and_Native: return randBool(), nil default: return false, fmt.Errorf("invalid billing type: %s", billingType) diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index cb68386d117..b32d40e983e 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -3,13 +3,11 @@ package loadvrfv2plus import ( "context" "math/big" - "os" "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/kelseyhightower/envconfig" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" @@ -23,9 +21,11 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" ) var ( @@ -39,28 +39,21 @@ var ( "branch": "vrfv2Plus_healthcheck", "commit": "vrfv2Plus_healthcheck", } - - testType = os.Getenv("TEST_TYPE") ) func TestVRFV2PlusPerformance(t *testing.T) { - cfg, err := ReadConfig() + l := logging.GetTestLogger(t) + + testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err) - var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) + testConfig, err := tc.GetConfig(testType, tc.VRFv2Plus) require.NoError(t, err) + cfgl := testConfig.Logging.Loki + vrfv2PlusConfig := testConfig.VRFv2Plus testReporter := &testreporters.VRFV2PlusTestReporter{} - SetPerformanceTestConfig(testType, &vrfv2PlusConfig, cfg) - - l := logging.GetTestLogger(t) - //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2PlusConfig.MinimumConfirmations = cfg.Common.MinimumConfirmations - vrfv2PlusConfig.SubscriptionBillingType = cfg.Common.SubBillingType - - lokiConfig := wasp.NewEnvLokiConfig() - lc, err := wasp.NewLokiClient(lokiConfig) + lc, err := wasp.NewLokiClient(wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken)) if err != nil { l.Error().Err(err).Msg(ErrLokiClient) return @@ -69,36 +62,37 @@ func TestVRFV2PlusPerformance(t *testing.T) { updatedLabels := UpdateLabels(labels, t) l.Info(). - Str("Test Type", testType). - Str("Test Duration", vrfv2PlusConfig.TestDuration.Truncate(time.Second).String()). - Int64("RPS", vrfv2PlusConfig.RPS). - Str("RateLimitUnitDuration", vrfv2PlusConfig.RateLimitUnitDuration.String()). - Uint16("RandomnessRequestCountPerRequest", vrfv2PlusConfig.RandomnessRequestCountPerRequest). - Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation). - Bool("UseExistingEnv", vrfv2PlusConfig.UseExistingEnv). + Str("Test Type", string(testType)). + Str("Test Duration", vrfv2PlusConfig.Performance.TestDuration.Duration.Truncate(time.Second).String()). + Int64("RPS", *vrfv2PlusConfig.Performance.RPS). + Str("RateLimitUnitDuration", vrfv2PlusConfig.Performance.RateLimitUnitDuration.String()). + Uint16("RandomnessRequestCountPerRequest", *vrfv2PlusConfig.General.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2PlusConfig.General.RandomnessRequestCountPerRequestDeviation). + Bool("UseExistingEnv", *vrfv2PlusConfig.Performance.UseExistingEnv). Msg("Performance Test Configuration") - if vrfv2PlusConfig.UseExistingEnv { + if *vrfv2PlusConfig.Performance.UseExistingEnv { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2PlusConfig.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress - vrfv2PlusConfig.ConsumerAddress = cfg.ExistingEnvConfig.ConsumerAddress - vrfv2PlusConfig.LinkAddress = cfg.ExistingEnvConfig.LinkAddress - vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.ExistingEnvConfig.SubFunding.SubFundsLink - vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.ExistingEnvConfig.SubFunding.SubFundsNative - vrfv2PlusConfig.SubID = cfg.ExistingEnvConfig.SubID - vrfv2PlusConfig.KeyHash = cfg.ExistingEnvConfig.KeyHash + vrfv2PlusConfig.Performance.CoordinatorAddress = testConfig.VRFv2Plus.ExistingEnvConfig.CoordinatorAddress + vrfv2PlusConfig.Performance.ConsumerAddress = testConfig.VRFv2Plus.ExistingEnvConfig.ConsumerAddress + vrfv2PlusConfig.Performance.LinkAddress = testConfig.VRFv2Plus.ExistingEnvConfig.LinkAddress + vrfv2PlusConfig.General.SubscriptionFundingAmountLink = testConfig.VRFv2Plus.ExistingEnvConfig.SubFunding.SubFundsLink + vrfv2PlusConfig.General.SubscriptionFundingAmountNative = testConfig.VRFv2Plus.ExistingEnvConfig.SubFunding.SubFundsNative + vrfv2PlusConfig.Performance.SubID = testConfig.VRFv2Plus.ExistingEnvConfig.SubID + vrfv2PlusConfig.Performance.KeyHash = testConfig.VRFv2Plus.ExistingEnvConfig.KeyHash env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&testConfig). WithCustomCleanup( func() { - teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) + teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) if env.EVMClient.NetworkSimulated() { l.Info(). Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if cfg.Common.CancelSubsAfterTestRun { + if *testConfig.VRFv2Plus.Common.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner cancelSubsAndReturnFunds(subIDs, l) } @@ -108,41 +102,43 @@ func TestVRFV2PlusPerformance(t *testing.T) { require.NoError(t, err, "error creating test env") - coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(vrfv2PlusConfig.CoordinatorAddress) + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(*vrfv2PlusConfig.Performance.CoordinatorAddress) require.NoError(t, err) var consumers []contracts.VRFv2PlusLoadTestConsumer - if cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { - linkToken, err := env.ContractLoader.LoadLINKToken(vrfv2PlusConfig.LinkAddress) + if *testConfig.VRFv2Plus.ExistingEnvConfig.CreateFundSubsAndAddConsumers { + linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2PlusConfig.Performance.LinkAddress) require.NoError(t, err) consumers, err = vrfv2plus.DeployVRFV2PlusConsumers(env.ContractDeployer, coordinator, 1) require.NoError(t, err) err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) l.Info(). - Str("Coordinator", cfg.ExistingEnvConfig.CoordinatorAddress). - Int("Number of Subs to create", vrfv2PlusConfig.NumberOfSubToCreate). + Str("Coordinator", *vrfv2PlusConfig.Performance.CoordinatorAddress). + Int("Number of Subs to create", *vrfv2PlusConfig.General.NumberOfSubToCreate). Msg("Creating and funding subscriptions, deploying and adding consumers to subs") subIDs, err = vrfv2plus.CreateFundSubsAndAddConsumers( env, - vrfv2PlusConfig, + big.NewFloat(*testConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), + big.NewFloat(*testConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, coordinator, consumers, - vrfv2PlusConfig.NumberOfSubToCreate, + *vrfv2PlusConfig.General.NumberOfSubToCreate, + vrfv2plus_config.BillingType(*vrfv2PlusConfig.General.SubscriptionBillingType), ) require.NoError(t, err) } else { - consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(vrfv2PlusConfig.ConsumerAddress) + consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(*vrfv2PlusConfig.Performance.ConsumerAddress) require.NoError(t, err) consumers = append(consumers, consumer) var ok bool - subID, ok := new(big.Int).SetString(vrfv2PlusConfig.SubID, 10) + subID := big.NewInt(int64(*vrfv2PlusConfig.Performance.SubID)) require.True(t, ok) subIDs = append(subIDs, subID) } - err = FundNodesIfNeeded(cfg, env.EVMClient, l) + err = FundNodesIfNeeded(&testConfig, env.EVMClient, l) require.NoError(t, err) vrfv2PlusContracts = &vrfv2plus.VRFV2_5Contracts{ @@ -155,7 +151,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { VRFV2PlusKeyData: vrfv2plus.VRFV2PlusKeyData{ VRFKey: nil, EncodedProvingKey: [2]*big.Int{}, - KeyHash: common.HexToHash(vrfv2PlusConfig.KeyHash), + KeyHash: common.HexToHash(*vrfv2PlusConfig.Performance.KeyHash), }, VRFJob: nil, PrimaryEthAddress: "", @@ -164,28 +160,28 @@ func TestVRFV2PlusPerformance(t *testing.T) { } else { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2PlusConfig.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding - vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink - vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.NewEnvConfig.Funding.SubFundsNative + testConfig.Common.ChainlinkNodeFunding = testConfig.VRFv2.NewEnvConfig.NodeSendingKeyFunding + vrfv2PlusConfig.General.SubscriptionFundingAmountLink = testConfig.VRFv2Plus.NewEnvConfig.Funding.SubFundsLink + vrfv2PlusConfig.General.SubscriptionFundingAmountNative = testConfig.VRFv2Plus.NewEnvConfig.Funding.SubFundsNative - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) require.NoError(t, err, "Error building ethereum network config") - env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&testConfig). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). WithCustomCleanup( func() { - teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) + teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) if env.EVMClient.NetworkSimulated() { l.Info(). Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if cfg.Common.CancelSubsAfterTestRun { + if *testConfig.VRFv2Plus.Common.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner cancelSubsAndReturnFunds(subIDs, l) } @@ -200,7 +196,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { env.ParallelTransactions(true) - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse)) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*vrfv2PlusConfig.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") linkToken, err := actions.DeployLINKToken(env.ContractDeployer) @@ -208,12 +204,12 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusContracts, subIDs, vrfv2PlusData, err = vrfv2plus.SetupVRFV2_5Environment( env, - vrfv2PlusConfig, + &testConfig, linkToken, mockETHLinkFeed, 0, 1, - vrfv2PlusConfig.NumberOfSubToCreate, + *vrfv2PlusConfig.General.NumberOfSubToCreate, l, ) require.NoError(t, err, "error setting up VRF v2_5 env") @@ -231,16 +227,16 @@ func TestVRFV2PlusPerformance(t *testing.T) { T: t, LoadType: wasp.RPS, GenName: "gun", - RateLimitUnitDuration: vrfv2PlusConfig.RateLimitUnitDuration, + RateLimitUnitDuration: vrfv2PlusConfig.Performance.RateLimitUnitDuration.Duration, Gun: NewSingleHashGun( vrfv2PlusContracts, vrfv2PlusData.KeyHash, subIDs, - vrfv2PlusConfig, + &testConfig, l, ), Labels: labels, - LokiConfig: lokiConfig, + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), CallTimeout: 2 * time.Minute, } require.Len(t, vrfv2PlusContracts.LoadTestConsumers, 1, "only one consumer should be created for Load Test") @@ -251,10 +247,9 @@ func TestVRFV2PlusPerformance(t *testing.T) { // is our "job" stable at all, no memory leaks, no flaking performance under some RPS? t.Run("vrfv2plus performance test", func(t *testing.T) { - singleFeedConfig.Schedule = wasp.Plain( - vrfv2PlusConfig.RPS, - vrfv2PlusConfig.TestDuration, + *vrfv2PlusConfig.Performance.RPS, + vrfv2PlusConfig.Performance.TestDuration.Duration, ) _, err = wasp.NewProfile(). Add(wasp.NewGenerator(singleFeedConfig)). @@ -297,15 +292,16 @@ func cancelSubsAndReturnFunds(subIDs []*big.Int, l zerolog.Logger) { } } -func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l zerolog.Logger) error { - if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { +func FundNodesIfNeeded(vrfv2plusTestConfig tc.VRFv2PlusTestConfig, client blockchain.EVMClient, l zerolog.Logger) error { + cfg := vrfv2plusTestConfig.GetVRFv2PlusConfig() + if *cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { address := common.HexToAddress(sendingKey) sendingKeyBalance, err := client.BalanceAt(context.Background(), address) if err != nil { return err } - fundingAtLeast := conversions.EtherToWei(big.NewFloat(cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) + fundingAtLeast := conversions.EtherToWei(big.NewFloat(*cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) fundingToSendEth := conversions.WeiToEther(fundingToSendWei) if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { @@ -338,7 +334,7 @@ func teardown( updatedLabels map[string]string, testReporter *testreporters.VRFV2PlusTestReporter, testType string, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + testConfig *tc.TestConfig, ) { //send final results to Loki metrics := GetLoadTestMetrics(consumer) @@ -351,11 +347,11 @@ func teardown( metrics.AverageFulfillmentInMillions, metrics.SlowestFulfillment, metrics.FastestFulfillment, - vrfv2PlusConfig, + testConfig, ) // send Slack notification - err := testReporter.SendSlackNotification(t, nil) + err := testReporter.SendSlackNotification(t, nil, testConfig) if err != nil { log.Warn().Err(err).Msg("Error sending Slack notification") } diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index 6603fc1685d..584a9fbc75e 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -5,30 +5,37 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestVersionUpgrade(t *testing.T) { t.Parallel() + + config, err := tc.GetConfig("Migration", tc.Node) + require.NoError(t, err, "Error getting config") + + err = config.ChainlinkUpgradeImage.Validate() + require.NoError(t, err, "Error validating upgrade image") + env, err := test_env.NewCLTestEnvBuilder(). + WithTestConfig(&config). WithTestInstance(t). WithStandardCleanup(). WithGeth(). WithCLNodes(1). + WithStandardCleanup(). Build() require.NoError(t, err) - upgradeImage, err := osutil.GetEnv("UPGRADE_IMAGE") - require.NoError(t, err, "Error getting upgrade image") - upgradeVersion, err := osutil.GetEnv("UPGRADE_VERSION") - require.NoError(t, err, "Error getting upgrade version") + // just restarting CL container with the same name, DB is still the same + // // [Database] // MigrateOnStartup = true // // by default - err = env.ClCluster.Nodes[0].UpgradeVersion(upgradeImage, upgradeVersion) + err = env.ClCluster.Nodes[0].UpgradeVersion(*config.ChainlinkUpgradeImage.Image, *config.ChainlinkUpgradeImage.Version) require.NoError(t, err) } diff --git a/integration-tests/performance/cron_test.go b/integration-tests/performance/cron_test.go deleted file mode 100644 index 38d861cee07..00000000000 --- a/integration-tests/performance/cron_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package performance - -import ( - "fmt" - "os" - "strings" - "testing" - "time" - - "github.com/google/uuid" - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - - "github.com/smartcontractkit/chainlink-testing-framework/networks" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/testsetups" -) - -func TestMain(m *testing.M) { - os.Exit(m.Run()) -} - -func CleanupPerformanceTest( - t *testing.T, - testEnvironment *environment.Environment, - chainlinkNodes []*client.ChainlinkK8sClient, - testReporter testreporters.ChainlinkProfileTestReporter, - chainClient blockchain.EVMClient, -) { - if chainClient != nil { - chainClient.GasStats().PrintStats() - } - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, &testReporter, zapcore.PanicLevel, chainClient) - require.NoError(t, err, "Error tearing down environment") -} - -func TestCronPerformance(t *testing.T) { - testEnvironment := setupCronTest(t) - if testEnvironment.WillUseRemoteRunner() { - return - } - - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") - mockServer, err := ctfClient.ConnectMockServer(testEnvironment) - require.NoError(t, err, "Creating mockserver client shouldn't fail") - chainlinkNode := chainlinkNodes[0] - err = mockServer.SetValuePath("/variable", 5) - require.NoError(t, err, "Setting value path in mockserver shouldn't fail") - - profileFunction := func(chainlinkNode *client.ChainlinkClient) { - if chainlinkNode != chainlinkNodes[len(chainlinkNodes)-1].ChainlinkClient { - // Not the last node, hence not all nodes started profiling yet. - return - } - bta := &client.BridgeTypeAttributes{ - Name: fmt.Sprintf("variable-%s", uuid.New().String()), - URL: fmt.Sprintf("%s/variable", mockServer.Config.ClusterURL), - RequestData: "{}", - } - err = chainlinkNode.MustCreateBridge(bta) - require.NoError(t, err, "Creating bridge in chainlink node shouldn't fail") - - job, err := chainlinkNode.MustCreateJob(&client.CronJobSpec{ - Schedule: "CRON_TZ=UTC * * * * * *", - ObservationSource: client.ObservationSourceSpecBridge(bta), - }) - require.NoError(t, err, "Creating Cron Job in chainlink node shouldn't fail") - - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - jobRuns, err := chainlinkNode.MustReadRunsByJob(job.Data.ID) - g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Reading Job run data shouldn't fail") - - g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 5), "Expected number of job runs to be greater than 5, but got %d", len(jobRuns.Data)) - - for _, jr := range jobRuns.Data { - g.Expect(jr.Attributes.Errors).Should(gomega.Equal([]interface{}{nil}), "Job run %s shouldn't have errors", jr.ID) - } - }, "2m", "1s").Should(gomega.Succeed()) - } - - profileTest := testsetups.NewChainlinkProfileTest(testsetups.ChainlinkProfileTestInputs{ - ProfileFunction: profileFunction, - ProfileDuration: 30 * time.Second, - ChainlinkNodes: []*client.ChainlinkK8sClient{chainlinkNode}, - }) - // Register cleanup for any test - t.Cleanup(func() { - CleanupPerformanceTest(t, testEnvironment, chainlinkNodes, profileTest.TestReporter, nil) - }) - profileTest.Setup(testEnvironment) - profileTest.Run() -} - -func setupCronTest(t *testing.T) (testEnvironment *environment.Environment) { - logging.Init() - network := networks.MustGetSelectedNetworksFromEnv()[0] - evmConfig := ethereum.New(nil) - if !network.Simulated { - evmConfig = ethereum.New(ðereum.Props{ - NetworkName: network.Name, - Simulated: network.Simulated, - WsURLs: network.URLs, - }) - } - baseTOML := `[WebServer] -HTTPWriteTimout = '300s'` - cd := chainlink.New(0, map[string]interface{}{ - "replicas": 1, - "toml": networks.AddNetworksConfig(baseTOML, network), - }) - - testEnvironment = environment.New(&environment.Config{ - NamespacePrefix: fmt.Sprintf("performance-cron-%s", strings.ReplaceAll(strings.ToLower(network.Name), " ", "-")), - Test: t, - PreventPodEviction: true, - }). - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddHelm(evmConfig). - AddHelm(cd) - err := testEnvironment.Run() - require.NoError(t, err, "Error launching test environment") - return testEnvironment -} diff --git a/integration-tests/performance/directrequest_test.go b/integration-tests/performance/directrequest_test.go deleted file mode 100644 index 0fe1ca37e15..00000000000 --- a/integration-tests/performance/directrequest_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package performance - -import ( - "fmt" - "math/big" - "strings" - "testing" - "time" - - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/testsetups" - - "github.com/google/uuid" -) - -func TestDirectRequestPerformance(t *testing.T) { - l := logging.GetTestLogger(t) - testEnvironment := setupDirectRequestTest(t) - if testEnvironment.WillUseRemoteRunner() { - return - } - - chainClient, err := blockchain.NewEVMClient(blockchain.SimulatedEVMNetwork, testEnvironment, l) - require.NoError(t, err, "Connecting to blockchain nodes shouldn't fail") - contractDeployer, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") - mockServerClient, err := ctfClient.ConnectMockServer(testEnvironment) - require.NoError(t, err, "Error connecting to mock server") - - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(.01)) - require.NoError(t, err, "Funding chainlink nodes with ETH shouldn't fail") - - lt, err := contractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - oracle, err := contractDeployer.DeployOracle(lt.Address()) - require.NoError(t, err, "Deploying Oracle Contract shouldn't fail") - consumer, err := contractDeployer.DeployAPIConsumer(lt.Address()) - require.NoError(t, err, "Deploying Consumer Contract shouldn't fail") - err = chainClient.SetDefaultWallet(0) - require.NoError(t, err, "Setting default wallet shouldn't fail") - err = lt.Transfer(consumer.Address(), big.NewInt(2e18)) - require.NoError(t, err, "Transferring %d to consumer contract shouldn't fail", big.NewInt(2e18)) - - err = mockServerClient.SetValuePath("/variable", 5) - require.NoError(t, err, "Setting mockserver value path shouldn't fail") - - jobUUID := uuid.New() - - bta := client.BridgeTypeAttributes{ - Name: fmt.Sprintf("five-%s", jobUUID.String()), - URL: fmt.Sprintf("%s/variable", mockServerClient.Config.ClusterURL), - } - err = chainlinkNodes[0].MustCreateBridge(&bta) - require.NoError(t, err, "Creating bridge shouldn't fail") - - os := &client.DirectRequestTxPipelineSpec{ - BridgeTypeAttributes: bta, - DataPath: "data,result", - } - ost, err := os.String() - require.NoError(t, err, "Building observation source spec shouldn't fail") - - _, err = chainlinkNodes[0].MustCreateJob(&client.DirectRequestJobSpec{ - Name: "direct_request", - MinIncomingConfirmations: "1", - ContractAddress: oracle.Address(), - EVMChainID: chainClient.GetChainID().String(), - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - }) - require.NoError(t, err, "Creating direct_request job shouldn't fail") - - profileFunction := func(chainlinkNode *client.ChainlinkClient) { - if chainlinkNode != chainlinkNodes[len(chainlinkNodes)-1].ChainlinkClient { - // Not the last node, hence not all nodes started profiling yet. - return - } - jobUUIDReplaces := strings.Replace(jobUUID.String(), "-", "", 4) - var jobID [32]byte - copy(jobID[:], jobUUIDReplaces) - err = consumer.CreateRequestTo( - oracle.Address(), - jobID, - big.NewInt(1e18), - fmt.Sprintf("%s/variable", mockServerClient.Config.ClusterURL), - "data,result", - big.NewInt(100), - ) - require.NoError(t, err, "Calling oracle contract shouldn't fail") - - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(testcontext.Get(t)) - g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") - g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") - l.Debug().Int64("Data", d.Int64()).Msg("Found on chain") - g.Expect(d.Int64()).Should(gomega.BeNumerically("==", 5), "Expected the on-chain data to be 5, but found %d", d.Int64()) - }, "2m", "1s").Should(gomega.Succeed()) - } - - profileTest := testsetups.NewChainlinkProfileTest(testsetups.ChainlinkProfileTestInputs{ - ProfileFunction: profileFunction, - ProfileDuration: 30 * time.Second, - ChainlinkNodes: chainlinkNodes, - }) - profileTest.Setup(testEnvironment) - t.Cleanup(func() { - CleanupPerformanceTest(t, testEnvironment, chainlinkNodes, profileTest.TestReporter, chainClient) - }) - profileTest.Run() -} - -func setupDirectRequestTest(t *testing.T) (testEnvironment *environment.Environment) { - network := networks.MustGetSelectedNetworksFromEnv()[0] - evmConfig := ethereum.New(nil) - if !network.Simulated { - evmConfig = ethereum.New(ðereum.Props{ - NetworkName: network.Name, - Simulated: network.Simulated, - WsURLs: network.URLs, - }) - } - baseTOML := `[WebServer] -HTTPWriteTimout = '300s'` - cd := chainlink.New(0, map[string]interface{}{ - "replicas": 1, - "toml": networks.AddNetworksConfig(baseTOML, network), - }) - - testEnvironment = environment.New(&environment.Config{ - NamespacePrefix: fmt.Sprintf("performance-cron-%s", strings.ReplaceAll(strings.ToLower(network.Name), " ", "-")), - Test: t, - PreventPodEviction: true, - }). - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddHelm(evmConfig). - AddHelm(cd) - err := testEnvironment.Run() - require.NoError(t, err, "Error launching test environment") - return testEnvironment -} diff --git a/integration-tests/performance/flux_test.go b/integration-tests/performance/flux_test.go deleted file mode 100644 index 5fa31b626ae..00000000000 --- a/integration-tests/performance/flux_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package performance - -import ( - "fmt" - "math/big" - "strings" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/google/uuid" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/testsetups" -) - -func TestFluxPerformance(t *testing.T) { - l := logging.GetTestLogger(t) - testEnvironment, testNetwork := setupFluxTest(t) - if testEnvironment.WillUseRemoteRunner() { - return - } - - chainClient, err := blockchain.NewEVMClient(testNetwork, testEnvironment, l) - require.NoError(t, err, "Connecting to blockchain nodes shouldn't fail") - contractDeployer, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") - nodeAddresses, err := actions.ChainlinkNodeAddresses(chainlinkNodes) - require.NoError(t, err, "Retreiving on-chain wallet addresses for chainlink nodes shouldn't fail") - mockServer, err := ctfClient.ConnectMockServer(testEnvironment) - require.NoError(t, err, "Creating mock server client shouldn't fail") - - chainClient.ParallelTransactions(true) - - adapterUUID := uuid.New().String() - adapterPath := fmt.Sprintf("/variable-%s", adapterUUID) - err = mockServer.SetValuePath(adapterPath, 1e5) - require.NoError(t, err, "Setting mockserver value path shouldn't fail") - - linkToken, err := contractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - fluxInstance, err := contractDeployer.DeployFluxAggregatorContract(linkToken.Address(), contracts.DefaultFluxAggregatorOptions()) - require.NoError(t, err, "Deploying Flux Aggregator Contract shouldn't fail") - err = chainClient.WaitForEvents() - require.NoError(t, err, "Failed waiting for deployment of flux aggregator contract") - - err = linkToken.Transfer(fluxInstance.Address(), big.NewInt(1e18)) - require.NoError(t, err, "Funding Flux Aggregator Contract shouldn't fail") - err = chainClient.WaitForEvents() - require.NoError(t, err, "Failed waiting for funding of flux aggregator contract") - - err = fluxInstance.UpdateAvailableFunds() - require.NoError(t, err, "Updating the available funds on the Flux Aggregator Contract shouldn't fail") - - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(.02)) - require.NoError(t, err, "Funding chainlink nodes with ETH shouldn't fail") - - err = fluxInstance.SetOracles( - contracts.FluxAggregatorSetOraclesOptions{ - AddList: nodeAddresses, - RemoveList: []common.Address{}, - AdminList: nodeAddresses, - MinSubmissions: 3, - MaxSubmissions: 3, - RestartDelayRounds: 0, - }) - require.NoError(t, err, "Setting oracle options in the Flux Aggregator contract shouldn't fail") - err = chainClient.WaitForEvents() - require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - oracles, err := fluxInstance.GetOracles(testcontext.Get(t)) - require.NoError(t, err, "Getting oracle details from the Flux aggregator contract shouldn't fail") - l.Info().Str("Oracles", strings.Join(oracles, ",")).Msg("Oracles set") - - adapterFullURL := fmt.Sprintf("%s%s", mockServer.Config.ClusterURL, adapterPath) - bta := &client.BridgeTypeAttributes{ - Name: fmt.Sprintf("variable-%s", adapterUUID), - URL: adapterFullURL, - } - for i, n := range chainlinkNodes { - err = n.MustCreateBridge(bta) - require.NoError(t, err, "Creating bridge shouldn't fail for node %d", i+1) - - fluxSpec := &client.FluxMonitorJobSpec{ - Name: fmt.Sprintf("flux-monitor-%s", adapterUUID), - ContractAddress: fluxInstance.Address(), - EVMChainID: chainClient.GetChainID().String(), - Threshold: 0, - AbsoluteThreshold: 0, - PollTimerPeriod: 15 * time.Second, // min 15s - IdleTimerDisabled: true, - ObservationSource: client.ObservationSourceSpecBridge(bta), - } - _, err = n.MustCreateJob(fluxSpec) - require.NoError(t, err, "Creating flux job shouldn't fail for node %d", i+1) - } - - profileFunction := func(chainlinkNode *client.ChainlinkClient) { - if chainlinkNode != chainlinkNodes[len(chainlinkNodes)-1].ChainlinkClient { - // Not the last node, hence not all nodes started profiling yet. - return - } - fluxRoundTimeout := 2 * time.Minute - fluxRound := contracts.NewFluxAggregatorRoundConfirmer(fluxInstance, big.NewInt(1), fluxRoundTimeout, l) - chainClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) - err = chainClient.WaitForEvents() - require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err := fluxInstance.GetContractData(testcontext.Get(t)) - require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") - l.Info().Interface("Data", data).Msg("Round data") - require.Equal(t, int64(1e5), data.LatestRoundData.Answer.Int64(), - "Expected latest round answer to be %d, but found %d", int64(1e5), data.LatestRoundData.Answer.Int64()) - require.Equal(t, int64(1), data.LatestRoundData.RoundId.Int64(), - "Expected latest round id to be %d, but found %d", int64(1), data.LatestRoundData.RoundId.Int64()) - require.Equal(t, int64(1), data.LatestRoundData.AnsweredInRound.Int64(), - "Expected latest round's answered in round to be %d, but found %d", int64(1), data.LatestRoundData.AnsweredInRound.Int64()) - require.Equal(t, int64(999999999999999997), data.AvailableFunds.Int64(), - "Expected available funds to be %d, but found %d", int64(999999999999999997), data.AvailableFunds.Int64()) - require.Equal(t, int64(3), data.AllocatedFunds.Int64(), - "Expected allocated funds to be %d, but found %d", int64(3), data.AllocatedFunds.Int64()) - - fluxRound = contracts.NewFluxAggregatorRoundConfirmer(fluxInstance, big.NewInt(2), fluxRoundTimeout, l) - chainClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) - err = mockServer.SetValuePath(adapterPath, 1e10) - require.NoError(t, err, "Setting value path in mock server shouldn't fail") - err = chainClient.WaitForEvents() - require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err = fluxInstance.GetContractData(testcontext.Get(t)) - require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") - require.Equal(t, int64(1e10), data.LatestRoundData.Answer.Int64(), - "Expected latest round answer to be %d, but found %d", int64(1e10), data.LatestRoundData.Answer.Int64()) - require.Equal(t, int64(2), data.LatestRoundData.RoundId.Int64(), - "Expected latest round id to be %d, but found %d", int64(2), data.LatestRoundData.RoundId.Int64()) - require.Equal(t, int64(999999999999999994), data.AvailableFunds.Int64(), - "Expected available funds to be %d, but found %d", int64(999999999999999994), data.AvailableFunds.Int64()) - require.Equal(t, int64(6), data.AllocatedFunds.Int64(), - "Expected allocated funds to be %d, but found %d", int64(6), data.AllocatedFunds.Int64()) - l.Info().Interface("data", data).Msg("Round data") - - for _, oracleAddr := range nodeAddresses { - payment, _ := fluxInstance.WithdrawablePayment(testcontext.Get(t), oracleAddr) - require.Equal(t, int64(2), payment.Int64(), - "Expected flux aggregator contract's withdrawable payment to be %d, but found %d", int64(2), payment.Int64()) - } - } - - profileTest := testsetups.NewChainlinkProfileTest(testsetups.ChainlinkProfileTestInputs{ - ProfileFunction: profileFunction, - ProfileDuration: 30 * time.Second, - ChainlinkNodes: chainlinkNodes, - }) - // Register cleanup - t.Cleanup(func() { - CleanupPerformanceTest(t, testEnvironment, chainlinkNodes, profileTest.TestReporter, chainClient) - }) - profileTest.Setup(testEnvironment) - profileTest.Run() -} - -func setupFluxTest(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] - evmConf := ethereum.New(nil) - if !testNetwork.Simulated { - evmConf = ethereum.New(ðereum.Props{ - NetworkName: testNetwork.Name, - Simulated: testNetwork.Simulated, - WsURLs: testNetwork.URLs, - }) - } - baseTOML := `[WebServer] -HTTPWriteTimout = '300s' - -[OCR] -Enabled = true` - cd := chainlink.New(0, map[string]interface{}{ - "replicas": 3, - "toml": networks.AddNetworksConfig(baseTOML, testNetwork), - }) - - testEnvironment = environment.New(&environment.Config{ - NamespacePrefix: fmt.Sprintf("performance-flux-%s", strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-")), - Test: t, - PreventPodEviction: true, - }). - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddHelm(evmConf). - AddHelm(cd) - err := testEnvironment.Run() - require.NoError(t, err, "Error running test environment") - return testEnvironment, testNetwork -} diff --git a/integration-tests/performance/keeper_test.go b/integration-tests/performance/keeper_test.go deleted file mode 100644 index ac6e9e6a579..00000000000 --- a/integration-tests/performance/keeper_test.go +++ /dev/null @@ -1,194 +0,0 @@ -package performance - -//revive:disable:dot-imports -import ( - "fmt" - "math/big" - "strings" - "testing" - "time" - - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/integration-tests/testsetups" -) - -var keeperDefaultRegistryConfig = contracts.KeeperRegistrySettings{ - PaymentPremiumPPB: uint32(200000000), - FlatFeeMicroLINK: uint32(0), - BlockCountPerTurn: big.NewInt(10), - CheckGasLimit: uint32(2500000), - StalenessSeconds: big.NewInt(90000), - GasCeilingMultiplier: uint16(1), - MinUpkeepSpend: big.NewInt(0), - MaxPerformGas: uint32(5000000), - FallbackGasPrice: big.NewInt(2e11), - FallbackLinkPrice: big.NewInt(2e18), - MaxCheckDataSize: uint32(5000), - MaxPerformDataSize: uint32(5000), -} - -func TestKeeperPerformance(t *testing.T) { - l := logging.GetTestLogger(t) - testEnvironment, chainClient, chainlinkNodes, contractDeployer, linkToken := setupKeeperTest(t, "basic-smoke") - if testEnvironment.WillUseRemoteRunner() { - return - } - registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( - t, - ethereum.RegistryVersion_1_1, - keeperDefaultRegistryConfig, - 1, - uint32(2500000), - linkToken, - contractDeployer, - chainClient, - big.NewInt(9e18), - ) - gom := gomega.NewGomegaWithT(t) - - profileFunction := func(chainlinkNode *client.ChainlinkClient) { - if chainlinkNode != chainlinkNodes[len(chainlinkNodes)-1].ChainlinkClient { - // Not the last node, hence not all nodes started profiling yet. - return - } - actions.CreateKeeperJobs(t, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) - err := chainClient.WaitForEvents() - require.NoError(t, err, "Error creating keeper jobs") - - gom.Eventually(func(g gomega.Gomega) { - // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 10 - for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(testcontext.Get(t)) - g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) - g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), - "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) - l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Number of upkeeps performed") - } - }, "5m", "1s").Should(gomega.Succeed()) - - // Cancel all the registered upkeeps via the registry - for i := 0; i < len(upkeepIDs); i++ { - err = registry.CancelUpkeep(upkeepIDs[i]) - require.NoError(t, err, "Could not cancel upkeep at index %d", i) - } - - err = chainClient.WaitForEvents() - require.NoError(t, err, "Error waiting for upkeeps to be cancelled") - - var countersAfterCancellation = make([]*big.Int, len(upkeepIDs)) - - for i := 0; i < len(upkeepIDs); i++ { - // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(testcontext.Get(t)) - require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) - l.Info().Int("Index", i).Int64("Upkeeps Performed", countersAfterCancellation[i].Int64()).Msg("Cancelled Upkeep") - } - - gom.Consistently(func(g gomega.Gomega) { - for i := 0; i < len(upkeepIDs); i++ { - // Expect the counter to remain constant because the upkeep was cancelled, so it shouldn't increase anymore - latestCounter, err := consumers[i].Counter(testcontext.Get(t)) - require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) - g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterCancellation[i].Int64()), - "Expected consumer counter to remain constant at %d, but got %d", - countersAfterCancellation[i].Int64(), latestCounter.Int64()) - } - }, "1m", "1s").Should(gomega.Succeed()) - } - profileTest := testsetups.NewChainlinkProfileTest(testsetups.ChainlinkProfileTestInputs{ - ProfileFunction: profileFunction, - ProfileDuration: 10 * time.Second, - ChainlinkNodes: chainlinkNodes, - }) - // Register cleanup - t.Cleanup(func() { - CleanupPerformanceTest(t, testEnvironment, chainlinkNodes, profileTest.TestReporter, chainClient) - }) - profileTest.Setup(testEnvironment) - profileTest.Run() -} - -func setupKeeperTest( - t *testing.T, - testName string, -) ( - *environment.Environment, - blockchain.EVMClient, - []*client.ChainlinkK8sClient, - contracts.ContractDeployer, - contracts.LinkToken, -) { - network := networks.MustGetSelectedNetworksFromEnv()[0] - evmConfig := eth.New(nil) - if !network.Simulated { - evmConfig = eth.New(ð.Props{ - NetworkName: network.Name, - Simulated: network.Simulated, - WsURLs: network.URLs, - }) - } - baseTOML := `[WebServer] -HTTPWriteTimout = '300s' - -[Keeper] -TurnLookBack = 0 - -[Keeper.Registry] -SyncInterval = '5s' -PerformGasOverhead = 150_000` - networkName := strings.ReplaceAll(strings.ToLower(network.Name), " ", "-") - cd := chainlink.New(0, map[string]interface{}{ - "replicas": 5, - "toml": networks.AddNetworksConfig(baseTOML, network), - }) - - testEnvironment := environment.New( - &environment.Config{ - NamespacePrefix: fmt.Sprintf("performance-keeper-%s-%s", testName, networkName), - Test: t, - PreventPodEviction: true, - }, - ). - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddHelm(evmConfig). - AddHelm(cd) - err := testEnvironment.Run() - require.NoError(t, err, "Error deploying test environment") - if testEnvironment.WillUseRemoteRunner() { - return testEnvironment, nil, nil, nil, nil - } - - l := logging.GetTestLogger(t) - chainClient, err := blockchain.NewEVMClient(network, testEnvironment, l) - require.NoError(t, err, "Connecting to blockchain nodes shouldn't fail") - contractDeployer, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") - chainClient.ParallelTransactions(true) - - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(.5)) - require.NoError(t, err, "Funding Chainlink nodes shouldn't fail") - - linkToken, err := contractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - return testEnvironment, chainClient, chainlinkNodes, contractDeployer, linkToken -} diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go deleted file mode 100644 index 7e0ed0111d8..00000000000 --- a/integration-tests/performance/ocr_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package performance - -import ( - "fmt" - "math/big" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/testsetups" -) - -func TestOCRBasic(t *testing.T) { - t.Parallel() - testEnvironment, testNetwork := setupOCRTest(t) - if testEnvironment.WillUseRemoteRunner() { - return - } - l := logging.GetTestLogger(t) - chainClient, err := blockchain.NewEVMClient(testNetwork, testEnvironment, l) - require.NoError(t, err, "Connecting to blockchain nodes shouldn't fail") - contractDeployer, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") - - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") - bootstrapNode, workerNodes := chainlinkNodes[0], chainlinkNodes[1:] - mockServer, err := ctfClient.ConnectMockServer(testEnvironment) - require.NoError(t, err, "Creating mockserver clients shouldn't fail") - - chainClient.ParallelTransactions(true) - - linkTokenContract, err := contractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - ocrInstances, err := actions.DeployOCRContracts(1, linkTokenContract, contractDeployer, workerNodes, chainClient) - require.NoError(t, err) - err = chainClient.WaitForEvents() - require.NoError(t, err, "Error waiting for events") - - profileFunction := func(chainlinkNode *client.ChainlinkClient) { - err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, mockServer, chainClient.GetChainID().String()) - require.NoError(t, err) - err = actions.StartNewRound(1, ocrInstances, chainClient, l) - require.NoError(t, err) - - answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) - - err = mockServer.SetValuePath("ocr", 10) - require.NoError(t, err) - err = actions.StartNewRound(2, ocrInstances, chainClient, l) - require.NoError(t, err) - - answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest OCR answer") - require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) - } - profileTest := testsetups.NewChainlinkProfileTest(testsetups.ChainlinkProfileTestInputs{ - ProfileFunction: profileFunction, - ProfileDuration: time.Minute, - ChainlinkNodes: chainlinkNodes, - }) - t.Cleanup(func() { - CleanupPerformanceTest(t, testEnvironment, chainlinkNodes, profileTest.TestReporter, chainClient) - }) - profileTest.Setup(testEnvironment) - profileTest.Run() -} - -func setupOCRTest(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] - evmConfig := ethereum.New(nil) - if !testNetwork.Simulated { - evmConfig = ethereum.New(ðereum.Props{ - NetworkName: testNetwork.Name, - Simulated: testNetwork.Simulated, - WsURLs: testNetwork.URLs, - }) - } - baseTOML := `[OCR] -Enabled = true - -[P2P] -[P2P.V2] -AnnounceAddresses = ["0.0.0.0:6690"] -ListenAddresses = ["0.0.0.0:6690"]` - - cd := chainlink.New(0, map[string]interface{}{ - "replicas": 6, - "toml": networks.AddNetworksConfig(baseTOML, testNetwork), - }) - - testEnvironment = environment.New(&environment.Config{ - NamespacePrefix: fmt.Sprintf("performance-ocr-%s", strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-")), - Test: t, - PreventPodEviction: true, - }). - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddHelm(evmConfig). - AddHelm(cd) - err := testEnvironment.Run() - require.NoError(t, err, "Error running test environment") - return testEnvironment, testNetwork -} diff --git a/integration-tests/performance/vrf_test.go b/integration-tests/performance/vrf_test.go deleted file mode 100644 index cad4ea5eebd..00000000000 --- a/integration-tests/performance/vrf_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package performance - -import ( - "fmt" - "math/big" - "strings" - "testing" - "time" - - "github.com/google/uuid" - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/testsetups" -) - -func TestVRFBasic(t *testing.T) { - t.Parallel() - l := logging.GetTestLogger(t) - testEnvironment, testNetwork := setupVRFTest(t) - if testEnvironment.WillUseRemoteRunner() { - return - } - - chainClient, err := blockchain.NewEVMClient(testNetwork, testEnvironment, l) - require.NoError(t, err, "Connecting client shouldn't fail") - cd, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") - chainClient.ParallelTransactions(true) - - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(.01)) - require.NoError(t, err, "Funding chainlink nodes with ETH shouldn't fail") - - lt, err := cd.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - bhs, err := cd.DeployBlockhashStore() - require.NoError(t, err, "Deploying Blockhash store shouldn't fail") - coordinator, err := cd.DeployVRFCoordinator(lt.Address(), bhs.Address()) - require.NoError(t, err, "Deploying VRF coordinator shouldn't fail") - consumer, err := cd.DeployVRFConsumer(lt.Address(), coordinator.Address()) - require.NoError(t, err, "Deploying VRF consumer contract shouldn't fail") - err = chainClient.WaitForEvents() - require.NoError(t, err, "Failed to wait for VRF setup contracts to deploy") - - err = lt.Transfer(consumer.Address(), big.NewInt(2e18)) - require.NoError(t, err, "Funding consumer contract shouldn't fail") - _, err = cd.DeployVRFContract() - require.NoError(t, err, "Deploying VRF contract shouldn't fail") - err = chainClient.WaitForEvents() - require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - - profileFunction := func(chainlinkNode *client.ChainlinkClient) { - nodeKey, err := chainlinkNode.MustCreateVRFKey() - require.NoError(t, err, "Creating VRF key shouldn't fail") - l.Debug().Interface("Key JSON", nodeKey).Msg("Created proving key") - pubKeyCompressed := nodeKey.Data.ID - jobUUID := uuid.New() - os := &client.VRFTxPipelineSpec{ - Address: coordinator.Address(), - } - ost, err := os.String() - require.NoError(t, err, "Building observation source spec shouldn't fail") - job, err := chainlinkNode.MustCreateJob(&client.VRFJobSpec{ - Name: fmt.Sprintf("vrf-%s", jobUUID), - CoordinatorAddress: coordinator.Address(), - MinIncomingConfirmations: 1, - PublicKey: pubKeyCompressed, - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - }) - require.NoError(t, err, "Creating VRF Job shouldn't fail") - - oracleAddr, err := chainlinkNode.PrimaryEthAddress() - require.NoError(t, err, "Getting primary ETH address of chainlink node shouldn't fail") - provingKey, err := actions.EncodeOnChainVRFProvingKey(*nodeKey) - require.NoError(t, err, "Encoding on-chain VRF Proving key shouldn't fail") - err = coordinator.RegisterProvingKey( - big.NewInt(1), - oracleAddr, - provingKey, - actions.EncodeOnChainExternalJobID(jobUUID), - ) - require.NoError(t, err, "Registering the on-chain VRF Proving key shouldn't fail") - encodedProvingKeys := make([][2]*big.Int, 0) - encodedProvingKeys = append(encodedProvingKeys, provingKey) - - requestHash, err := coordinator.HashOfKey(testcontext.Get(t), encodedProvingKeys[0]) - require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") - err = consumer.RequestRandomness(requestHash, big.NewInt(1)) - require.NoError(t, err, "Requesting randomness shouldn't fail") - - gom := gomega.NewGomegaWithT(t) - timeout := time.Minute * 2 - gom.Eventually(func(g gomega.Gomega) { - jobRuns, err := chainlinkNodes[0].MustReadRunsByJob(job.Data.ID) - g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - - out, err := consumer.RandomnessOutput(testcontext.Get(t)) - g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") - // Checks that the job has actually run - g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), - fmt.Sprintf("Expected the VRF job to run once or more after %s", timeout)) - - // TODO: This is an imperfect check, given it's a random number, it CAN be 0, but chances are unlikely. - // So we're just checking that the answer has changed to something other than the default (0) - // There's a better formula to ensure that VRF response is as expected, detailed under Technical Walkthrough. - // https://blog.chain.link/chainlink-vrf-on-chain-verifiable-randomness/ - g.Expect(out.Uint64()).ShouldNot(gomega.BeNumerically("==", 0), "Expected the VRF job give an answer other than 0") - l.Debug().Uint64("Output", out.Uint64()).Msg("Randomness fulfilled") - }, timeout, "1s").Should(gomega.Succeed()) - } - profileTest := testsetups.NewChainlinkProfileTest(testsetups.ChainlinkProfileTestInputs{ - ProfileFunction: profileFunction, - ProfileDuration: 30 * time.Second, - ChainlinkNodes: chainlinkNodes, - }) - t.Cleanup(func() { - CleanupPerformanceTest(t, testEnvironment, chainlinkNodes, profileTest.TestReporter, chainClient) - }) - profileTest.Setup(testEnvironment) - profileTest.Run() -} - -func setupVRFTest(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] - evmConfig := ethereum.New(nil) - if !testNetwork.Simulated { - evmConfig = ethereum.New(ðereum.Props{ - NetworkName: testNetwork.Name, - Simulated: testNetwork.Simulated, - WsURLs: testNetwork.URLs, - }) - } - baseTOML := `[WebServer] -HTTPWriteTimout = '300s'` - cd := chainlink.New(0, map[string]interface{}{ - "toml": networks.AddNetworksConfig(baseTOML, testNetwork), - }) - - testEnvironment = environment.New(&environment.Config{ - NamespacePrefix: fmt.Sprintf("smoke-vrf-%s", strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-")), - Test: t, - PreventPodEviction: true, - }). - AddHelm(evmConfig). - AddHelm(cd) - err := testEnvironment.Run() - require.NoError(t, err, "Error running test environment") - return testEnvironment, testNetwork -} diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index dae977d3eea..36b5d3eb985 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -12,6 +12,7 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" @@ -24,6 +25,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( @@ -47,9 +50,9 @@ HistoryDepth = 400 [EVM.GasEstimator] Mode = 'FixedPrice' LimitDefault = 5_000_000` - activeEVMNetwork = networks.MustGetSelectedNetworksFromEnv()[0] + defaultAutomationSettings = map[string]interface{}{ - "toml": networks.AddNetworkDetailedConfig(baseTOML, networkTOML, activeEVMNetwork), + "toml": "", "db": map[string]interface{}{ "stateful": false, "capacity": "1Gi", @@ -67,7 +70,7 @@ LimitDefault = 5_000_000` } defaultReorgEthereumSettings = &reorg.Props{ - NetworkName: activeEVMNetwork.Name, + NetworkName: "", NetworkType: "geth-reorg", Values: map[string]interface{}{ "geth": map[string]interface{}{ @@ -126,9 +129,9 @@ func TestAutomationReorg(t *testing.T) { l := logging.GetTestLogger(t) registryVersions := map[string]ethereum.KeeperRegistryVersion{ - "registry_2_0": ethereum.RegistryVersion_2_0, - "registry_2_1_conditional": ethereum.RegistryVersion_2_1, - "registry_2_1_logtrigger": ethereum.RegistryVersion_2_1, + "registry_2_0": ethereum.RegistryVersion_2_0, + // "registry_2_1_conditional": ethereum.RegistryVersion_2_1, + // "registry_2_1_logtrigger": ethereum.RegistryVersion_2_1, } for name, registryVersion := range registryVersions { @@ -136,22 +139,38 @@ func TestAutomationReorg(t *testing.T) { registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() - network := networks.MustGetSelectedNetworksFromEnv()[0] + config, err := tc.GetConfig("Reorg", tc.Automation) + if err != nil { + t.Fatal(err) + } + + network := networks.MustGetSelectedNetworkConfig(config.Network)[0] defaultAutomationSettings["replicas"] = numberOfNodes - cd := chainlink.New(0, defaultAutomationSettings) + defaultAutomationSettings["toml"] = networks.AddNetworkDetailedConfig(baseTOML, config.Pyroscope, networkTOML, network) + + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) + ctf_config.MightConfigOverridePyroscopeKey(config.Pyroscope, target) + } + + cd := chainlink.NewWithOverride(0, defaultAutomationSettings, config.ChainlinkImage, overrideFn) + + ethSetting := defaultReorgEthereumSettings + ethSetting.NetworkName = network.Name + testEnvironment := environment. New(&environment.Config{ NamespacePrefix: fmt.Sprintf("automation-reorg-%d", automationReorgBlocks), TTL: time.Hour * 1, Test: t}). - AddHelm(reorg.New(defaultReorgEthereumSettings)). + AddHelm(reorg.New(ethSetting)). AddChart(blockscout.New(&blockscout.Props{ Name: "geth-blockscout", - WsURL: activeEVMNetwork.URL, - HttpURL: activeEVMNetwork.HTTPURLs[0]})). + WsURL: network.URL, + HttpURL: network.HTTPURLs[0]})). AddHelm(cd) - err := testEnvironment.Run() + err = testEnvironment.Run() require.NoError(t, err, "Error setting up test environment") if testEnvironment.WillUseRemoteRunner() { @@ -168,7 +187,7 @@ func TestAutomationReorg(t *testing.T) { // Register cleanup for any test t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, &config, chainClient) require.NoError(t, err, "Error tearing down environment") }) diff --git a/integration-tests/reorg/reorg_test.go b/integration-tests/reorg/reorg_test.go deleted file mode 100644 index fc35047d0e4..00000000000 --- a/integration-tests/reorg/reorg_test.go +++ /dev/null @@ -1,228 +0,0 @@ -package reorg - -import ( - "fmt" - "math/big" - "os" - "strings" - "testing" - "time" - - "github.com/google/uuid" - "github.com/onsi/gomega" - "github.com/rs/zerolog/log" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" -) - -const ( - baseDRTOML = ` -[Feature] -LogPoller = true -` - networkDRTOML = ` -Enabled = true -FinalityDepth = %s -LogPollInterval = '1s' - -[EVM.HeadTracker] -HistoryDepth = %s -` -) - -const ( - EVMFinalityDepth = "200" - EVMTrackerHistoryDepth = "400" - reorgBlocks = 10 - minIncomingConfirmations = "200" - timeout = "15m" - interval = "2s" -) - -var ( - networkSettings = blockchain.EVMNetwork{ - Name: "geth", - Simulated: true, - ChainID: 1337, - PrivateKeys: []string{ - "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - }, - ChainlinkTransactionLimit: 500000, - Timeout: blockchain.JSONStrDuration{Duration: 2 * time.Minute}, - MinimumConfirmations: 1, - GasEstimationBuffer: 10000, - } -) - -func TestMain(m *testing.M) { - os.Exit(m.Run()) -} - -func CleanupReorgTest( - t *testing.T, - testEnvironment *environment.Environment, - chainlinkNodes []*client.ChainlinkK8sClient, - chainClient blockchain.EVMClient, -) { - if chainClient != nil { - chainClient.GasStats().PrintStats() - } - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) - require.NoError(t, err, "Error tearing down environment") -} - -func TestDirectRequestReorg(t *testing.T) { - logging.Init() - l := logging.GetTestLogger(t) - testEnvironment := environment.New(&environment.Config{ - TTL: 1 * time.Hour, - Test: t, - }) - err := testEnvironment. - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddChart(blockscout.New(&blockscout.Props{ - WsURL: "ws://geth-ethereum-geth:8546", - HttpURL: "http://geth-ethereum-geth:8544", - })). - AddHelm(reorg.New(&reorg.Props{ - NetworkName: "geth", - NetworkType: "geth-reorg", - Values: map[string]interface{}{ - "geth": map[string]interface{}{ - "genesis": map[string]interface{}{ - "networkId": "1337", - }, - }, - }, - })). - Run() - require.NoError(t, err, "Error deploying test environment") - if testEnvironment.WillUseRemoteRunner() { - return - } - - // related https://app.shortcut.com/chainlinklabs/story/38295/creating-an-evm-chain-via-cli-or-api-immediately-polling-the-nodes-and-returning-an-error - // node must work and reconnect even if network is not working - time.Sleep(90 * time.Second) - activeEVMNetwork = networks.SimulatedEVMNonDev - netCfg := fmt.Sprintf(networkDRTOML, EVMFinalityDepth, EVMTrackerHistoryDepth) - chainlinkDeployment := chainlink.New(0, map[string]interface{}{ - "replicas": 1, - "toml": networks.AddNetworkDetailedConfig(baseDRTOML, netCfg, activeEVMNetwork), - }) - - err = testEnvironment.AddHelm(chainlinkDeployment).Run() - require.NoError(t, err, "Error adding to test environment") - - chainClient, err := blockchain.NewEVMClient(networkSettings, testEnvironment, l) - require.NoError(t, err, "Error connecting to blockchain") - cd, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Error building contract deployer") - chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) - require.NoError(t, err, "Error connecting to Chainlink nodes") - ms, err := ctfClient.ConnectMockServer(testEnvironment) - require.NoError(t, err, "Error connecting to Mockserver") - - t.Cleanup(func() { - CleanupReorgTest(t, testEnvironment, chainlinkNodes, chainClient) - }) - - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(10)) - require.NoError(t, err, "Error funding Chainlink nodes") - - lt, err := cd.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - oracle, err := cd.DeployOracle(lt.Address()) - require.NoError(t, err, "Deploying Oracle Contract shouldn't fail") - consumer, err := cd.DeployAPIConsumer(lt.Address()) - require.NoError(t, err, "Deploying Consumer Contract shouldn't fail") - err = chainClient.SetDefaultWallet(0) - require.NoError(t, err, "Setting default wallet shouldn't fail") - err = lt.Transfer(consumer.Address(), big.NewInt(2e18)) - require.NoError(t, err, "Transferring %d to consumer contract shouldn't fail", big.NewInt(2e18)) - - err = ms.SetValuePath("/variable", 5) - require.NoError(t, err, "Setting mockserver value path shouldn't fail") - - jobUUID := uuid.New() - - bta := client.BridgeTypeAttributes{ - Name: fmt.Sprintf("five-%s", jobUUID.String()), - URL: fmt.Sprintf("%s/variable", ms.Config.ClusterURL), - } - err = chainlinkNodes[0].MustCreateBridge(&bta) - require.NoError(t, err, "Creating bridge shouldn't fail") - - drps := &client.DirectRequestTxPipelineSpec{ - BridgeTypeAttributes: bta, - DataPath: "data,result", - } - ost, err := drps.String() - require.NoError(t, err, "Building observation source spec shouldn't fail") - - _, err = chainlinkNodes[0].MustCreateJob(&client.DirectRequestJobSpec{ - Name: "direct_request", - MinIncomingConfirmations: minIncomingConfirmations, - ContractAddress: oracle.Address(), - EVMChainID: chainClient.GetChainID().String(), - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - }) - require.NoError(t, err, "Creating direct_request job shouldn't fail") - - rc, err := NewReorgController( - &ReorgConfig{ - FromPodLabel: reorg.TXNodesAppLabel, - ToPodLabel: reorg.MinerNodesAppLabel, - Network: chainClient, - Env: testEnvironment, - BlockConsensusThreshold: 3, - Timeout: 1800 * time.Second, - }, - ) - require.NoError(t, err, "Error getting reorg controller") - rc.ReOrg(reorgBlocks) - rc.WaitReorgStarted() - - jobUUIDReplaces := strings.Replace(jobUUID.String(), "-", "", 4) - var jobID [32]byte - copy(jobID[:], jobUUIDReplaces) - err = consumer.CreateRequestTo( - oracle.Address(), - jobID, - big.NewInt(1e18), - fmt.Sprintf("%s/variable", ms.Config.ClusterURL), - "data,result", - big.NewInt(100), - ) - require.NoError(t, err, "Calling oracle contract shouldn't fail") - - err = rc.WaitDepthReached() - require.NoError(t, err, "Error waiting for depth to be reached") - - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(testcontext.Get(t)) - g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") - g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") - log.Debug().Int64("Data", d.Int64()).Msg("Found on chain") - g.Expect(d.Int64()).Should(gomega.BeNumerically("==", 5), "Expected the on-chain data to be 5, but found %d", d.Int64()) - }, timeout, interval).Should(gomega.Succeed()) -} diff --git a/integration-tests/scripts/buildTests b/integration-tests/scripts/buildTests index edf4043ce1c..749bb545110 100755 --- a/integration-tests/scripts/buildTests +++ b/integration-tests/scripts/buildTests @@ -21,6 +21,6 @@ OIFS=$IFS IFS=' ' for x in $tosplit do - go test -c ./"${x}" + go test -c -tags embed ./"${x}" done IFS=$OIFS diff --git a/integration-tests/smoke/README.md b/integration-tests/smoke/README.md index 1dedc4e3840..266720c7bc6 100644 --- a/integration-tests/smoke/README.md +++ b/integration-tests/smoke/README.md @@ -52,18 +52,32 @@ Here is an example for 3 nodes cluster ``` ### Running against Live Testnets +1. Prepare your `overrides.toml` file with selected network and CL image name and version and save anywhere inside `integration-tests` folder. +```toml +[ChainlinkImage] +image="your-image" +version="your-version" +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai=["https://http.endpoint.com"] + +[Network.RpcWsUrls] +polygon_mumbai=["wss://ws.endpoint.com"] + +[Network.WalletKeys] +polygon_mumbai=["my_so_private_key"] ``` -SELECTED_NETWORKS= \ -_KEYS= \ -_URLS= \ -_HTTP_URLS= \ +Then execute: +```bash go test -v -run ${TestName} ``` ### Debugging CL client API calls -``` +```bash export CL_CLIENT_DEBUG=true ``` \ No newline at end of file diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 0bdb6cf1f4e..3f69e7f829c 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -17,8 +17,8 @@ import ( ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" - - "github.com/kelseyhightower/envconfig" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types" "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" @@ -68,18 +68,27 @@ var ( func TestMain(m *testing.M) { logging.Init() - fmt.Printf("Running Smoke Test on %s\n", networks.MustGetSelectedNetworksFromEnv()[0].Name) // Print to get around disabled logging - fmt.Printf("Chainlink Image %s\n", os.Getenv("CHAINLINK_IMAGE")) // Print to get around disabled logging - fmt.Printf("Chainlink Version %s\n", os.Getenv("CHAINLINK_VERSION")) // Print to get around disabled logging + // config, err := tc.GetConfig(tc.NoTest, tc.Smoke, tc.Automation) + // if err != nil { + // panic(err) + // } + // fmt.Printf("Running Smoke Test on %s\n", networks.MustGetSelectedNetworkConfig(config.Network)[0].Name) // Print to get around disabled logging + // fmt.Printf("Chainlink Image %v\n", config.ChainlinkImage.Image) // Print to get around disabled logging + // fmt.Printf("Chainlink Version %v\n", config.ChainlinkImage.Version) // Print to get around disabled logging os.Exit(m.Run()) } func TestAutomationBasic(t *testing.T) { - SetupAutomationBasic(t, false) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } + SetupAutomationBasic(t, false, &config) } -func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { +func SetupAutomationBasic(t *testing.T, nodeUpgrade bool, automationTestConfig types.AutomationTestConfig) { t.Parallel() + registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1_conditional": ethereum.RegistryVersion_2_1, @@ -93,19 +102,14 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { name := name registryVersion := registryVersion t.Run(name, func(t *testing.T) { + cfg := tc.MustCopy(automationTestConfig) t.Parallel() l := logging.GetTestLogger(t) - var ( - upgradeImage string - upgradeVersion string - err error - ) + var err error if nodeUpgrade { - upgradeImage = os.Getenv("UPGRADE_IMAGE") - upgradeVersion = os.Getenv("UPGRADE_VERSION") - if len(upgradeImage) == 0 || len(upgradeVersion) == 0 { - t.Fatal("UPGRADE_IMAGE and UPGRADE_VERSION must be set to upgrade nodes") + if cfg.GetChainlinkUpgradeImageConfig() == nil { + t.Fatal("[ChainlinkUpgradeImage] must be set in TOML config to upgrade nodes") } } @@ -116,7 +120,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { isMercury := isMercuryV02 || isMercuryV03 a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, isMercuryV02, isMercuryV03, + t, registryVersion, automationDefaultRegistryConfig, isMercuryV02, isMercuryV03, automationTestConfig, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -172,10 +176,11 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { l.Info().Msgf("Total time taken to get 5 performs for each upkeep: %s", time.Since(startTime)) if nodeUpgrade { + require.NotNil(t, cfg.GetChainlinkImageConfig(), "unable to upgrade node version, [ChainlinkUpgradeImage] was not set, must both a new image or a new version") expect := 5 // Upgrade the nodes one at a time and check that the upkeeps are still being performed for i := 0; i < 5; i++ { - err = actions.UpgradeChainlinkNodeVersionsLocal(upgradeImage, upgradeVersion, a.DockerEnv.ClCluster.Nodes[i]) + err = actions.UpgradeChainlinkNodeVersionsLocal(*cfg.GetChainlinkImageConfig().Image, *cfg.GetChainlinkImageConfig().Version, a.DockerEnv.ClCluster.Nodes[i]) require.NoError(t, err, "Error when upgrading node %d", i) time.Sleep(time.Second * 10) expect = expect + 5 @@ -229,8 +234,13 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } + a := setupAutomationTestDocker( - t, ethereum.RegistryVersion_2_1, automationDefaultRegistryConfig, false, false, + t, ethereum.RegistryVersion_2_1, automationDefaultRegistryConfig, false, false, &config, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -308,7 +318,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) } - err := a.ChainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") var countersAfterSetNoMatch = make([]*big.Int, len(upkeepIDs)) @@ -404,8 +414,12 @@ func TestAutomationAddFunds(t *testing.T) { registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -432,7 +446,7 @@ func TestAutomationAddFunds(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Grant permission to the registry to fund the upkeep - err := a.LinkToken.Approve(a.Registry.Address(), big.NewInt(9e18)) + err = a.LinkToken.Approve(a.Registry.Address(), big.NewInt(9e18)) require.NoError(t, err, "Could not approve permissions for the registry on the link token contract") err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") @@ -467,8 +481,12 @@ func TestAutomationPauseUnPause(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -503,7 +521,7 @@ func TestAutomationPauseUnPause(t *testing.T) { require.NoError(t, err, "Could not pause upkeep at index %d", i) } - err := a.ChainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for upkeeps to be paused") var countersAfterPause = make([]*big.Int, len(upkeepIDs)) @@ -562,8 +580,12 @@ func TestAutomationRegisterUpkeep(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -645,8 +667,12 @@ func TestAutomationPauseRegistry(t *testing.T) { registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -675,7 +701,7 @@ func TestAutomationPauseRegistry(t *testing.T) { }, "4m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~1m for performing each upkeep once, ~2m buffer // Pause the registry - err := a.Registry.Pause() + err = a.Registry.Pause() require.NoError(t, err, "Error pausing registry") err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for registry to pause") @@ -715,8 +741,12 @@ func TestAutomationKeeperNodesDown(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -750,7 +780,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { }, "4m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~1m for performing each upkeep once, ~2m buffer // Take down 1 node. Currently, using 4 nodes so f=1 and is the max nodes that can go down. - err := nodesWithoutBootstrap[0].MustDeleteJob("1") + err = nodesWithoutBootstrap[0].MustDeleteJob("1") require.NoError(t, err, "Error deleting job from Chainlink node") err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for blockchain events") @@ -814,8 +844,12 @@ func TestAutomationPerformSimulation(t *testing.T) { registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumersPerformance, _ := actions.DeployPerformanceConsumers( @@ -848,7 +882,7 @@ func TestAutomationPerformSimulation(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Set performGas on consumer to be low, so that performUpkeep starts becoming successful - err := consumerPerformance.SetPerformGasToBurn(testcontext.Get(t), big.NewInt(100000)) + err = consumerPerformance.SetPerformGasToBurn(testcontext.Get(t), big.NewInt(100000)) require.NoError(t, err, "Perform gas should be set successfully on consumer") err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for set perform gas tx") @@ -878,8 +912,12 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) consumersPerformance, upkeepIDs := actions.DeployPerformanceConsumers( @@ -914,7 +952,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Increase gas limit for the upkeep, higher than the performGasBurn - err := a.Registry.SetUpkeepGasLimit(upkeepID, uint32(4500000)) + err = a.Registry.SetUpkeepGasLimit(upkeepID, uint32(4500000)) require.NoError(t, err, "Error setting upkeep gas limit") err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetUpkeepGasLimit tx") @@ -992,8 +1030,13 @@ func TestUpdateCheckData(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } + a := setupAutomationTestDocker( - t, registryVersion, automationDefaultRegistryConfig, false, false, + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, ) performDataChecker, upkeepIDs := actions.DeployPerformDataCheckerConsumers( @@ -1027,7 +1070,7 @@ func TestUpdateCheckData(t *testing.T) { require.NoError(t, err, "Could not update check data for upkeep at index %d", i) } - err := a.ChainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error while waiting for check data update") // retrieve new check data for all upkeeps @@ -1052,23 +1095,20 @@ func TestUpdateCheckData(t *testing.T) { } } -type TestConfig struct { - ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:"1"` -} - func setupAutomationTestDocker( t *testing.T, registryVersion ethereum.KeeperRegistryVersion, registryConfig contracts.KeeperRegistrySettings, isMercuryV02 bool, isMercuryV03 bool, + automationTestConfig types.AutomationTestConfig, ) automationv2.AutomationTest { require.False(t, isMercuryV02 && isMercuryV03, "Cannot run test with both Mercury V02 and V03 on") l := logging.GetTestLogger(t) // Add registry version to config registryConfig.RegistryVersion = registryVersion - network := networks.MustGetSelectedNetworksFromEnv()[0] + network := networks.MustGetSelectedNetworkConfig(automationTestConfig.GetNetworkConfig())[0] // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) @@ -1084,17 +1124,16 @@ func setupAutomationTestDocker( //launch the environment var env *test_env.CLClusterTestEnv var err error - var testConfig TestConfig - err = envconfig.Process("AUTOMATION", &testConfig) require.NoError(t, err) - l.Debug().Msgf("Funding amount: %f", testConfig.ChainlinkNodeFunding) + l.Debug().Msgf("Funding amount: %f", *automationTestConfig.GetCommonConfig().ChainlinkNodeFunding) clNodesCount := 5 if isMercuryV02 || isMercuryV03 { env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(automationTestConfig). WithGeth(). WithMockAdapter(). - WithFunding(big.NewFloat(testConfig.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*automationTestConfig.GetCommonConfig().ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "Error deploying test environment for Mercury") @@ -1120,19 +1159,20 @@ func setupAutomationTestDocker( node.SetChainConfig(clNodeConfig, wsUrls, httpUrls, network, false) - err = env.StartClCluster(clNodeConfig, clNodesCount, secretsConfig) + err = env.StartClCluster(clNodeConfig, clNodesCount, secretsConfig, automationTestConfig) require.NoError(t, err, "Error starting CL nodes test environment for Mercury") - err = env.FundChainlinkNodes(big.NewFloat(testConfig.ChainlinkNodeFunding)) + err = env.FundChainlinkNodes(big.NewFloat(*automationTestConfig.GetCommonConfig().ChainlinkNodeFunding)) require.NoError(t, err, "Error funding CL nodes") } else { env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(automationTestConfig). WithGeth(). WithMockAdapter(). WithCLNodes(clNodesCount). WithCLNodeConfig(clNodeConfig). - WithFunding(big.NewFloat(testConfig.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*automationTestConfig.GetCommonConfig().ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "Error deploying test environment") diff --git a/integration-tests/smoke/automation_upgrade_test.go b/integration-tests/smoke/automation_upgrade_test.go index 28285543621..6601457de8b 100644 --- a/integration-tests/smoke/automation_upgrade_test.go +++ b/integration-tests/smoke/automation_upgrade_test.go @@ -2,8 +2,14 @@ package smoke import ( "testing" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestAutomationNodeUpgrade(t *testing.T) { - SetupAutomationBasic(t, true) + config, err := tc.GetConfig(t.Name(), tc.Automation) + if err != nil { + t.Fatal(err, "Error getting config") + } + SetupAutomationBasic(t, true, &config) } diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index df1b85882e9..b6e04612aca 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -13,14 +13,21 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestCronBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Cron) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodes(1). @@ -65,8 +72,14 @@ func TestCronJobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Cron) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodes(1). diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 345971e8922..c8cec4e385b 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -19,14 +19,21 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestFluxBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.Flux) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodes(3). diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index fd8dfc780af..5c603c5b08f 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -12,14 +12,21 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestForwarderOCRBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.ForwarderOcr) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithForwarders(). diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index f1a01643f5e..9385fb4f9a5 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -17,14 +17,22 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestForwarderOCR2Basic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.ForwarderOcr2) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index cac5aa93d36..0ebbc1e083d 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -22,6 +22,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) const ( @@ -86,7 +88,12 @@ func TestKeeperBasicSmoke(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -100,7 +107,7 @@ func TestKeeperBasicSmoke(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -162,7 +169,12 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -176,7 +188,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -266,7 +278,12 @@ func TestKeeperSimulation(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumersPerformance, upkeepIDs := actions.DeployPerformanceKeeperContracts( t, registryVersion, @@ -284,7 +301,7 @@ func TestKeeperSimulation(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -339,7 +356,11 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumersPerformance, upkeepIDs := actions.DeployPerformanceKeeperContracts( t, registryVersion, @@ -357,7 +378,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -452,7 +473,11 @@ func TestKeeperRegisterUpkeep(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, registrar, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -466,7 +491,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -541,7 +566,11 @@ func TestKeeperAddFunds(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -555,7 +584,7 @@ func TestKeeperAddFunds(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -604,7 +633,11 @@ func TestKeeperRemove(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -618,7 +651,7 @@ func TestKeeperRemove(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -682,7 +715,11 @@ func TestKeeperPauseRegistry(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -696,7 +733,7 @@ func TestKeeperPauseRegistry(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -743,7 +780,11 @@ func TestKeeperPauseRegistry(t *testing.T) { func TestKeeperMigrateRegistry(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, ethereum.RegistryVersion_1_2, @@ -757,7 +798,7 @@ func TestKeeperMigrateRegistry(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -835,7 +876,11 @@ func TestKeeperNodeDown(t *testing.T) { t.Run(fmt.Sprintf("registry_1_%d", registryVersion), func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -941,7 +986,11 @@ type nodeAndJob struct { func TestKeeperPauseUnPauseUpkeep(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, ethereum.RegistryVersion_1_3, @@ -955,7 +1004,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -1031,7 +1080,11 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { func TestKeeperUpdateCheckData(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, performDataChecker, upkeepIDs := actions.DeployPerformDataCheckerContracts( t, ethereum.RegistryVersion_1_3, @@ -1046,7 +1099,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") @@ -1090,7 +1143,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { }, "3m", "1s").Should(gomega.Succeed()) } -func setupKeeperTest(t *testing.T) ( +func setupKeeperTest(t *testing.T, config *tc.TestConfig) ( blockchain.EVMClient, []*client.ChainlinkClient, contracts.ContractDeployer, @@ -1107,6 +1160,7 @@ func setupKeeperTest(t *testing.T) ( env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(config). WithGeth(). WithCLNodes(5). WithCLNodeConfig(clNodeConfig). @@ -1128,10 +1182,14 @@ func setupKeeperTest(t *testing.T) ( func TestKeeperJobReplacement(t *testing.T) { t.Parallel() + l := logging.GetTestLogger(t) registryVersion := ethereum.RegistryVersion_1_3 + config, err := tc.GetConfig("Smoke", tc.Keeper) + if err != nil { + t.Fatal(err) + } - l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -1145,7 +1203,7 @@ func TestKeeperJobReplacement(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) require.NoError(t, err, "Error creating keeper jobs") err = chainClient.WaitForEvents() require.NoError(t, err, "Error creating keeper jobs") diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 4eadf6b3913..593b4eb879a 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -17,6 +17,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + lp_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/log_poller" logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -25,262 +27,42 @@ import ( // consistency test with no network disruptions with approximate emission of 1500-1600 logs per second for ~110-120 seconds // 6 filters are registered func TestLogPollerFewFiltersFixedDepth(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 4, - UseFinalityTag: false, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, - }, - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - // this was added only to support triggering tests from CI with custom values - err := cfg.OverrideFromEnv() - require.NoError(t, err, fmt.Sprintf("Error overriding config from env: %v", err)) - - executeBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t) } func TestLogPollerFewFiltersFinalityTag(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 4, - UseFinalityTag: true, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, - }, - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - executeBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t) } // consistency test with no network disruptions with approximate emission of 1000-1100 logs per second for ~110-120 seconds // 900 filters are registered func TestLogPollerManyFiltersFixedDepth(t *testing.T) { t.Skip("Execute manually, when needed as it runs for a long time") - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 300, - EventsPerTx: 3, - UseFinalityTag: false, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 30, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, - }, - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - executeBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t) } func TestLogPollerManyFiltersFinalityTag(t *testing.T) { t.Skip("Execute manually, when needed as it runs for a long time") - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 300, - EventsPerTx: 3, - UseFinalityTag: true, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 30, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, - }, - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - executeBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t) } // consistency test that introduces random distruptions by pausing either Chainlink or Postgres containers for random interval of 5-20 seconds // with approximate emission of 520-550 logs per second for ~110 seconds // 6 filters are registered func TestLogPollerWithChaosFixedDepth(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 100, - UseFinalityTag: false, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 70, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 100, - MaxEmitWaitTimeMs: 300, - }, - }, - ChaosConfig: &logpoller.ChaosConfig{ - ExperimentCount: 4, - TargetComponent: "chainlink", - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - executeBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t) } func TestLogPollerWithChaosFinalityTag(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 100, - UseFinalityTag: true, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 120, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 100, - MaxEmitWaitTimeMs: 300, - }, - }, - ChaosConfig: &logpoller.ChaosConfig{ - ExperimentCount: 6, - TargetComponent: "chainlink", - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - executeBasicLogPollerTest(t, &cfg) -} - -func TestLogPollerWithChaosPostgresFinalityTag(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 100, - UseFinalityTag: true, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 120, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 100, - MaxEmitWaitTimeMs: 300, - }, - }, - ChaosConfig: &logpoller.ChaosConfig{ - ExperimentCount: 6, - TargetComponent: "postgres", - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - - executeBasicLogPollerTest(t, &cfg) + executeBasicLogPollerTest(t) } func TestLogPollerWithChaosPostgresFixedDepth(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 100, - UseFinalityTag: false, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 120, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 100, - MaxEmitWaitTimeMs: 300, - }, - }, - ChaosConfig: &logpoller.ChaosConfig{ - ExperimentCount: 6, - TargetComponent: "postgres", - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit + executeBasicLogPollerTest(t) +} - executeBasicLogPollerTest(t, &cfg) +func TestLogPollerWithChaosPostgresFinalityTag(t *testing.T) { + executeBasicLogPollerTest(t) } // consistency test that registers filters after events were emitted and then triggers replay via API @@ -289,72 +71,31 @@ func TestLogPollerWithChaosPostgresFixedDepth(t *testing.T) { // with approximate emission of 24 logs per second for ~110 seconds // 6 filters are registered func TestLogPollerReplayFixedDepth(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 4, - UseFinalityTag: false, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, - }, - }, - } - - eventsToEmit := []abi.Event{} - for _, event := range logpoller.EmitterABI.Events { - eventsToEmit = append(eventsToEmit, event) - } - - cfg.General.EventsToEmit = eventsToEmit - consistencyTimeout := "5m" - - executeLogPollerReplay(t, &cfg, consistencyTimeout) + executeLogPollerReplay(t, "5m") } func TestLogPollerReplayFinalityTag(t *testing.T) { - cfg := logpoller.Config{ - General: &logpoller.General{ - Generator: logpoller.GeneratorType_Looped, - Contracts: 2, - EventsPerTx: 4, - UseFinalityTag: true, - }, - LoopedConfig: &logpoller.LoopedConfig{ - ContractConfig: logpoller.ContractConfig{ - ExecutionCount: 100, - }, - FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 200, - MaxEmitWaitTimeMs: 500, - }, - }, - } + executeLogPollerReplay(t, "5m") +} + +// HELPER FUNCTIONS +func executeBasicLogPollerTest(t *testing.T) { + testConfig, err := tc.GetConfig(t.Name(), tc.LogPoller) + require.NoError(t, err, "Error getting config") eventsToEmit := []abi.Event{} for _, event := range logpoller.EmitterABI.Events { eventsToEmit = append(eventsToEmit, event) } + cfg := testConfig.LogPoller cfg.General.EventsToEmit = eventsToEmit - consistencyTimeout := "5m" - executeLogPollerReplay(t, &cfg, consistencyTimeout) -} - -func executeBasicLogPollerTest(t *testing.T, cfg *logpoller.Config) { l := logging.GetTestLogger(t) coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ - lpTestEnv := prepareEnvironment(l, t, cfg) + lpTestEnv := prepareEnvironment(l, t, &testConfig) testEnv := lpTestEnv.testEnv - var err error // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) @@ -417,13 +158,23 @@ func executeBasicLogPollerTest(t *testing.T, cfg *logpoller.Config) { conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, cfg, startBlock, endBlock, "5m") } -func executeLogPollerReplay(t *testing.T, cfg *logpoller.Config, consistencyTimeout string) { +func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { + testConfig, err := tc.GetConfig(t.Name(), tc.LogPoller) + require.NoError(t, err, "Error getting config") + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg := testConfig.LogPoller + cfg.General.EventsToEmit = eventsToEmit + l := logging.GetTestLogger(t) coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ - lpTestEnv := prepareEnvironment(l, t, cfg) + lpTestEnv := prepareEnvironment(l, t, &testConfig) testEnv := lpTestEnv.testEnv - var err error // Save block number before starting to emit events, so that we can later use it when querying logs sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) @@ -505,7 +256,8 @@ type logPollerEnvironment struct { // prepareEnvironment prepares environment for log poller tests by starting DON, private Ethereum network, // deploying registry and log emitter contracts and registering log triggered upkeeps -func prepareEnvironment(l zerolog.Logger, t *testing.T, cfg *logpoller.Config) logPollerEnvironment { +func prepareEnvironment(l zerolog.Logger, t *testing.T, testConfig *tc.TestConfig) logPollerEnvironment { + cfg := testConfig.LogPoller if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { l.Warn().Msg("No events to emit specified, using all events from log emitter contract") for _, event := range logpoller.EmitterABI.Events { @@ -517,11 +269,17 @@ func prepareEnvironment(l zerolog.Logger, t *testing.T, cfg *logpoller.Config) l var ( err error - upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) + upKeepsNeeded = *cfg.General.Contracts * len(cfg.General.EventsToEmit) ) chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := logpoller.SetupLogPollerTestDocker( - t, ethereum.RegistryVersion_2_1, logpoller.DefaultOCRRegistryConfig, upKeepsNeeded, time.Duration(500*time.Millisecond), cfg.General.UseFinalityTag, + t, + ethereum.RegistryVersion_2_1, + logpoller.DefaultOCRRegistryConfig, + upKeepsNeeded, + time.Duration(500*time.Millisecond), + *cfg.General.UseFinalityTag, + testConfig, ) _, upkeepIDs := actions.DeployConsumers( @@ -591,7 +349,7 @@ func waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l zerolog.Logger, core // conditionallyWaitUntilNodesHaveTheSameLogsAsEvm checks whether all CL nodes have the same number of logs as EVM node // if not, then it prints missing logs and wait for some time and checks again -func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, allNodesLogCountMatches bool, lpTestEnv logPollerEnvironment, cfg *logpoller.Config, startBlock, endBlock int64, waitDuration string) { +func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, allNodesLogCountMatches bool, lpTestEnv logPollerEnvironment, cfg *lp_config.Config, startBlock, endBlock int64, waitDuration string) { logCountWaitDuration, err := time.ParseDuration(waitDuration) require.NoError(t, err, "Error parsing log count wait duration") diff --git a/integration-tests/smoke/log_poller_test.go_test_list.json b/integration-tests/smoke/log_poller_test.go_test_list.json index 5193c3fd06c..2159654e283 100644 --- a/integration-tests/smoke/log_poller_test.go_test_list.json +++ b/integration-tests/smoke/log_poller_test.go_test_list.json @@ -35,7 +35,7 @@ { "name": "TestLogPollerManyFiltersFixedDepth", "label": "ubuntu-latest" - }, + }, { "name": "TestLogPollerManyFiltersFinalityTag", "label": "ubuntu-latest" diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index f45c324faed..9e06ee527cd 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -11,12 +11,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" ) @@ -42,11 +42,17 @@ func TestOCRv2Basic(t *testing.T) { l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + config, err := tc.GetConfig("Smoke", tc.OCR2) + if err != nil { + t.Fatal(err) + } + + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) require.NoError(t, err, "Error building ethereum network config") env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), @@ -124,11 +130,17 @@ func TestOCRv2Request(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + config, err := tc.GetConfig("Smoke", tc.ForwarderOcr) + if err != nil { + t.Fatal(err) + } + + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) require.NoError(t, err, "Error building ethereum network config") env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), @@ -202,8 +214,14 @@ func TestOCRv2Request(t *testing.T) { func TestOCRv2JobReplacement(t *testing.T) { l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.OCR2) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 773476826c7..c01ac46fbbc 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -10,6 +10,7 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" @@ -23,12 +24,21 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) +var ocr2vrfSmokeConfig *testconfig.TestConfig + func TestOCR2VRFRedeemModel(t *testing.T) { t.Parallel() t.Skip("VRFv3 is on pause, skipping") l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.OCR2) + if err != nil { + t.Fatal(err) + } + testEnvironment, testNetwork := setupOCR2VRFEnvironment(t) if testEnvironment.WillUseRemoteRunner() { return @@ -44,7 +54,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { require.NoError(t, err, "Retreiving on-chain wallet addresses for chainlink nodes shouldn't fail") t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.ErrorLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.ErrorLevel, &config, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -91,6 +101,11 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { t.Parallel() t.Skip("VRFv3 is on pause, skipping") l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.OCR2) + if err != nil { + t.Fatal(err) + } + testEnvironment, testNetwork := setupOCR2VRFEnvironment(t) if testEnvironment.WillUseRemoteRunner() { return @@ -106,7 +121,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { require.NoError(t, err, "Retreiving on-chain wallet addresses for chainlink nodes shouldn't fail") t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.ErrorLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.ErrorLevel, &config, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -149,7 +164,15 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { } func setupOCR2VRFEnvironment(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] + if ocr2vrfSmokeConfig == nil { + c, err := testconfig.GetConfig("Smoke", tc.OCR2VRF) + if err != nil { + t.Fatal(err) + } + ocr2vrfSmokeConfig = &c + } + + testNetwork = networks.MustGetSelectedNetworkConfig(ocr2vrfSmokeConfig.Network)[0] evmConfig := eth.New(nil) if !testNetwork.Simulated { evmConfig = eth.New(ð.Props{ @@ -159,14 +182,20 @@ func setupOCR2VRFEnvironment(t *testing.T) (testEnvironment *environment.Environ }) } - cd := chainlink.New(0, map[string]interface{}{ + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(ocr2vrfSmokeConfig.ChainlinkImage, target) + ctf_config.MightConfigOverridePyroscopeKey(ocr2vrfSmokeConfig.Pyroscope, target) + } + + cd := chainlink.NewWithOverride(0, map[string]interface{}{ "replicas": 6, "toml": networks.AddNetworkDetailedConfig( config.BaseOCR2Config, + ocr2vrfSmokeConfig.Pyroscope, config.DefaultOCR2VRFNetworkDetailTomlConfig, testNetwork, ), - }) + }, ocr2vrfSmokeConfig.ChainlinkImage, overrideFn) testEnvironment = environment.New(&environment.Config{ NamespacePrefix: fmt.Sprintf("smoke-ocr2vrf-%s", strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-")), diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 5f091835894..549d6b0a63b 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -11,17 +11,25 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestOCRBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + config, err := tc.GetConfig("Smoke", tc.OCR) + if err != nil { + t.Fatal(err) + } + + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) require.NoError(t, err, "Error building ethereum network config") env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodes(6). @@ -67,8 +75,14 @@ func TestOCRJobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.OCR) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodes(6). diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index dc08b19b16f..f7f5c54069e 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -16,14 +16,22 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestRunLogBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.RunLog) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithMockAdapter(). WithCLNodes(1). diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 51ea3828cfd..c296fcd0f34 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -17,14 +17,21 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv1" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestVRFBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.VRF) + if err != nil { + t.Fatal(err) + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(.1)). @@ -111,9 +118,14 @@ func TestVRFBasic(t *testing.T) { func TestVRFJobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.VRF) + if err != nil { + t.Fatal(err) + } env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(.1)). diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 7274d8b8772..0d136b62a98 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -9,47 +9,48 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestVRFv2Basic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.VRFv2) + require.NoError(t, err, "Error getting config") + useVRFOwner := false useTestCoordinator := false - - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) require.NoError(t, err, "Error building ethereum network config") - var vrfv2Config vrfv2_config.VRFV2Config - err = envconfig.Process("VRFV2", &vrfv2Config) - require.NoError(t, err) - env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2.General.LinkNativeFeedResponse)) require.NoError(t, err) linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err) @@ -60,7 +61,7 @@ func TestVRFv2Basic(t *testing.T) { numberOfTxKeysToCreate := 1 vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, - vrfv2Config, + &config, useVRFOwner, useTestCoordinator, linkToken, @@ -81,7 +82,7 @@ func TestVRFv2Basic(t *testing.T) { vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) t.Run("Request Randomness", func(t *testing.T) { - testConfig := vrfv2Config + configCopy := config.MustCopy().(tc.TestConfig) subBalanceBeforeRequest := subscription.Balance jobRunsBeforeTest, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2Data.VRFJob.Data.ID) @@ -89,14 +90,17 @@ func TestVRFv2Basic(t *testing.T) { // test and assert randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, - vrfv2Data, subID, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, - l, + vrfv2Data, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -115,7 +119,7 @@ func TestVRFv2Basic(t *testing.T) { require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, *config.VRFv2.General.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") @@ -123,10 +127,10 @@ func TestVRFv2Basic(t *testing.T) { }) t.Run("Direct Funding (VRFV2Wrapper)", func(t *testing.T) { - testConfig := vrfv2Config + configCopy := config.MustCopy().(tc.TestConfig) wrapperContracts, wrapperSubID, err := vrfv2_actions.SetupVRFV2WrapperEnvironment( env, - testConfig, + &configCopy, linkToken, mockETHLinkFeed, vrfv2Contracts.Coordinator, @@ -145,14 +149,17 @@ func TestVRFv2Basic(t *testing.T) { // Request Randomness and wait for fulfillment event randomWordsFulfilledEvent, err := vrfv2_actions.DirectFundingRequestRandomnessAndWaitForFulfillment( + l, wrapperConsumer, vrfv2Contracts.Coordinator, - vrfv2Data, *wrapperSubID, - vrfv2Config.RandomnessRequestCountPerRequest, - vrfv2Config, - testConfig.RandomWordsFulfilledEventTimeout, - l, + vrfv2Data, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, ) require.NoError(t, err, "Error requesting randomness and waiting for fulfilment") @@ -175,7 +182,7 @@ func TestVRFv2Basic(t *testing.T) { require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) // Check random word count - require.Equal(t, testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) + require.Equal(t, *configCopy.VRFv2.General.NumberOfWords, uint32(len(consumerStatus.RandomWords))) for _, w := range consumerStatus.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") @@ -196,10 +203,10 @@ func TestVRFv2Basic(t *testing.T) { }) t.Run("Oracle Withdraw", func(t *testing.T) { - testConfig := vrfv2Config + configCopy := config.MustCopy().(tc.TestConfig) subIDsForOracleWithDraw, err := vrfv2_actions.CreateFundSubsAndAddConsumers( env, - testConfig, + big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, vrfv2Contracts.Coordinator, vrfv2Contracts.LoadTestConsumers, @@ -210,14 +217,17 @@ func TestVRFv2Basic(t *testing.T) { subIDForOracleWithdraw := subIDsForOracleWithDraw[0] fulfilledEventLink, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, - vrfv2Data, subIDForOracleWithdraw, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, - l, + vrfv2Data, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, ) require.NoError(t, err) @@ -249,10 +259,10 @@ func TestVRFv2Basic(t *testing.T) { }) t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { - testConfig := vrfv2Config + configCopy := config.MustCopy().(tc.TestConfig) subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( env, - testConfig, + big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, vrfv2Contracts.Coordinator, vrfv2Contracts.LoadTestConsumers, @@ -323,14 +333,13 @@ func TestVRFv2Basic(t *testing.T) { }) t.Run("Owner Canceling Sub And Returning Funds While Having Pending Requests", func(t *testing.T) { - testConfig := vrfv2Config - + configCopy := config.MustCopy().(tc.TestConfig) // Underfund subscription to force fulfillments to fail - testConfig.SubscriptionFundingAmountLink = float64(0.000000000000000001) // 1 Juel + configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) // 1 Juel subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( env, - testConfig, + big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, vrfv2Contracts.Coordinator, vrfv2Contracts.LoadTestConsumers, @@ -354,14 +363,17 @@ func TestVRFv2Basic(t *testing.T) { // Request randomness - should fail due to underfunded subscription randomWordsFulfilledEventTimeout := 5 * time.Second _, err = vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, - vrfv2Data, subIDForCancelling, - testConfig.RandomnessRequestCountPerRequest, - testConfig, + vrfv2Data, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, randomWordsFulfilledEventTimeout, - l, ) require.Error(t, err, "Error should occur while waiting for fulfilment due to low sub balance") @@ -438,28 +450,30 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.VRFv2) + if err != nil { + t.Fatal(err) + } + useVRFOwner := false useTestCoordinator := false - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) require.NoError(t, err, "Error building ethereum network config") - var vrfv2Config vrfv2_config.VRFV2Config - err = envconfig.Process("VRFV2", &vrfv2Config) - require.NoError(t, err) - env, err := test_env.NewCLTestEnvBuilder(). + WithTestConfig(&config). WithTestInstance(t). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2.General.LinkNativeFeedResponse)) require.NoError(t, err) linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err) @@ -470,7 +484,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { numberOfTxKeysToCreate := 2 vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, - vrfv2Config, + &config, useVRFOwner, useTestCoordinator, linkToken, @@ -491,7 +505,6 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { - testConfig := vrfv2Config txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") require.NoError(t, err, "error reading tx keys") @@ -500,14 +513,17 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { var fulfillmentTxFromAddresses []string for i := 0; i < numberOfTxKeysToCreate+1; i++ { randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, - vrfv2Data, subID, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, - l, + vrfv2Data, + *config.VRFv2.General.MinimumConfirmations, + *config.VRFv2.General.CallbackGasLimit, + *config.VRFv2.General.NumberOfWords, + *config.VRFv2.General.RandomnessRequestCountPerRequest, + *config.VRFv2.General.RandomnessRequestCountPerRequestDeviation, + config.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -534,28 +550,27 @@ func TestVRFOwner(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Smoke", tc.VRFv2) + require.NoError(t, err, "Error getting config") + useVRFOwner := true useTestCoordinator := true - - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) require.NoError(t, err, "Error building ethereum network config") - var vrfv2Config vrfv2_config.VRFV2Config - err = envconfig.Process("VRFV2", &vrfv2Config) - require.NoError(t, err) - env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) - mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(*config.VRFv2.General.LinkNativeFeedResponse)) require.NoError(t, err) linkToken, err := actions.DeployLINKToken(env.ContractDeployer) @@ -567,7 +582,7 @@ func TestVRFOwner(t *testing.T) { numberOfTxKeysToCreate := 1 vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, - vrfv2Config, + &config, useVRFOwner, useTestCoordinator, linkToken, @@ -588,7 +603,7 @@ func TestVRFOwner(t *testing.T) { vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) t.Run("Request Randomness With Force-Fulfill", func(t *testing.T) { - testConfig := vrfv2Config + configCopy := config.MustCopy().(tc.TestConfig) vrfCoordinatorOwner, err := vrfv2Contracts.Coordinator.GetOwner(testcontext.Get(t)) require.NoError(t, err) @@ -617,16 +632,19 @@ func TestVRFOwner(t *testing.T) { // test and assert _, randFulfilledEvent, _, err := vrfv2_actions.RequestRandomnessWithForceFulfillAndWaitForFulfillment( + l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, vrfv2Contracts.VRFOwner, vrfv2Data, - testConfig.RandomnessRequestCountPerRequest, - testConfig, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, conversions.EtherToWei(big.NewFloat(5)), common.HexToAddress(linkToken.Address()), time.Minute*2, - l, ) require.NoError(t, err, "error requesting randomness with force-fulfillment and waiting for fulfilment") require.Equal(t, 0, randFulfilledEvent.Payment.Cmp(big.NewInt(0)), "Forced Fulfilled Randomness's Payment should be 0") @@ -636,7 +654,7 @@ func TestVRFOwner(t *testing.T) { require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, *configCopy.VRFv2.General.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") @@ -651,11 +669,11 @@ func TestVRFOwner(t *testing.T) { coordinatorFallbackWeiPerUnitLinkConfig, err := vrfv2Contracts.Coordinator.GetFallbackWeiPerUnitLink(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator FallbackWeiPerUnitLink") - require.Equal(t, testConfig.StalenessSeconds, coordinatorConfig.StalenessSeconds) - require.Equal(t, testConfig.GasAfterPaymentCalculation, coordinatorConfig.GasAfterPaymentCalculation) - require.Equal(t, testConfig.MinimumConfirmations, coordinatorConfig.MinimumRequestConfirmations) - require.Equal(t, testConfig.FulfillmentFlatFeeLinkPPMTier1, coordinatorFeeConfig.FulfillmentFlatFeeLinkPPMTier1) - require.Equal(t, testConfig.ReqsForTier2, coordinatorFeeConfig.ReqsForTier2.Int64()) - require.Equal(t, testConfig.FallbackWeiPerUnitLink, coordinatorFallbackWeiPerUnitLinkConfig.Int64()) + require.Equal(t, *configCopy.VRFv2.General.StalenessSeconds, coordinatorConfig.StalenessSeconds) + require.Equal(t, *configCopy.VRFv2.General.GasAfterPaymentCalculation, coordinatorConfig.GasAfterPaymentCalculation) + require.Equal(t, *configCopy.VRFv2.General.MinimumConfirmations, coordinatorConfig.MinimumRequestConfirmations) + require.Equal(t, *configCopy.VRFv2.General.FulfillmentFlatFeeLinkPPMTier1, coordinatorFeeConfig.FulfillmentFlatFeeLinkPPMTier1) + require.Equal(t, *configCopy.VRFv2.General.ReqsForTier2, coordinatorFeeConfig.ReqsForTier2.Int64()) + require.Equal(t, *configCopy.VRFv2.General.FallbackWeiPerUnitLink, coordinatorFallbackWeiPerUnitLinkConfig.Int64()) }) } diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 5417e61d5ed..f1f11d3e1f0 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -10,18 +10,19 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) @@ -29,25 +30,28 @@ func TestVRFv2Plus(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) - require.NoError(t, err, "Error building ethereum network config") + config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + if err != nil { + t.Fatal(err) + } - var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) - require.NoError(t, err) + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). + WithGeth(). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse)) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") linkToken, err := actions.DeployLINKToken(env.ContractDeployer) @@ -59,7 +63,7 @@ func TestVRFv2Plus(t *testing.T) { numberOfTxKeysToCreate := 2 vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, - vrfv2PlusConfig, + &config, linkToken, mockETHLinkFeed, numberOfTxKeysToCreate, @@ -77,7 +81,7 @@ func TestVRFv2Plus(t *testing.T) { vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) t.Run("Link Billing", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) var isNativeBilling = false subBalanceBeforeRequest := subscription.Balance @@ -91,9 +95,12 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subID, isNativeBilling, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -113,7 +120,7 @@ func TestVRFv2Plus(t *testing.T) { require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, *configCopy.VRFv2Plus.General.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") @@ -121,7 +128,8 @@ func TestVRFv2Plus(t *testing.T) { }) t.Run("Native Billing", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) + testConfig := configCopy.VRFv2Plus.General var isNativeBilling = true subNativeTokenBalanceBeforeRequest := subscription.NativeBalance @@ -135,9 +143,12 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subID, isNativeBilling, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -156,17 +167,17 @@ func TestVRFv2Plus(t *testing.T) { require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, *testConfig.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) t.Run("Direct Funding (VRFV2PlusWrapper)", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( env, - testConfig, + &configCopy, linkToken, mockETHLinkFeed, vrfv2PlusContracts.Coordinator, @@ -176,7 +187,8 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) t.Run("Link Billing", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) + testConfig := configCopy.VRFv2Plus.General var isNativeBilling = false wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) @@ -192,8 +204,12 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, wrapperSubID, isNativeBilling, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -218,14 +234,15 @@ func TestVRFv2Plus(t *testing.T) { //require.Equal(t, 1, consumerStatus.Paid.Cmp(randomWordsFulfilledEvent.Payment), "Expected Consumer contract pay more than the Coordinator Sub") vrfv2plus.LogFulfillmentDetailsLinkBilling(l, wrapperConsumerJuelsBalanceBeforeRequest, wrapperConsumerJuelsBalanceAfterRequest, consumerStatus, randomWordsFulfilledEvent) - require.Equal(t, testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) + require.Equal(t, *testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) for _, w := range consumerStatus.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) t.Run("Native Billing", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) + testConfig := configCopy.VRFv2Plus.General var isNativeBilling = true wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) @@ -241,8 +258,12 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, wrapperSubID, isNativeBilling, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -267,7 +288,7 @@ func TestVRFv2Plus(t *testing.T) { //require.Equal(t, 1, consumerStatus.Paid.Cmp(randomWordsFulfilledEvent.Payment), "Expected Consumer contract pay more than the Coordinator Sub") vrfv2plus.LogFulfillmentDetailsNativeBilling(l, wrapperConsumerBalanceBeforeRequestWei, wrapperConsumerBalanceAfterRequestWei, consumerStatus, randomWordsFulfilledEvent) - require.Equal(t, testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) + require.Equal(t, *testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) for _, w := range consumerStatus.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") @@ -275,14 +296,16 @@ func TestVRFv2Plus(t *testing.T) { }) }) t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, - testConfig, + big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), + big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, vrfv2PlusContracts.Coordinator, vrfv2PlusContracts.LoadTestConsumers, 1, + vrfv2plus_config.BillingType(*configCopy.GetVRFv2PlusConfig().General.SubscriptionBillingType), ) require.NoError(t, err) subIDForCancelling := subIDsForCancelling[0] @@ -367,18 +390,22 @@ func TestVRFv2Plus(t *testing.T) { }) t.Run("Owner Canceling Sub And Returning Funds While Having Pending Requests", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) + testConfig := configCopy.VRFv2Plus.General + //underfund subs in order rand fulfillments to fail - testConfig.SubscriptionFundingAmountNative = float64(0.000000000000000001) //1 Wei - testConfig.SubscriptionFundingAmountLink = float64(0.000000000000000001) //1 Juels + testConfig.SubscriptionFundingAmountNative = ptr.Ptr(float64(0.000000000000000001)) //1 Wei + testConfig.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) //1 Juels subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, - testConfig, + big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), + big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, vrfv2PlusContracts.Coordinator, vrfv2PlusContracts.LoadTestConsumers, 1, + vrfv2plus_config.BillingType(*configCopy.GetVRFv2PlusConfig().General.SubscriptionBillingType), ) require.NoError(t, err) @@ -405,8 +432,11 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subIDForCancelling, false, - testConfig.RandomnessRequestCountPerRequest, - testConfig, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, randomWordsFulfilledEventTimeout, l, ) @@ -419,8 +449,11 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subIDForCancelling, true, - testConfig.RandomnessRequestCountPerRequest, - testConfig, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, randomWordsFulfilledEventTimeout, l, ) @@ -523,14 +556,16 @@ func TestVRFv2Plus(t *testing.T) { }) t.Run("Owner Withdraw", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) subIDsForWithdraw, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, - testConfig, + big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), + big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, vrfv2PlusContracts.Coordinator, vrfv2PlusContracts.LoadTestConsumers, 1, + vrfv2plus_config.BillingType(*configCopy.GetVRFv2PlusConfig().General.SubscriptionBillingType), ) require.NoError(t, err) subIDForWithdraw := subIDsForWithdraw[0] @@ -541,9 +576,12 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subIDForWithdraw, false, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err) @@ -554,9 +592,12 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subIDForWithdraw, true, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err) @@ -608,25 +649,27 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) - require.NoError(t, err, "Error building ethereum network config") + config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + if err != nil { + t.Fatal(err) + } - var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) - require.NoError(t, err) + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse)) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") linkToken, err := actions.DeployLINKToken(env.ContractDeployer) @@ -635,7 +678,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { numberOfTxKeysToCreate := 2 vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, - vrfv2PlusConfig, + &config, linkToken, mockETHLinkFeed, numberOfTxKeysToCreate, @@ -653,7 +696,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { - testConfig := vrfv2PlusConfig + configCopy := config.MustCopy().(tc.TestConfig) var isNativeBilling = false txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") require.NoError(t, err, "error reading tx keys") @@ -668,9 +711,12 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { vrfv2PlusData, subID, isNativeBilling, - testConfig.RandomnessRequestCountPerRequest, - testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -698,24 +744,26 @@ func TestVRFv2PlusMigration(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - network, err := actions.EthereumNetworkConfigFromEnvOrDefault(l) - require.NoError(t, err, "Error building ethereum network config") + config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + if err != nil { + t.Fatal(err) + } - var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig - err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) - require.NoError(t, err) + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). + WithTestConfig(&config). WithPrivateEthereumNetwork(network). WithCLNodes(1). - WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). Build() require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) - mockETHLinkFeedAddress, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse)) + mockETHLinkFeedAddress, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") linkAddress, err := actions.DeployLINKToken(env.ContractDeployer) @@ -723,7 +771,7 @@ func TestVRFv2PlusMigration(t *testing.T) { vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, - vrfv2PlusConfig, + &config, linkAddress, mockETHLinkFeedAddress, 0, @@ -758,15 +806,16 @@ func TestVRFv2PlusMigration(t *testing.T) { _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) require.NoError(t, err, fmt.Errorf("%s, err: %w", vrfv2plus.ErrRegisteringProvingKey, err)) + vrfv2PlusConfig := config.VRFv2Plus.General err = newCoordinator.SetConfig( - vrfv2PlusConfig.MinimumConfirmations, - vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, - vrfv2PlusConfig.StalenessSeconds, - vrfv2PlusConfig.GasAfterPaymentCalculation, - big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse), + *vrfv2PlusConfig.MinimumConfirmations, + *vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, + *vrfv2PlusConfig.StalenessSeconds, + *vrfv2PlusConfig.GasAfterPaymentCalculation, + big.NewInt(*vrfv2PlusConfig.LinkNativeFeedResponse), vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionFeeConfig{ - FulfillmentFlatFeeLinkPPM: vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, - FulfillmentFlatFeeNativePPM: vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + FulfillmentFlatFeeLinkPPM: *vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, + FulfillmentFlatFeeNativePPM: *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, }, ) require.NoError(t, err) @@ -781,7 +830,7 @@ func TestVRFv2PlusMigration(t *testing.T) { CoordinatorAddress: newCoordinator.Address(), FromAddresses: []string{vrfv2PlusData.PrimaryEthAddress}, EVMChainID: vrfv2PlusData.ChainID.String(), - MinIncomingConfirmations: int(vrfv2PlusConfig.MinimumConfirmations), + MinIncomingConfirmations: int(*vrfv2PlusConfig.MinimumConfirmations), PublicKey: vrfv2PlusData.VRFKey.Data.ID, EstimateGasMultiplier: 1, BatchFulfillmentEnabled: false, @@ -878,7 +927,11 @@ func TestVRFv2PlusMigration(t *testing.T) { vrfv2PlusData, subID, false, - vrfv2PlusConfig, + *config.VRFv2Plus.General.MinimumConfirmations, + *config.VRFv2Plus.General.CallbackGasLimit, + *config.VRFv2Plus.General.NumberOfWords, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -890,9 +943,12 @@ func TestVRFv2PlusMigration(t *testing.T) { vrfv2PlusData, subID, true, - vrfv2PlusConfig, + *config.VRFv2Plus.General.MinimumConfirmations, + *config.VRFv2Plus.General.CallbackGasLimit, + *config.VRFv2Plus.General.NumberOfWords, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") - } diff --git a/integration-tests/soak/forwarder_ocr_test.go b/integration-tests/soak/forwarder_ocr_test.go index 538f5ff1569..b5355a6c3f5 100644 --- a/integration-tests/soak/forwarder_ocr_test.go +++ b/integration-tests/soak/forwarder_ocr_test.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/integration-tests/actions" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -21,9 +22,12 @@ ForwardersEnabled = true` // fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) // fmt.Println("---------------------") - ocrSoakTest, err := testsetups.NewOCRSoakTest(t, true) + config, err := tc.GetConfig("Soak", tc.OCR) + require.NoError(t, err, "Error getting config") + + ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config, true) require.NoError(t, err, "Error creating soak test") - ocrSoakTest.DeployEnvironment(customNetworkTOML) + ocrSoakTest.DeployEnvironment(customNetworkTOML, &config) if ocrSoakTest.Environment().WillUseRemoteRunner() { return } @@ -32,6 +36,6 @@ ForwardersEnabled = true` l.Error().Err(err).Msg("Error tearing down environment") } }) - ocrSoakTest.Setup() + ocrSoakTest.Setup(&config) ocrSoakTest.Run() } diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index 4d7971d678e..e25391e8450 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/integration-tests/actions" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -21,10 +22,13 @@ func TestOCRSoak(t *testing.T) { // fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCR1Config, customNetworkTOML, network)) // fmt.Println("---------------------") - ocrSoakTest, err := testsetups.NewOCRSoakTest(t, false) + config, err := tc.GetConfig("Soak", tc.OCR) + require.NoError(t, err, "Error getting config") + + ocrSoakTest, err := testsetups.NewOCRSoakTest(t, &config, false) require.NoError(t, err, "Error creating soak test") if !ocrSoakTest.Interrupted() { - ocrSoakTest.DeployEnvironment(customNetworkTOML) + ocrSoakTest.DeployEnvironment(customNetworkTOML, &config) } if ocrSoakTest.Environment().WillUseRemoteRunner() { return @@ -39,7 +43,7 @@ func TestOCRSoak(t *testing.T) { require.NoError(t, err, "Error loading state") ocrSoakTest.Resume() } else { - ocrSoakTest.Setup() + ocrSoakTest.Setup(&config) ocrSoakTest.Run() } } diff --git a/integration-tests/testconfig/automation/automation.toml b/integration-tests/testconfig/automation/automation.toml new file mode 100644 index 00000000000..58b95f9b4cc --- /dev/null +++ b/integration-tests/testconfig/automation/automation.toml @@ -0,0 +1,49 @@ +# product defaults +[Common] +chainlink_node_funding = 0.5 + +# reorg test specific overrides +[Reorg.Automation] +[Reorg.Automation.General] +number_of_nodes=6 +duration=100 +block_time=1 +spec_type="minimum" +chainlink_node_log_level="info" +use_prometheus=false + +# load test specific overrides +[Load.Common] +chainlink_node_funding = 100 + +[Load.Automation] +[Load.Automation.General] +number_of_nodes=6 +duration=900 +block_time=1 +spec_type="minimum" +chainlink_node_log_level="info" +use_prometheus=false + +[[Load.Automation.Load]] +number_of_upkeeps=5 +number_of_events = 1 +number_of_spam_matching_events = 1 +number_of_spam_non_matching_events = 0 +check_burn_amount = 0 +perform_burn_amount = 0 +upkeep_gas_limit = 1000000 +shared_trigger = false + +[[Load.Automation.Load]] +number_of_upkeeps=5 +number_of_events = 1 +number_of_spam_matching_events = 0 +number_of_spam_non_matching_events = 1 +check_burn_amount = 0 +perform_burn_amount = 0 +upkeep_gas_limit = 1000000 +shared_trigger = true + +[Load.Pyroscope] +enabled=false \ No newline at end of file diff --git a/integration-tests/testconfig/automation/config.go b/integration-tests/testconfig/automation/config.go new file mode 100644 index 00000000000..4431a640d0d --- /dev/null +++ b/integration-tests/testconfig/automation/config.go @@ -0,0 +1,91 @@ +package automation + +import ( + "errors" + "math/big" +) + +type Config struct { + General *General `toml:"General"` + Load []Load `toml:"Load"` +} + +func (c *Config) Validate() error { + if c.General != nil { + if err := c.General.Validate(); err != nil { + return err + } + } + if len(c.Load) > 0 { + for _, load := range c.Load { + if err := load.Validate(); err != nil { + return err + } + } + } + return nil +} + +// General is a common configuration for all automation performance tests +type General struct { + NumberOfNodes *int `toml:"number_of_nodes"` + Duration *int `toml:"duration"` + BlockTime *int `toml:"block_time"` + SpecType *string `toml:"spec_type"` + ChainlinkNodeLogLevel *string `toml:"chainlink_node_log_level"` + UsePrometheus *bool `toml:"use_prometheus"` +} + +func (c *General) Validate() error { + if c.NumberOfNodes == nil || *c.NumberOfNodes < 1 { + return errors.New("number_of_nodes must be set to a positive integer") + } + if c.Duration == nil || *c.Duration < 1 { + return errors.New("duration must be set to a positive integer") + } + if c.BlockTime == nil || *c.BlockTime < 1 { + return errors.New("block_time must be set to a positive integer") + } + if c.SpecType == nil { + return errors.New("spec_type must be set") + } + if c.ChainlinkNodeLogLevel == nil { + return errors.New("chainlink_node_log_level must be set") + } + + return nil +} + +type Load struct { + NumberOfUpkeeps *int `toml:"number_of_upkeeps"` + NumberOfEvents *int `toml:"number_of_events"` + NumberOfSpamMatchingEvents *int `toml:"number_of_spam_matching_events"` + NumberOfSpamNonMatchingEvents *int `toml:"number_of_spam_non_matching_events"` + CheckBurnAmount *big.Int `toml:"check_burn_amount"` + PerformBurnAmount *big.Int `toml:"perform_burn_amount"` + SharedTrigger *bool `toml:"shared_trigger"` + UpkeepGasLimit *uint32 `toml:"upkeep_gas_limit"` +} + +func (c *Load) Validate() error { + if c.NumberOfUpkeeps == nil || *c.NumberOfUpkeeps < 1 { + return errors.New("number_of_upkeeps must be set to a positive integer") + } + if c.NumberOfEvents == nil || *c.NumberOfEvents < 0 { + return errors.New("number_of_events must be set to a non-negative integer") + } + if c.NumberOfSpamMatchingEvents == nil || *c.NumberOfSpamMatchingEvents < 0 { + return errors.New("number_of_spam_matching_events must be set to a non-negative integer") + } + if c.NumberOfSpamNonMatchingEvents == nil || *c.NumberOfSpamNonMatchingEvents < 0 { + return errors.New("number_of_spam_non_matching_events must be set to a non-negative integer") + } + if c.CheckBurnAmount == nil || c.CheckBurnAmount.Cmp(big.NewInt(0)) < 0 { + return errors.New("check_burn_amount must be set to a non-negative integer") + } + if c.PerformBurnAmount == nil || c.PerformBurnAmount.Cmp(big.NewInt(0)) < 0 { + return errors.New("perform_burn_amount must be set to a non-negative integer") + } + + return nil +} diff --git a/integration-tests/testconfig/automation/example.toml b/integration-tests/testconfig/automation/example.toml new file mode 100644 index 00000000000..a8e0510d83c --- /dev/null +++ b/integration-tests/testconfig/automation/example.toml @@ -0,0 +1,90 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Common +[Common] +chainlink_node_funding = 0.5 + +# Product part +[Automation] +[Automation.General] +number_of_nodes=6 +duration=100 +block_time=1 +number_of_events=1 +spec_type="minimum" +chainlink_node_log_level="info" +use_prometheus=false + +# upgrade test specific override +[TestAutomationNodeUpgrade.ChainlinkUpgradeImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.8.0" \ No newline at end of file diff --git a/integration-tests/testconfig/configs_embed.go b/integration-tests/testconfig/configs_embed.go new file mode 100644 index 00000000000..67e954ff503 --- /dev/null +++ b/integration-tests/testconfig/configs_embed.go @@ -0,0 +1,21 @@ +//go:build embed +// +build embed + +package testconfig + +import "embed" + +//go:embed default.toml +//go:embed automation/automation.toml +//go:embed functions/functions.toml +//go:embed keeper/keeper.toml +//go:embed log_poller/log_poller.toml +//go:embed node/node.toml +//go:embed ocr/ocr.toml +//go:embed vrfv2/vrfv2.toml +//go:embed vrfv2plus/vrfv2plus.toml +var embeddedConfigsFs embed.FS + +func init() { + areConfigsEmbedded = true +} diff --git a/integration-tests/testconfig/configs_noembed.go b/integration-tests/testconfig/configs_noembed.go new file mode 100644 index 00000000000..95572c4a044 --- /dev/null +++ b/integration-tests/testconfig/configs_noembed.go @@ -0,0 +1,12 @@ +//go:build !embed +// +build !embed + +package testconfig + +import "embed" + +var embeddedConfigsFs embed.FS + +func init() { + areConfigsEmbedded = false +} diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml new file mode 100644 index 00000000000..a65c23d70dc --- /dev/null +++ b/integration-tests/testconfig/default.toml @@ -0,0 +1,22 @@ +[Logging] +test_log_collect=false + +[Logging.LogStream] +log_targets=["file"] +log_producer_timeout="10s" +log_producer_retry_limit=10 + +[Network] +selected_networks=["simulated"] + +[PrivateEthereumNetwork] +consensus_type="pow" +execution_layer="geth" + +[PrivateEthereumNetwork.EthereumChainConfig] +seconds_per_slot=3 +slots_per_epoch=2 +genesis_delay=15 +validator_count=4 +chain_id=1337 +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] \ No newline at end of file diff --git a/integration-tests/testconfig/functions/config.go b/integration-tests/testconfig/functions/config.go new file mode 100644 index 00000000000..88c0e052951 --- /dev/null +++ b/integration-tests/testconfig/functions/config.go @@ -0,0 +1,118 @@ +package functions + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/utils/net" +) + +const ( + ErrReadPerfConfig = "failed to read TOML config for performance tests" + ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" +) + +type Config struct { + Performance *Performance `toml:"Performance"` + Common *Common `toml:"Common"` +} + +type Common struct { + NodeFunds *big.Float `toml:"node_funds"` + SubFunds *big.Int `toml:"sub_funds"` + LINKTokenAddr *string `toml:"link_token_addr"` + Coordinator *string `toml:"coordinator_addr"` + Router *string `toml:"router_addr"` + LoadTestClient *string `toml:"client_addr"` + SubscriptionID *uint64 `toml:"subscription_id"` + DONID *string `toml:"don_id"` + GatewayURL *string `toml:"gateway_url"` + Receiver *string `toml:"receiver"` + FunctionsCallPayloadHTTP *string `toml:"functions_call_payload_http"` + FunctionsCallPayloadWithSecrets *string `toml:"functions_call_payload_with_secrets"` + FunctionsCallPayloadReal *string `toml:"functions_call_payload_real"` + SecretsSlotID *uint8 `toml:"secrets_slot_id"` + SecretsVersionID *uint64 `toml:"secrets_version_id"` + // Secrets these are for CI secrets + Secrets *string `toml:"secrets"` +} + +func (c *Common) Validate() error { + if c.SubFunds == nil { + return errors.New("sub_funds must be set") + } + if c.LINKTokenAddr == nil || *c.LINKTokenAddr == "" { + return errors.New("link_token_addr must be set") + } + if !common.IsHexAddress(*c.LINKTokenAddr) { + return errors.New("link_token_addr must be a valid address") + } + if c.Coordinator == nil || *c.Coordinator == "" { + return errors.New("coordinator must be set") + } + if !common.IsHexAddress(*c.Coordinator) { + return errors.New("coordinator must be a valid address") + } + if c.Router == nil || *c.Router == "" { + return errors.New("router must be set") + } + if !common.IsHexAddress(*c.Router) { + return errors.New("router must be a valid address") + } + if c.DONID == nil || *c.DONID == "" { + return errors.New("don_id must be set") + } + if c.GatewayURL == nil || *c.GatewayURL == "" { + return errors.New("gateway_url must be set") + } + if !net.IsValidURL(*c.GatewayURL) { + return errors.New("gateway_url must be a valid URL") + } + if c.Receiver == nil || *c.Receiver == "" { + return errors.New("receiver must be set") + } + if !common.IsHexAddress(*c.Receiver) { + return errors.New("receiver must be a valid address") + } + return nil +} + +type Performance struct { + RPS *int64 `toml:"rps"` + RequestsPerCall *uint32 `toml:"requests_per_call"` + Duration *blockchain.StrDuration `toml:"duration"` +} + +func (c *Performance) Validate() error { + if c.RPS == nil || *c.RPS < 1 { + return errors.New("rps must be greater than 0") + } + if c.RequestsPerCall != nil && *c.RequestsPerCall < 1 { + return errors.New("requests_per_call must be greater than 0") + } + if c.Duration == nil || c.Duration.Duration < 1 { + return errors.New("duration must be greater than 0") + } + return nil +} + +func (c *Config) Validate() error { + if c == nil { + return nil + } + if c.Common != nil { + if err := c.Common.Validate(); err != nil { + return err + } + } + if c.Performance != nil { + if err := c.Performance.Validate(); err != nil { + return err + } + } + + return nil +} diff --git a/integration-tests/testconfig/functions/example.toml b/integration-tests/testconfig/functions/example.toml new file mode 100644 index 00000000000..7628fbeee32 --- /dev/null +++ b/integration-tests/testconfig/functions/example.toml @@ -0,0 +1,113 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use simulated network +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Common +[Common] +chainlink_node_funding = 0.5 + +# Product part +[Functions] +[Functions.Common] +# Polygon Mumbai only for now +receiver = "0x3098B6665589959711A48a6bAe5B7F2908f6a3bE" +don_id = "fun-staging-mumbai-1" +gateway_url = "https://gateway-stg-one.main.stage.cldev.sh" +link_token_addr = "0x326C977E6efc84E512bB9C30f76E30c160eD06FB" +coordinator_addr = "0x6D6a83BB356b7242E88C1A2b290102fde26590D0" +router_addr = "0x2673266D3Cd08b53494B5a92B66DEec7F1408E7A" + +# comment "client_addr" and "subscription_id" and test will create a new pair +# get it from logs and save +client_addr = "0x89D4b58D859a536D0B888ecD5093eF5FF9e4F977" +subscription_id = 47 +sub_funds = 10 + +functions_call_payload_with_secrets = "return Functions.encodeString(JSON.stringify(secrets))" +functions_call_payload_http = """ +const response = await Functions.makeHttpRequest({ url: 'http://dummyjson.com/products/1' }); +return Functions.encodeUint256(response.data.id); +""" +functions_call_payload_real = """ +const arg1 = args[0]; +const arg2 = args[1]; +const arg3 = args[2]; +const arg4 = args[3]; + +const response = await Functions.makeHttpRequest({ url: 'http://dummyjson.com/products/${arg1}' }); +return Functions.encodeString(JSON.stringify(secrets)); +""" +secrets_slot_id = 0 +secrets_version_id = 1693945705 + +[Functions.Performance] +rps = 95 +requests_per_call = 20 +duration = "10m" \ No newline at end of file diff --git a/integration-tests/load/functions/config.toml b/integration-tests/testconfig/functions/functions.toml similarity index 63% rename from integration-tests/load/functions/config.toml rename to integration-tests/testconfig/functions/functions.toml index 2de3ba9282c..a4bcb6439e4 100644 --- a/integration-tests/load/functions/config.toml +++ b/integration-tests/testconfig/functions/functions.toml @@ -1,42 +1,6 @@ -[Soak] -rps = 1 -requests_per_call = 40 -duration = "10m" - -[Stress] -rps = 1 -requests_per_call = 78 -duration = "10m" - -[SecretsSoak] -rps = 1 -requests_per_call = 20 -duration = "10m" - -[SecretsStress] -rps = 1 -requests_per_call = 40 -duration = "10m" - -[RealSoak] -rps = 1 -requests_per_call = 20 -duration = "10m" - -[RealStress] -rps = 1 -requests_per_call = 40 -duration = "10m" - -[GatewayListSoak] -rps = 95 -duration = "10m" - -[GatewaySetSoak] -rps = 95 -duration = "10m" - -[Common] +# product defaults +[Functions] +[Functions.Common] # Polygon Mumbai only for now receiver = "0x3098B6665589959711A48a6bAe5B7F2908f6a3bE" don_id = "fun-staging-mumbai-1" @@ -66,8 +30,62 @@ const response = await Functions.makeHttpRequest({ url: 'http://dummyjson.com/pr return Functions.encodeString(JSON.stringify(secrets)); """ secrets_slot_id = 0 -secrets_version = 1693945705 +secrets_version_id = 1693945705 # uncomment to upload new secrets to s4 and use it in your run # TODO: not working now -#secrets = "{\"secrets\": \"secretValue\"}" \ No newline at end of file +#secrets = "{\"secrets\": \"secretValue\"}" + +# gateway-list specific test configuration +[GatewayList.Functions] +[GatewayList.Functions.Performance] +rps = 95 +duration = "10m" + +# gateway-set specific test configuration +[GatewaySet.Functions] +[GatewaySet.Functions.Performance] +rps = 95 +duration = "10m" + +# real-soak specific test configuration +[RealSoak.Functions] +[RealSoak.Functions.Performance] +rps = 1 +requests_per_call = 20 +duration = "10m" + +# real-stress specific test configuration +[RealStress.Functions] +[RealStress.Functions.Performance] +rps = 1 +requests_per_call = 40 +duration = "10m" + +# secrets-soak specific test configuration +[SecretsSoak.Functions] +[SecretsSoak.Functions.Performance] +rps = 1 +requests_per_call = 20 +duration = "10m" + +# secrets-stress specific test configuration +[SecretsStress.Functions] +[SecretsStress.Functions.Performance] +rps = 1 +requests_per_call = 40 +duration = "10m" + +# soak specific test configuration +[Soak.Functions] +[Soak.Functions.Performance] +rps = 1 +requests_per_call = 40 +duration = "10m" + +# soak specific test configuration +[Stress.Functions] +[Stress.Functions.Performance] +rps = 1 +requests_per_call = 78 +duration = "10m" \ No newline at end of file diff --git a/integration-tests/testconfig/keeper/config.go b/integration-tests/testconfig/keeper/config.go new file mode 100644 index 00000000000..da6cd7acc98 --- /dev/null +++ b/integration-tests/testconfig/keeper/config.go @@ -0,0 +1,85 @@ +package keeper + +import ( + "errors" +) + +type Config struct { + Common *Common `toml:"Common"` +} + +func (c *Config) Validate() error { + if c.Common == nil { + return nil + } + return c.Common.Validate() +} + +type Common struct { + RegistryToTest *string `toml:"registry_to_test"` + NumberOfRegistries *int `toml:"number_of_registries"` + NumberOfNodes *int `toml:"number_of_nodes"` + NumberOfUpkeeps *int `toml:"number_of_upkeeps"` + UpkeepGasLimit *int64 `toml:"upkeep_gas_limit"` + CheckGasToBurn *int64 `toml:"check_gas_to_burn"` + PerformGasToBurn *int64 `toml:"perform_gas_to_burn"` + MaxPerformGas *int64 `toml:"max_perform_gas"` + BlockRange *int64 `toml:"block_range"` + BlockInterval *int64 `toml:"block_interval"` + ForceSingleTxKey *bool `toml:"forces_single_tx_key"` + DeleteJobsOnEnd *bool `toml:"delete_jobs_on_end"` + RegistryAddress *string `toml:"registry_address"` + RegistrarAddress *string `toml:"registrar_address"` + LinkTokenAddress *string `toml:"link_token_address"` + EthFeedAddress *string `toml:"eth_feed_address"` + GasFeedAddress *string `toml:"gas_feed_address"` +} + +func (c *Common) Validate() error { + if c.RegistryToTest == nil || *c.RegistryToTest == "" { + return errors.New("registry_to_test must be set") + } + if c.NumberOfRegistries == nil || *c.NumberOfRegistries <= 0 { + return errors.New("number_of_registries must be a positive integer") + } + if c.NumberOfNodes == nil || *c.NumberOfNodes <= 0 { + return errors.New("number_of_nodes must be a positive integer") + } + if c.NumberOfUpkeeps == nil || *c.NumberOfUpkeeps <= 0 { + return errors.New("number_of_upkeeps must be a positive integer") + } + if c.UpkeepGasLimit == nil || *c.UpkeepGasLimit <= 0 { + return errors.New("upkeep_gas_limit must be a positive integer") + } + if c.CheckGasToBurn == nil || *c.CheckGasToBurn <= 0 { + return errors.New("check_gas_to_burn must be a positive integer") + } + if c.PerformGasToBurn == nil || *c.PerformGasToBurn <= 0 { + return errors.New("perform_gas_to_burn must be a positive integer") + } + if c.MaxPerformGas == nil || *c.MaxPerformGas <= 0 { + return errors.New("max_perform_gas must be a positive integer") + } + if c.BlockRange == nil || *c.BlockRange <= 0 { + return errors.New("block_range must be a positive integer") + } + if c.BlockInterval == nil || *c.BlockInterval <= 0 { + return errors.New("block_interval must be a positive integer") + } + if c.RegistryAddress == nil { + c.RegistryAddress = new(string) + } + if c.RegistrarAddress == nil { + c.RegistrarAddress = new(string) + } + if c.LinkTokenAddress == nil { + c.LinkTokenAddress = new(string) + } + if c.EthFeedAddress == nil { + c.EthFeedAddress = new(string) + } + if c.GasFeedAddress == nil { + c.GasFeedAddress = new(string) + } + return nil +} diff --git a/integration-tests/testconfig/keeper/example.toml b/integration-tests/testconfig/keeper/example.toml new file mode 100644 index 00000000000..6bdd6537bc3 --- /dev/null +++ b/integration-tests/testconfig/keeper/example.toml @@ -0,0 +1,88 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Product part +[Common] +chainlink_node_funding = 0.5 + +[Keeper.Common] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 500 +upkeep_gas_limit = 150000 +check_gas_to_burn = 100000 +perform_gas_to_burn = 50000 +max_perform_gas = 5000000 +block_range = 3600 +block_interval = 20 +forces_single_tx_key = false +delete_jobs_on_end = true \ No newline at end of file diff --git a/integration-tests/testconfig/keeper/keeper.toml b/integration-tests/testconfig/keeper/keeper.toml new file mode 100644 index 00000000000..516dbb35a63 --- /dev/null +++ b/integration-tests/testconfig/keeper/keeper.toml @@ -0,0 +1,17 @@ +# product defaults +[Common] +chainlink_node_funding = 0.5 + +[Keeper.Common] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 500 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 100000 +perform_gas_to_burn = 50000 +max_perform_gas = 5000000 +block_range = 3600 +block_interval = 20 +forces_single_tx_key = false +delete_jobs_on_end = true \ No newline at end of file diff --git a/integration-tests/testconfig/log_poller/config.go b/integration-tests/testconfig/log_poller/config.go new file mode 100644 index 00000000000..96c3b55c276 --- /dev/null +++ b/integration-tests/testconfig/log_poller/config.go @@ -0,0 +1,158 @@ +package logpoller + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +const ( + ErrReadPerfConfig = "failed to read TOML config for performance tests" + ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" +) + +type GeneratorType = string + +const ( + GeneratorType_WASP = "wasp" + GeneratorType_Looped = "looped" +) + +type Config struct { + General *General `toml:"General"` + ChaosConfig *ChaosConfig `toml:"Chaos"` + Wasp *WaspConfig `toml:"Wasp"` + LoopedConfig *LoopedConfig `toml:"Looped"` +} + +func (c *Config) Validate() error { + if c.General == nil { + return fmt.Errorf("General config must be set") + } + + err := c.General.Validate() + if err != nil { + return fmt.Errorf("General config validation failed: %w", err) + } + + switch *c.General.Generator { + case GeneratorType_WASP: + if c.Wasp == nil { + return fmt.Errorf("wasp config is nil") + } + err = c.Wasp.Validate() + if err != nil { + return fmt.Errorf("wasp config validation failed: %w", err) + } + case GeneratorType_Looped: + if c.LoopedConfig == nil { + return fmt.Errorf("looped config is nil") + } + err = c.LoopedConfig.Validate() + if err != nil { + return fmt.Errorf("looped config validation failed: %w", err) + } + default: + return fmt.Errorf("unknown generator type: %s", *c.General.Generator) + } + + if c.ChaosConfig != nil { + if err := c.ChaosConfig.Validate(); err != nil { + return fmt.Errorf("chaos config validation failed: %w", err) + } + } + + return nil +} + +type LoopedConfig struct { + ExecutionCount *int `toml:"execution_count"` + MinEmitWaitTimeMs *int `toml:"min_emit_wait_time_ms"` + MaxEmitWaitTimeMs *int `toml:"max_emit_wait_time_ms"` +} + +func (l *LoopedConfig) Validate() error { + if l.ExecutionCount == nil || *l.ExecutionCount == 0 { + return fmt.Errorf("execution_count must be set and > 0") + } + + if l.MinEmitWaitTimeMs == nil || *l.MinEmitWaitTimeMs == 0 { + return fmt.Errorf("min_emit_wait_time_ms must be set and > 0") + } + + if l.MaxEmitWaitTimeMs == nil || *l.MaxEmitWaitTimeMs == 0 { + return fmt.Errorf("max_emit_wait_time_ms must be set and > 0") + } + + return nil +} + +type General struct { + Generator *string `toml:"generator"` + EventsToEmit []abi.Event `toml:"-"` + Contracts *int `toml:"contracts"` + EventsPerTx *int `toml:"events_per_tx"` + UseFinalityTag *bool `toml:"use_finality_tag"` +} + +func (g *General) Validate() error { + if g.Generator == nil || *g.Generator == "" { + return fmt.Errorf("generator is empty") + } + + if g.Contracts == nil || *g.Contracts == 0 { + return fmt.Errorf("contracts is 0, but must be > 0") + } + + if g.EventsPerTx == nil || *g.EventsPerTx == 0 { + return fmt.Errorf("events_per_tx is 0, but must be > 0") + } + + return nil +} + +type ChaosConfig struct { + ExperimentCount *int `toml:"experiment_count"` + TargetComponent *string `toml:"target_component"` +} + +func (c *ChaosConfig) Validate() error { + if c.ExperimentCount != nil && *c.ExperimentCount == 0 { + return fmt.Errorf("experiment_count must be > 0") + } + + return nil +} + +type WaspConfig struct { + RPS *int64 `toml:"rps"` + LPS *int64 `toml:"lps"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + Duration *blockchain.StrDuration `toml:"duration"` + CallTimeout *blockchain.StrDuration `toml:"call_timeout"` +} + +func (w *WaspConfig) Validate() error { + if w.RPS == nil && w.LPS == nil { + return fmt.Errorf("either RPS or LPS needs to be set") + } + if *w.RPS == 0 && *w.LPS == 0 { + return fmt.Errorf("either RPS or LPS needs to be a positive integer") + } + if *w.RPS != 0 && *w.LPS != 0 { + return fmt.Errorf("only one of RPS or LPS can be set") + } + if w.Duration == nil || w.Duration.Duration == 0 { + return fmt.Errorf("duration must be set and > 0") + } + if w.CallTimeout == nil || w.CallTimeout.Duration == 0 { + return fmt.Errorf("call_timeout must be set and > 0") + } + if w.RateLimitUnitDuration == nil || w.RateLimitUnitDuration.Duration == 0 { + return fmt.Errorf("rate_limit_unit_duration must be set and > 0") + } + + return nil +} diff --git a/integration-tests/testconfig/log_poller/example.toml b/integration-tests/testconfig/log_poller/example.toml new file mode 100644 index 00000000000..90ec89a3ed1 --- /dev/null +++ b/integration-tests/testconfig/log_poller/example.toml @@ -0,0 +1,87 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Common +[Common] +chainlink_node_funding = 0.5 + +# Product part +[LogPoller] +[LogPoller.General] +generator = "looped" +contracts = 2 +events_per_tx = 4 +use_finality_tag = true + +[LogPoller.Looped] +execution_count = 100 +min_emit_wait_time_ms = 200 +max_emit_wait_time_ms = 500 \ No newline at end of file diff --git a/integration-tests/testconfig/log_poller/log_poller.toml b/integration-tests/testconfig/log_poller/log_poller.toml new file mode 100644 index 00000000000..2f46ebf11c2 --- /dev/null +++ b/integration-tests/testconfig/log_poller/log_poller.toml @@ -0,0 +1,61 @@ +# product defaults +[LogPoller] +[LogPoller.General] +generator = "looped" +contracts = 2 +events_per_tx = 4 +use_finality_tag = true + +[LogPoller.Looped] +execution_count = 100 +min_emit_wait_time_ms = 200 +max_emit_wait_time_ms = 500 + +# test-specific +[TestLogPollerFewFiltersFixedDepth.LogPoller.General] +use_finality_tag = false + +[TestLogManyFiltersPollerFinalityTag.LogPoller.General] +contracts = 300 +events_per_tx = 3 + +[TestLogManyFiltersPollerFinalityTag.LogPoller.Looped] +execution_count = 30 + +[TestLogManyFiltersPollerFixedDepth.LogPoller.General] +use_finality_tag = false +contracts = 300 +events_per_tx = 3 + +[TestLogManyFiltersPollerFixedDepth.LogPoller.Looped] +execution_count = 30 + +[TestLogPollerWithChaosFinalityTag.LogPoller.General] +execution_count = 30 +[TestLogPollerWithChaosFinalityTag.LogPoller.Chaos] +experiment_count = 4 +target_component = "chainlink" + +[TestLogPollerWithChaosFixedDepth.LogPoller.General] +execution_count = 30 +use_finality_tag = false +[TestLogPollerWithChaosFixedDepth.LogPoller.Chaos] +experiment_count = 4 +target_component = "chainlink" + +[TestLogPollerWithChaosPostgresFinalityTag.LogPoller.General] +execution_count = 30 +[TestLogPollerWithChaosPostgresFinalityTag.LogPoller.Chaos] +experiment_count = 4 +target_component = "postgres" + +[TestLogPollerWithChaosPostgresFixedDepth.LogPoller.General] +execution_count = 30 +use_finality_tag = false +[TestLogPollerWithChaosPostgresFixedDepth.LogPoller.Chaos] +experiment_count = 4 +target_component = "postgres" + +[TestLogPollerReplayFixedDepth.LogPoller.General] +use_finality_tag = false + diff --git a/integration-tests/testconfig/node/example.toml b/integration-tests/testconfig/node/example.toml new file mode 100644 index 00000000000..55ee1ffccf1 --- /dev/null +++ b/integration-tests/testconfig/node/example.toml @@ -0,0 +1,79 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Common +[Common] +chainlink_node_funding = 0.5 + +# Test-specific part +[ChainlinkUpgradeImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.8.0" \ No newline at end of file diff --git a/integration-tests/testconfig/node/node.toml b/integration-tests/testconfig/node/node.toml new file mode 100644 index 00000000000..683b55a35a1 --- /dev/null +++ b/integration-tests/testconfig/node/node.toml @@ -0,0 +1,5 @@ +# original image +[ChainlinkImage] + +# image to upgrade to +[ChainlinkUpgradeImage] \ No newline at end of file diff --git a/integration-tests/testconfig/ocr/example.toml b/integration-tests/testconfig/ocr/example.toml new file mode 100644 index 00000000000..6cbdbef1555 --- /dev/null +++ b/integration-tests/testconfig/ocr/example.toml @@ -0,0 +1,96 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# load test specific configuration +[Load.OCR] +[Load.OCR.Common] +eth_funds = 3 + +[Load.OCR.Load] +test_duration = "3m" +rate_limit_unit_duration = "1m" +rate = 3 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# soak test specific configuration +[Soak.Common] +chainlink_node_funding = 100 + +[Soak.OCR] +[Soak.OCR.Common] +test_duration="15m" + +[Soak.OCR.Soak] +ocr_version="1" +number_of_contracts=2 +time_between_rounds="1m" \ No newline at end of file diff --git a/integration-tests/testconfig/ocr/ocr.go b/integration-tests/testconfig/ocr/ocr.go new file mode 100644 index 00000000000..0fd995a96f1 --- /dev/null +++ b/integration-tests/testconfig/ocr/ocr.go @@ -0,0 +1,136 @@ +package ocr + +import ( + "errors" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +type Config struct { + Soak *SoakConfig `toml:"Soak"` + Load *Load `toml:"Load"` + Volume *Volume `toml:"Volume"` + Common *Common `toml:"Common"` +} + +func (o *Config) Validate() error { + if o.Common != nil { + if err := o.Common.Validate(); err != nil { + return err + } + } + if o.Soak != nil { + if err := o.Soak.Validate(); err != nil { + return err + } + } + if o.Volume != nil { + if err := o.Volume.Validate(); err != nil { + return err + } + } + return nil +} + +type Common struct { + ETHFunds *int `toml:"eth_funds"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` +} + +func (o *Common) Validate() error { + if o.ETHFunds != nil && *o.ETHFunds < 0 { + return errors.New("eth_funds must be set and cannot be negative") + } + return nil +} + +type Load struct { + Rate *int64 `toml:"rate"` + RequestsPerUnit *int `toml:"requests_per_unit"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + VerificationInterval *blockchain.StrDuration `toml:"verification_interval"` + VerificationTimeout *blockchain.StrDuration `toml:"verification_timeout"` + EAChangeInterval *blockchain.StrDuration `toml:"ea_change_interval"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` +} + +func (o *Load) Validate() error { + if o.TestDuration == nil { + return errors.New("load test duration must be set") + } + if o.Rate == nil || *o.Rate <= 0 { + return errors.New("rate must be set and be a positive integer") + } + if o.RequestsPerUnit == nil || *o.RequestsPerUnit <= 0 { + return errors.New("vu_requests_per_unit must be set and be a positive integer") + } + if o.RateLimitUnitDuration == nil || o.RateLimitUnitDuration.Duration == 0 { + return errors.New("rate_limit_unit_duration must be set and be a positive integer") + } + if o.VerificationInterval == nil || o.VerificationInterval.Duration == 0 { + return errors.New("verification_interval must be set and be a positive integer") + } + if o.VerificationTimeout == nil || o.VerificationTimeout.Duration == 0 { + return errors.New("verification_timeout must be set and be a positive integer") + } + if o.EAChangeInterval == nil || o.EAChangeInterval.Duration == 0 { + return errors.New("ea_change_interval must be set and be a positive integer") + } + + return nil +} + +type Volume struct { + Rate *int64 `toml:"rate"` + VURequestsPerUnit *int `toml:"vu_requests_per_unit"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + VerificationInterval *blockchain.StrDuration `toml:"verification_interval"` + VerificationTimeout *blockchain.StrDuration `toml:"verification_timeout"` + EAChangeInterval *blockchain.StrDuration `toml:"ea_change_interval"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` +} + +func (o *Volume) Validate() error { + if o.TestDuration == nil { + return errors.New("volume test duration must be set") + } + if o.Rate == nil || *o.Rate <= 0 { + return errors.New("rate must be set and be a positive integer") + } + if o.VURequestsPerUnit == nil || *o.VURequestsPerUnit <= 0 { + return errors.New("vu_requests_per_unit must be set and be a positive integer") + } + if o.RateLimitUnitDuration == nil || o.RateLimitUnitDuration.Duration == 0 { + return errors.New("rate_limit_unit_duration must be set and be a positive integer") + } + if o.VerificationInterval == nil || o.VerificationInterval.Duration == 0 { + return errors.New("verification_interval must be set and be a positive integer") + } + if o.VerificationTimeout == nil || o.VerificationTimeout.Duration == 0 { + return errors.New("verification_timeout must be set and be a positive integer") + } + if o.EAChangeInterval == nil || o.EAChangeInterval.Duration == 0 { + return errors.New("ea_change_interval must be set and be a positive integer") + } + + return nil +} + +type SoakConfig struct { + OCRVersion *string `toml:"ocr_version"` + NumberOfContracts *int `toml:"number_of_contracts"` + TimeBetweenRounds *blockchain.StrDuration `toml:"time_between_rounds"` +} + +func (o *SoakConfig) Validate() error { + if o.OCRVersion == nil || *o.OCRVersion == "" { + return errors.New("ocr_version must be set to either 1 or 2") + } + if o.NumberOfContracts == nil || *o.NumberOfContracts <= 1 { + return errors.New("number_of_contracts must be set and be greater than 1") + } + if o.TimeBetweenRounds == nil || o.TimeBetweenRounds.Duration == 0 { + return errors.New("time_between_rounds must be set and be a positive integer") + } + return nil +} diff --git a/integration-tests/testconfig/ocr/ocr.toml b/integration-tests/testconfig/ocr/ocr.toml new file mode 100644 index 00000000000..8d3c73ca761 --- /dev/null +++ b/integration-tests/testconfig/ocr/ocr.toml @@ -0,0 +1,43 @@ +# product defaults +[Common] +chainlink_node_funding = 0.5 + +# load test specific configuration +[Load.OCR] +[Load.OCR.Common] +eth_funds = 3 + +[Load.OCR.Load] +test_duration = "3m" +rate_limit_unit_duration = "1m" +rate = 3 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# volume test specific configuration +[Volume.OCR] +[Volume.OCR.Common] +eth_funds = 3 + +[Volume.OCR.Volume] +test_duration = "3m" +rate_limit_unit_duration = "1m" +vu_requests_per_unit = 10 +rate = 1 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# soak test specific configuration +[Soak.Common] +chainlink_node_funding = 100 + +[Soak.OCR] +[Soak.OCR.Common] +test_duration="15m" + +[Soak.OCR.Soak] +ocr_version="1" +number_of_contracts=2 +time_between_rounds="1m" \ No newline at end of file diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go new file mode 100644 index 00000000000..c80202bf45c --- /dev/null +++ b/integration-tests/testconfig/testconfig.go @@ -0,0 +1,540 @@ +package testconfig + +import ( + "embed" + "encoding/base64" + "fmt" + "os" + "slices" + "strings" + + "github.com/barkimedes/go-deepcopy" + "github.com/google/uuid" + "github.com/pelletier/go-toml/v2" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/text/cases" + "golang.org/x/text/language" + + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + k8s_config "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" + a_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" + f_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/functions" + keeper_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/keeper" + lp_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/log_poller" + ocr_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" + vrf_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrf" + vrfv2_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" +) + +type GlobalTestConfig interface { + GetChainlinkImageConfig() *ctf_config.ChainlinkImageConfig + GetLoggingConfig() *ctf_config.LoggingConfig + GetNetworkConfig() *ctf_config.NetworkConfig + GetPrivateEthereumNetworkConfig() *test_env.EthereumNetwork + GetPyroscopeConfig() *ctf_config.PyroscopeConfig +} + +type UpgradeableChainlinkTestConfig interface { + GetChainlinkUpgradeImageConfig() *ctf_config.ChainlinkImageConfig +} + +type CommonTestConfig interface { + GetCommonConfig() *Common +} + +type VRFv2TestConfig interface { + GetVRFv2Config() *vrfv2_config.Config +} + +type VRFv2PlusTestConfig interface { + GetVRFv2PlusConfig() *vrfv2plus_config.Config +} + +type FunctionsTestConfig interface { + GetFunctionsConfig() *f_config.Config +} + +type KeeperTestConfig interface { + GetKeeperConfig() *keeper_config.Config +} + +type OcrTestConfig interface { + GetOCRConfig() *ocr_config.Config +} + +type NamedConfiguration interface { + GetConfigurationName() string +} + +type TestConfig struct { + ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` + ChainlinkUpgradeImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"` + Logging *ctf_config.LoggingConfig `toml:"Logging"` + Network *ctf_config.NetworkConfig `toml:"Network"` + Pyroscope *ctf_config.PyroscopeConfig `toml:"Pyroscope"` + PrivateEthereumNetwork *ctf_test_env.EthereumNetwork `toml:"PrivateEthereumNetwork"` + + Common *Common `toml:"Common"` + Automation *a_config.Config `toml:"Automation"` + Functions *f_config.Config `toml:"Functions"` + Keeper *keeper_config.Config `toml:"Keeper"` + LogPoller *lp_config.Config `toml:"LogPoller"` + OCR *ocr_config.Config `toml:"OCR"` + VRF *vrf_config.Config `toml:"VRF"` + VRFv2 *vrfv2_config.Config `toml:"VRFv2"` + VRFv2Plus *vrfv2plus_config.Config `toml:"VRFv2Plus"` + + ConfigurationName string `toml:"-"` +} + +var embeddedConfigs embed.FS +var areConfigsEmbedded bool + +func init() { + embeddedConfigs = embeddedConfigsFs +} + +// Returns Grafana URL from Logging config +func (c *TestConfig) GetGrafanaBaseURL() (string, error) { + if c.Logging.Grafana == nil || c.Logging.Grafana.BaseUrl == nil { + return "", errors.New("grafana base url not set") + } + + return strings.TrimSuffix(*c.Logging.Grafana.BaseUrl, "/"), nil +} + +// Returns Grafana Dashboard URL from Logging config +func (c *TestConfig) GetGrafanaDashboardURL() (string, error) { + if c.Logging.Grafana == nil || c.Logging.Grafana.DashboardUrl == nil { + return "", errors.New("grafana dashboard url not set") + } + + url := *c.Logging.Grafana.DashboardUrl + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + + return url, nil +} + +// Saves Test Config to a local file +func (c *TestConfig) Save() (string, error) { + filePath := fmt.Sprintf("test_config-%s.toml", uuid.New()) + + content, err := toml.Marshal(*c) + if err != nil { + return "", errors.Wrapf(err, "error marshaling test config") + } + + err = os.WriteFile(filePath, content, 0600) + if err != nil { + return "", errors.Wrapf(err, "error writing test config") + } + + return filePath, nil +} + +// MustCopy Returns a deep copy of the Test Config or panics on error +func (c TestConfig) MustCopy() any { + return deepcopy.MustAnything(c).(TestConfig) +} + +// MustCopy Returns a deep copy of struct passed to it and returns a typed copy (or panics on error) +func MustCopy[T any](c T) T { + return deepcopy.MustAnything(c).(T) +} + +func (c *TestConfig) GetLoggingConfig() *ctf_config.LoggingConfig { + return c.Logging +} + +func (c TestConfig) GetNetworkConfig() *ctf_config.NetworkConfig { + return c.Network +} + +func (c TestConfig) GetChainlinkImageConfig() *ctf_config.ChainlinkImageConfig { + return c.ChainlinkImage +} + +func (c TestConfig) GetPrivateEthereumNetworkConfig() *ctf_test_env.EthereumNetwork { + return c.PrivateEthereumNetwork +} + +func (c TestConfig) GetPyroscopeConfig() *ctf_config.PyroscopeConfig { + return c.Pyroscope +} + +func (c TestConfig) GetCommonConfig() *Common { + return c.Common +} + +func (c TestConfig) GetVRFv2Config() *vrfv2_config.Config { + return c.VRFv2 +} + +func (c TestConfig) GetFunctionsConfig() *f_config.Config { + return c.Functions +} + +func (c TestConfig) GetVRFv2PlusConfig() *vrfv2plus_config.Config { + return c.VRFv2Plus +} + +func (c TestConfig) GetChainlinkUpgradeImageConfig() *ctf_config.ChainlinkImageConfig { + return c.ChainlinkUpgradeImage +} + +func (c TestConfig) GetKeeperConfig() *keeper_config.Config { + return c.Keeper +} + +func (c TestConfig) GetOCRConfig() *ocr_config.Config { + return c.OCR +} + +func (c TestConfig) GetConfigurationName() string { + return c.ConfigurationName +} + +type Common struct { + ChainlinkNodeFunding *float64 `toml:"chainlink_node_funding"` +} + +func (c *Common) Validate() error { + if c.ChainlinkNodeFunding != nil && *c.ChainlinkNodeFunding < 0 { + return fmt.Errorf("chainlink node funding must be positive") + } + + return nil +} + +type Product string + +const ( + Automation Product = "automation" + Cron Product = "cron" + DirectRequest Product = "direct_request" + Flux Product = "flux" + ForwarderOcr Product = "forwarder_ocr" + ForwarderOcr2 Product = "forwarder_ocr2" + Functions Product = "functions" + Keeper Product = "keeper" + LogPoller Product = "log_poller" + Node Product = "node" + OCR Product = "ocr" + OCR2 Product = "ocr2" + OCR2VRF Product = "ocr2vrf" + RunLog Product = "runlog" + VRF Product = "vrf" + VRFv2 Product = "vrfv2" + VRFv2Plus Product = "vrfv2plus" +) + +var TestTypesWithLoki = []string{"Load", "Soak", "Stress", "Spike", "Volume"} + +const TestTypeEnvVarName = "TEST_TYPE" + +func GetConfigurationNameFromEnv() (string, error) { + testType := os.Getenv(TestTypeEnvVarName) + if testType == "" { + return "", fmt.Errorf("%s env var not set", TestTypeEnvVarName) + } + + return cases.Title(language.English, cases.NoLower).String(testType), nil +} + +const ( + Base64OverrideEnvVarName = k8s_config.EnvBase64ConfigOverride + NoKey = "NO_KEY" +) + +func GetConfig(configurationName string, product Product) (TestConfig, error) { + logger := logging.GetTestLogger(nil) + + configurationName = strings.ReplaceAll(configurationName, "/", "_") + configurationName = strings.ReplaceAll(configurationName, " ", "_") + configurationName = cases.Title(language.English, cases.NoLower).String(configurationName) + fileNames := []string{ + "default.toml", + fmt.Sprintf("%s.toml", product), + "overrides.toml", + } + + testConfig := TestConfig{} + testConfig.ConfigurationName = configurationName + logger.Debug().Msgf("Will apply configuration named '%s' if it is found in any of the configs", configurationName) + + var handleSpecialOverrides = func(logger zerolog.Logger, filename, configurationName string, target *TestConfig, content []byte, product Product) error { + switch product { + case Automation: + return handleAutomationConfigOverride(logger, filename, configurationName, target, content) + default: + err := ctf_config.BytesToAnyTomlStruct(logger, filename, configurationName, &testConfig, content) + if err != nil { + return errors.Wrapf(err, "error reading file %s", filename) + } + + return nil + } + } + + // read embedded configs is build tag "embed" is set + // this makes our life much easier when using a binary + if areConfigsEmbedded { + logger.Info().Msg("Reading embedded configs") + embeddedFiles := []string{"default.toml", fmt.Sprintf("%s/%s.toml", product, product)} + for _, fileName := range embeddedFiles { + file, err := embeddedConfigs.ReadFile(fileName) + if err != nil && errors.Is(err, os.ErrNotExist) { + logger.Debug().Msgf("Embedded config file %s not found. Continuing", fileName) + continue + } else if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading embedded config") + } + + err = handleSpecialOverrides(logger, fileName, configurationName, &testConfig, file, product) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error unmarshalling embedded config") + } + } + } + + logger.Info().Msg("Reading configs from file system") + for _, fileName := range fileNames { + logger.Debug().Msgf("Looking for config file %s", fileName) + filePath, err := osutil.FindFile(fileName, osutil.DEFAULT_STOP_FILE_NAME, 3) + + if err != nil && errors.Is(err, os.ErrNotExist) { + logger.Debug().Msgf("Config file %s not found", fileName) + continue + } else if err != nil { + return TestConfig{}, errors.Wrapf(err, "error looking for file %s", filePath) + } + logger.Debug().Str("location", filePath).Msgf("Found config file %s", fileName) + + content, err := readFile(filePath) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) + } + + err = handleSpecialOverrides(logger, fileName, configurationName, &testConfig, content, product) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) + } + } + + logger.Info().Msg("Reading configs from Base64 override env var") + configEncoded, isSet := os.LookupEnv(Base64OverrideEnvVarName) + if isSet && configEncoded != "" { + logger.Debug().Msgf("Found base64 config override environment variable '%s' found", Base64OverrideEnvVarName) + decoded, err := base64.StdEncoding.DecodeString(configEncoded) + if err != nil { + return TestConfig{}, err + } + + err = handleSpecialOverrides(logger, Base64OverrideEnvVarName, configurationName, &testConfig, decoded, product) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error unmarshaling base64 config") + } + } else { + logger.Debug().Msg("Base64 config override from environment variable not found") + } + + // it neede some custom logic, so we do it separately + err := testConfig.readNetworkConfiguration() + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading network config") + } + + logger.Debug().Msg("Validating test config") + err = testConfig.Validate() + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error validating test config") + } + + if testConfig.Common == nil { + testConfig.Common = &Common{} + } + + logger.Debug().Msg("Correct test config constructed successfully") + return testConfig, nil +} + +func (c *TestConfig) readNetworkConfiguration() error { + // currently we need to read that kind of secrets only for network configuration + if c == nil { + c.Network = &ctf_config.NetworkConfig{} + } + + c.Network.UpperCaseNetworkNames() + err := c.Network.Default() + if err != nil { + return errors.Wrapf(err, "error reading default network config") + } + + // this is the only value we need to generate dynamically before starting a new simulated chain + if c.PrivateEthereumNetwork != nil && c.PrivateEthereumNetwork.EthereumChainConfig != nil { + c.PrivateEthereumNetwork.EthereumChainConfig.GenerateGenesisTimestamp() + } + + return nil +} + +func (c *TestConfig) Validate() error { + defer func() { + if r := recover(); r != nil { + panic(fmt.Errorf("Panic during test config validation: '%v'. Most probably due to presence of partial product config", r)) + } + }() + if c.ChainlinkImage == nil { + return fmt.Errorf("chainlink image config must be set") + } + if err := c.ChainlinkImage.Validate(); err != nil { + return errors.Wrapf(err, "chainlink image config validation failed") + } + if c.ChainlinkUpgradeImage != nil { + if err := c.ChainlinkUpgradeImage.Validate(); err != nil { + return errors.Wrapf(err, "chainlink upgrade image config validation failed") + } + } + if err := c.Network.Validate(); err != nil { + return errors.Wrapf(err, "network config validation failed") + } + + if c.Logging == nil { + return fmt.Errorf("logging config must be set") + } + + if err := c.Logging.Validate(); err != nil { + return errors.Wrapf(err, "logging config validation failed") + } + + // require Loki config only if these tests run locally + _, willUseRemoteRunner := os.LookupEnv(k8s_config.EnvVarJobImage) + _, isInsideK8s := os.LookupEnv(k8s_config.EnvVarInsideK8s) + if (!willUseRemoteRunner && !isInsideK8s) && slices.Contains(TestTypesWithLoki, c.ConfigurationName) { + if c.Logging.Loki == nil { + return fmt.Errorf("for local execution you must set Loki config in logging config") + } + + if err := c.Logging.Loki.Validate(); err != nil { + return errors.Wrapf(err, "loki config validation failed") + } + } + + if c.Logging.LogStream != nil && slices.Contains(c.Logging.LogStream.LogTargets, "loki") { + if c.Logging.Loki == nil { + return fmt.Errorf("in order to use Loki as logging target you must set Loki config in logging config") + } + + if err := c.Logging.Loki.Validate(); err != nil { + return errors.Wrapf(err, "loki config validation failed") + } + } + + if c.Pyroscope != nil { + if err := c.Pyroscope.Validate(); err != nil { + return errors.Wrapf(err, "pyroscope config validation failed") + } + } + + if c.PrivateEthereumNetwork != nil { + if err := c.PrivateEthereumNetwork.Validate(); err != nil { + return errors.Wrapf(err, "private ethereum network config validation failed") + } + } + + if c.Common != nil { + if err := c.Common.Validate(); err != nil { + return errors.Wrapf(err, "Common config validation failed") + } + } + + if c.Automation != nil { + if err := c.Automation.Validate(); err != nil { + return errors.Wrapf(err, "Automation config validation failed") + } + } + + if c.Functions != nil { + if err := c.Functions.Validate(); err != nil { + return errors.Wrapf(err, "Functions config validation failed") + } + } + + if c.Keeper != nil { + if err := c.Keeper.Validate(); err != nil { + return errors.Wrapf(err, "Keeper config validation failed") + } + } + + if c.LogPoller != nil { + if err := c.LogPoller.Validate(); err != nil { + return errors.Wrapf(err, "LogPoller config validation failed") + } + } + + if c.OCR != nil { + if err := c.OCR.Validate(); err != nil { + return errors.Wrapf(err, "OCR config validation failed") + } + } + + if c.VRF != nil { + if err := c.VRF.Validate(); err != nil { + return errors.Wrapf(err, "VRF config validation failed") + } + } + + if c.VRFv2 != nil { + if err := c.VRFv2.Validate(); err != nil { + return errors.Wrapf(err, "VRFv2 config validation failed") + } + } + + if c.VRFv2Plus != nil { + if err := c.VRFv2Plus.Validate(); err != nil { + return errors.Wrapf(err, "VRFv2Plus config validation failed") + } + } + + return nil +} + +func readFile(filePath string) ([]byte, error) { + content, err := os.ReadFile(filePath) + if err != nil { + return nil, errors.Wrapf(err, "error reading file %s", filePath) + } + + return content, nil +} + +func handleAutomationConfigOverride(logger zerolog.Logger, filename, configurationName string, target *TestConfig, content []byte) error { + logger.Debug().Msgf("Handling automation config override for %s", filename) + oldConfig := MustCopy(target) + newConfig := TestConfig{} + + err := ctf_config.BytesToAnyTomlStruct(logger, filename, configurationName, &target, content) + if err != nil { + return errors.Wrapf(err, "error reading file %s", filename) + } + + err = ctf_config.BytesToAnyTomlStruct(logger, filename, configurationName, &newConfig, content) + if err != nil { + return errors.Wrapf(err, "error reading file %s", filename) + } + + // override instead of merging + if (newConfig.Automation != nil && len(newConfig.Automation.Load) > 0) && (oldConfig != nil && oldConfig.Automation != nil && len(oldConfig.Automation.Load) > 0) { + target.Automation.Load = newConfig.Automation.Load + } + + return nil +} diff --git a/integration-tests/testconfig/testconfig_test.go b/integration-tests/testconfig/testconfig_test.go new file mode 100644 index 00000000000..4a9dbdaade3 --- /dev/null +++ b/integration-tests/testconfig/testconfig_test.go @@ -0,0 +1,88 @@ +package testconfig + +import ( + "encoding/base64" + "math/big" + "os" + "testing" + + "github.com/pelletier/go-toml/v2" + "github.com/test-go/testify/require" + + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + a_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" +) + +func TestBase64ConfigRead(t *testing.T) { + networkConfigTOML := ` + [RpcHttpUrls] + arbitrum_goerli = ["https://devnet-1.mt/ABC/rpc/"] + optimism_goerli = ["https://devnet-3.mt/ABC/rpc/"] + + [RpcWsUrls] + arbitrum_goerli = ["wss://devnet-1.mt/ABC/rpc/"] + optimism_goerli = ["wss://devnet-2.mt/ABC/rpc/"] + ` + networksEncoded := base64.StdEncoding.EncodeToString([]byte(networkConfigTOML)) + os.Setenv(ctf_config.Base64NetworkConfigEnvVarName, networksEncoded) + + testConfig := TestConfig{ + Automation: &a_config.Config{ + General: &a_config.General{ + NumberOfNodes: ptr.Ptr(7), + Duration: ptr.Ptr(9), + BlockTime: ptr.Ptr(10), + SpecType: ptr.Ptr("minimum"), + ChainlinkNodeLogLevel: ptr.Ptr("debug"), + }, + Load: []a_config.Load{ + { + NumberOfUpkeeps: ptr.Ptr(1), + NumberOfEvents: ptr.Ptr(2), + NumberOfSpamMatchingEvents: ptr.Ptr(3), + NumberOfSpamNonMatchingEvents: ptr.Ptr(4), + CheckBurnAmount: big.NewInt(5), + PerformBurnAmount: big.NewInt(6), + SharedTrigger: ptr.Ptr(true), + }, + { + NumberOfUpkeeps: ptr.Ptr(3), + NumberOfEvents: ptr.Ptr(2), + NumberOfSpamMatchingEvents: ptr.Ptr(3), + NumberOfSpamNonMatchingEvents: ptr.Ptr(7), + CheckBurnAmount: big.NewInt(5), + PerformBurnAmount: big.NewInt(6), + SharedTrigger: ptr.Ptr(false), + }, + }, + }, + Network: &ctf_config.NetworkConfig{ + SelectedNetworks: []string{"OPTIMISM_GOERLI"}, + RpcHttpUrls: map[string][]string{ + "OPTIMISM_GOERLI": {"http://localhost:8545"}, + }, + WalletKeys: map[string][]string{ + "OPTIMISM_GOERLI": {"0x3333333333333333333333333333333333333333"}, + }, + }, + } + + configMarshalled, err := toml.Marshal(testConfig) + require.NoError(t, err, "Error marshalling test config") + + testConfigEncoded := base64.StdEncoding.EncodeToString(configMarshalled) + os.Setenv(Base64OverrideEnvVarName, testConfigEncoded) + + readConfig, err := GetConfig("test", Automation) + require.NoError(t, err, "Error reading config") + + require.NotNil(t, readConfig.Automation, "Automation config read from base64 is nil") + require.Equal(t, testConfig.Automation.General, readConfig.Automation.General, "General automation config does not match expected") + require.EqualValues(t, testConfig.Automation.Load, readConfig.Automation.Load, "Load automation config does not match expected") + require.NotNil(t, readConfig.Network, "Network config read from base64 is nil") + require.Equal(t, testConfig.Network.SelectedNetworks, readConfig.Network.SelectedNetworks, "SelectedNetwork config entry read from base64 does not match expected") + require.Equal(t, []string{"http://localhost:8545"}, readConfig.Network.RpcHttpUrls["OPTIMISM_GOERLI"], "RpcHttpUrls config entry read from base64 does not match expected") + require.Equal(t, []string{"wss://devnet-2.mt/ABC/rpc/"}, readConfig.Network.RpcWsUrls["OPTIMISM_GOERLI"], "RpcWsUrls config entry read from base64 network defaults does not match expected") + require.Equal(t, testConfig.Network.WalletKeys, readConfig.Network.WalletKeys, "WalletKeys config entry read from base64 does not match expected") +} diff --git a/integration-tests/testconfig/vrf/config.go b/integration-tests/testconfig/vrf/config.go new file mode 100644 index 00000000000..d009f5bf667 --- /dev/null +++ b/integration-tests/testconfig/vrf/config.go @@ -0,0 +1,8 @@ +package vrf + +type Config struct { +} + +func (o *Config) Validate() error { + return nil +} diff --git a/integration-tests/testconfig/vrfv2/config.go b/integration-tests/testconfig/vrfv2/config.go new file mode 100644 index 00000000000..f539d91799c --- /dev/null +++ b/integration-tests/testconfig/vrfv2/config.go @@ -0,0 +1,330 @@ +package testconfig + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +const ( + ErrDeviationShouldBeLessThanOriginal = "`RandomnessRequestCountPerRequestDeviation` should be less than `RandomnessRequestCountPerRequest`" +) + +type Config struct { + Common *Common `toml:"Common"` + General *General `toml:"General"` + ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` + NewEnvConfig *NewEnvConfig `toml:"NewEnv"` + Performance *PerformanceConfig `toml:"Performance"` +} + +func (c *Config) Validate() error { + if c.Common != nil { + if err := c.Common.Validate(); err != nil { + return err + } + } + if c.General != nil { + if err := c.General.Validate(); err != nil { + return err + } + } + if c.Performance != nil { + if err := c.Performance.Validate(); err != nil { + return err + } + if *c.Performance.UseExistingEnv { + if c.ExistingEnvConfig != nil { + if err := c.ExistingEnvConfig.Validate(); err != nil { + return err + } + } + } else { + if c.NewEnvConfig != nil { + if err := c.NewEnvConfig.Validate(); err != nil { + return err + } + } + } + } + + return nil +} + +type Common struct { + CancelSubsAfterTestRun *bool `toml:"cancel_subs_after_test_run"` +} + +func (c *Common) Validate() error { + return nil +} + +type PerformanceConfig struct { + TestDuration *blockchain.StrDuration `toml:"test_duration"` + RPS *int64 `toml:"rps"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + + // Using existing environment and contracts + UseExistingEnv *bool `toml:"use_existing_env"` + CoordinatorAddress *string + ConsumerAddress *string + LinkAddress *string + SubID *uint64 + KeyHash *string +} + +func (c *PerformanceConfig) Validate() error { + if c.TestDuration == nil || c.TestDuration.Duration == 0 { + return errors.New("test_duration must be set to a positive value") + } + if c.RPS == nil || *c.RPS == 0 { + return errors.New("rps must be set to a positive value") + } + if c.RateLimitUnitDuration == nil { + return errors.New("rate_limit_unit_duration must be set ") + } + if c.UseExistingEnv == nil { + return errors.New("use_existing_env must be set ") + } + + return nil +} + +type ExistingEnvConfig struct { + CoordinatorAddress *string `toml:"coordinator_address"` + ConsumerAddress *string `toml:"consumer_address"` + LinkAddress *string `toml:"link_address"` + SubID *uint64 `toml:"sub_id"` + KeyHash *string `toml:"key_hash"` + CreateFundSubsAndAddConsumers *bool `toml:"create_fund_subs_and_add_consumers"` + NodeSendingKeys []string `toml:"node_sending_keys"` + Funding +} + +func (c *ExistingEnvConfig) Validate() error { + if c.CreateFundSubsAndAddConsumers == nil { + return errors.New("create_fund_subs_and_add_consumers must be set ") + } + if c.CoordinatorAddress == nil { + return errors.New("coordinator_address must be set when using existing environment") + } + if !common.IsHexAddress(*c.CoordinatorAddress) { + return errors.New("coordinator_address must be a valid hex address") + } + if c.KeyHash == nil { + return errors.New("key_hash must be set when using existing environment") + } + if *c.KeyHash == "" { + return errors.New("key_hash must be a non-empty string") + } + if c.LinkAddress != nil && !common.IsHexAddress(*c.LinkAddress) { + return errors.New("link_address must be a valid hex address") + } + + if *c.CreateFundSubsAndAddConsumers { + if err := c.Funding.Validate(); err != nil { + return err + } + if err := c.Funding.SubFunding.Validate(); err != nil { + return err + } + } else { + if c.ConsumerAddress == nil || *c.ConsumerAddress == "" { + return errors.New("consumer_address must be set when using existing environment") + } + if !common.IsHexAddress(*c.ConsumerAddress) { + return errors.New("consumer_address must be a valid hex address") + } + if c.SubID == nil { + return errors.New("sub_id must be set when using existing environment") + } + if *c.SubID == 0 { + return errors.New("sub_id must be a positive value") + } + } + + if c.NodeSendingKeys != nil { + for _, key := range c.NodeSendingKeys { + if !common.IsHexAddress(key) { + return errors.New("node_sending_keys must be a valid hex address") + } + } + } + + return nil +} + +type NewEnvConfig struct { + *Funding +} + +func (c *NewEnvConfig) Validate() error { + if c.Funding != nil { + return c.Funding.Validate() + } + + return nil +} + +type Funding struct { + SubFunding + NodeSendingKeyFunding *float64 `toml:"node_sending_key_funding"` + NodeSendingKeyFundingMin *float64 `toml:"node_sending_key_funding_min"` +} + +func (c *Funding) Validate() error { + if c.NodeSendingKeyFunding != nil && *c.NodeSendingKeyFunding <= 0 { + return errors.New("when set node_sending_key_funding must be a positive value") + } + if c.NodeSendingKeyFundingMin != nil && *c.NodeSendingKeyFundingMin <= 0 { + return errors.New("when set node_sending_key_funding_min must be a positive value") + } + + return nil +} + +type SubFunding struct { + SubFundsLink *float64 `toml:"sub_funds_link"` +} + +func (c *SubFunding) Validate() error { + if c.SubFundsLink != nil && *c.SubFundsLink < 0 { + return errors.New("when set sub_funds_link must be a non-negative value") + } + + return nil +} + +type General struct { + CLNodeMaxGasPriceGWei *int64 `toml:"max_gas_price_gwei"` // Max gas price in GWei for the chainlink node + LinkNativeFeedResponse *int64 `toml:"link_native_feed_response"` // Response of the LINK/ETH feed + MinimumConfirmations *uint16 `toml:"minimum_confirmations" ` // Minimum number of confirmations for the VRF Coordinator + SubscriptionFundingAmountLink *float64 `toml:"subscription_funding_amount_link"` // Amount of LINK to fund the subscription with + NumberOfWords *uint32 `toml:"number_of_words" ` // Number of words to request + CallbackGasLimit *uint32 `toml:"callback_gas_limit" ` // Gas limit for the callback + MaxGasLimitCoordinatorConfig *uint32 `toml:"max_gas_limit_coordinator_config"` // Max gas limit for the VRF Coordinator config + FallbackWeiPerUnitLink *int64 `toml:"fallback_wei_per_unit_link"` // Fallback wei per unit LINK for the VRF Coordinator config + StalenessSeconds *uint32 `toml:"staleness_seconds" ` // Staleness in seconds for the VRF Coordinator config + GasAfterPaymentCalculation *uint32 `toml:"gas_after_payment_calculation" ` // Gas after payment calculation for the VRF Coordinator + FulfillmentFlatFeeLinkPPMTier1 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_1"` + FulfillmentFlatFeeLinkPPMTier2 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_2"` + FulfillmentFlatFeeLinkPPMTier3 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_3"` + FulfillmentFlatFeeLinkPPMTier4 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_4"` + FulfillmentFlatFeeLinkPPMTier5 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_5"` + ReqsForTier2 *int64 `toml:"reqs_for_tier_2"` + ReqsForTier3 *int64 `toml:"reqs_for_tier_3"` + ReqsForTier4 *int64 `toml:"reqs_for_tier_4"` + ReqsForTier5 *int64 `toml:"reqs_for_tier_5"` + + NumberOfSubToCreate *int `toml:"number_of_sub_to_create"` // Number of subscriptions to create + + RandomnessRequestCountPerRequest *uint16 `toml:"randomness_request_count_per_request"` // How many randomness requests to send per request + RandomnessRequestCountPerRequestDeviation *uint16 `toml:"randomness_request_count_per_request_deviation"` // How many randomness requests to send per request + + RandomWordsFulfilledEventTimeout *blockchain.StrDuration `toml:"random_words_fulfilled_event_timeout"` // How long to wait for the RandomWordsFulfilled event to be emitted + + // Wrapper Config + WrapperGasOverhead *uint32 `toml:"wrapped_gas_overhead"` + CoordinatorGasOverhead *uint32 `toml:"coordinator_gas_overhead"` + WrapperPremiumPercentage *uint8 `toml:"wrapper_premium_percentage"` + WrapperMaxNumberOfWords *uint8 `toml:"wrapper_max_number_of_words"` + WrapperConsumerFundingAmountNativeToken *float64 `toml:"wrapper_consumer_funding_amount_native_token"` + WrapperConsumerFundingAmountLink *int64 `toml:"wrapper_consumer_funding_amount_link"` +} + +func (c *General) Validate() error { + if c.CLNodeMaxGasPriceGWei == nil || *c.CLNodeMaxGasPriceGWei == 0 { + return errors.New("max_gas_price_gwei must be set to a positive value") + } + if c.LinkNativeFeedResponse == nil || *c.LinkNativeFeedResponse == 0 { + return errors.New("link_native_feed_response must be set to a positive value") + } + if c.MinimumConfirmations == nil { + return errors.New("minimum_confirmations must be set to a non-negative value") + } + if c.SubscriptionFundingAmountLink == nil || *c.SubscriptionFundingAmountLink == 0 { + return errors.New("subscription_funding_amount_link must be set to a positive value") + } + if c.NumberOfWords == nil || *c.NumberOfWords == 0 { + return errors.New("number_of_words must be set to a positive value") + } + if c.CallbackGasLimit == nil || *c.CallbackGasLimit == 0 { + return errors.New("callback_gas_limit must be set to a positive value") + } + if c.MaxGasLimitCoordinatorConfig == nil || *c.MaxGasLimitCoordinatorConfig == 0 { + return errors.New("max_gas_limit_coordinator_config must be set to a positive value") + } + if c.FallbackWeiPerUnitLink == nil || *c.FallbackWeiPerUnitLink == 0 { + return errors.New("fallback_wei_per_unit_link must be set to a positive value") + } + if c.StalenessSeconds == nil || *c.StalenessSeconds == 0 { + return errors.New("staleness_seconds must be set to a positive value") + } + if c.GasAfterPaymentCalculation == nil || *c.GasAfterPaymentCalculation == 0 { + return errors.New("gas_after_payment_calculation must be set to a positive value") + } + if c.FulfillmentFlatFeeLinkPPMTier1 == nil || *c.FulfillmentFlatFeeLinkPPMTier1 == 0 { + return errors.New("fulfilment_flat_fee_link_ppm_tier_1 must be set to a positive value") + } + if c.FulfillmentFlatFeeLinkPPMTier2 == nil || *c.FulfillmentFlatFeeLinkPPMTier2 == 0 { + return errors.New("fulfilment_flat_fee_link_ppm_tier_2 must be set to a positive value") + } + if c.FulfillmentFlatFeeLinkPPMTier3 == nil || *c.FulfillmentFlatFeeLinkPPMTier3 == 0 { + return errors.New("fulfilment_flat_fee_link_ppm_tier_3 must be set to a positive value") + } + if c.FulfillmentFlatFeeLinkPPMTier4 == nil || *c.FulfillmentFlatFeeLinkPPMTier4 == 0 { + return errors.New("fulfilment_flat_fee_link_ppm_tier_4 must be set to a positive value") + } + if c.FulfillmentFlatFeeLinkPPMTier5 == nil || *c.FulfillmentFlatFeeLinkPPMTier5 == 0 { + return errors.New("fulfilment_flat_fee_link_ppm_tier_5 must be set to a positive value") + } + if c.ReqsForTier2 == nil || *c.ReqsForTier2 < 0 { + return errors.New("reqs_for_tier_2 must be set to a non-negative value") + } + if c.ReqsForTier3 == nil || *c.ReqsForTier3 < 0 { + return errors.New("reqs_for_tier_3 must be set to a non-negative value") + } + if c.ReqsForTier4 == nil || *c.ReqsForTier4 < 0 { + return errors.New("reqs_for_tier_4 must be set to a non-negative value") + } + if c.ReqsForTier5 == nil || *c.ReqsForTier5 < 0 { + return errors.New("reqs_for_tier_5 must be set to a non-negative value") + } + if c.NumberOfSubToCreate == nil || *c.NumberOfSubToCreate == 0 { + return errors.New("number_of_sub_to_create must be set to a positive value") + } + if c.RandomnessRequestCountPerRequest == nil || *c.RandomnessRequestCountPerRequest == 0 { + return errors.New("randomness_request_count_per_request must be set to a positive value") + } + if c.RandomnessRequestCountPerRequestDeviation == nil { + return errors.New("randomness_request_count_per_request_deviation must be set to a non-negative value") + } + if c.RandomWordsFulfilledEventTimeout == nil || c.RandomWordsFulfilledEventTimeout.Duration == 0 { + return errors.New("random_words_fulfilled_event_timeout must be set to a positive value") + } + if c.WrapperGasOverhead == nil { + return errors.New("wrapped_gas_overhead must be set to a non-negative value") + } + if c.CoordinatorGasOverhead == nil || *c.CoordinatorGasOverhead == 0 { + return errors.New("coordinator_gas_overhead must be set to a non-negative value") + } + if c.WrapperPremiumPercentage == nil || *c.WrapperPremiumPercentage == 0 { + return errors.New("wrapper_premium_percentage must be set to a positive value") + } + if c.WrapperMaxNumberOfWords == nil || *c.WrapperMaxNumberOfWords == 0 { + return errors.New("wrapper_max_number_of_words must be set to a positive value") + } + if c.WrapperConsumerFundingAmountNativeToken == nil || *c.WrapperConsumerFundingAmountNativeToken < 0 { + return errors.New("wrapper_consumer_funding_amount_native_token must be set to a non-negative value") + } + if c.WrapperConsumerFundingAmountLink == nil || *c.WrapperConsumerFundingAmountLink < 0 { + return errors.New("wrapper_consumer_funding_amount_link must be set to a non-negative value") + } + if *c.RandomnessRequestCountPerRequest <= *c.RandomnessRequestCountPerRequestDeviation { + return errors.New(ErrDeviationShouldBeLessThanOriginal) + } + + return nil +} diff --git a/integration-tests/testconfig/vrfv2/example.toml b/integration-tests/testconfig/vrfv2/example.toml new file mode 100644 index 00000000000..bc826ebe05e --- /dev/null +++ b/integration-tests/testconfig/vrfv2/example.toml @@ -0,0 +1,136 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Common +[Common] +chainlink_node_funding = 0.5 + +# Product part +[VRFv2] +[VRFv2.Common] +cancel_subs_after_test_run = true + +[VRFv2.General] +max_gas_price_gwei = 1000 +link_native_feed_response = 1000000000000000000 +minimum_confirmations = 3 +subscription_funding_amount_link = 5.0 +number_of_words = 3 +callback_gas_limit = 1000000 +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = 60000000000000000 +staleness_seconds = 86400 +gas_after_payment_calculation = 33825 +fulfilment_flat_fee_link_ppm_tier_1 = 500 +fulfilment_flat_fee_link_ppm_tier_2 = 500 +fulfilment_flat_fee_link_ppm_tier_3 = 500 +fulfilment_flat_fee_link_ppm_tier_4 = 500 +fulfilment_flat_fee_link_ppm_tier_5 = 500 +reqs_for_tier_2 = 0 +reqs_for_tier_3 = 0 +reqs_for_tier_4 = 0 +reqs_for_tier_5 = 0 +number_of_sub_to_create = 1 +randomness_request_count_per_request = 1 +randomness_request_count_per_request_deviation = 0 +random_words_fulfilled_event_timeout = "2m" +wrapped_gas_overhead = 50000 +coordinator_gas_overhead = 52000 +wrapper_premium_percentage = 25 +wrapper_max_number_of_words = 10 +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 10 + +[VRFv2.Performance] +rate_limit_unit_duration = "3s" +rps = 1 + +[VRFv2.NewEnv] +sub_funds_link = 1000 +node_sending_key_funding = 1000 + +[VRFv2.ExistingEnv] +coordinator_address = "" +consumer_address = "" +sub_id = 1 +key_hash = "" +create_fund_subs_and_add_consumers = true +link_address = "" +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [ + "", + "", + "", + "", + "", + "", +] \ No newline at end of file diff --git a/integration-tests/testconfig/vrfv2/vrfv2.toml b/integration-tests/testconfig/vrfv2/vrfv2.toml new file mode 100644 index 00000000000..64e628c4afa --- /dev/null +++ b/integration-tests/testconfig/vrfv2/vrfv2.toml @@ -0,0 +1,143 @@ +# default config +[Common] +chainlink_node_funding = 0.1 + +[VRFv2] +[VRFv2.General] +max_gas_price_gwei = 10 +link_native_feed_response = 1000000000000000000 +minimum_confirmations = 3 +subscription_funding_amount_link = 5.0 +number_of_words = 3 +callback_gas_limit = 1000000 +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = 60000000000000000 +staleness_seconds = 86400 +gas_after_payment_calculation = 33825 +fulfilment_flat_fee_link_ppm_tier_1 = 500 +fulfilment_flat_fee_link_ppm_tier_2 = 500 +fulfilment_flat_fee_link_ppm_tier_3 = 500 +fulfilment_flat_fee_link_ppm_tier_4 = 500 +fulfilment_flat_fee_link_ppm_tier_5 = 500 +reqs_for_tier_2 = 0 +reqs_for_tier_3 = 0 +reqs_for_tier_4 = 0 +reqs_for_tier_5 = 0 +number_of_sub_to_create = 1 +randomness_request_count_per_request = 1 +randomness_request_count_per_request_deviation = 0 +random_words_fulfilled_event_timeout = "2m" +wrapped_gas_overhead = 50000 +coordinator_gas_overhead = 52000 +wrapper_premium_percentage = 25 +wrapper_max_number_of_words = 10 +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 10 + +# load test specific config +[Load.VRFv2] +[Load.VRFv2.Common] +cancel_subs_after_test_run = true + +[Load.VRFv2.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Load.VRFv2.Performance] +# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds +rate_limit_unit_duration = "3s" +rps = 1 + +[Load.VRFv2.NewEnv] +sub_funds_link = 1000 +node_sending_key_funding = 1000 + +[Load.VRFv2.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] + +# soak test specific config +[Soak.VRFv2] +[VRFv2.Common] +cancel_subs_after_test_run = true + +[Soak.VRFv2.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Soak.VRFv2.Performance] +# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds +rate_limit_unit_duration = "6s" +rps = 1 + +[Soak.VRFv2.NewEnv] +sub_funds_link = 1000 +node_sending_key_funding = 1000 + +[Soak.VRFv2.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] + +# spike test specific config +[Spike.VRFv2] +[Spike.VRFv2.Common] +cancel_subs_after_test_run = true + +[Spike.VRFv2.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Spike.VRFv2.Performance] +# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds +rate_limit_unit_duration = "1m" +rps = 1 + +[Spike.VRFv2.NewEnv] +sub_funds_link = 1000 +node_sending_key_funding = 1000 + +[Spike.VRFv2.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] + +# stress test specific config +[Stress.VRFv2] +[Stress.VRFv2.Common] +cancel_subs_after_test_run = true + +[Stress.VRFv2.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Stress.VRFv2.Performance] +# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx +rate_limit_unit_duration = "1s" +rps = 3 + +[Stress.VRFv2.NewEnv] +sub_funds_link = 1000 +node_sending_key_funding = 1000 + +[Stress.VRFv2.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] diff --git a/integration-tests/testconfig/vrfv2plus/config.go b/integration-tests/testconfig/vrfv2plus/config.go new file mode 100644 index 00000000000..667803e06b6 --- /dev/null +++ b/integration-tests/testconfig/vrfv2plus/config.go @@ -0,0 +1,158 @@ +package testconfig + +import ( + "errors" + + vrfv2 "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" +) + +type BillingType string + +const ( + BillingType_Link BillingType = "LINK" + BillingType_Native BillingType = "NATIVE" + BillingType_Link_and_Native BillingType = "LINK_AND_NATIVE" +) + +type Config struct { + Common *Common `toml:"Common"` + General *General `toml:"General"` + ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` + NewEnvConfig *NewEnvConfig `toml:"NewEnv"` + Performance *vrfv2.PerformanceConfig `toml:"Performance"` +} + +func (c *Config) Validate() error { + if c.Common != nil { + if err := c.Common.Validate(); err != nil { + return err + } + } + if c.General != nil { + if err := c.General.Validate(); err != nil { + return err + } + } + if c.Performance != nil { + if err := c.Performance.Validate(); err != nil { + return err + } + if *c.Performance.UseExistingEnv { + if c.ExistingEnvConfig != nil { + if err := c.ExistingEnvConfig.Validate(); err != nil { + return err + } + } + } else { + if c.NewEnvConfig != nil { + if err := c.NewEnvConfig.Validate(); err != nil { + return err + } + } + } + } + + return nil +} + +type Common struct { + *vrfv2.Common +} + +func (c *Common) Validate() error { + if c.Common == nil { + return nil + } + return c.Common.Validate() +} + +type General struct { + *vrfv2.General + SubscriptionBillingType *string `toml:"subscription_billing_type"` // Billing type for the subscription + SubscriptionFundingAmountNative *float64 `toml:"subscription_funding_amount_native"` // Amount of LINK to fund the subscription with + FulfillmentFlatFeeLinkPPM *uint32 `toml:"fulfillment_flat_fee_link_ppm"` // Flat fee in ppm for LINK for the VRF Coordinator config + FulfillmentFlatFeeNativePPM *uint32 `toml:"fulfillment_flat_fee_native_ppm"` // Flat fee in ppm for native currency for the VRF Coordinator config +} + +func (c *General) Validate() error { + if err := c.General.Validate(); err != nil { + return err + } + if c.SubscriptionBillingType == nil || *c.SubscriptionBillingType == "" { + return errors.New("subscription_billing_type must be set to either: LINK, NATIVE, LINK_AND_NATIVE") + } + if c.SubscriptionFundingAmountNative == nil || *c.SubscriptionFundingAmountNative <= 0 { + return errors.New("subscription_funding_amount_native must be greater than 0") + } + if c.FulfillmentFlatFeeLinkPPM == nil || *c.FulfillmentFlatFeeLinkPPM <= 0 { + return errors.New("fulfillment_flat_fee_link_ppm must be greater than 0") + } + if c.FulfillmentFlatFeeNativePPM == nil || *c.FulfillmentFlatFeeNativePPM <= 0 { + return errors.New("fulfillment_flat_fee_native_ppm must be greater than 0") + } + + return nil +} + +type NewEnvConfig struct { + *Funding +} + +func (c *NewEnvConfig) Validate() error { + if c.Funding == nil { + return nil + } + + return c.Funding.Validate() +} + +type ExistingEnvConfig struct { + *vrfv2.ExistingEnvConfig + Funding +} + +func (c *ExistingEnvConfig) Validate() error { + if c.ExistingEnvConfig != nil { + if err := c.ExistingEnvConfig.Validate(); err != nil { + return err + } + } + + return c.Funding.Validate() +} + +type Funding struct { + SubFunding + NodeSendingKeyFunding *float64 `toml:"node_sending_key_funding"` + NodeSendingKeyFundingMin *float64 `toml:"node_sending_key_funding_min"` +} + +func (c *Funding) Validate() error { + if c.NodeSendingKeyFunding != nil && *c.NodeSendingKeyFunding <= 0 { + return errors.New("when set node_sending_key_funding must be a positive value") + } + if c.NodeSendingKeyFundingMin != nil && *c.NodeSendingKeyFundingMin <= 0 { + return errors.New("when set node_sending_key_funding_min must be a positive value") + } + + return c.SubFunding.Validate() +} + +type SubFunding struct { + SubFundsLink *float64 `toml:"sub_funds_link"` + SubFundsNative *float64 `toml:"sub_funds_native"` +} + +func (c *SubFunding) Validate() error { + if c.SubFundsLink == nil || c.SubFundsNative == nil { + return errors.New("both sub_funds_link and sub_funds_native must be set") + } + if c.SubFundsLink != nil && *c.SubFundsLink < 0 { + return errors.New("sub_funds_link must be a non-negative number") + } + if c.SubFundsNative != nil && *c.SubFundsNative < 0 { + return errors.New("sub_funds_native must be a non-negative number") + } + + return nil +} diff --git a/integration-tests/testconfig/vrfv2plus/example.toml b/integration-tests/testconfig/vrfv2plus/example.toml new file mode 100644 index 00000000000..a9ce5b2dcf5 --- /dev/null +++ b/integration-tests/testconfig/vrfv2plus/example.toml @@ -0,0 +1,144 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# Common +[Common] +chainlink_node_funding = 0.5 + +# Product part +[VRFv2Plus] +[VRFv2Plus.Common] +cancel_subs_after_test_run = true + +[VRFv2Plus.General] +max_gas_price_gwei = 1000 +link_native_feed_response = 1000000000000000000 +minimum_confirmations = 3 +subscription_billing_type = "LINK_AND_NATIVE" +subscription_funding_amount_link = 5.0 +number_of_words = 3 +callback_gas_limit = 1000000 +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = 60000000000000000 +staleness_seconds = 86400 +gas_after_payment_calculation = 33825 +fulfilment_flat_fee_link_ppm_tier_1 = 500 +fulfilment_flat_fee_link_ppm_tier_2 = 500 +fulfilment_flat_fee_link_ppm_tier_3 = 500 +fulfilment_flat_fee_link_ppm_tier_4 = 500 +fulfilment_flat_fee_link_ppm_tier_5 = 500 +reqs_for_tier_2 = 0 +reqs_for_tier_3 = 0 +reqs_for_tier_4 = 0 +reqs_for_tier_5 = 0 +number_of_sub_to_create = 1 +randomness_request_count_per_request = 1 +randomness_request_count_per_request_deviation = 0 +random_words_fulfilled_event_timeout = "2m" +wrapped_gas_overhead = 50000 +coordinator_gas_overhead = 52000 +wrapper_premium_percentage = 25 +wrapper_max_number_of_words = 10 +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 10 +subscription_funding_amount_native=1 +fulfillment_flat_fee_link_ppm=500 +fulfillment_flat_fee_native_ppm=500 + +[VRFv2Plus.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 +use_existing_env = false + +[VRFv2Plus.NewEnv] +sub_funds_link = 1 +sub_funds_native = 1 +node_funds = 10 +node_sending_key_funding = 1000 + +[VRFv2Plus.ExistingEnv] +coordinator_address = "" +consumer_address = "" +sub_id = 1 +key_hash = "" +create_fund_subs_and_add_consumers = true +link_address = "" +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [ + "", + "", + "", + "", + "", + "", +] \ No newline at end of file diff --git a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml new file mode 100644 index 00000000000..5e187d9de3a --- /dev/null +++ b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml @@ -0,0 +1,161 @@ +# default config +[Common] +chainlink_node_funding = 0.1 + +[VRFv2Plus] +[VRFv2Plus.General] +max_gas_price_gwei = 10 +link_native_feed_response = 1000000000000000000 +minimum_confirmations = 3 +subscription_billing_type = "LINK_AND_NATIVE" +subscription_funding_amount_link = 5.0 +number_of_words = 3 +callback_gas_limit = 1000000 +max_gas_limit_coordinator_config = 2500000 +fallback_wei_per_unit_link = 60000000000000000 +staleness_seconds = 86400 +gas_after_payment_calculation = 33825 +fulfilment_flat_fee_link_ppm_tier_1 = 500 +fulfilment_flat_fee_link_ppm_tier_2 = 500 +fulfilment_flat_fee_link_ppm_tier_3 = 500 +fulfilment_flat_fee_link_ppm_tier_4 = 500 +fulfilment_flat_fee_link_ppm_tier_5 = 500 +reqs_for_tier_2 = 0 +reqs_for_tier_3 = 0 +reqs_for_tier_4 = 0 +reqs_for_tier_5 = 0 +number_of_sub_to_create = 1 +randomness_request_count_per_request = 1 +randomness_request_count_per_request_deviation = 0 +random_words_fulfilled_event_timeout = "2m" +wrapped_gas_overhead = 50000 +coordinator_gas_overhead = 52000 +wrapper_premium_percentage = 25 +wrapper_max_number_of_words = 10 +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 10 +subscription_funding_amount_native=1 +fulfillment_flat_fee_link_ppm=500 +fulfillment_flat_fee_native_ppm=500 + +# load test specific config +[Load.VRFv2Plus] +[Load.VRFv2Plus.Common] +cancel_subs_after_test_run = true + +[Load.VRFv2Plus.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Load.VRFv2Plus.Performance] +test_duration = "2m" +# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds +rate_limit_unit_duration = "3s" +rps = 1 + +[Load.VRFv2Plus.NewEnv] +sub_funds_link = 1 +sub_funds_native = 1 +node_funds = 10 +node_sending_key_funding = 1000 + +[Load.VRFv2Plus.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +link_address = "" +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] + +# soak test specific config +[Soak.VRFv2Plus] +[Soak.VRFv2Plus.Common] +cancel_subs_after_test_run = true + +[Soak.VRFv2Plus.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Soak.VRFv2Plus.Performance] +test_duration = "2m" +# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds +rate_limit_unit_duration = "6s" +rps = 1 +use_existing_env = false + +[Soak.VRFv2Plus.NewEnv] +sub_funds_link = 1 +sub_funds_native = 1 +node_funds = 10 +node_sending_key_funding = 1000 + +[Soak.VRFv2Plus.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] + +# spike test specific config +[Spike.VRFv2Plus] +[Spike.VRFv2Plus.Common] +cancel_subs_after_test_run = true + +[Spike.VRFv2Plus.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Spike.VRFv2Plus.Performance] +test_duration = "2m" +# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds +rate_limit_unit_duration = "1m" +rps = 1 + +[Spike.VRFv2Plus.NewEnv] +sub_funds_link = 1 +sub_funds_native = 1 +node_funds = 10 +node_sending_key_funding = 1000 + +[Spike.VRFv2Plus.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] + +# stress test specific config +[Stress.VRFv2Plus] +[Stress.VRFv2Plus.Common] +cancel_subs_after_test_run = true + +[Stress.VRFv2Plus.General] +minimum_confirmations = 3 +randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 + +[Stress.VRFv2Plus.Performance] +test_duration = "2m" +# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx +rate_limit_unit_duration = "1s" +rps = 3 + +[Stress.VRFv2Plus.NewEnv] +sub_funds_link = 1 +sub_funds_native = 1 +node_funds = 10 +node_sending_key_funding = 1000 + +[Stress.VRFv2Plus.ExistingEnv] +sub_id = 1 +create_fund_subs_and_add_consumers = true +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [] \ No newline at end of file diff --git a/integration-tests/testreporters/keeper_benchmark.go b/integration-tests/testreporters/keeper_benchmark.go index 5db6cca3ec4..b878ff67a31 100644 --- a/integration-tests/testreporters/keeper_benchmark.go +++ b/integration-tests/testreporters/keeper_benchmark.go @@ -18,10 +18,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" ) -var ( - DashboardUrl = os.Getenv("GRAFANA_DASHBOARD_URL") -) - // KeeperBenchmarkTestReporter enables reporting on the keeper benchmark test type KeeperBenchmarkTestReporter struct { Reports []KeeperBenchmarkTestReport `json:"reports"` @@ -249,7 +245,7 @@ func (k *KeeperBenchmarkTestReporter) WriteReport(folderLocation string) error { } // SendSlackNotification sends a slack notification on the results of the test -func (k *KeeperBenchmarkTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { +func (k *KeeperBenchmarkTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client, grafanaUrlProvider testreporters.GrafanaURLProvider) error { if slackClient == nil { slackClient = slack.New(testreporters.SlackAPIKey) } @@ -267,7 +263,17 @@ func (k *KeeperBenchmarkTestReporter) SendSlackNotification(t *testing.T, slackC return err } - formattedDashboardUrl := fmt.Sprintf("%s&from=%d&to=%d&var-namespace=%s&var-cl_node=chainlink-0-0", DashboardUrl, k.Summary.StartTime, k.Summary.EndTime, k.namespace) + grafanaUrl, err := grafanaUrlProvider.GetGrafanaBaseURL() + if err != nil { + return err + } + + dashboardUrl, err := grafanaUrlProvider.GetGrafanaDashboardURL() + if err != nil { + return err + } + + formattedDashboardUrl := fmt.Sprintf("%s%s?from=%d&to=%d&var-namespace=%s&var-cl_node=chainlink-0-0", grafanaUrl, dashboardUrl, k.Summary.StartTime, k.Summary.EndTime, k.namespace) log.Info().Str("Dashboard", formattedDashboardUrl).Msg("Dashboard URL") if err := testreporters.UploadSlackFile(slackClient, slack.FileUploadParameters{ diff --git a/integration-tests/testreporters/ocr.go b/integration-tests/testreporters/ocr.go index 31f5eeab1b9..9a9c4d0d4a0 100644 --- a/integration-tests/testreporters/ocr.go +++ b/integration-tests/testreporters/ocr.go @@ -235,7 +235,7 @@ func (o *OCRSoakTestReporter) WriteReport(folderLocation string) error { } // SendNotification sends a slack message to a slack webhook and uploads test artifacts -func (o *OCRSoakTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { +func (o *OCRSoakTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client, _ testreporters.GrafanaURLProvider) error { if slackClient == nil { slackClient = slack.New(testreporters.SlackAPIKey) } diff --git a/integration-tests/testreporters/profile.go b/integration-tests/testreporters/profile.go index ab9dec138e4..464cfd685e7 100644 --- a/integration-tests/testreporters/profile.go +++ b/integration-tests/testreporters/profile.go @@ -54,7 +54,7 @@ func (c *ChainlinkProfileTestReporter) WriteReport(folderLocation string) error } // SendNotification hasn't been implemented for this test -func (c *ChainlinkProfileTestReporter) SendSlackNotification(_ *testing.T, _ *slack.Client) error { +func (c *ChainlinkProfileTestReporter) SendSlackNotification(_ *testing.T, _ *slack.Client, _ testreporters.GrafanaURLProvider) error { log.Warn().Msg("No Slack notification integration for Chainlink profile tests") return nil } diff --git a/integration-tests/testreporters/vrfv2.go b/integration-tests/testreporters/vrfv2.go index 2a72e4d91d0..4f4c7dc8fd1 100644 --- a/integration-tests/testreporters/vrfv2.go +++ b/integration-tests/testreporters/vrfv2.go @@ -3,15 +3,14 @@ package testreporters import ( "fmt" "math/big" - "os" + "strings" "testing" "time" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" - "github.com/slack-go/slack" "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/types" ) type VRFV2TestReporter struct { @@ -21,7 +20,7 @@ type VRFV2TestReporter struct { AverageFulfillmentInMillions *big.Int SlowestFulfillment *big.Int FastestFulfillment *big.Int - Vrfv2Config *vrfv2_config.VRFV2Config + VRFv2TestConfig types.VRFv2TestConfig } func (o *VRFV2TestReporter) SetReportData( @@ -31,7 +30,7 @@ func (o *VRFV2TestReporter) SetReportData( AverageFulfillmentInMillions *big.Int, SlowestFulfillment *big.Int, FastestFulfillment *big.Int, - vrfv2Config vrfv2_config.VRFV2Config, + vrfv2TestConfig types.VRFv2TestConfig, ) { o.TestType = testType o.RequestCount = RequestCount @@ -39,7 +38,7 @@ func (o *VRFV2TestReporter) SetReportData( o.AverageFulfillmentInMillions = AverageFulfillmentInMillions o.SlowestFulfillment = SlowestFulfillment o.FastestFulfillment = FastestFulfillment - o.Vrfv2Config = &vrfv2Config + o.VRFv2TestConfig = vrfv2TestConfig } // SendSlackNotification sends a slack message to a slack webhook @@ -54,7 +53,14 @@ func (o *VRFV2TestReporter) SendSlackNotification(t *testing.T, slackClient *sla headerText = fmt.Sprintf(":x: VRF V2 %s Test FAILED :x:", o.TestType) } - messageBlocks := testreporters.SlackNotifyBlocks(headerText, os.Getenv("SELECTED_NETWORKS"), []string{ + perfCfg := o.VRFv2TestConfig.GetVRFv2Config().Performance + var sb strings.Builder + for _, n := range o.VRFv2TestConfig.GetNetworkConfig().SelectedNetworks { + sb.WriteString(n) + sb.WriteString(", ") + } + + messageBlocks := testreporters.SlackNotifyBlocks(headerText, sb.String(), []string{ fmt.Sprintf( "Summary\n"+ "Perf Test Type: %s\n"+ @@ -70,17 +76,17 @@ func (o *VRFV2TestReporter) SendSlackNotification(t *testing.T, slackClient *sla "RandomnessRequestCountPerRequest: %d\n"+ "RandomnessRequestCountPerRequestDeviation: %d\n", o.TestType, - o.Vrfv2Config.TestDuration.Truncate(time.Second).String(), - o.Vrfv2Config.UseExistingEnv, + perfCfg.TestDuration.Duration.Truncate(time.Second).String(), + *perfCfg.UseExistingEnv, o.RequestCount.String(), o.FulfilmentCount.String(), o.AverageFulfillmentInMillions.String(), o.SlowestFulfillment.String(), o.FastestFulfillment.String(), - o.Vrfv2Config.RPS, - o.Vrfv2Config.RateLimitUnitDuration.String(), - o.Vrfv2Config.RandomnessRequestCountPerRequest, - o.Vrfv2Config.RandomnessRequestCountPerRequestDeviation, + *perfCfg.RPS, + perfCfg.RateLimitUnitDuration.String(), + *o.VRFv2TestConfig.GetVRFv2Config().General.RandomnessRequestCountPerRequest, + *o.VRFv2TestConfig.GetVRFv2Config().General.RandomnessRequestCountPerRequestDeviation, ), }) diff --git a/integration-tests/testreporters/vrfv2plus.go b/integration-tests/testreporters/vrfv2plus.go index 21e4e52695a..8d384b07868 100644 --- a/integration-tests/testreporters/vrfv2plus.go +++ b/integration-tests/testreporters/vrfv2plus.go @@ -3,11 +3,11 @@ package testreporters import ( "fmt" "math/big" - "os" + "strings" "testing" "time" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" + "github.com/smartcontractkit/chainlink/integration-tests/types" "github.com/slack-go/slack" @@ -21,7 +21,7 @@ type VRFV2PlusTestReporter struct { AverageFulfillmentInMillions *big.Int SlowestFulfillment *big.Int FastestFulfillment *big.Int - Vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig + VRFv2PlusTestConfig types.VRFv2PlusTestConfig } func (o *VRFV2PlusTestReporter) SetReportData( @@ -31,7 +31,7 @@ func (o *VRFV2PlusTestReporter) SetReportData( AverageFulfillmentInMillions *big.Int, SlowestFulfillment *big.Int, FastestFulfillment *big.Int, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + vtfv2PlusTestConfig types.VRFv2PlusTestConfig, ) { o.TestType = testType o.RequestCount = RequestCount @@ -39,11 +39,11 @@ func (o *VRFV2PlusTestReporter) SetReportData( o.AverageFulfillmentInMillions = AverageFulfillmentInMillions o.SlowestFulfillment = SlowestFulfillment o.FastestFulfillment = FastestFulfillment - o.Vrfv2PlusConfig = &vrfv2PlusConfig + o.VRFv2PlusTestConfig = vtfv2PlusTestConfig } // SendSlackNotification sends a slack message to a slack webhook -func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { +func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client, vtfv2PlusTestConfig types.VRFv2PlusTestConfig) error { if slackClient == nil { slackClient = slack.New(testreporters.SlackAPIKey) } @@ -54,7 +54,8 @@ func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient headerText = fmt.Sprintf(":x: VRF V2 Plus %s Test FAILED :x:", o.TestType) } - messageBlocks := testreporters.SlackNotifyBlocks(headerText, os.Getenv("SELECTED_NETWORKS"), []string{ + vrfv2lusConfig := o.VRFv2PlusTestConfig.GetVRFv2PlusConfig().Performance + messageBlocks := testreporters.SlackNotifyBlocks(headerText, strings.Join(vtfv2PlusTestConfig.GetNetworkConfig().SelectedNetworks, ","), []string{ fmt.Sprintf( "Summary\n"+ "Perf Test Type: %s\n"+ @@ -70,17 +71,17 @@ func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient "RandomnessRequestCountPerRequest: %d\n"+ "RandomnessRequestCountPerRequestDeviation: %d\n", o.TestType, - o.Vrfv2PlusConfig.TestDuration.Truncate(time.Second).String(), - o.Vrfv2PlusConfig.UseExistingEnv, + vrfv2lusConfig.TestDuration.Duration.Truncate(time.Second).String(), + *vrfv2lusConfig.UseExistingEnv, o.RequestCount.String(), o.FulfilmentCount.String(), o.AverageFulfillmentInMillions.String(), o.SlowestFulfillment.String(), o.FastestFulfillment.String(), - o.Vrfv2PlusConfig.RPS, - o.Vrfv2PlusConfig.RateLimitUnitDuration.String(), - o.Vrfv2PlusConfig.RandomnessRequestCountPerRequest, - o.Vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation, + *vrfv2lusConfig.RPS, + vrfv2lusConfig.RateLimitUnitDuration.String(), + *o.VRFv2PlusTestConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequest, + *o.VRFv2PlusTestConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequestDeviation, ), }) diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index 575ed3e7de5..1330ea291b8 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -38,6 +38,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + tt "github.com/smartcontractkit/chainlink/integration-tests/types" ) // KeeperBenchmarkTest builds a test to check that chainlink nodes are able to upkeep a specified amount of Upkeep @@ -59,6 +60,7 @@ type KeeperBenchmarkTest struct { namespace string chainlinkNodes []*client.ChainlinkK8sClient chainClient blockchain.EVMClient + testConfig tt.KeeperBenchmarkTestConfig contractDeployer contracts.ContractDeployer linkToken contracts.LinkToken @@ -113,13 +115,14 @@ func NewKeeperBenchmarkTest(t *testing.T, inputs KeeperBenchmarkTestInputs) *Kee } // Setup prepares contracts for the test -func (k *KeeperBenchmarkTest) Setup(env *environment.Environment) { +func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.KeeperBenchmarkTestConfig) { startTime := time.Now() k.TestReporter.Summary.StartTime = startTime.UnixMilli() k.ensureInputValues() k.env = env k.namespace = k.env.Cfg.Namespace inputs := k.Inputs + k.testConfig = config k.keeperRegistries = make([]contracts.KeeperRegistry, len(inputs.RegistryVersions)) k.keeperRegistrars = make([]contracts.KeeperRegistrar, len(inputs.RegistryVersions)) @@ -202,7 +205,7 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment) { } k.log.Info().Str("Setup Time", time.Since(startTime).String()).Msg("Finished Keeper Benchmark Test Setup") - err = k.SendSlackNotification(nil) + err = k.SendSlackNotification(nil, config) if err != nil { k.log.Warn().Msg("Sending test start slack notification failed") } @@ -405,9 +408,10 @@ func (k *KeeperBenchmarkTest) TearDownVals(t *testing.T) ( string, []*client.ChainlinkK8sClient, reportModel.TestReporter, + reportModel.GrafanaURLProvider, blockchain.EVMClient, ) { - return t, k.namespace, k.chainlinkNodes, &k.TestReporter, k.chainClient + return t, k.namespace, k.chainlinkNodes, &k.TestReporter, k.testConfig, k.chainClient } // ********************* @@ -571,13 +575,23 @@ func (k *KeeperBenchmarkTest) ensureInputValues() { } } -func (k *KeeperBenchmarkTest) SendSlackNotification(slackClient *slack.Client) error { +func (k *KeeperBenchmarkTest) SendSlackNotification(slackClient *slack.Client, config tt.KeeperBenchmarkTestConfig) error { if slackClient == nil { slackClient = slack.New(reportModel.SlackAPIKey) } + grafanaUrl, err := config.GetGrafanaBaseURL() + if err != nil { + return err + } + + dashboardUrl, err := config.GetGrafanaDashboardURL() + if err != nil { + return err + } + headerText := ":white_check_mark: Automation Benchmark Test STARTED :white_check_mark:" - formattedDashboardUrl := fmt.Sprintf("%s&from=%d&to=%s&var-namespace=%s&var-cl_node=chainlink-0-0", testreporters.DashboardUrl, k.TestReporter.Summary.StartTime, "now", k.env.Cfg.Namespace) + formattedDashboardUrl := fmt.Sprintf("%s%s?from=%d&to=%s&var-namespace=%s&var-cl_node=chainlink-0-0", grafanaUrl, dashboardUrl, k.TestReporter.Summary.StartTime, "now", k.env.Cfg.Namespace) log.Info().Str("Dashboard", formattedDashboardUrl).Msg("Dashboard URL") notificationBlocks := []slack.Block{} diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index f7e0e65d36d..a2e2fe42bae 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -10,7 +10,6 @@ import ( "os" "os/signal" "sort" - "strconv" "strings" "syscall" "testing" @@ -19,7 +18,6 @@ import ( geth "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/kelseyhightower/envconfig" "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog" "github.com/stretchr/testify/require" @@ -29,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" @@ -44,6 +43,9 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + tt "github.com/smartcontractkit/chainlink/integration-tests/types" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) const ( @@ -53,7 +55,7 @@ const ( // OCRSoakTest defines a typical OCR soak test type OCRSoakTest struct { - Inputs *OCRSoakTestInputs + Config *tc.TestConfig TestReporter testreporters.OCRSoakTestReporter OperatorForwarderFlow bool @@ -80,51 +82,18 @@ type OCRSoakTest struct { ocrV2InstanceMap map[string]contracts.OffchainAggregatorV2 // address : instance } -// OCRSoakTestInputs define required inputs to run an OCR soak test -type OCRSoakTestInputs struct { - OCRVersion string `envconfig:"OCR_VERSION" default:"1"` // Version of OCR to use (1 or 2) - TestDuration time.Duration `envconfig:"TEST_DURATION" default:"15m"` // How long to run the test for - NumberOfContracts int `envconfig:"NUMBER_CONTRACTS" default:"2"` // Number of OCR contracts to launch - ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with - bigChainlinkNodeFunding *big.Float // Convenience conversions for funding - TimeBetweenRounds time.Duration `envconfig:"TIME_BETWEEN_ROUNDS" default:"1m"` // How long to wait before starting a new round; controls frequency of rounds -} - -func (i OCRSoakTestInputs) setForRemoteRunner() { - os.Setenv("TEST_OCR_VERSION", i.OCRVersion) - os.Setenv("TEST_OCR_TEST_DURATION", i.TestDuration.String()) - os.Setenv("TEST_OCR_NUMBER_CONTRACTS", fmt.Sprint(i.NumberOfContracts)) - os.Setenv("TEST_OCR_CHAINLINK_NODE_FUNDING", strconv.FormatFloat(i.ChainlinkNodeFunding, 'f', -1, 64)) - os.Setenv("TEST_OCR_TIME_BETWEEN_ROUNDS", i.TimeBetweenRounds.String()) - - selectedNetworks := strings.Split(os.Getenv("SELECTED_NETWORKS"), ",") - for _, networkPrefix := range selectedNetworks { - urlEnv := fmt.Sprintf("%s_URLS", networkPrefix) - httpEnv := fmt.Sprintf("%s_HTTP_URLS", networkPrefix) - os.Setenv(fmt.Sprintf("TEST_%s", urlEnv), os.Getenv(urlEnv)) - os.Setenv(fmt.Sprintf("TEST_%s", httpEnv), os.Getenv(httpEnv)) - } -} - // NewOCRSoakTest creates a new OCR soak test to setup and run -func NewOCRSoakTest(t *testing.T, forwarderFlow bool) (*OCRSoakTest, error) { - var testInputs OCRSoakTestInputs - err := envconfig.Process("OCR", &testInputs) - if err != nil { - return nil, err - } - testInputs.setForRemoteRunner() - +func NewOCRSoakTest(t *testing.T, config *tc.TestConfig, forwarderFlow bool) (*OCRSoakTest, error) { test := &OCRSoakTest{ - Inputs: &testInputs, + Config: config, OperatorForwarderFlow: forwarderFlow, TestReporter: testreporters.OCRSoakTestReporter{ - OCRVersion: testInputs.OCRVersion, + OCRVersion: *config.OCR.Soak.OCRVersion, StartTime: time.Now(), }, t: t, startTime: time.Now(), - timeLeft: testInputs.TestDuration, + timeLeft: config.OCR.Common.TestDuration.Duration, log: logging.GetTestLogger(t), ocrRoundStates: make([]*testreporters.OCRRoundState, 0), ocrV1InstanceMap: make(map[string]contracts.OffchainAggregator), @@ -134,9 +103,9 @@ func NewOCRSoakTest(t *testing.T, forwarderFlow bool) (*OCRSoakTest, error) { } // DeployEnvironment deploys the test environment, starting all Chainlink nodes and other components for the test -func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { - network := networks.MustGetSelectedNetworksFromEnv()[0] // Environment currently being used to soak test on - nsPre := fmt.Sprintf("soak-ocr-v%s-", o.Inputs.OCRVersion) +func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string, ocrTestConfig tt.OcrTestConfig) { + network := networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] // Environment currently being used to soak test on + nsPre := fmt.Sprintf("soak-ocr-v%s-", *ocrTestConfig.GetOCRConfig().Soak.OCRVersion) if o.OperatorForwarderFlow { nsPre = fmt.Sprintf("%sforwarder-", nsPre) } @@ -149,18 +118,24 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { } var conf string - if o.Inputs.OCRVersion == "1" { + if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { conf = config.BaseOCR1Config - } else if o.Inputs.OCRVersion == "2" { + } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "2" { conf = config.BaseOCR2Config } - cd := chainlink.New(0, map[string]any{ + + var overrideFn = func(_ interface{}, target interface{}) { + ctf_config.MustConfigOverrideChainlinkVersion(ocrTestConfig.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(ocrTestConfig.GetPyroscopeConfig(), target) + } + + cd := chainlink.NewWithOverride(0, map[string]any{ "replicas": 6, - "toml": networks.AddNetworkDetailedConfig(conf, customChainlinkNetworkTOML, network), + "toml": networks.AddNetworkDetailedConfig(conf, ocrTestConfig.GetPyroscopeConfig(), customChainlinkNetworkTOML, network), "db": map[string]any{ "stateful": true, // stateful DB by default for soak tests }, - }) + }, ocrTestConfig.GetChainlinkImageConfig(), overrideFn) testEnvironment := environment.New(baseEnvironmentConfig). AddHelm(mockservercfg.New(nil)). @@ -178,9 +153,9 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { } // LoadEnvironment loads an existing test environment using the provided URLs -func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, mockServerURL string) { +func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, mockServerURL string, ocrTestConfig tt.OcrTestConfig) { var ( - network = networks.MustGetSelectedNetworksFromEnv()[0] + network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] err error ) o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) @@ -197,10 +172,10 @@ func (o *OCRSoakTest) Environment() *environment.Environment { return o.testEnvironment } -func (o *OCRSoakTest) Setup() { +func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { var ( err error - network = networks.MustGetSelectedNetworksFromEnv()[0] + network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] ) // Environment currently being used to soak test on @@ -221,7 +196,8 @@ func (o *OCRSoakTest) Setup() { require.NoError(o.t, err, "Deploying Link Token Contract shouldn't fail") // Fund Chainlink nodes, excluding the bootstrap node - err = actions.FundChainlinkNodes(o.workerNodes, o.chainClient, o.Inputs.bigChainlinkNodeFunding) + o.log.Info().Float64("ETH amount per node", *o.Config.Common.ChainlinkNodeFunding).Msg("Funding Chainlink nodes") + err = actions.FundChainlinkNodes(o.workerNodes, o.chainClient, big.NewFloat(*o.Config.Common.ChainlinkNodeFunding)) require.NoError(o.t, err, "Error funding Chainlink nodes") if o.OperatorForwarderFlow { @@ -244,23 +220,23 @@ func (o *OCRSoakTest) Setup() { o.ocrV1Instances = actions.DeployOCRContractsForwarderFlow( o.t, - o.Inputs.NumberOfContracts, + *o.Config.OCR.Soak.NumberOfContracts, linkTokenContract, contractDeployer, o.workerNodes, authorizedForwarders, o.chainClient, ) - } else if o.Inputs.OCRVersion == "1" { + } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { o.ocrV1Instances, err = actions.DeployOCRContracts( - o.Inputs.NumberOfContracts, + *o.Config.OCR.Soak.NumberOfContracts, linkTokenContract, contractDeployer, o.workerNodes, o.chainClient, ) require.NoError(o.t, err) - } else if o.Inputs.OCRVersion == "2" { + } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "2" { var transmitters []string for _, node := range o.workerNodes { nodeAddress, err := node.PrimaryEthAddress() @@ -269,7 +245,7 @@ func (o *OCRSoakTest) Setup() { } ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() o.ocrV2Instances, err = actions.DeployOCRv2Contracts( - o.Inputs.NumberOfContracts, + *ocrTestConfig.GetOCRConfig().Soak.NumberOfContracts, linkTokenContract, contractDeployer, transmitters, @@ -285,11 +261,11 @@ func (o *OCRSoakTest) Setup() { err = o.chainClient.WaitForEvents() require.NoError(o.t, err, "Error waiting for OCR contracts to be deployed") - if o.Inputs.OCRVersion == "1" { + if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { for _, ocrInstance := range o.ocrV1Instances { o.ocrV1InstanceMap[ocrInstance.Address()] = ocrInstance } - } else if o.Inputs.OCRVersion == "2" { + } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "2" { for _, ocrInstance := range o.ocrV2Instances { o.ocrV2InstanceMap[ocrInstance.Address()] = ocrInstance } @@ -300,6 +276,9 @@ func (o *OCRSoakTest) Setup() { // Run starts the OCR soak test func (o *OCRSoakTest) Run() { + config, err := tc.GetConfig("soak", tc.OCR) + require.NoError(o.t, err, "Error getting config") + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) latestBlockNum, err := o.chainClient.LatestBlockNumber(ctx) cancel() @@ -309,21 +288,21 @@ func (o *OCRSoakTest) Run() { startingValue := 5 if o.OperatorForwarderFlow { actions.CreateOCRJobsWithForwarder(o.t, o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) - } else if o.Inputs.OCRVersion == "1" { + } else if *config.OCR.Soak.OCRVersion == "1" { err := actions.CreateOCRJobs(o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) require.NoError(o.t, err, "Error creating OCR jobs") - } else if o.Inputs.OCRVersion == "2" { + } else if *config.OCR.Soak.OCRVersion == "2" { err := actions.CreateOCRv2Jobs(o.ocrV2Instances, o.bootstrapNode, o.workerNodes, o.mockServer, startingValue, o.chainClient.GetChainID().Uint64(), o.OperatorForwarderFlow) require.NoError(o.t, err, "Error creating OCR jobs") } o.log.Info(). - Str("Test Duration", o.Inputs.TestDuration.Truncate(time.Second).String()). - Int("Number of OCR Contracts", o.Inputs.NumberOfContracts). - Str("OCR Version", o.Inputs.OCRVersion). + Str("Test Duration", o.Config.OCR.Common.TestDuration.Duration.Truncate(time.Second).String()). + Int("Number of OCR Contracts", *config.OCR.Soak.NumberOfContracts). + Str("OCR Version", *config.OCR.Soak.OCRVersion). Msg("Starting OCR Soak Test") - o.testLoop(o.Inputs.TestDuration, startingValue) + o.testLoop(o.Config.OCR.Common.TestDuration.Duration, startingValue) o.complete() } @@ -333,9 +312,10 @@ func (o *OCRSoakTest) TearDownVals(t *testing.T) ( string, []*client.ChainlinkK8sClient, reportModel.TestReporter, + reportModel.GrafanaURLProvider, blockchain.EVMClient, ) { - return t, o.namespace, append(o.workerNodes, o.bootstrapNode), &o.TestReporter, o.chainClient + return t, o.namespace, append(o.workerNodes, o.bootstrapNode), &o.TestReporter, o.Config, o.chainClient } // ********************* @@ -375,9 +355,9 @@ func (o *OCRSoakTest) SaveState() error { StartingBlockNum: o.startingBlockNum, StartTime: o.startTime, TimeRunning: time.Since(o.startTime), - TestDuration: o.Inputs.TestDuration, + TestDuration: o.Config.OCR.Common.TestDuration.Duration, OCRContractAddresses: ocrAddresses, - OCRVersion: o.Inputs.OCRVersion, + OCRVersion: *o.Config.OCR.Soak.OCRVersion, ChainURL: o.chainClient.GetNetworkConfig().URL, MockServerURL: "http://mockserver:1080", // TODO: Make this dynamic @@ -426,15 +406,16 @@ func (o *OCRSoakTest) LoadState() error { OCRVersion: testState.OCRVersion, StartTime: testState.StartTime, } + duration := blockchain.StrDuration{Duration: testState.TestDuration} o.ocrRoundStates = testState.OCRRoundStates o.testIssues = testState.TestIssues - o.Inputs.TestDuration = testState.TestDuration + o.Config.OCR.Common.TestDuration = &duration o.timeLeft = testState.TestDuration - testState.TimeRunning o.startTime = testState.StartTime o.startingBlockNum = testState.StartingBlockNum - o.Inputs.OCRVersion = testState.OCRVersion + o.Config.OCR.Soak.OCRVersion = &testState.OCRVersion - network := networks.MustGetSelectedNetworksFromEnv()[0] + network := networks.MustGetSelectedNetworkConfig(o.Config.Network)[0] o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) if err != nil { return err @@ -488,13 +469,13 @@ func (o *OCRSoakTest) Resume() { Message: "Test Resumed", }) o.log.Info(). - Str("Total Duration", o.Inputs.TestDuration.String()). + Str("Total Duration", o.Config.OCR.Common.TestDuration.String()). Str("Time Left", o.timeLeft.String()). Msg("Resuming OCR Soak Test") - ocrAddresses := make([]common.Address, o.Inputs.NumberOfContracts) + ocrAddresses := make([]common.Address, *o.Config.OCR.Soak.NumberOfContracts) - if o.Inputs.OCRVersion == "1" { + if *o.Config.OCR.Soak.OCRVersion == "1" { for i, ocrInstance := range o.ocrV1Instances { ocrAddresses[i] = common.HexToAddress(ocrInstance.Address()) } @@ -505,7 +486,7 @@ func (o *OCRSoakTest) Resume() { Topics: [][]common.Hash{{contractABI.Events["AnswerUpdated"].ID}}, FromBlock: big.NewInt(0).SetUint64(o.startingBlockNum), } - } else if o.Inputs.OCRVersion == "2" { + } else if *o.Config.OCR.Soak.OCRVersion == "2" { for i, ocrInstance := range o.ocrV2Instances { ocrAddresses[i] = common.HexToAddress(ocrInstance.Address()) } @@ -569,7 +550,7 @@ func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { return case <-newRoundTrigger.C: err := o.triggerNewRound(newValue) - timerReset := o.Inputs.TimeBetweenRounds + timerReset := o.Config.OCR.Soak.TimeBetweenRounds.Duration if err != nil { timerReset = time.Second * 5 o.log.Error().Err(err). @@ -641,7 +622,7 @@ func (o *OCRSoakTest) observeOCREvents() error { for { select { case event := <-eventLogs: - if o.Inputs.OCRVersion == "1" { + if *o.Config.OCR.Soak.OCRVersion == "1" { answerUpdated, err := o.ocrV1Instances[0].ParseEventAnswerUpdated(event) if err != nil { o.log.Warn(). @@ -657,7 +638,7 @@ func (o *OCRSoakTest) observeOCREvents() error { Uint64("Round ID", answerUpdated.RoundId.Uint64()). Int64("Answer", answerUpdated.Current.Int64()). Msg("Answer Updated Event") - } else if o.Inputs.OCRVersion == "2" { + } else if *o.Config.OCR.Soak.OCRVersion == "2" { answerUpdated, err := o.ocrV2Instances[0].ParseEventAnswerUpdated(event) if err != nil { o.log.Warn(). @@ -704,9 +685,9 @@ func (o *OCRSoakTest) triggerNewRound(newValue int) error { } var err error - if o.Inputs.OCRVersion == "1" { + if *o.Config.OCR.Soak.OCRVersion == "1" { err = actions.SetAllAdapterResponsesToTheSameValue(newValue, o.ocrV1Instances, o.workerNodes, o.mockServer) - } else if o.Inputs.OCRVersion == "2" { + } else if *o.Config.OCR.Soak.OCRVersion == "2" { err = actions.SetOCR2AllAdapterResponsesToTheSameValue(newValue, o.ocrV2Instances, o.workerNodes, o.mockServer) } if err != nil { @@ -718,11 +699,11 @@ func (o *OCRSoakTest) triggerNewRound(newValue int) error { Answer: int64(newValue), FoundEvents: make(map[string][]*testreporters.FoundEvent), } - if o.Inputs.OCRVersion == "1" { + if *o.Config.OCR.Soak.OCRVersion == "1" { for _, ocrInstance := range o.ocrV1Instances { expectedState.FoundEvents[ocrInstance.Address()] = make([]*testreporters.FoundEvent, 0) } - } else if o.Inputs.OCRVersion == "2" { + } else if *o.Config.OCR.Soak.OCRVersion == "2" { for _, ocrInstance := range o.ocrV2Instances { expectedState.FoundEvents[ocrInstance.Address()] = make([]*testreporters.FoundEvent, 0) } @@ -763,7 +744,7 @@ func (o *OCRSoakTest) collectEvents() error { sortedFoundEvents := make([]*testreporters.FoundEvent, 0) for _, event := range contractEvents { - if o.Inputs.OCRVersion == "1" { + if *o.Config.OCR.Soak.OCRVersion == "1" { answerUpdated, err := o.ocrV1Instances[0].ParseEventAnswerUpdated(event) if err != nil { return fmt.Errorf("error parsing EventAnswerUpdated for event: %v, %w", event, err) @@ -775,7 +756,7 @@ func (o *OCRSoakTest) collectEvents() error { RoundID: answerUpdated.RoundId.Uint64(), BlockNumber: event.BlockNumber, }) - } else if o.Inputs.OCRVersion == "2" { + } else if *o.Config.OCR.Soak.OCRVersion == "2" { answerUpdated, err := o.ocrV2Instances[0].ParseEventAnswerUpdated(event) if err != nil { return fmt.Errorf("error parsing EventAnswerUpdated for event: %v, %w", event, err) @@ -819,26 +800,26 @@ func (o *OCRSoakTest) collectEvents() error { // ensureValues ensures that all values needed to run the test are present func (o *OCRSoakTest) ensureInputValues() error { - inputs := o.Inputs - if inputs.OCRVersion != "1" && inputs.OCRVersion != "2" { - return fmt.Errorf("OCR version must be 1 or 2, found %s", inputs.OCRVersion) + ocrConfig := o.Config.OCR.Soak + if *ocrConfig.OCRVersion != "1" && *ocrConfig.OCRVersion != "2" { + return fmt.Errorf("OCR version must be 1 or 2, found %s", *ocrConfig.OCRVersion) } - if inputs.NumberOfContracts <= 0 { - return fmt.Errorf("Number of OCR contracts must be greater than 0, found %d", inputs.NumberOfContracts) + if ocrConfig.NumberOfContracts != nil && *ocrConfig.NumberOfContracts <= 0 { + return fmt.Errorf("Number of OCR contracts must be set and greater than 0, found %d", ocrConfig.NumberOfContracts) } - if inputs.ChainlinkNodeFunding <= 0 { - return fmt.Errorf("Chainlink node funding must be greater than 0, found %f", inputs.ChainlinkNodeFunding) + if o.Config.Common.ChainlinkNodeFunding != nil && *o.Config.Common.ChainlinkNodeFunding <= 0 { + return fmt.Errorf("Chainlink node funding must be greater than 0, found %f", *o.Config.Common.ChainlinkNodeFunding) } - if inputs.TestDuration <= time.Minute { - return fmt.Errorf("Test duration must be greater than 1 minute, found %s", inputs.TestDuration.String()) + if o.Config.OCR.Common.TestDuration != nil && o.Config.OCR.Common.TestDuration.Duration <= time.Minute { + return fmt.Errorf("Test duration must be greater than 1 minute, found %s", o.Config.OCR.Common.TestDuration) } - if inputs.TimeBetweenRounds >= time.Hour { - return fmt.Errorf("Time between rounds must be less than 1 hour, found %s", inputs.TimeBetweenRounds.String()) + if ocrConfig.TimeBetweenRounds != nil && ocrConfig.TimeBetweenRounds.Duration >= time.Hour { + return fmt.Errorf("Time between rounds must be less than 1 hour, found %s", ocrConfig.TimeBetweenRounds) } - if inputs.TimeBetweenRounds < time.Second*30 { - return fmt.Errorf("Time between rounds must be greater or equal to 30 seconds, found %s", inputs.TimeBetweenRounds.String()) + if ocrConfig.TimeBetweenRounds != nil && ocrConfig.TimeBetweenRounds.Duration < time.Second*30 { + return fmt.Errorf("Time between rounds must be greater or equal to 30 seconds, found %s", ocrConfig.TimeBetweenRounds) } - o.Inputs.bigChainlinkNodeFunding = big.NewFloat(inputs.ChainlinkNodeFunding) + return nil } diff --git a/integration-tests/types/testconfigs.go b/integration-tests/types/testconfigs.go new file mode 100644 index 00000000000..0c704f0cd7b --- /dev/null +++ b/integration-tests/types/testconfigs.go @@ -0,0 +1,44 @@ +package types + +import ( + "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" +) + +type VRFv2TestConfig interface { + tc.CommonTestConfig + tc.GlobalTestConfig + tc.VRFv2TestConfig +} + +type VRFv2PlusTestConfig interface { + tc.CommonTestConfig + tc.GlobalTestConfig + tc.VRFv2PlusTestConfig +} + +type FunctionsTestConfig interface { + tc.CommonTestConfig + tc.GlobalTestConfig + tc.FunctionsTestConfig +} + +type AutomationTestConfig interface { + tc.GlobalTestConfig + tc.CommonTestConfig + tc.UpgradeableChainlinkTestConfig +} + +type KeeperBenchmarkTestConfig interface { + tc.GlobalTestConfig + tc.CommonTestConfig + tc.KeeperTestConfig + tc.NamedConfiguration + testreporters.GrafanaURLProvider +} + +type OcrTestConfig interface { + tc.GlobalTestConfig + tc.CommonTestConfig + tc.OcrTestConfig +} diff --git a/integration-tests/universal/log_poller/config.go b/integration-tests/universal/log_poller/config.go deleted file mode 100644 index b8773d836f6..00000000000 --- a/integration-tests/universal/log_poller/config.go +++ /dev/null @@ -1,249 +0,0 @@ -package logpoller - -import ( - "fmt" - "os" - "strconv" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog/log" - - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" -) - -const ( - DefaultConfigFilename = "config.toml" - - ErrReadPerfConfig = "failed to read TOML config for performance tests" - ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" -) - -type GeneratorType = string - -const ( - GeneratorType_WASP = "wasp" - GeneratorType_Looped = "looped" -) - -type Config struct { - General *General `toml:"general"` - ChaosConfig *ChaosConfig `toml:"chaos"` - Wasp *WaspConfig `toml:"wasp"` - LoopedConfig *LoopedConfig `toml:"looped"` -} - -type LoopedConfig struct { - ContractConfig `toml:"contract"` - FuzzConfig `toml:"fuzz"` -} - -type ContractConfig struct { - ExecutionCount int `toml:"execution_count"` -} - -type FuzzConfig struct { - MinEmitWaitTimeMs int `toml:"min_emit_wait_time_ms"` - MaxEmitWaitTimeMs int `toml:"max_emit_wait_time_ms"` -} - -type General struct { - Generator string `toml:"generator"` - EventsToEmit []abi.Event `toml:"-"` - Contracts int `toml:"contracts"` - EventsPerTx int `toml:"events_per_tx"` - UseFinalityTag bool `toml:"use_finality_tag"` -} - -type ChaosConfig struct { - ExperimentCount int `toml:"experiment_count"` - TargetComponent string `toml:"target_component"` -} - -type WaspConfig struct { - Load *Load `toml:"load"` -} - -type Load struct { - RPS int64 `toml:"rps"` - LPS int64 `toml:"lps"` - RateLimitUnitDuration *commonconfig.Duration `toml:"rate_limit_unit_duration"` - Duration *commonconfig.Duration `toml:"duration"` - CallTimeout *commonconfig.Duration `toml:"call_timeout"` -} - -func ReadConfig(configName string) (*Config, error) { - var cfg *Config - d, err := os.ReadFile(configName) - if err != nil { - return nil, fmt.Errorf("%w: %s", err, ErrReadPerfConfig) - } - err = toml.Unmarshal(d, &cfg) - if err != nil { - return nil, fmt.Errorf("%w: %s", err, ErrUnmarshalPerfConfig) - } - - if err := cfg.validate(); err != nil { - return nil, err - } - - log.Debug().Interface("Config", cfg).Msg("Parsed config") - return cfg, nil -} - -func (c *Config) OverrideFromEnv() error { - if contr := os.Getenv("CONTRACTS"); contr != "" { - c.General.Contracts = mustParseInt(contr) - } - - if eventsPerTx := os.Getenv("EVENTS_PER_TX"); eventsPerTx != "" { - c.General.EventsPerTx = mustParseInt(eventsPerTx) - } - - if useFinalityTag := os.Getenv("USE_FINALITY_TAG"); useFinalityTag != "" { - c.General.UseFinalityTag = mustParseBool(useFinalityTag) - } - - if duration := os.Getenv("LOAD_DURATION"); duration != "" { - d, err := commonconfig.ParseDuration(duration) - if err != nil { - return err - } - - if c.General.Generator == GeneratorType_WASP { - c.Wasp.Load.Duration = &d - } else { - // this is completely arbitrary and practice shows that even with this values - // test executes much longer than specified, probably due to network latency - c.LoopedConfig.FuzzConfig.MinEmitWaitTimeMs = 400 - c.LoopedConfig.FuzzConfig.MaxEmitWaitTimeMs = 600 - // divide by 4 based on past runs, but we should do it in a better way - c.LoopedConfig.ContractConfig.ExecutionCount = int(d.Duration().Seconds() / 4) - } - } - - return nil -} - -func (c *Config) validate() error { - if c.General == nil { - return fmt.Errorf("General config is nil") - } - - err := c.General.validate() - if err != nil { - return fmt.Errorf("General config validation failed: %w", err) - } - - switch c.General.Generator { - case GeneratorType_WASP: - if c.Wasp == nil { - return fmt.Errorf("wasp config is nil") - } - if c.Wasp.Load == nil { - return fmt.Errorf("wasp load config is nil") - } - - err = c.Wasp.validate() - if err != nil { - return fmt.Errorf("wasp config validation failed: %w", err) - } - case GeneratorType_Looped: - if c.LoopedConfig == nil { - return fmt.Errorf("looped config is nil") - } - - err = c.LoopedConfig.validate() - if err != nil { - return fmt.Errorf("looped config validation failed: %w", err) - } - default: - return fmt.Errorf("unknown generator type: %s", c.General.Generator) - } - - return nil -} - -func (g *General) validate() error { - if g.Generator == "" { - return fmt.Errorf("generator is empty") - } - - if g.Contracts == 0 { - return fmt.Errorf("contracts is 0, but must be > 0") - } - - if g.EventsPerTx == 0 { - return fmt.Errorf("events_per_tx is 0, but must be > 0") - } - - return nil -} - -func (w *WaspConfig) validate() error { - if w.Load == nil { - return fmt.Errorf("Load config is nil") - } - - err := w.Load.validate() - if err != nil { - return fmt.Errorf("Load config validation failed: %w", err) - } - - return nil -} - -func (l *Load) validate() error { - if l.RPS == 0 && l.LPS == 0 { - return fmt.Errorf("either RPS or LPS needs to be set") - } - - if l.RPS != 0 && l.LPS != 0 { - return fmt.Errorf("only one of RPS or LPS can be set") - } - - if l.Duration == nil { - return fmt.Errorf("duration is nil") - } - - if l.CallTimeout == nil { - return fmt.Errorf("call_timeout is nil") - } - if l.RateLimitUnitDuration == nil { - return fmt.Errorf("rate_limit_unit_duration is nil") - } - - return nil -} - -func (l *LoopedConfig) validate() error { - if l.ExecutionCount == 0 { - return fmt.Errorf("execution_count is 0, but must be > 0") - } - - if l.MinEmitWaitTimeMs == 0 { - return fmt.Errorf("min_emit_wait_time_ms is 0, but must be > 0") - } - - if l.MaxEmitWaitTimeMs == 0 { - return fmt.Errorf("max_emit_wait_time_ms is 0, but must be > 0") - } - - return nil -} - -func mustParseInt(s string) int { - i, err := strconv.Atoi(s) - if err != nil { - panic(err) - } - return i -} - -func mustParseBool(s string) bool { - b, err := strconv.ParseBool(s) - if err != nil { - panic(err) - } - return b -} diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 597b82ef6c3..db7eaee625b 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -44,6 +44,9 @@ import ( le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + lp_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/log_poller" ) var ( @@ -153,7 +156,7 @@ type ExpectedFilter struct { } // GetExpectedFilters returns a slice of ExpectedFilter structs based on the provided log emitters and config -func GetExpectedFilters(logEmitters []*contracts.LogEmitter, cfg *Config) []ExpectedFilter { +func GetExpectedFilters(logEmitters []*contracts.LogEmitter, cfg *lp_config.Config) []ExpectedFilter { expectedFilters := make([]ExpectedFilter, 0) for _, emitter := range logEmitters { for _, event := range cfg.General.EventsToEmit { @@ -230,11 +233,11 @@ func getStringSlice(length int) []string { } // emitEvents emits events from the provided log emitter concurrently according to the provided config -func emitEvents(ctx context.Context, l zerolog.Logger, logEmitter *contracts.LogEmitter, cfg *Config, wg *sync.WaitGroup, results chan LogEmitterChannel) { +func emitEvents(ctx context.Context, l zerolog.Logger, logEmitter *contracts.LogEmitter, cfg *lp_config.Config, wg *sync.WaitGroup, results chan LogEmitterChannel) { address := (*logEmitter).Address().String() localCounter := 0 defer wg.Done() - for i := 0; i < cfg.LoopedConfig.ExecutionCount; i++ { + for i := 0; i < *cfg.LoopedConfig.ExecutionCount; i++ { for _, event := range cfg.General.EventsToEmit { select { case <-ctx.Done(): @@ -245,13 +248,13 @@ func emitEvents(ctx context.Context, l zerolog.Logger, logEmitter *contracts.Log var err error switch event.Name { case "Log1": - _, err = (*logEmitter).EmitLogInts(getIntSlice(cfg.General.EventsPerTx)) + _, err = (*logEmitter).EmitLogInts(getIntSlice(*cfg.General.EventsPerTx)) case "Log2": - _, err = (*logEmitter).EmitLogIntsIndexed(getIntSlice(cfg.General.EventsPerTx)) + _, err = (*logEmitter).EmitLogIntsIndexed(getIntSlice(*cfg.General.EventsPerTx)) case "Log3": - _, err = (*logEmitter).EmitLogStrings(getStringSlice(cfg.General.EventsPerTx)) + _, err = (*logEmitter).EmitLogStrings(getStringSlice(*cfg.General.EventsPerTx)) case "Log4": - _, err = (*logEmitter).EmitLogIntMultiIndexed(1, 1, cfg.General.EventsPerTx) + _, err = (*logEmitter).EmitLogIntMultiIndexed(1, 1, *cfg.General.EventsPerTx) default: err = fmt.Errorf("unknown event name: %s", event.Name) } @@ -262,13 +265,13 @@ func emitEvents(ctx context.Context, l zerolog.Logger, logEmitter *contracts.Log } return } - localCounter += cfg.General.EventsPerTx + localCounter += *cfg.General.EventsPerTx - randomWait(cfg.LoopedConfig.FuzzConfig.MinEmitWaitTimeMs, cfg.LoopedConfig.FuzzConfig.MaxEmitWaitTimeMs) + randomWait(*cfg.LoopedConfig.MinEmitWaitTimeMs, *cfg.LoopedConfig.MaxEmitWaitTimeMs) } if (i+1)%10 == 0 { - l.Info().Str("Emitter address", address).Str("Index", fmt.Sprintf("%d/%d", i+1, cfg.LoopedConfig.ExecutionCount)).Msg("Emitted all three events") + l.Info().Str("Emitter address", address).Str("Index", fmt.Sprintf("%d/%d", i+1, *cfg.LoopedConfig.ExecutionCount)).Msg("Emitted all three events") } } } @@ -496,7 +499,7 @@ func (m *MissingLogs) IsEmpty() bool { } // GetMissingLogs returns a map of CL node name to missing logs in that node compared to EVM node to which the provided evm client is connected -func GetMissingLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *Config) (MissingLogs, error) { +func GetMissingLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *lp_config.Config) (MissingLogs, error) { wg := &sync.WaitGroup{} type dbQueryResult struct { @@ -669,7 +672,7 @@ func GetMissingLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmit } // PrintMissingLogsInfo prints various useful information about the missing logs -func PrintMissingLogsInfo(missingLogs map[string][]geth_types.Log, l zerolog.Logger, cfg *Config) { +func PrintMissingLogsInfo(missingLogs map[string][]geth_types.Log, l zerolog.Logger, cfg *lp_config.Config) { var findHumanName = func(topic common.Hash) string { for _, event := range cfg.General.EventsToEmit { if event.ID == topic { @@ -720,7 +723,7 @@ func PrintMissingLogsInfo(missingLogs map[string][]geth_types.Log, l zerolog.Log // getEVMLogs returns a slice of all logs emitted by the provided log emitters in the provided block range, // which are present in the EVM node to which the provided evm client is connected -func getEVMLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, l zerolog.Logger, cfg *Config) ([]geth_types.Log, error) { +func getEVMLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, l zerolog.Logger, cfg *lp_config.Config) ([]geth_types.Log, error) { allLogsInEVMNode := make([]geth_types.Log, 0) for j := 0; j < len(logEmitters); j++ { address := (*logEmitters[j]).Address() @@ -751,8 +754,8 @@ func getEVMLogs(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, } // ExecuteGenerator executes the configured generator and returns the total number of logs emitted -func ExecuteGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { - if cfg.General.Generator == GeneratorType_WASP { +func ExecuteGenerator(t *testing.T, cfg *lp_config.Config, logEmitters []*contracts.LogEmitter) (int, error) { + if *cfg.General.Generator == lp_config.GeneratorType_WASP { return runWaspGenerator(t, cfg, logEmitters) } @@ -760,14 +763,14 @@ func ExecuteGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi } // runWaspGenerator runs the wasp generator and returns the total number of logs emitted -func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { +func runWaspGenerator(t *testing.T, cfg *lp_config.Config, logEmitters []*contracts.LogEmitter) (int, error) { l := logging.GetTestLogger(t) var RPSprime int64 // if LPS is set, we need to calculate based on countract count and events per transaction - if cfg.Wasp.Load.LPS > 0 { - RPSprime = cfg.Wasp.Load.LPS / int64(cfg.General.Contracts) / int64(cfg.General.EventsPerTx) / int64(len(cfg.General.EventsToEmit)) + if *cfg.Wasp.LPS > 0 { + RPSprime = *cfg.Wasp.LPS / int64(*cfg.General.Contracts) / int64(*cfg.General.EventsPerTx) / int64(len(cfg.General.EventsToEmit)) if RPSprime < 1 { return 0, fmt.Errorf("invalid load configuration, effective RPS would have been zero. Adjust LPS, contracts count, events per tx or events to emit") @@ -775,8 +778,8 @@ func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi } // if RPS is set simply split it between contracts - if cfg.Wasp.Load.RPS > 0 { - RPSprime = cfg.Wasp.Load.RPS / int64(cfg.General.Contracts) + if *cfg.Wasp.RPS > 0 { + RPSprime = *cfg.Wasp.RPS / int64(*cfg.General.Contracts) } counter := &Counter{ @@ -791,16 +794,16 @@ func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi T: t, LoadType: wasp.RPS, GenName: fmt.Sprintf("log_poller_gen_%s", (*logEmitter).Address().String()), - RateLimitUnitDuration: cfg.Wasp.Load.RateLimitUnitDuration.Duration(), - CallTimeout: cfg.Wasp.Load.CallTimeout.Duration(), + RateLimitUnitDuration: cfg.Wasp.RateLimitUnitDuration.Duration, + CallTimeout: cfg.Wasp.CallTimeout.Duration, Schedule: wasp.Plain( RPSprime, - cfg.Wasp.Load.Duration.Duration(), + cfg.Wasp.Duration.Duration, ), Gun: NewLogEmitterGun( logEmitter, cfg.General.EventsToEmit, - cfg.General.EventsPerTx, + *cfg.General.EventsPerTx, l, ), SharedData: counter, @@ -818,7 +821,7 @@ func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi } // runLoopedGenerator runs the looped generator and returns the total number of logs emitted -func runLoopedGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { +func runLoopedGenerator(t *testing.T, cfg *lp_config.Config, logEmitters []*contracts.LogEmitter) (int, error) { l := logging.GetTestLogger(t) // Start emitting events in parallel, each contract is emitting events in a separate goroutine @@ -870,15 +873,15 @@ func runLoopedGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogE } // GetExpectedLogCount returns the expected number of logs to be emitted based on the provided config -func GetExpectedLogCount(cfg *Config) int64 { - if cfg.General.Generator == GeneratorType_WASP { - if cfg.Wasp.Load.RPS != 0 { - return cfg.Wasp.Load.RPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) * int64(cfg.General.EventsPerTx) +func GetExpectedLogCount(cfg *lp_config.Config) int64 { + if *cfg.General.Generator == lp_config.GeneratorType_WASP { + if *cfg.Wasp.RPS != 0 { + return *cfg.Wasp.RPS * int64(cfg.Wasp.Duration.Seconds()) * int64(*cfg.General.EventsPerTx) } - return cfg.Wasp.Load.LPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) + return *cfg.Wasp.LPS * int64(cfg.Wasp.Duration.Duration.Seconds()) } - return int64(len(cfg.General.EventsToEmit) * cfg.LoopedConfig.ExecutionCount * cfg.General.Contracts * cfg.General.EventsPerTx) + return int64(len(cfg.General.EventsToEmit) * *cfg.LoopedConfig.ExecutionCount * *cfg.General.Contracts * *cfg.General.EventsPerTx) } type PauseData struct { @@ -939,20 +942,20 @@ type ChaosPauseData struct { } // ExecuteChaosExperiment executes the configured chaos experiment, which consist of pausing CL node or Postgres containers -func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, cfg *Config, errorCh chan error) { - if cfg.ChaosConfig == nil || cfg.ChaosConfig.ExperimentCount == 0 { +func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, cfg *lp_config.Config, errorCh chan error) { + if cfg.ChaosConfig == nil || *cfg.ChaosConfig.ExperimentCount == 0 { errorCh <- nil return } - chaosChan := make(chan ChaosPauseData, cfg.ChaosConfig.ExperimentCount) + chaosChan := make(chan ChaosPauseData, *cfg.ChaosConfig.ExperimentCount) wg := &sync.WaitGroup{} go func() { // if we wanted to have more than 1 container paused, we'd need to make sure we aren't trying to pause an already paused one guardChan := make(chan struct{}, 1) - for i := 0; i < cfg.ChaosConfig.ExperimentCount; i++ { + for i := 0; i < *cfg.ChaosConfig.ExperimentCount; i++ { i := i wg.Add(1) guardChan <- struct{}{} @@ -963,7 +966,7 @@ func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv current := i + 1 l.Info().Str("Current/Total", fmt.Sprintf("%d/%d", current, cfg.ChaosConfig.ExperimentCount)).Msg("Done with experiment") }() - chaosChan <- chaosPauseSyncFn(l, testEnv, cfg.ChaosConfig.TargetComponent) + chaosChan <- chaosPauseSyncFn(l, testEnv, *cfg.ChaosConfig.TargetComponent) time.Sleep(10 * time.Second) }() } @@ -1015,8 +1018,8 @@ func GetFinalityDepth(chainId int64) (int64, error) { } // GetEndBlockToWaitFor returns the end block to wait for based on chain id and finality tag provided in config -func GetEndBlockToWaitFor(endBlock, chainId int64, cfg *Config) (int64, error) { - if cfg.General.UseFinalityTag { +func GetEndBlockToWaitFor(endBlock, chainId int64, cfg *lp_config.Config) (int64, error) { + if *cfg.General.UseFinalityTag { return endBlock + 1, nil } @@ -1076,6 +1079,7 @@ func SetupLogPollerTestDocker( upkeepsNeeded int, lpPollingInterval time.Duration, finalityTagEnabled bool, + testConfig *tc.TestConfig, ) ( blockchain.EVMClient, []*client.ChainlinkClient, @@ -1086,9 +1090,10 @@ func SetupLogPollerTestDocker( *test_env.CLClusterTestEnv, ) { l := logging.GetTestLogger(t) + // Add registry version to config registryConfig.RegistryVersion = registryVersion - network := networks.MustGetSelectedNetworksFromEnv()[0] + network := networks.MustGetSelectedNetworkConfig(testConfig.Network)[0] finalityDepth, err := GetFinalityDepth(network.ChainID) require.NoError(t, err, "Error getting finality depth") @@ -1137,6 +1142,7 @@ func SetupLogPollerTestDocker( require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). + WithTestConfig(testConfig). WithTestInstance(t). WithPrivateEthereumNetwork(cfg). WithCLNodes(clNodesCount). @@ -1204,9 +1210,9 @@ func SetupLogPollerTestDocker( } // UploadLogEmitterContractsAndWaitForFinalisation uploads the configured number of log emitter contracts and waits for the upload blocks to be finalised -func UploadLogEmitterContractsAndWaitForFinalisation(l zerolog.Logger, t *testing.T, testEnv *test_env.CLClusterTestEnv, cfg *Config) []*contracts.LogEmitter { +func UploadLogEmitterContractsAndWaitForFinalisation(l zerolog.Logger, t *testing.T, testEnv *test_env.CLClusterTestEnv, cfg *lp_config.Config) []*contracts.LogEmitter { logEmitters := make([]*contracts.LogEmitter, 0) - for i := 0; i < cfg.General.Contracts; i++ { + for i := 0; i < *cfg.General.Contracts; i++ { logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() logEmitters = append(logEmitters, &logEmitter) require.NoError(t, err, "Error deploying log emitter contract") @@ -1265,7 +1271,7 @@ func AssertContractAddressUniquneness(logEmitters []*contracts.LogEmitter) error // RegisterFiltersAndAssertUniquness registers the configured log filters and asserts that the filters are unique // meaning that for each log emitter address and topic there is only one filter -func RegisterFiltersAndAssertUniquness(l zerolog.Logger, registry contracts.KeeperRegistry, upkeepIDs []*big.Int, logEmitters []*contracts.LogEmitter, cfg *Config, upKeepsNeeded int) error { +func RegisterFiltersAndAssertUniquness(l zerolog.Logger, registry contracts.KeeperRegistry, upkeepIDs []*big.Int, logEmitters []*contracts.LogEmitter, cfg *lp_config.Config, upKeepsNeeded int) error { uniqueFilters := make(map[string]bool) upkeepIdIndex := 0 From 0656408b5635b8012a485a0c0e1d2df75caaa6bf Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 24 Jan 2024 10:22:16 -0500 Subject: [PATCH 198/234] Updates GitHub Action to Fixed Version (#11871) Co-authored-by: Bartek Tofel --- .github/workflows/live-testnet-tests.yml | 110 +++++++++++------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 9e12089531d..611fd3815bd 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -1,7 +1,7 @@ # *** # This workflow is a monstrosity of copy-paste, and that's to increase legibility in reporting and running, so the code be damned. # I suspect this can be cleaned up significantly with some clever trickery of the GitHub actions matrices, but I am not that clever. -# We want each chain to run in parallel, but each test within the chain needs to be able to run sequentially +# We want each chain to run in parallel, but each test within the chain needs to be able to run sequentially # (we're trying to eliminate this as a requirement, should make it a lot easier). # Each chain can have a variety of tests to run. # We also want reporting to be clear in the start-slack-thread and post-test-results-to-slack jobs. @@ -59,7 +59,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Chainlink Image uses: ./.github/actions/build-chainlink-image - with: + with: tag_suffix: "" dockerfile: core/chainlink.Dockerfile git_commit_sha: ${{ github.sha }} @@ -73,7 +73,7 @@ jobs: permissions: id-token: write contents: read - name: Build Tests Binary + name: Build Tests Binary runs-on: ubuntu-latest steps: - name: Collect Metrics @@ -89,7 +89,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download token: ${{ secrets.GITHUB_TOKEN }} @@ -227,7 +227,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-sepolia pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -244,9 +244,9 @@ jobs: - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -264,7 +264,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 bsc-testnet-tests: # TODO: BSC RPCs are all in a bad state right now, so we're skipping these tests until they're fixed @@ -289,7 +289,7 @@ jobs: test: TestAutomationBasic/registry_2_1_logtrigger name: BSC Testnet ${{ matrix.product }} Tests runs-on: ubuntu-latest - steps: + steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 @@ -299,7 +299,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-bsc-testnet pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -316,9 +316,9 @@ jobs: - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + name: tests + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -336,7 +336,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 optimism-sepolia-smoke-tests: environment: integration @@ -369,7 +369,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-optimism-sepolia pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -382,13 +382,13 @@ jobs: network: "optimism_sepolia" httpEndpoints: ${{ secrets.QA_OPTIMISM_SEPOLIA_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_OPTIMISM_SEPOLIA_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -406,7 +406,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 arbitrum-sepolia-smoke-tests: environment: integration @@ -439,7 +439,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-arbitrum-sepolia pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -452,13 +452,13 @@ jobs: network: "arbitrum_sepolia" httpEndpoints: ${{ secrets.QA_ARBITRUM_SEPOLIA_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_ARBITRUM_SEPOLIA_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -476,7 +476,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 base-sepolia-smoke-tests: environment: integration @@ -505,7 +505,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-base-sepolia pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -518,13 +518,13 @@ jobs: network: "base_sepolia" httpEndpoints: ${{ secrets.QA_BASE_SEPOLIA_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_BASE_SEPOLIA_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -542,8 +542,8 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + polygon-mumbai-smoke-tests: environment: integration permissions: @@ -575,7 +575,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-polygon-mumbai pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -588,13 +588,13 @@ jobs: network: "polygon_mumbai" httpEndpoints: ${{ secrets.QA_POLYGON_MUMBAI_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_POLYGON_MUMBAI_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -612,7 +612,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 avalanche-fuji-smoke-tests: environment: integration @@ -645,7 +645,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-avalanche-fuji pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -658,13 +658,13 @@ jobs: network: "avalanche_fuji" httpEndpoints: ${{ secrets.QA_AVALANCHE_FUJI_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_AVALANCHE_FUJI_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -682,7 +682,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 fantom-testnet-smoke-tests: environment: integration @@ -715,7 +715,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-fantom-testnet pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -728,13 +728,13 @@ jobs: network: "fantom_testnet" httpEndpoints: ${{ secrets.QA_FANTOM_TESTNET_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_FANTOM_TESTNET_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -752,7 +752,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 celo-alfajores-smoke-tests: environment: integration @@ -781,7 +781,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-celo-alfajores pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -794,13 +794,13 @@ jobs: network: "celo_alfajores" httpEndpoints: ${{ secrets.QA_CELO_ALFAJORES_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_CELO_ALFAJORES_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -818,8 +818,8 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 - + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + scroll-sepolia-smoke-tests: # TODO: Disabled until bug TT-767 is fixed if: false @@ -849,7 +849,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-scroll-sepolia pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -862,13 +862,13 @@ jobs: network: "scroll_sepolia" httpEndpoints: ${{ secrets.QA_SCROLL_SEPOLIA_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_SCROLL_SEPOLIA_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -886,7 +886,7 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 linea-goerli-smoke-tests: environment: integration @@ -915,7 +915,7 @@ jobs: runId: ${{ github.run_id }} testLogCollect: ${{ vars.TEST_LOG_COLLECT }} chainlinkImage: ${{ env.CHAINLINK_IMAGE }} - chainlinkVersion: ${{ github.sha }} + chainlinkVersion: ${{ github.sha }} pyroscopeServer: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 pyroscopeEnvironment: ci-smoke-${{ matrix.product }}-linea-goerli pyroscopeKey: ${{ secrets.QA_PYROSCOPE_KEY }} @@ -928,13 +928,13 @@ jobs: network: "linea_goerli" httpEndpoints: ${{ secrets.QA_LINEA_GOERLI_HTTP_URLS }} wsEndpoints: ${{ secrets.QA_LINEA_GOERLI_URLS }} - fundingKeys: ${{ secrets.QA_EVM_KEYS }} + fundingKeys: ${{ secrets.QA_EVM_KEYS }} - name: Download Tests Binary uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: tests + name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -952,4 +952,4 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@952abab6367aaff96102abf9ef751ef36fe9ea29 # v2.3.3 \ No newline at end of file + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 \ No newline at end of file From c026cc3d9ee1a2d9d25b83cc62be93087c857fcb Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 24 Jan 2024 10:23:02 -0500 Subject: [PATCH 199/234] Parameterizing TestPriorityLevelNodeSelector (#11872) --- .../node_selector_priority_level_test.go | 133 +++++++++--------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/common/client/node_selector_priority_level_test.go b/common/client/node_selector_priority_level_test.go index ac84645e91c..15a7a7ac60b 100644 --- a/common/client/node_selector_priority_level_test.go +++ b/common/client/node_selector_priority_level_test.go @@ -17,72 +17,75 @@ func TestPriorityLevelNodeSelector(t *testing.T) { t.Parallel() type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] - n1 := newMockNode[types.ID, Head, nodeClient](t) - n1.On("State").Return(nodeStateAlive) - n1.On("Order").Return(int32(1)) - - n2 := newMockNode[types.ID, Head, nodeClient](t) - n2.On("State").Return(nodeStateAlive) - n2.On("Order").Return(int32(1)) - - n3 := newMockNode[types.ID, Head, nodeClient](t) - n3.On("State").Return(nodeStateAlive) - n3.On("Order").Return(int32(1)) - - nodes = append(nodes, n1, n2, n3) - selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) - assert.Same(t, nodes[0], selector.Select()) - assert.Same(t, nodes[1], selector.Select()) - assert.Same(t, nodes[2], selector.Select()) - assert.Same(t, nodes[0], selector.Select()) - assert.Same(t, nodes[1], selector.Select()) - assert.Same(t, nodes[2], selector.Select()) -} - -func TestPriorityLevelNodeSelector_None(t *testing.T) { - t.Parallel() - - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) - if i == 0 { - // first node is out of sync - node.On("State").Return(nodeStateOutOfSync) - node.On("Order").Return(int32(1)) - } else { - // others are unreachable - node.On("State").Return(nodeStateUnreachable) - node.On("Order").Return(int32(1)) - } - nodes = append(nodes, node) + type testNode struct { + order int32 + state nodeState + } + type testCase struct { + name string + nodes []testNode + expect []int // indexes of the nodes expected to be returned by Select } - selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) - assert.Nil(t, selector.Select()) -} - -func TestPriorityLevelNodeSelector_DifferentOrder(t *testing.T) { - t.Parallel() - - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] - n1 := newMockNode[types.ID, Head, nodeClient](t) - n1.On("State").Return(nodeStateAlive) - n1.On("Order").Return(int32(1)) - - n2 := newMockNode[types.ID, Head, nodeClient](t) - n2.On("State").Return(nodeStateAlive) - n2.On("Order").Return(int32(2)) - - n3 := newMockNode[types.ID, Head, nodeClient](t) - n3.On("State").Return(nodeStateAlive) - n3.On("Order").Return(int32(3)) + testCases := []testCase{ + { + name: "TwoNodesSameOrder: Highest Allowed Order", + nodes: []testNode{ + {order: 1, state: nodeStateAlive}, + {order: 1, state: nodeStateAlive}, + }, + expect: []int{0, 1, 0, 1, 0, 1}, + }, + { + name: "TwoNodesSameOrder: Lowest Allowed Order", + nodes: []testNode{ + {order: 100, state: nodeStateAlive}, + {order: 100, state: nodeStateAlive}, + }, + expect: []int{0, 1, 0, 1, 0, 1}, + }, + { + name: "NoneAvailable", + nodes: []testNode{ + {order: 1, state: nodeStateOutOfSync}, + {order: 1, state: nodeStateUnreachable}, + {order: 1, state: nodeStateUnreachable}, + }, + expect: []int{}, // no nodes should be selected + }, + { + name: "DifferentOrder", + nodes: []testNode{ + {order: 1, state: nodeStateAlive}, + {order: 2, state: nodeStateAlive}, + {order: 3, state: nodeStateAlive}, + }, + expect: []int{0, 0}, // only the highest order node should be selected + }, + } - nodes = append(nodes, n1, n2, n3) - selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) - assert.Same(t, nodes[0], selector.Select()) - assert.Same(t, nodes[0], selector.Select()) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var nodes []Node[types.ID, Head, nodeClient] + for _, tn := range tc.nodes { + node := newMockNode[types.ID, Head, nodeClient](t) + node.On("State").Return(tn.state) + node.On("Order").Return(tn.order) + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) + for _, idx := range tc.expect { + if idx >= len(nodes) { + t.Fatalf("Invalid node index %d in test case '%s'", idx, tc.name) + } + assert.Same(t, nodes[idx], selector.Select()) + } + + // Check for nil selection if expected slice is empty + if len(tc.expect) == 0 { + assert.Nil(t, selector.Select()) + } + }) + } } From b7260d415b44afc3b8a7129845d5856a55445fb7 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 24 Jan 2024 10:00:19 -0600 Subject: [PATCH 200/234] core/plugins: add per-plugin env vars (#11526) * core/plugins: add per-plugin env vars * Adding coverage (#11851) * add TestIntegration_OCR2_plugins behind build tag; swap in freeport for fixed const --------- Co-authored-by: Patrick --- .github/workflows/ci-core.yml | 11 ++++ core/cmd/shell_test.go | 56 +++++++++++++++++++ core/config/env/env.go | 49 ++++++++++------ core/config/env/env_test.go | 24 ++++++++ .../ocr2/features_ocr2_plugin_test.go | 14 +++++ .../features/ocr2/features_ocr2_test.go | 5 +- core/scripts/go.mod | 2 + core/scripts/go.sum | 6 +- core/services/chainlink/relayer_factory.go | 15 ++++- core/services/ocr2/delegate.go | 27 ++++++--- core/services/ocr2/plugins/median/services.go | 14 ++++- core/services/ocr2/validate/config.go | 2 +- core/services/ocr2/validate/validate.go | 9 +-- docs/CHANGELOG.md | 6 ++ go.mod | 1 + go.sum | 2 + integration-tests/go.mod | 2 + integration-tests/go.sum | 2 + integration-tests/smoke/ocr2_test.go | 4 +- plugins/{utils.go => cmd.go} | 6 +- plugins/cmd_test.go | 51 +++++++++++++++++ plugins/env.go | 31 ++++++++++ plugins/env_test.go | 25 +++++++++ plugins/loop_registry.go | 17 ++++-- plugins/{config.go => registrar.go} | 9 +-- plugins/testdata/invalid.env | 2 + plugins/testdata/valid.env | 1 + 27 files changed, 338 insertions(+), 55 deletions(-) create mode 100644 core/config/env/env_test.go create mode 100644 core/internal/features/ocr2/features_ocr2_plugin_test.go rename plugins/{utils.go => cmd.go} (73%) create mode 100644 plugins/cmd_test.go create mode 100644 plugins/env.go create mode 100644 plugins/env_test.go rename plugins/{config.go => registrar.go} (77%) create mode 100644 plugins/testdata/invalid.env create mode 100644 plugins/testdata/valid.env diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 30534873134..6ba88247220 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -71,6 +71,17 @@ jobs: run: go build -o chainlink.test . - name: Setup DB run: ./chainlink.test local db preparetest + - name: Install LOOP Plugins + run: | + pushd $(go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-feeds) + go install ./cmd/chainlink-feeds + popd + pushd $(go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-solana) + go install ./pkg/solana/cmd/chainlink-solana + popd + pushd $(go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-starknet/relayer) + go install ./pkg/chainlink/cmd/chainlink-starknet + popd - name: Increase Race Timeout if: github.event.schedule != '' run: | diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index ec9606e0ac6..f265d5f4787 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -380,6 +380,17 @@ func TestSetupSolanaRelayer(t *testing.T) { } }) + t2Config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c.Solana = solana.TOMLConfigs{ + &solana.TOMLConfig{ + ChainID: ptr[string]("solana-id-1"), + Enabled: ptr(true), + Chain: solcfg.Chain{}, + Nodes: []*solcfg.Node{}, + }, + } + }) + rf := chainlink.RelayerFactory{ Logger: lggr, LoopRegistry: reg, @@ -435,6 +446,23 @@ func TestSetupSolanaRelayer(t *testing.T) { _, err := rf.NewSolana(ks, duplicateConfig.SolanaConfigs()) require.Error(t, err) }) + + t.Run("plugin env parsing fails", func(t *testing.T) { + t.Setenv("CL_SOLANA_CMD", "phony_solana_cmd") + t.Setenv("CL_SOLANA_ENV", "fake_path") + + _, err := rf.NewSolana(ks, t2Config.SolanaConfigs()) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to parse Solana env file") + }) + + t.Run("plugin already registered", func(t *testing.T) { + t.Setenv("CL_SOLANA_CMD", "phony_solana_cmd") + + _, err := rf.NewSolana(ks, tConfig.SolanaConfigs()) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to create Solana LOOP command") + }) } func TestSetupStarkNetRelayer(t *testing.T) { @@ -465,6 +493,17 @@ func TestSetupStarkNetRelayer(t *testing.T) { }, } }) + + t2Config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c.Starknet = stkcfg.TOMLConfigs{ + &stkcfg.TOMLConfig{ + ChainID: ptr[string]("starknet-id-3"), + Enabled: ptr(true), + Chain: stkcfg.Chain{}, + Nodes: []*config.Node{}, + }, + } + }) rf := chainlink.RelayerFactory{ Logger: lggr, LoopRegistry: reg, @@ -520,6 +559,23 @@ func TestSetupStarkNetRelayer(t *testing.T) { _, err := rf.NewStarkNet(ks, duplicateConfig.StarknetConfigs()) require.Error(t, err) }) + + t.Run("plugin env parsing fails", func(t *testing.T) { + t.Setenv("CL_STARKNET_CMD", "phony_starknet_cmd") + t.Setenv("CL_STARKNET_ENV", "fake_path") + + _, err := rf.NewStarkNet(ks, t2Config.StarknetConfigs()) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to parse Starknet env file") + }) + + t.Run("plugin already registered", func(t *testing.T) { + t.Setenv("CL_STARKNET_CMD", "phony_starknet_cmd") + + _, err := rf.NewStarkNet(ks, tConfig.StarknetConfigs()) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to create StarkNet LOOP command") + }) } // flagSetApplyFromAction applies the flags from action to the flagSet. diff --git a/core/config/env/env.go b/core/config/env/env.go index 37ae131ddf1..f22310a6cf8 100644 --- a/core/config/env/env.go +++ b/core/config/env/env.go @@ -1,6 +1,7 @@ package env import ( + "fmt" "os" "strings" @@ -8,12 +9,24 @@ import ( ) var ( - Config = Var("CL_CONFIG") + Config = Var("CL_CONFIG") + DatabaseAllowSimplePasswords = Var("CL_DATABASE_ALLOW_SIMPLE_PASSWORDS") + DatabaseURL = Secret("CL_DATABASE_URL") + DatabaseBackupURL = Secret("CL_DATABASE_BACKUP_URL") + PasswordKeystore = Secret("CL_PASSWORD_KEYSTORE") + PasswordVRF = Secret("CL_PASSWORD_VRF") + PyroscopeAuthToken = Secret("CL_PYROSCOPE_AUTH_TOKEN") + PrometheusAuthToken = Secret("CL_PROMETHEUS_AUTH_TOKEN") + ThresholdKeyShare = Secret("CL_THRESHOLD_KEY_SHARE") + // Migrations env vars + EVMChainIDNotNullMigration0195 = "CL_EVM_CHAINID_NOT_NULL_MIGRATION_0195" +) - // LOOPP commands and vars - MedianPluginCmd = Var("CL_MEDIAN_CMD") - SolanaPluginCmd = Var("CL_SOLANA_CMD") - StarknetPluginCmd = Var("CL_STARKNET_CMD") +// LOOPP commands and vars +var ( + MedianPlugin = NewPlugin("median") + SolanaPlugin = NewPlugin("solana") + StarknetPlugin = NewPlugin("starknet") // PrometheusDiscoveryHostName is the externally accessible hostname // published by the node in the `/discovery` endpoint. Generally, it is expected to match // the public hostname of node. @@ -22,24 +35,13 @@ var ( // In house we observed that the resolved value of os.Hostname was not accessible to // outside of the given pod PrometheusDiscoveryHostName = Var("CL_PROMETHEUS_DISCOVERY_HOSTNAME") - // EnvLooopHostName is the hostname used for HTTP communication between the + // LOOPPHostName is the hostname used for HTTP communication between the // node and LOOPps. In most cases this does not need to be set explicitly. LOOPPHostName = Var("CL_LOOPP_HOSTNAME") // Work around for Solana LOOPPs configured with zero values. MinOCR2MaxDurationQuery = Var("CL_MIN_OCR2_MAX_DURATION_QUERY") // PipelineOvertime is an undocumented escape hatch for overriding the default padding in pipeline executions. PipelineOvertime = Var("CL_PIPELINE_OVERTIME") - - DatabaseAllowSimplePasswords = Var("CL_DATABASE_ALLOW_SIMPLE_PASSWORDS") - DatabaseURL = Secret("CL_DATABASE_URL") - DatabaseBackupURL = Secret("CL_DATABASE_BACKUP_URL") - PasswordKeystore = Secret("CL_PASSWORD_KEYSTORE") - PasswordVRF = Secret("CL_PASSWORD_VRF") - PyroscopeAuthToken = Secret("CL_PYROSCOPE_AUTH_TOKEN") - PrometheusAuthToken = Secret("CL_PROMETHEUS_AUTH_TOKEN") - ThresholdKeyShare = Secret("CL_THRESHOLD_KEY_SHARE") - // Migrations env vars - EVMChainIDNotNullMigration0195 = "CL_EVM_CHAINID_NOT_NULL_MIGRATION_0195" ) type Var string @@ -54,3 +56,16 @@ func (e Var) IsTrue() bool { return strings.ToLower(e.Get()) == "true" } type Secret string func (e Secret) Get() models.Secret { return models.Secret(os.Getenv(string(e))) } + +type Plugin struct { + Cmd Var + Env Var +} + +func NewPlugin(kind string) Plugin { + kind = strings.ToUpper(kind) + return Plugin{ + Cmd: Var(fmt.Sprintf("CL_%s_CMD", kind)), + Env: Var(fmt.Sprintf("CL_%s_ENV", kind)), + } +} diff --git a/core/config/env/env_test.go b/core/config/env/env_test.go new file mode 100644 index 00000000000..b6638758d6f --- /dev/null +++ b/core/config/env/env_test.go @@ -0,0 +1,24 @@ +package env + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewPlugin(t *testing.T) { + for _, tt := range []struct { + name string + kind string + exp Plugin + }{ + {"lower", "foo", Plugin{Cmd: "CL_FOO_CMD", Env: "CL_FOO_ENV"}}, + {"upper", "BAR", Plugin{Cmd: "CL_BAR_CMD", Env: "CL_BAR_ENV"}}, + {"mixed", "Baz", Plugin{Cmd: "CL_BAZ_CMD", Env: "CL_BAZ_ENV"}}, + } { + t.Run(tt.name, func(t *testing.T) { + got := NewPlugin(tt.kind) + require.Equal(t, tt.exp, got) + }) + } +} diff --git a/core/internal/features/ocr2/features_ocr2_plugin_test.go b/core/internal/features/ocr2/features_ocr2_plugin_test.go new file mode 100644 index 00000000000..96a9f32e957 --- /dev/null +++ b/core/internal/features/ocr2/features_ocr2_plugin_test.go @@ -0,0 +1,14 @@ +//go:build integration + +package ocr2_test + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/v2/core/config/env" +) + +func TestIntegration_OCR2_plugins(t *testing.T) { + t.Setenv(string(env.MedianPlugin.Cmd), "chainlink-feeds") + testIntegration_OCR2(t) +} diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 002f1d192ae..938b7aa2a66 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -189,7 +189,10 @@ func setupNodeOCR2( func TestIntegration_OCR2(t *testing.T) { t.Parallel() + testIntegration_OCR2(t) +} +func testIntegration_OCR2(t *testing.T) { for _, test := range []struct { name string chainReaderAndCodec bool @@ -199,8 +202,6 @@ func TestIntegration_OCR2(t *testing.T) { } { test := test t.Run(test.name, func(t *testing.T) { - t.Parallel() - owner, b, ocrContractAddress, ocrContract := setupOCR2Contracts(t) lggr := logger.TestLogger(t) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index fea7da60757..c533d486f60 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -166,6 +166,8 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/hashicorp/consul/sdk v0.14.1 // indirect + github.com/hashicorp/go-envparse v0.1.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-plugin v1.5.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index bd13587c679..b956198b8f2 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -694,6 +694,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= +github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= @@ -711,8 +713,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 49582e2d52a..c42ca77dc39 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -116,7 +116,7 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solana.TOMLConf lggr := solLggr.Named(relayID.ChainID) - if cmdName := env.SolanaPluginCmd.Get(); cmdName != "" { + if cmdName := env.SolanaPlugin.Cmd.Get(); cmdName != "" { // setup the solana relayer to be a LOOP cfgTOML, err := toml.Marshal(struct { @@ -126,10 +126,14 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solana.TOMLConf if err != nil { return nil, fmt.Errorf("failed to marshal Solana configs: %w", err) } - + envVars, err := plugins.ParseEnvFile(env.SolanaPlugin.Env.Get()) + if err != nil { + return nil, fmt.Errorf("failed to parse Solana env file: %w", err) + } solCmdFn, err := plugins.NewCmdFactory(r.Register, plugins.CmdConfig{ ID: relayID.Name(), Cmd: cmdName, + Env: envVars, }) if err != nil { return nil, fmt.Errorf("failed to create Solana LOOP command: %w", err) @@ -187,7 +191,7 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML lggr := starkLggr.Named(relayID.ChainID) - if cmdName := env.StarknetPluginCmd.Get(); cmdName != "" { + if cmdName := env.StarknetPlugin.Cmd.Get(); cmdName != "" { // setup the starknet relayer to be a LOOP cfgTOML, err := toml.Marshal(struct { Starknet config.TOMLConfig @@ -196,9 +200,14 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML return nil, fmt.Errorf("failed to marshal StarkNet configs: %w", err) } + envVars, err := plugins.ParseEnvFile(env.StarknetPlugin.Env.Get()) + if err != nil { + return nil, fmt.Errorf("failed to parse Starknet env file: %w", err) + } starknetCmdFn, err := plugins.NewCmdFactory(r.Register, plugins.CmdConfig{ ID: relayID.Name(), Cmd: cmdName, + Env: envVars, }) if err != nil { return nil, fmt.Errorf("failed to create StarkNet LOOP command: %w", err) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 2ea99d65cfe..c838316b1cc 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -25,6 +25,7 @@ import ( ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" ocr2keepers21config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" + "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink-vrf/altbn_128" dkgpkg "github.com/smartcontractkit/chainlink-vrf/dkg" @@ -504,12 +505,6 @@ type connProvider interface { ClientConn() grpc.ClientConnInterface } -func defaultPathFromPluginName(pluginName string) string { - // By default we install the command on the system path, in the - // form: `chainlink-` - return fmt.Sprintf("chainlink-%s", pluginName) -} - func (d *Delegate) newServicesGenericPlugin( ctx context.Context, lggr logger.SugaredLogger, @@ -530,9 +525,11 @@ func (d *Delegate) newServicesGenericPlugin( return nil, err } + plugEnv := env.NewPlugin(p.PluginName) + command := p.Command if command == "" { - command = defaultPathFromPluginName(p.PluginName) + command = plugEnv.Cmd.Get() } // Add the default pipeline to the pluginConfig @@ -587,8 +584,22 @@ func (d *Delegate) newServicesGenericPlugin( OffchainConfigDigester: provider.OffchainConfigDigester(), } + envVars, err := plugins.ParseEnvFile(plugEnv.Env.Get()) + if err != nil { + return nil, fmt.Errorf("failed to parse median env file: %w", err) + } + if len(p.EnvVars) > 0 { + for k, v := range p.EnvVars { + envVars = append(envVars, k+"="+v) + } + } + pluginLggr := lggr.Named(p.PluginName).Named(spec.ContractID).Named(spec.GetID()) - cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(fmt.Sprintf("%s-%s-%s", p.PluginName, spec.ContractID, spec.GetID()), command) + cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(plugins.CmdConfig{ + ID: fmt.Sprintf("%s-%s-%s", p.PluginName, spec.ContractID, spec.GetID()), + Cmd: command, + Env: envVars, + }) if err != nil { return nil, fmt.Errorf("failed to register loop: %w", err) } diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index bdf6feaed47..2a874ff1756 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -125,10 +125,20 @@ func NewMedianServices(ctx context.Context, CreatedAt: time.Now(), }, lggr) - if cmdName := env.MedianPluginCmd.Get(); cmdName != "" { + if cmdName := env.MedianPlugin.Cmd.Get(); cmdName != "" { // use unique logger names so we can use it to register a loop medianLggr := lggr.Named("Median").Named(spec.ContractID).Named(spec.GetID()) - cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), cmdName) + envVars, err2 := plugins.ParseEnvFile(env.MedianPlugin.Env.Get()) + if err2 != nil { + err = fmt.Errorf("failed to parse median env file: %w", err2) + abort() + return + } + cmdFn, telem, err2 := cfg.RegisterLOOP(plugins.CmdConfig{ + ID: medianLggr.Name(), + Cmd: cmdName, + Env: envVars, + }) if err2 != nil { err = fmt.Errorf("failed to register loop: %w", err2) abort() diff --git a/core/services/ocr2/validate/config.go b/core/services/ocr2/validate/config.go index 549b929b23c..0084a18308b 100644 --- a/core/services/ocr2/validate/config.go +++ b/core/services/ocr2/validate/config.go @@ -49,7 +49,7 @@ func ToLocalConfig(ocr2Config OCR2Config, insConf InsecureConfig, spec job.OCR2O ContractTransmitterTransmitTimeout: ocr2Config.ContractTransmitterTransmitTimeout(), DatabaseTimeout: ocr2Config.DatabaseTimeout(), } - if spec.Relay == relay.Solana && env.MedianPluginCmd.Get() != "" { + if spec.Relay == relay.Solana && env.MedianPlugin.Cmd.Get() != "" { // Work around for Solana Feeds configured with zero values to support LOOP Plugins. minOCR2MaxDurationQuery, err := getMinOCR2MaxDurationQuery() if err != nil { diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index bb9bb03a8ac..ad54ba4fea2 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -136,10 +136,11 @@ type Config struct { } type innerConfig struct { - Command string `json:"command"` - ProviderType string `json:"providerType"` - PluginName string `json:"pluginName"` - TelemetryType string `json:"telemetryType"` + Command string `json:"command"` + EnvVars map[string]string `json:"envVars"` + ProviderType string `json:"providerType"` + PluginName string `json:"pluginName"` + TelemetryType string `json:"telemetryType"` Config } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0718d9f8286..4430c75ee4a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `chainlink health` CLI command and HTML `/health` endpoint, to provide human-readable views of the underlying JSON health data. - New job type `stream` to represent streamspecs. This job type is not yet used anywhere but will be required for Data Streams V1. +- Environment variables `CL_MEDIAN_ENV`, `CL_SOLANA_ENV`, and `CL_STARKNET_ENV` for setting environment variables in LOOP Plugins with an `.env` file. + ``` + echo "Foo=Bar" >> median.env + echo "Baz=Val" >> median.env + CL_MEDIAN_ENV="median.env" + ``` ### Fixed diff --git a/go.mod b/go.mod index 0bc08adc3fc..0eed0de9a0f 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/graph-gophers/dataloader v5.0.0+incompatible github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/consul/sdk v0.14.1 + github.com/hashicorp/go-envparse v0.1.0 github.com/hashicorp/go-plugin v1.5.2 github.com/hdevalence/ed25519consensus v0.1.0 github.com/jackc/pgconn v1.14.1 diff --git a/go.sum b/go.sum index ba26cb877d8..46a9a5343e5 100644 --- a/go.sum +++ b/go.sum @@ -685,6 +685,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= +github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 9fe026cdd97..69613eccb7a 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -248,8 +248,10 @@ require ( github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/consul/api v1.25.1 // indirect + github.com/hashicorp/consul/sdk v0.14.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-envparse v0.1.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-msgpack v0.5.5 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 95d294e611f..beadcb56af0 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -906,6 +906,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= +github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 9e06ee527cd..0898dd69985 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -24,8 +24,8 @@ import ( func TestOCRv2Basic(t *testing.T) { t.Parallel() - noMedianPlugin := map[string]string{string(env.MedianPluginCmd): ""} - medianPlugin := map[string]string{string(env.MedianPluginCmd): "chainlink-feeds"} + noMedianPlugin := map[string]string{string(env.MedianPlugin.Cmd): ""} + medianPlugin := map[string]string{string(env.MedianPlugin.Cmd): "chainlink-feeds"} for _, test := range []struct { name string env map[string]string diff --git a/plugins/utils.go b/plugins/cmd.go similarity index 73% rename from plugins/utils.go rename to plugins/cmd.go index 5e5e4142e86..9a312c53882 100644 --- a/plugins/utils.go +++ b/plugins/cmd.go @@ -7,8 +7,9 @@ import ( // CmdConfig is configuration used to register the LOOP and generate an exec type CmdConfig struct { - ID string // unique string used by the node to track the LOOP. typically supplied by the loop logger name - Cmd string // string value of executable to exec + ID string // unique string used by the node to track the LOOP. typically supplied by the loop logger name + Cmd string // string value of executable to exec + Env []string // environment variables as described in [exec.Cmd.Env] } // NewCmdFactory is helper to ensure synchronization between the loop registry and os cmd to exec the LOOP @@ -19,6 +20,7 @@ func NewCmdFactory(register func(id string) (*RegisteredLoop, error), lcfg CmdCo } return func() *exec.Cmd { cmd := exec.Command(lcfg.Cmd) //#nosec G204 -- we control the value of the cmd so the lint/sec error is a false positive + cmd.Env = append(cmd.Env, lcfg.Env...) cmd.Env = append(cmd.Env, registeredLoop.EnvCfg.AsCmdEnv()...) return cmd }, nil diff --git a/plugins/cmd_test.go b/plugins/cmd_test.go new file mode 100644 index 00000000000..8068977314a --- /dev/null +++ b/plugins/cmd_test.go @@ -0,0 +1,51 @@ +package plugins + +import ( + "fmt" + "strings" + "testing" + + "github.com/smartcontractkit/chainlink-common/pkg/loop" +) + +func TestNewCmdFactory_RegisterSuccess(t *testing.T) { + mockRegister := func(id string) (*RegisteredLoop, error) { + return &RegisteredLoop{EnvCfg: loop.EnvConfig{}}, nil + } + + cmdConfig := CmdConfig{ + ID: "test-loop", + Cmd: "echo", + Env: []string{"TEST_ENV=1"}, + } + + cmdFactory, err := NewCmdFactory(mockRegister, cmdConfig) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + cmd := cmdFactory() + if cmd.Args[0] != "echo" { + t.Errorf("Expected command 'echo', got %s", cmd.Args[0]) + } +} + +func TestNewCmdFactory_RegisterFail(t *testing.T) { + mockRegister := func(id string) (*RegisteredLoop, error) { + return nil, fmt.Errorf("registration failed") + } + + cmdConfig := CmdConfig{ + ID: "test-loop", + Cmd: "echo", + Env: []string{"TEST_ENV=1"}, + } + + _, err := NewCmdFactory(mockRegister, cmdConfig) + if err == nil { + t.Fatal("Expected error, got nil") + } + if !strings.Contains(err.Error(), "failed to register") { + t.Errorf("Unexpected error message: %v", err) + } +} diff --git a/plugins/env.go b/plugins/env.go new file mode 100644 index 00000000000..016a4e862d8 --- /dev/null +++ b/plugins/env.go @@ -0,0 +1,31 @@ +package plugins + +import ( + "os" + + "github.com/hashicorp/go-envparse" +) + +// ParseEnvFile returns a slice of key/value pairs parsed from the file at filepath. +// As a special case, empty filepath returns nil without error. +func ParseEnvFile(filepath string) ([]string, error) { + if filepath == "" { + return nil, nil + } + f, err := os.Open(filepath) + if err != nil { + return nil, err + } + defer func() { + _ = f.Close() + }() + m, err := envparse.Parse(f) + if err != nil { + return nil, err + } + r := make([]string, 0, len(m)) + for k, v := range m { + r = append(r, k+"="+v) + } + return r, nil +} diff --git a/plugins/env_test.go b/plugins/env_test.go new file mode 100644 index 00000000000..6dd171b6cc0 --- /dev/null +++ b/plugins/env_test.go @@ -0,0 +1,25 @@ +package plugins + +import ( + _ "embed" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseEnvFile(t *testing.T) { + t.Run("valid", func(t *testing.T) { + got, err := ParseEnvFile("testdata/valid.env") + require.NoError(t, err) + require.Equal(t, []string{"GOMEMLIMIT=1MiB"}, got) + }) + t.Run("invalid", func(t *testing.T) { + _, err := ParseEnvFile("testdata/invalid.env") + require.Error(t, err) + }) + t.Run("missing", func(t *testing.T) { + _, err := ParseEnvFile("testdata/missing.env") + require.ErrorIs(t, err, os.ErrNotExist) + }) +} diff --git a/plugins/loop_registry.go b/plugins/loop_registry.go index 7a5274803d6..a2fcd8ef379 100644 --- a/plugins/loop_registry.go +++ b/plugins/loop_registry.go @@ -2,19 +2,18 @@ package plugins import ( "errors" + "fmt" "sort" "sync" + "github.com/hashicorp/consul/sdk/freeport" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/config" ) -const ( - pluginDefaultPort = 2112 -) - var ErrExists = errors.New("plugin already registered") type RegisteredLoop struct { @@ -49,8 +48,14 @@ func (m *LoopRegistry) Register(id string) (*RegisteredLoop, error) { if _, exists := m.registry[id]; exists { return nil, ErrExists } - nextPort := pluginDefaultPort + len(m.registry) - envCfg := loop.EnvConfig{PrometheusPort: nextPort} + ports, err := freeport.Take(1) + if err != nil { + return nil, fmt.Errorf("failed to get free port: %v", err) + } + if len(ports) != 1 { + return nil, fmt.Errorf("failed to get free port: no ports returned") + } + envCfg := loop.EnvConfig{PrometheusPort: ports[0]} if m.cfgTracing != nil { envCfg.TracingEnabled = m.cfgTracing.Enabled() diff --git a/plugins/config.go b/plugins/registrar.go similarity index 77% rename from plugins/config.go rename to plugins/registrar.go index 01574d82099..90300b738b6 100644 --- a/plugins/config.go +++ b/plugins/registrar.go @@ -8,7 +8,7 @@ import ( // RegistrarConfig generates contains static configuration inher type RegistrarConfig interface { - RegisterLOOP(loopId string, cmdName string) (func() *exec.Cmd, loop.GRPCOpts, error) + RegisterLOOP(config CmdConfig) (func() *exec.Cmd, loop.GRPCOpts, error) } type registarConfig struct { @@ -27,11 +27,8 @@ func NewRegistrarConfig(grpcOpts loop.GRPCOpts, loopRegistrationFn func(loopId s } // RegisterLOOP calls the configured loopRegistrationFn. The loopRegistrationFn must act as a global registry for LOOPs and must be idempotent. -func (pc *registarConfig) RegisterLOOP(loopID string, cmdName string) (func() *exec.Cmd, loop.GRPCOpts, error) { - cmdFn, err := NewCmdFactory(pc.loopRegistrationFn, CmdConfig{ - ID: loopID, - Cmd: cmdName, - }) +func (pc *registarConfig) RegisterLOOP(cfg CmdConfig) (func() *exec.Cmd, loop.GRPCOpts, error) { + cmdFn, err := NewCmdFactory(pc.loopRegistrationFn, cfg) if err != nil { return nil, loop.GRPCOpts{}, err } diff --git a/plugins/testdata/invalid.env b/plugins/testdata/invalid.env new file mode 100644 index 00000000000..b2f2407bccf --- /dev/null +++ b/plugins/testdata/invalid.env @@ -0,0 +1,2 @@ +FOO BAR +Baz: "Value" diff --git a/plugins/testdata/valid.env b/plugins/testdata/valid.env new file mode 100644 index 00000000000..5a73d037c60 --- /dev/null +++ b/plugins/testdata/valid.env @@ -0,0 +1 @@ +GOMEMLIMIT=1MiB From 59d1c9954662200f787968ce7d89c53036e33dd0 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 24 Jan 2024 08:26:50 -0800 Subject: [PATCH 201/234] support customized block number for conditional (#11804) --- core/scripts/chaincli/DEBUGGING.md | 4 ++-- core/scripts/chaincli/handler/debug.go | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/core/scripts/chaincli/DEBUGGING.md b/core/scripts/chaincli/DEBUGGING.md index 703261fcb54..6466a40fc31 100644 --- a/core/scripts/chaincli/DEBUGGING.md +++ b/core/scripts/chaincli/DEBUGGING.md @@ -41,10 +41,10 @@ For detailed transaction simulation logs, set up Tenderly credentials. Refer to Execute the following command based on your upkeep type: -- For conditional upkeep: +- For conditional upkeep, if a block number is given we use that block, otherwise we use the latest block: ```bash - go run main.go keeper debug UPKEEP_ID + go run main.go keeper debug UPKEEP_ID [OPTIONAL BLOCK_NUMBER] ``` - For log trigger upkeep: diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 2c97adace26..8b06937fc2c 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -68,8 +68,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } chainID := chainIDBig.Int64() - var triggerCallOpts *bind.CallOpts // use latest block for conditionals, but use block from tx for log triggers - latestCallOpts := &bind.CallOpts{Context: ctx} // always use latest block + // Log triggers: always use block from tx + // Conditional: use latest block if no block number is provided, otherwise use block from user input + var triggerCallOpts *bind.CallOpts // use a certain block + latestCallOpts := &bind.CallOpts{Context: ctx} // use the latest block // connect to registry contract registryAddress := gethcommon.HexToAddress(k.cfg.RegistryAddress) @@ -139,8 +141,21 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { // check upkeep if triggerType == ConditionTrigger { message("upkeep identified as conditional trigger") + + if len(args) > 1 { + // if a block number is provided, use that block for both checkUpkeep and simulatePerformUpkeep + blockNum, err = strconv.ParseUint(args[1], 10, 64) + if err != nil { + failCheckArgs("unable to parse block number", err) + } + triggerCallOpts = &bind.CallOpts{Context: ctx, BlockNumber: new(big.Int).SetUint64(blockNum)} + } else { + // if no block number is provided, use latest block for both checkUpkeep and simulatePerformUpkeep + triggerCallOpts = latestCallOpts + } + var tmpCheckResult iregistry21.CheckUpkeep0 - tmpCheckResult, err = keeperRegistry21.CheckUpkeep0(latestCallOpts, upkeepID) + tmpCheckResult, err = keeperRegistry21.CheckUpkeep0(triggerCallOpts, upkeepID) if err != nil { failUnknown("failed to check upkeep: ", err) } @@ -251,11 +266,12 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { resolveIneligible(fmt.Sprintf("invalid trigger type: %d", triggerType)) } upkeepNeeded, performData = checkResult.UpkeepNeeded, checkResult.PerformData - // handle streams lookup + if checkResult.UpkeepFailureReason != 0 { message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %s", getCheckUpkeepFailureReason(checkResult.UpkeepFailureReason))) } + // handle data streams lookup if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { mc := &types2.MercuryCredentials{LegacyURL: k.cfg.DataStreamsLegacyURL, URL: k.cfg.DataStreamsURL, Username: k.cfg.DataStreamsID, Password: k.cfg.DataStreamsKey} mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) From 1022aa078c0bb5cdfe8c25a24c54a1e445a15176 Mon Sep 17 00:00:00 2001 From: Gabriel Paradiso Date: Wed, 24 Jan 2024 17:39:54 +0100 Subject: [PATCH 202/234] [FUN-877] persist data fetched from allowlist contract (#11648) * feat: add allowlist cache layer migration * feat: add getAllowedSendersInRange and getAllowedSendersCount methods for Tos * feat: iterate over allowlist contract response, load and update allowlist * feat: reduce gas by removing mapping and using EnumerableSet .length() and .at() methods * feat: implement orm level for allowlist * fix: fix iteration loop and add tests * fix: correct var naming, zero indexing array, doc * chore: rename cache to stored, split batchSize onchain vs stored * fix: remove id from allowlist primary key, use it only for ordering * feat: store allowlist in batches * chore: correct CreateAllowedSenders naming * chore: CreateAllowedSenders in main routine * fix: add a trottle to prevent RPC rate limiting * chore: split orm in subscriptions and allowlist pkgs * feat: put the new functionality under a feature flag * fix: fix subscriptions consumers address * fix: store only on feature enable to prevent blocked addresses not being sync * chore: rename CachedSubscriptions to StoredSubscriptions * feat: implement GetBlockedSendersInRange and GetBlockedSendersCount in tos contract * feat: implement sync of blocked senders with allowed * chore: add checks and table constraints * chore: methods renaming * chore: correct log and var name --- .../gas-snapshots/functions.gas-snapshot | 150 +++++++------- .../accessControl/TermsOfServiceAllowList.sol | 63 +++++- .../interfaces/ITermsOfServiceAllowList.sol | 32 +++ .../FunctionsTermsOfServiceAllowList.t.sol | 138 +++++++++++++ .../functions_allow_list.go | 100 ++++++++- ...rapper-dependency-versions-do-not-edit.txt | 2 +- core/services/functions/connector_handler.go | 8 +- .../functions/connector_handler_test.go | 7 +- .../functions/{ => allowlist}/allowlist.go | 167 ++++++++++++++- .../functions/allowlist/allowlist_test.go | 184 +++++++++++++++++ .../mocks/onchain_allowlist.go | 0 .../handlers/functions/allowlist/mocks/orm.go | 116 +++++++++++ .../handlers/functions/allowlist/orm.go | 123 ++++++++++++ .../handlers/functions/allowlist/orm_test.go | 190 ++++++++++++++++++ .../handlers/functions/allowlist_test.go | 107 ---------- .../handlers/functions/handler.functions.go | 31 +-- .../functions/handler.functions_test.go | 9 +- .../mocks/onchain_subscriptions.go | 0 .../{ => subscriptions}/mocks/orm.go | 19 +- .../functions/{ => subscriptions}/orm.go | 49 +++-- .../functions/{ => subscriptions}/orm_test.go | 49 +++-- .../{ => subscriptions}/subscriptions.go | 30 +-- .../{ => subscriptions}/subscriptions_test.go | 42 ++-- .../{ => subscriptions}/user_subscriptions.go | 6 +- .../user_subscriptions_test.go | 16 +- .../ocr2/plugins/functions/config/config.go | 59 +++--- .../services/ocr2/plugins/functions/plugin.go | 17 +- .../ocr2/plugins/functions/plugin_test.go | 11 +- .../migrations/0221_functions_allowlist.sql | 22 ++ 29 files changed, 1392 insertions(+), 355 deletions(-) rename core/services/gateway/handlers/functions/{ => allowlist}/allowlist.go (51%) create mode 100644 core/services/gateway/handlers/functions/allowlist/allowlist_test.go rename core/services/gateway/handlers/functions/{ => allowlist}/mocks/onchain_allowlist.go (100%) create mode 100644 core/services/gateway/handlers/functions/allowlist/mocks/orm.go create mode 100644 core/services/gateway/handlers/functions/allowlist/orm.go create mode 100644 core/services/gateway/handlers/functions/allowlist/orm_test.go delete mode 100644 core/services/gateway/handlers/functions/allowlist_test.go rename core/services/gateway/handlers/functions/{ => subscriptions}/mocks/onchain_subscriptions.go (100%) rename core/services/gateway/handlers/functions/{ => subscriptions}/mocks/orm.go (73%) rename core/services/gateway/handlers/functions/{ => subscriptions}/orm.go (73%) rename core/services/gateway/handlers/functions/{ => subscriptions}/orm_test.go (81%) rename core/services/gateway/handlers/functions/{ => subscriptions}/subscriptions.go (90%) rename core/services/gateway/handlers/functions/{ => subscriptions}/subscriptions_test.go (89%) rename core/services/gateway/handlers/functions/{ => subscriptions}/user_subscriptions.go (95%) rename core/services/gateway/handlers/functions/{ => subscriptions}/user_subscriptions_test.go (92%) create mode 100644 core/store/migrate/migrations/0221_functions_allowlist.sql diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index d0893969bff..39efc0d10ea 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14579333) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14579311) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14579327) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14590747) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14590724) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14590696) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14590647) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14590636) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14590680) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14805978) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14805956) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14805972) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14817392) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14817369) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14817341) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14817292) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14817281) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14817325) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -28,7 +28,7 @@ FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 38251) FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8810) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_RevertIfInvalidCommitment() (gas: 13302) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_Success() (gas: 180763) -FunctionsBilling__StartBilling:test__FulfillAndBill_HasUniqueGlobalRequestId() (gas: 398312) +FunctionsBilling__StartBilling:test__FulfillAndBill_HasUniqueGlobalRequestId() (gas: 398400) FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 497786) FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 198990) @@ -61,7 +61,7 @@ FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 280 FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 151496) FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 321059) FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 334680) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2509962) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2510006) FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 540441) FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) @@ -82,17 +82,17 @@ FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfLengt FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotNewContract() (gas: 19048) FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotOwner() (gas: 23392) FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_Success() (gas: 118479) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfConsumerNotAllowed() (gas: 59347) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfConsumerNotAllowed() (gas: 59391) FunctionsRouter_SendRequest:test_SendRequest_RevertIfDuplicateRequestId() (gas: 193436) FunctionsRouter_SendRequest:test_SendRequest_RevertIfEmptyData() (gas: 29426) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfIncorrectDonId() (gas: 57925) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 186932) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfIncorrectDonId() (gas: 57904) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 187020) FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 50947) FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidDonId() (gas: 25082) FunctionsRouter_SendRequest:test_SendRequest_RevertIfNoSubscription() (gas: 29132) FunctionsRouter_SendRequest:test_SendRequest_RevertIfPaused() (gas: 34291) FunctionsRouter_SendRequest:test_SendRequest_Success() (gas: 286243) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfConsumerNotAllowed() (gas: 65843) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfConsumerNotAllowed() (gas: 65887) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfEmptyData() (gas: 36012) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfIncorrectDonId() (gas: 29896) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalidCallbackGasLimit() (gas: 57533) @@ -100,7 +100,7 @@ FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalid FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfNoSubscription() (gas: 35717) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfPaused() (gas: 40810) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_Success() (gas: 292812) -FunctionsRouter_SendRequestToProposed:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 193424) +FunctionsRouter_SendRequestToProposed:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 193512) FunctionsRouter_SetAllowListId:test_SetAllowListId_Success() (gas: 30688) FunctionsRouter_SetAllowListId:test_UpdateConfig_RevertIfNotOwner() (gas: 13403) FunctionsRouter_Unpause:test_Unpause_RevertIfNotOwner() (gas: 13293) @@ -109,30 +109,30 @@ FunctionsRouter_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 24437) FunctionsRouter_UpdateConfig:test_UpdateConfig_Success() (gas: 60676) FunctionsRouter_UpdateContracts:test_UpdateContracts_RevertIfNotOwner() (gas: 13336) FunctionsRouter_UpdateContracts:test_UpdateContracts_Success() (gas: 38732) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 60326) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfPaused() (gas: 60987) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderBecomesBlocked() (gas: 94677) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderIsNotNewOwner() (gas: 62693) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_Success() (gas: 215197) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumers() (gas: 137893) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumersAfterConfigUpdate() (gas: 164837) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 60414) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfPaused() (gas: 61031) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderBecomesBlocked() (gas: 139404) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderIsNotNewOwner() (gas: 62781) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_Success() (gas: 215285) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumers() (gas: 138025) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumersAfterConfigUpdate() (gas: 164969) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNoSubscription() (gas: 12946) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotAllowedSender() (gas: 57809) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotSubscriptionOwner() (gas: 87177) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotAllowedSender() (gas: 102448) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotSubscriptionOwner() (gas: 87199) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfPaused() (gas: 18094) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_Success() (gas: 95480) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_Success() (gas: 95524) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNoSubscription() (gas: 15041) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotAllowedSender() (gas: 57885) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89287) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotAllowedSender() (gas: 102524) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89309) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPaused() (gas: 20148) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 194325) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitAllBalanceAsDeposit() (gas: 114506) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitSomeBalanceAsDeposit() (gas: 125832) -FunctionsSubscriptions_CancelSubscription_ReceiveDeposit:test_CancelSubscription_SuccessRecieveDeposit() (gas: 74973) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 194369) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitAllBalanceAsDeposit() (gas: 114541) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitSomeBalanceAsDeposit() (gas: 125867) +FunctionsSubscriptions_CancelSubscription_ReceiveDeposit:test_CancelSubscription_SuccessRecieveDeposit() (gas: 75017) FunctionsSubscriptions_Constructor:test_Constructor_Success() (gas: 7654) -FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfNotAllowedSender() (gas: 28660) +FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfNotAllowedSender() (gas: 28704) FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfPaused() (gas: 17994) -FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_Success() (gas: 351726) +FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_Success() (gas: 351858) FunctionsSubscriptions_GetConsumer:test_GetConsumer_Success() (gas: 16226) FunctionsSubscriptions_GetFlags:test_GetFlags_SuccessInvalidSubscription() (gas: 13101) FunctionsSubscriptions_GetFlags:test_GetFlags_SuccessValidSubscription() (gas: 40903) @@ -168,23 +168,23 @@ FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessPaysRecipient() ( FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessSetsBalanceToZero() (gas: 37790) FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessFalseIfNoPendingRequests() (gas: 14981) FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 176494) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfEmptyNewOwner() (gas: 27611) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfInvalidNewOwner() (gas: 57709) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfEmptyNewOwner() (gas: 27655) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfInvalidNewOwner() (gas: 57797) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNoSubscription() (gas: 15001) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 75131) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 119770) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotSubscriptionOwner() (gas: 17960) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfPaused() (gas: 20128) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_Success() (gas: 68196) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_SuccessChangeProposedOwner() (gas: 82749) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_Success() (gas: 68240) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_SuccessChangeProposedOwner() (gas: 82837) FunctionsSubscriptions_RecoverFunds:test_OwnerCancelSubscription_RevertIfNotOwner() (gas: 15554) FunctionsSubscriptions_RecoverFunds:test_RecoverFunds_Success() (gas: 41111) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfInvalidConsumer() (gas: 30260) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfInvalidConsumer() (gas: 30304) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNoSubscription() (gas: 15019) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 57800) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87223) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 102439) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87245) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPaused() (gas: 18049) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191858) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_Success() (gas: 41979) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191902) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_Success() (gas: 42023) FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNoSubscription() (gas: 12891) FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNotOwner() (gas: 15684) FunctionsSubscriptions_SetFlags:test_SetFlags_Success() (gas: 35594) @@ -192,34 +192,44 @@ FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertIfPaused() (ga FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertIfTimeoutNotExceeded() (gas: 25261) FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertInvalidRequest() (gas: 28242) FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_Success() (gas: 57754) -FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfNotAllowedSender() (gas: 26390) +FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfNotAllowedSender() (gas: 26434) FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfPaused() (gas: 15759) -FunctionsSubscriptions_createSubscription:test_CreateSubscription_Success() (gas: 152576) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:testAcceptTermsOfService_InvalidSigner_vuln() (gas: 94830) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfAcceptorIsNotSender() (gas: 25837) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfBlockedSender() (gas: 44348) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfInvalidSigner() (gas: 23597) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientContractIsNotSender() (gas: 1866530) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientIsNotSender() (gas: 26003) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForContract() (gas: 1946562) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForSelf() (gas: 103411) -FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_RevertIfNotOwner() (gas: 15469) -FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_Success() (gas: 51794) -FunctionsTermsOfServiceAllowList_Constructor:test_Constructor_Success() (gas: 12187) -FunctionsTermsOfServiceAllowList_GetAllAllowedSenders:test_GetAllAllowedSenders_Success() (gas: 19243) -FunctionsTermsOfServiceAllowList_GetConfig:test_GetConfig_Success() (gas: 15773) -FunctionsTermsOfServiceAllowList_GetMessage:test_GetMessage_Success() (gas: 11583) -FunctionsTermsOfServiceAllowList_HasAccess:test_HasAccess_FalseWhenEnabled() (gas: 15925) -FunctionsTermsOfServiceAllowList_HasAccess:test_HasAccess_TrueWhenDisabled() (gas: 23430) -FunctionsTermsOfServiceAllowList_IsBlockedSender:test_IsBlockedSender_SuccessFalse() (gas: 15354) -FunctionsTermsOfServiceAllowList_IsBlockedSender:test_IsBlockedSender_SuccessTrue() (gas: 41957) -FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_RevertIfNotOwner() (gas: 13525) -FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_Success() (gas: 95199) -FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 13727) +FunctionsSubscriptions_createSubscription:test_CreateSubscription_Success() (gas: 152708) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:testAcceptTermsOfService_InvalidSigner_vuln() (gas: 94864) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfAcceptorIsNotSender() (gas: 25859) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfBlockedSender() (gas: 88990) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfInvalidSigner() (gas: 23619) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientContractIsNotSender() (gas: 1866552) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientIsNotSender() (gas: 26025) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForContract() (gas: 1946628) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForSelf() (gas: 103533) +FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_RevertIfNotOwner() (gas: 15491) +FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_Success() (gas: 96662) +FunctionsTermsOfServiceAllowList_Constructor:test_Constructor_Success() (gas: 12253) +FunctionsTermsOfServiceAllowList_GetAllAllowedSenders:test_GetAllAllowedSenders_Success() (gas: 19199) +FunctionsTermsOfServiceAllowList_GetAllowedSendersCount:test_GetAllowedSendersCount_Success() (gas: 12995) +FunctionsTermsOfServiceAllowList_GetAllowedSendersInRange:test_GetAllowedSendersInRange_RevertIfAllowedSendersIsEmpty() (gas: 12184656) +FunctionsTermsOfServiceAllowList_GetAllowedSendersInRange:test_GetAllowedSendersInRange_RevertIfEndIsAfterLastAllowedSender() (gas: 16571) +FunctionsTermsOfServiceAllowList_GetAllowedSendersInRange:test_GetAllowedSendersInRange_RevertIfStartIsAfterEnd() (gas: 13301) +FunctionsTermsOfServiceAllowList_GetAllowedSendersInRange:test_GetAllowedSendersInRange_Success() (gas: 20448) +FunctionsTermsOfServiceAllowList_GetBlockedSendersCount:test_GetBlockedSendersCount_Success() (gas: 12931) +FunctionsTermsOfServiceAllowList_GetBlockedSendersInRange:test_GetBlockedSendersInRange_RevertIfAllowedSendersIsEmpty() (gas: 12184660) +FunctionsTermsOfServiceAllowList_GetBlockedSendersInRange:test_GetBlockedSendersInRange_RevertIfEndIsAfterLastAllowedSender() (gas: 16549) +FunctionsTermsOfServiceAllowList_GetBlockedSendersInRange:test_GetBlockedSendersInRange_RevertIfStartIsAfterEnd() (gas: 13367) +FunctionsTermsOfServiceAllowList_GetBlockedSendersInRange:test_GetBlockedSendersInRange_Success() (gas: 18493) +FunctionsTermsOfServiceAllowList_GetConfig:test_GetConfig_Success() (gas: 15751) +FunctionsTermsOfServiceAllowList_GetMessage:test_GetMessage_Success() (gas: 11593) +FunctionsTermsOfServiceAllowList_HasAccess:test_HasAccess_FalseWhenEnabled() (gas: 15969) +FunctionsTermsOfServiceAllowList_HasAccess:test_HasAccess_TrueWhenDisabled() (gas: 23496) +FunctionsTermsOfServiceAllowList_IsBlockedSender:test_IsBlockedSender_SuccessFalse() (gas: 15445) +FunctionsTermsOfServiceAllowList_IsBlockedSender:test_IsBlockedSender_SuccessTrue() (gas: 86643) +FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_RevertIfNotOwner() (gas: 13502) +FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_Success() (gas: 96216) +FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 13749) FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_Success() (gas: 22073) -Gas_AcceptTermsOfService:test_AcceptTermsOfService_Gas() (gas: 84690) -Gas_AddConsumer:test_AddConsumer_Gas() (gas: 79087) -Gas_CreateSubscription:test_CreateSubscription_Gas() (gas: 73375) +Gas_AcceptTermsOfService:test_AcceptTermsOfService_Gas() (gas: 84702) +Gas_AddConsumer:test_AddConsumer_Gas() (gas: 79131) +Gas_CreateSubscription:test_CreateSubscription_Gas() (gas: 73419) Gas_FundSubscription:test_FundSubscription_Gas() (gas: 38546) Gas_SendRequest:test_SendRequest_MaximumGas() (gas: 979631) Gas_SendRequest:test_SendRequest_MinimumGas() (gas: 157578) \ No newline at end of file diff --git a/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol index 3bb7c7b04cc..3d93a3fef04 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol @@ -20,7 +20,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, string public constant override typeAndVersion = "Functions Terms of Service Allow List v1.1.0"; EnumerableSet.AddressSet private s_allowedSenders; - mapping(address => bool) private s_blockedSenders; + EnumerableSet.AddressSet private s_blockedSenders; event AddedAccess(address user); event BlockedAccess(address user); @@ -29,6 +29,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, error InvalidSignature(); error InvalidUsage(); error RecipientIsBlocked(); + error InvalidCalldata(); TermsOfServiceAllowListConfig private s_config; @@ -70,7 +71,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, /// @inheritdoc ITermsOfServiceAllowList function acceptTermsOfService(address acceptor, address recipient, bytes32 r, bytes32 s, uint8 v) external override { - if (s_blockedSenders[recipient]) { + if (s_blockedSenders.contains(recipient)) { revert RecipientIsBlocked(); } @@ -101,6 +102,32 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, return s_allowedSenders.values(); } + /// @inheritdoc ITermsOfServiceAllowList + function getAllowedSendersCount() external view override returns (uint64) { + return uint64(s_allowedSenders.length()); + } + + /// @inheritdoc ITermsOfServiceAllowList + function getAllowedSendersInRange( + uint64 allowedSenderIdxStart, + uint64 allowedSenderIdxEnd + ) external view override returns (address[] memory allowedSenders) { + if ( + allowedSenderIdxStart > allowedSenderIdxEnd || + allowedSenderIdxEnd >= s_allowedSenders.length() || + s_allowedSenders.length() == 0 + ) { + revert InvalidCalldata(); + } + + allowedSenders = new address[]((allowedSenderIdxEnd - allowedSenderIdxStart) + 1); + for (uint256 i = 0; i <= allowedSenderIdxEnd - allowedSenderIdxStart; ++i) { + allowedSenders[i] = s_allowedSenders.at(uint256(allowedSenderIdxStart + i)); + } + + return allowedSenders; + } + /// @inheritdoc IAccessController function hasAccess(address user, bytes calldata /* data */) external view override returns (bool) { if (!s_config.enabled) { @@ -118,19 +145,45 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, if (!s_config.enabled) { return false; } - return s_blockedSenders[sender]; + return s_blockedSenders.contains(sender); } /// @inheritdoc ITermsOfServiceAllowList function blockSender(address sender) external override onlyOwner { s_allowedSenders.remove(sender); - s_blockedSenders[sender] = true; + s_blockedSenders.add(sender); emit BlockedAccess(sender); } /// @inheritdoc ITermsOfServiceAllowList function unblockSender(address sender) external override onlyOwner { - s_blockedSenders[sender] = false; + s_blockedSenders.remove(sender); emit UnblockedAccess(sender); } + + /// @inheritdoc ITermsOfServiceAllowList + function getBlockedSendersCount() external view override returns (uint64) { + return uint64(s_blockedSenders.length()); + } + + /// @inheritdoc ITermsOfServiceAllowList + function getBlockedSendersInRange( + uint64 blockedSenderIdxStart, + uint64 blockedSenderIdxEnd + ) external view override returns (address[] memory blockedSenders) { + if ( + blockedSenderIdxStart > blockedSenderIdxEnd || + blockedSenderIdxEnd >= s_blockedSenders.length() || + s_blockedSenders.length() == 0 + ) { + revert InvalidCalldata(); + } + + blockedSenders = new address[]((blockedSenderIdxEnd - blockedSenderIdxStart) + 1); + for (uint256 i = 0; i <= blockedSenderIdxEnd - blockedSenderIdxStart; ++i) { + blockedSenders[i] = s_blockedSenders.at(uint256(blockedSenderIdxStart + i)); + } + + return blockedSenders; + } } diff --git a/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol index 209d25c0ab3..65db9c42b69 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol @@ -22,6 +22,22 @@ interface ITermsOfServiceAllowList { /// @return addresses - all allowed addresses function getAllAllowedSenders() external view returns (address[] memory); + /// @notice Get details about the total number of allowed senders + /// @return count - total number of allowed senders in the system + function getAllowedSendersCount() external view returns (uint64); + + /// @notice Retrieve a list of allowed senders using an inclusive range + /// @dev WARNING: getAllowedSendersInRange uses EnumerableSet .length() and .at() methods to iterate over the list + /// without the need for an extra mapping. These method can not guarantee the ordering when new elements are added. + /// Evaluate if eventual consistency will satisfy your usecase before using it. + /// @param allowedSenderIdxStart - index of the allowed sender to start the range at + /// @param allowedSenderIdxEnd - index of the allowed sender to end the range at + /// @return allowedSenders - allowed addresses in the range provided + function getAllowedSendersInRange( + uint64 allowedSenderIdxStart, + uint64 allowedSenderIdxEnd + ) external view returns (address[] memory allowedSenders); + /// @notice Allows access to the sender based on acceptance of the Terms of Service /// @param acceptor - The wallet address that has accepted the Terms of Service on the UI /// @param recipient - The recipient address that the acceptor is taking responsibility for @@ -37,6 +53,22 @@ interface ITermsOfServiceAllowList { /// @notice Re-allows a previously blocked sender to accept the Terms of Service /// @param sender - Address of the sender to unblock function unblockSender(address sender) external; + + /// @notice Get details about the total number of blocked senders + /// @return count - total number of blocked senders in the system + function getBlockedSendersCount() external view returns (uint64); + + /// @notice Retrieve a list of blocked senders using an inclusive range + /// @dev WARNING: getBlockedSendersInRange uses EnumerableSet .length() and .at() methods to iterate over the list + /// without the need for an extra mapping. These method can not guarantee the ordering when new elements are added. + /// Evaluate if eventual consistency will satisfy your usecase before using it. + /// @param blockedSenderIdxStart - index of the blocked sender to start the range at + /// @param blockedSenderIdxEnd - index of the blocked sender to end the range at + /// @return blockedSenders - blocked addresses in the range provided + function getBlockedSendersInRange( + uint64 blockedSenderIdxStart, + uint64 blockedSenderIdxEnd + ) external view returns (address[] memory blockedSenders); } // ================================================================ diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol index 08c981dd4f9..e121f7b881d 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsTermsOfServiceAllowList.t.sol @@ -247,6 +247,69 @@ contract FunctionsTermsOfServiceAllowList_GetAllAllowedSenders is FunctionsOwner } } +/// @notice #getAllowedSendersCount +contract FunctionsTermsOfServiceAllowList_GetAllowedSendersCount is FunctionsOwnerAcceptTermsOfServiceSetup { + function test_GetAllowedSendersCount_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + uint96 allowedSendersCount = s_termsOfServiceAllowList.getAllowedSendersCount(); + // One allowed sender was made during setup + assertEq(allowedSendersCount, 1); + } +} + +/// @notice #getAllowedSendersInRange +contract FunctionsTermsOfServiceAllowList_GetAllowedSendersInRange is FunctionsOwnerAcceptTermsOfServiceSetup { + function test_GetAllowedSendersInRange_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + address[] memory expectedSenders = new address[](1); + expectedSenders[0] = OWNER_ADDRESS; + + assertEq(s_termsOfServiceAllowList.getAllowedSendersInRange(0, 0), expectedSenders); + } + + function test_GetAllowedSendersInRange_RevertIfAllowedSendersIsEmpty() public { + // setup a new empty s_termsOfServiceAllowList + FunctionsRoutesSetup.setUp(); + + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + uint64 AllowedSendersCount = s_termsOfServiceAllowList.getAllowedSendersCount(); + uint64 expected = 0; + assertEq(AllowedSendersCount, expected); + + vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector); + s_termsOfServiceAllowList.getAllowedSendersInRange(0, 0); + } + + function test_GetAllowedSendersInRange_RevertIfStartIsAfterEnd() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector); + + s_termsOfServiceAllowList.getAllowedSendersInRange(1, 0); + } + + function test_GetAllowedSendersInRange_RevertIfEndIsAfterLastAllowedSender() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + uint64 AllowedSendersCount = s_termsOfServiceAllowList.getAllowedSendersCount(); + vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector); + s_termsOfServiceAllowList.getAllowedSendersInRange(1, AllowedSendersCount + 1); + } +} + /// @notice #hasAccess contract FunctionsTermsOfServiceAllowList_HasAccess is FunctionsRoutesSetup { function test_HasAccess_FalseWhenEnabled() public { @@ -373,3 +436,78 @@ contract FunctionsTermsOfServiceAllowList_UnblockSender is FunctionsRoutesSetup s_termsOfServiceAllowList.acceptTermsOfService(STRANGER_ADDRESS, STRANGER_ADDRESS, r, s, v); } } + +/// @notice #getBlockedSendersCount +contract FunctionsTermsOfServiceAllowList_GetBlockedSendersCount is FunctionsRoutesSetup { + function setUp() public virtual override { + FunctionsRoutesSetup.setUp(); + + s_termsOfServiceAllowList.blockSender(STRANGER_ADDRESS); + } + + function test_GetBlockedSendersCount_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + uint96 blockedSendersCount = s_termsOfServiceAllowList.getBlockedSendersCount(); + // One blocked sender was made during setup + assertEq(blockedSendersCount, 1); + } +} + +/// @notice #getBlockedSendersInRange +contract FunctionsTermsOfServiceAllowList_GetBlockedSendersInRange is FunctionsRoutesSetup { + function setUp() public virtual override { + FunctionsRoutesSetup.setUp(); + + s_termsOfServiceAllowList.blockSender(STRANGER_ADDRESS); + } + + function test_GetBlockedSendersInRange_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + address[] memory expectedBlockedSenders = new address[](1); + expectedBlockedSenders[0] = STRANGER_ADDRESS; + + assertEq(s_termsOfServiceAllowList.getBlockedSendersInRange(0, 0), expectedBlockedSenders); + } + + function test_GetBlockedSendersInRange_RevertIfAllowedSendersIsEmpty() public { + // setup a new empty s_termsOfServiceBlockList + FunctionsRoutesSetup.setUp(); + + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + uint64 BlockedSendersCount = s_termsOfServiceAllowList.getBlockedSendersCount(); + uint64 expected = 0; + assertEq(BlockedSendersCount, expected); + + vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector); + s_termsOfServiceAllowList.getBlockedSendersInRange(0, 0); + } + + function test_GetBlockedSendersInRange_RevertIfStartIsAfterEnd() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector); + + s_termsOfServiceAllowList.getBlockedSendersInRange(1, 0); + } + + function test_GetBlockedSendersInRange_RevertIfEndIsAfterLastAllowedSender() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + uint64 BlockedSendersCount = s_termsOfServiceAllowList.getBlockedSendersCount(); + vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector); + s_termsOfServiceAllowList.getBlockedSendersInRange(1, BlockedSendersCount + 1); + } +} diff --git a/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go b/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go index b0c4894f14c..ff15cc75d41 100644 --- a/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go +++ b/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go @@ -36,8 +36,8 @@ type TermsOfServiceAllowListConfig struct { } var TermsOfServiceAllowListMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidUsage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RecipientIsBlocked\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"AddedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"BlockedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"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\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"UnblockedAccess\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"}],\"name\":\"acceptTermsOfService\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"blockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAllowedSenders\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"getMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"hasAccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"isBlockedSender\",\"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\":\"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\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"unblockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60806040523480156200001157600080fd5b50604051620012c9380380620012c9833981016040819052620000349162000269565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d9565b505050620000d2816200018460201b60201c565b50620002ea565b336001600160a01b03821603620001335760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200018e6200020b565b805160058054602080850180516001600160a81b0319909316941515610100600160a81b03198116959095176101006001600160a01b039485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910160405180910390a150565b6000546001600160a01b03163314620002675760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b6000604082840312156200027c57600080fd5b604080519081016001600160401b0381118282101715620002ad57634e487b7160e01b600052604160045260246000fd5b60405282518015158114620002c157600080fd5b815260208301516001600160a01b0381168114620002de57600080fd5b60208201529392505050565b610fcf80620002fa6000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806382184c7b1161008c578063a39b06e311610066578063a39b06e3146101b8578063a5e1d61d146101d9578063c3f909d4146101ec578063f2fde38b1461024b57600080fd5b806382184c7b1461016a57806389f9a2c41461017d5780638da5cb5b1461019057600080fd5b80636b14daf8116100bd5780636b14daf81461012a57806379ba50971461014d578063817ef62e1461015557600080fd5b8063181f5a77146100e45780633908c4d41461010257806347663acb14610117575b600080fd5b6100ec61025e565b6040516100f99190610c54565b60405180910390f35b610115610110366004610ce9565b61027a565b005b610115610125366004610d4a565b6104f5565b61013d610138366004610d65565b610580565b60405190151581526020016100f9565b6101156105aa565b61015d6106ac565b6040516100f99190610de8565b610115610178366004610d4a565b6106bd565b61011561018b366004610e42565b610750565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6101cb6101c6366004610ecb565b61080b565b6040519081526020016100f9565b61013d6101e7366004610d4a565b610869565b60408051808201825260008082526020918201528151808301835260055460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff6101009092048216928401928352845190815291511691810191909152016100f9565b610115610259366004610d4a565b6108aa565b6040518060600160405280602c8152602001610f97602c913981565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602052604090205460ff16156102da576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006102e6868661080b565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206005546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156103c0573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614610417576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061045c57503373ffffffffffffffffffffffffffffffffffffffff87161480159061045c5750333b155b15610493576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61049e6002866108be565b156104ed5760405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a15b505050505050565b6104fd6108e0565b73ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b91015b60405180910390a150565b60055460009060ff16610595575060016105a3565b6105a0600285610963565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610630576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606106b86002610992565b905090565b6106c56108e0565b6106d060028261099f565b5073ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19101610575565b6107586108e0565b805160058054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a9101610575565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b1660348201526000906048016040516020818303038152906040528051906020012090505b92915050565b60055460009060ff1661087e57506000919050565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205460ff1690565b6108b26108e0565b6108bb816109c1565b50565b60006105a38373ffffffffffffffffffffffffffffffffffffffff8416610ab6565b60005473ffffffffffffffffffffffffffffffffffffffff163314610961576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610627565b565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156105a3565b606060006105a383610b05565b60006105a38373ffffffffffffffffffffffffffffffffffffffff8416610b61565b3373ffffffffffffffffffffffffffffffffffffffff821603610a40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610627565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054610afd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610863565b506000610863565b606081600001805480602002602001604051908101604052809291908181526020018280548015610b5557602002820191906000526020600020905b815481526020019060010190808311610b41575b50505050509050919050565b60008181526001830160205260408120548015610c4a576000610b85600183610efe565b8554909150600090610b9990600190610efe565b9050818114610bfe576000866000018281548110610bb957610bb9610f38565b9060005260206000200154905080876000018481548110610bdc57610bdc610f38565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610c0f57610c0f610f67565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610863565b6000915050610863565b600060208083528351808285015260005b81811015610c8157858101830151858201604001528201610c65565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ce457600080fd5b919050565b600080600080600060a08688031215610d0157600080fd5b610d0a86610cc0565b9450610d1860208701610cc0565b93506040860135925060608601359150608086013560ff81168114610d3c57600080fd5b809150509295509295909350565b600060208284031215610d5c57600080fd5b6105a382610cc0565b600080600060408486031215610d7a57600080fd5b610d8384610cc0565b9250602084013567ffffffffffffffff80821115610da057600080fd5b818601915086601f830112610db457600080fd5b813581811115610dc357600080fd5b876020828501011115610dd557600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b81811015610e3657835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610e04565b50909695505050505050565b600060408284031215610e5457600080fd5b6040516040810181811067ffffffffffffffff82111715610e9e577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358015158114610eb157600080fd5b8152610ebf60208401610cc0565b60208201529392505050565b60008060408385031215610ede57600080fd5b610ee783610cc0565b9150610ef560208401610cc0565b90509250929050565b81810381811115610863577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e312e30a164736f6c6343000813000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidUsage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RecipientIsBlocked\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"AddedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"BlockedAccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"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\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"UnblockedAccess\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"}],\"name\":\"acceptTermsOfService\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"blockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAllowedSenders\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowedSendersCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"allowedSenderIdxStart\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"allowedSenderIdxEnd\",\"type\":\"uint64\"}],\"name\":\"getAllowedSendersInRange\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"allowedSenders\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockedSendersCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockedSenderIdxStart\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockedSenderIdxEnd\",\"type\":\"uint64\"}],\"name\":\"getBlockedSendersInRange\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"blockedSenders\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"acceptor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"getMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"hasAccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"isBlockedSender\",\"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\":\"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\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"unblockSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"signerPublicKey\",\"type\":\"address\"}],\"internalType\":\"structTermsOfServiceAllowListConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040523480156200001157600080fd5b506040516200173438038062001734833981016040819052620000349162000269565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d9565b505050620000d2816200018460201b60201c565b50620002ea565b336001600160a01b03821603620001335760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200018e6200020b565b805160068054602080850180516001600160a81b0319909316941515610100600160a81b03198116959095176101006001600160a01b039485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910160405180910390a150565b6000546001600160a01b03163314620002675760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b6000604082840312156200027c57600080fd5b604080519081016001600160401b0381118282101715620002ad57634e487b7160e01b600052604160045260246000fd5b60405282518015158114620002c157600080fd5b815260208301516001600160a01b0381168114620002de57600080fd5b60208201529392505050565b61143a80620002fa6000396000f3fe608060405234801561001057600080fd5b506004361061011b5760003560e01c8063817ef62e116100b2578063a39b06e311610081578063c3f909d411610066578063c3f909d4146102c3578063cc7ebf4914610322578063f2fde38b1461032a57600080fd5b8063a39b06e314610237578063a5e1d61d146102b057600080fd5b8063817ef62e146101e157806382184c7b146101e957806389f9a2c4146101fc5780638da5cb5b1461020f57600080fd5b80633908c4d4116100ee5780633908c4d41461018e57806347663acb146101a35780636b14daf8146101b657806379ba5097146101d957600080fd5b806301a05958146101205780630a8c9c2414610146578063181f5a771461016657806320229a861461017b575b600080fd5b61012861033d565b60405167ffffffffffffffff90911681526020015b60405180910390f35b610159610154366004610fd6565b61034e565b60405161013d9190611009565b61016e6104bc565b60405161013d9190611063565b610159610189366004610fd6565b6104d8565b6101a161019c3660046110f3565b61063e565b005b6101a16101b1366004611154565b6108e9565b6101c96101c436600461116f565b61094a565b604051901515815260200161013d565b6101a1610974565b610159610a76565b6101a16101f7366004611154565b610a82565b6101a161020a366004611221565b610ae8565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161013d565b6102a26102453660046112aa565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b16603482015260009060480160405160208183030381529060405280519060200120905092915050565b60405190815260200161013d565b6101c96102be366004611154565b610ba3565b60408051808201825260008082526020918201528151808301835260065460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff61010090920482169284019283528451908152915116918101919091520161013d565b610128610bc3565b6101a1610338366004611154565b610bcf565b60006103496004610be3565b905090565b60608167ffffffffffffffff168367ffffffffffffffff16118061038557506103776002610be3565b8267ffffffffffffffff1610155b8061039757506103956002610be3565b155b156103ce576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103d88383611303565b6103e3906001611324565b67ffffffffffffffff1667ffffffffffffffff811115610405576104056111f2565b60405190808252806020026020018201604052801561042e578160200160208202803683370190505b50905060005b61043e8484611303565b67ffffffffffffffff1681116104b45761046d6104658267ffffffffffffffff8716611345565b600290610bed565b82828151811061047f5761047f611358565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526104ad81611387565b9050610434565b505b92915050565b6040518060600160405280602c8152602001611402602c913981565b60608167ffffffffffffffff168367ffffffffffffffff16118061050f57506105016004610be3565b8267ffffffffffffffff1610155b80610521575061051f6004610be3565b155b15610558576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105628383611303565b61056d906001611324565b67ffffffffffffffff1667ffffffffffffffff81111561058f5761058f6111f2565b6040519080825280602002602001820160405280156105b8578160200160208202803683370190505b50905060005b6105c88484611303565b67ffffffffffffffff1681116104b4576105f76105ef8267ffffffffffffffff8716611345565b600490610bed565b82828151811061060957610609611358565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015261063781611387565b90506105be565b610649600485610bf9565b15610680576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051606087811b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009081166020808501919091529188901b16603483015282516028818403018152604890920190925280519101206000906040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206006546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156107b4573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161461080b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061085057503373ffffffffffffffffffffffffffffffffffffffff8716148015906108505750333b155b15610887576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610892600286610c28565b156108e15760405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a15b505050505050565b6108f1610c4a565b6108fc600482610ccd565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b906020015b60405180910390a150565b60065460009060ff1661095f5750600161096d565b61096a600285610bf9565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146109fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606103496002610cef565b610a8a610c4a565b610a95600282610ccd565b50610aa1600482610c28565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19060200161093f565b610af0610c4a565b805160068054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910161093f565b60065460009060ff16610bb857506000919050565b6104b6600483610bf9565b60006103496002610be3565b610bd7610c4a565b610be081610cfc565b50565b60006104b6825490565b600061096d8383610df1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561096d565b600061096d8373ffffffffffffffffffffffffffffffffffffffff8416610e1b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ccb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016109f1565b565b600061096d8373ffffffffffffffffffffffffffffffffffffffff8416610e6a565b6060600061096d83610f5d565b3373ffffffffffffffffffffffffffffffffffffffff821603610d7b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016109f1565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110610e0857610e08611358565b9060005260206000200154905092915050565b6000818152600183016020526040812054610e62575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556104b6565b5060006104b6565b60008181526001830160205260408120548015610f53576000610e8e6001836113bf565b8554909150600090610ea2906001906113bf565b9050818114610f07576000866000018281548110610ec257610ec2611358565b9060005260206000200154905080876000018481548110610ee557610ee5611358565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610f1857610f186113d2565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506104b6565b60009150506104b6565b606081600001805480602002602001604051908101604052809291908181526020018280548015610fad57602002820191906000526020600020905b815481526020019060010190808311610f99575b50505050509050919050565b803567ffffffffffffffff81168114610fd157600080fd5b919050565b60008060408385031215610fe957600080fd5b610ff283610fb9565b915061100060208401610fb9565b90509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561105757835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101611025565b50909695505050505050565b600060208083528351808285015260005b8181101561109057858101830151858201604001528201611074565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610fd157600080fd5b600080600080600060a0868803121561110b57600080fd5b611114866110cf565b9450611122602087016110cf565b93506040860135925060608601359150608086013560ff8116811461114657600080fd5b809150509295509295909350565b60006020828403121561116657600080fd5b61096d826110cf565b60008060006040848603121561118457600080fd5b61118d846110cf565b9250602084013567ffffffffffffffff808211156111aa57600080fd5b818601915086601f8301126111be57600080fd5b8135818111156111cd57600080fd5b8760208285010111156111df57600080fd5b6020830194508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006040828403121561123357600080fd5b6040516040810181811067ffffffffffffffff8211171561127d577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528235801515811461129057600080fd5b815261129e602084016110cf565b60208201529392505050565b600080604083850312156112bd57600080fd5b6112c6836110cf565b9150611000602084016110cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b67ffffffffffffffff8281168282160390808211156104b4576104b46112d4565b67ffffffffffffffff8181168382160190808211156104b4576104b46112d4565b808201808211156104b6576104b66112d4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113b8576113b86112d4565b5060010190565b818103818111156104b6576104b66112d4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e312e30a164736f6c6343000813000a", } var TermsOfServiceAllowListABI = TermsOfServiceAllowListMetaData.ABI @@ -198,6 +198,94 @@ func (_TermsOfServiceAllowList *TermsOfServiceAllowListCallerSession) GetAllAllo return _TermsOfServiceAllowList.Contract.GetAllAllowedSenders(&_TermsOfServiceAllowList.CallOpts) } +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCaller) GetAllowedSendersCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _TermsOfServiceAllowList.contract.Call(opts, &out, "getAllowedSendersCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListSession) GetAllowedSendersCount() (uint64, error) { + return _TermsOfServiceAllowList.Contract.GetAllowedSendersCount(&_TermsOfServiceAllowList.CallOpts) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCallerSession) GetAllowedSendersCount() (uint64, error) { + return _TermsOfServiceAllowList.Contract.GetAllowedSendersCount(&_TermsOfServiceAllowList.CallOpts) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCaller) GetAllowedSendersInRange(opts *bind.CallOpts, allowedSenderIdxStart uint64, allowedSenderIdxEnd uint64) ([]common.Address, error) { + var out []interface{} + err := _TermsOfServiceAllowList.contract.Call(opts, &out, "getAllowedSendersInRange", allowedSenderIdxStart, allowedSenderIdxEnd) + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListSession) GetAllowedSendersInRange(allowedSenderIdxStart uint64, allowedSenderIdxEnd uint64) ([]common.Address, error) { + return _TermsOfServiceAllowList.Contract.GetAllowedSendersInRange(&_TermsOfServiceAllowList.CallOpts, allowedSenderIdxStart, allowedSenderIdxEnd) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCallerSession) GetAllowedSendersInRange(allowedSenderIdxStart uint64, allowedSenderIdxEnd uint64) ([]common.Address, error) { + return _TermsOfServiceAllowList.Contract.GetAllowedSendersInRange(&_TermsOfServiceAllowList.CallOpts, allowedSenderIdxStart, allowedSenderIdxEnd) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCaller) GetBlockedSendersCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _TermsOfServiceAllowList.contract.Call(opts, &out, "getBlockedSendersCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListSession) GetBlockedSendersCount() (uint64, error) { + return _TermsOfServiceAllowList.Contract.GetBlockedSendersCount(&_TermsOfServiceAllowList.CallOpts) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCallerSession) GetBlockedSendersCount() (uint64, error) { + return _TermsOfServiceAllowList.Contract.GetBlockedSendersCount(&_TermsOfServiceAllowList.CallOpts) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCaller) GetBlockedSendersInRange(opts *bind.CallOpts, blockedSenderIdxStart uint64, blockedSenderIdxEnd uint64) ([]common.Address, error) { + var out []interface{} + err := _TermsOfServiceAllowList.contract.Call(opts, &out, "getBlockedSendersInRange", blockedSenderIdxStart, blockedSenderIdxEnd) + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListSession) GetBlockedSendersInRange(blockedSenderIdxStart uint64, blockedSenderIdxEnd uint64) ([]common.Address, error) { + return _TermsOfServiceAllowList.Contract.GetBlockedSendersInRange(&_TermsOfServiceAllowList.CallOpts, blockedSenderIdxStart, blockedSenderIdxEnd) +} + +func (_TermsOfServiceAllowList *TermsOfServiceAllowListCallerSession) GetBlockedSendersInRange(blockedSenderIdxStart uint64, blockedSenderIdxEnd uint64) ([]common.Address, error) { + return _TermsOfServiceAllowList.Contract.GetBlockedSendersInRange(&_TermsOfServiceAllowList.CallOpts, blockedSenderIdxStart, blockedSenderIdxEnd) +} + func (_TermsOfServiceAllowList *TermsOfServiceAllowListCaller) GetConfig(opts *bind.CallOpts) (TermsOfServiceAllowListConfig, error) { var out []interface{} err := _TermsOfServiceAllowList.contract.Call(opts, &out, "getConfig") @@ -1193,6 +1281,14 @@ func (_TermsOfServiceAllowList *TermsOfServiceAllowList) Address() common.Addres type TermsOfServiceAllowListInterface interface { GetAllAllowedSenders(opts *bind.CallOpts) ([]common.Address, error) + GetAllowedSendersCount(opts *bind.CallOpts) (uint64, error) + + GetAllowedSendersInRange(opts *bind.CallOpts, allowedSenderIdxStart uint64, allowedSenderIdxEnd uint64) ([]common.Address, error) + + GetBlockedSendersCount(opts *bind.CallOpts) (uint64, error) + + GetBlockedSendersInRange(opts *bind.CallOpts, blockedSenderIdxStart uint64, blockedSenderIdxEnd uint64) ([]common.Address, error) + GetConfig(opts *bind.CallOpts) (TermsOfServiceAllowListConfig, error) GetMessage(opts *bind.CallOpts, acceptor common.Address, recipient common.Address) ([32]byte, error) diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 132f38507ad..16bb718e7c1 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,6 +1,6 @@ GETH_VERSION: 1.13.8 functions: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRequest.bin 3c972870b0afeb6d73a29ebb182f24956a2cebb127b21c4f867d1ecf19a762db -functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.abi ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.bin 6beec092fbb3b619dfe69f1ad23392b0bbaf00327b335e4080f921c7122a57e4 +functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.abi ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServiceAllowList.bin 586696a0cacc0e5112bdd6c99535748a3fbf08c9319360aee868831d38a96d7b functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 18df644c876..c8c522e6a62 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -26,6 +26,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + fallow "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist" + fsub "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" "github.com/smartcontractkit/chainlink/v2/core/services/s4" ) @@ -37,9 +39,9 @@ type functionsConnectorHandler struct { signerKey *ecdsa.PrivateKey nodeAddress string storage s4.Storage - allowlist functions.OnchainAllowlist + allowlist fallow.OnchainAllowlist rateLimiter *hc.RateLimiter - subscriptions functions.OnchainSubscriptions + subscriptions fsub.OnchainSubscriptions minimumBalance assets.Link listener FunctionsListener offchainTransmitter OffchainTransmitter @@ -72,7 +74,7 @@ func InternalId(sender []byte, requestId []byte) RequestID { return RequestID(crypto.Keccak256Hash(append(sender, requestId...)).Bytes()) } -func NewFunctionsConnectorHandler(pluginConfig *config.PluginConfig, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions functions.OnchainSubscriptions, listener FunctionsListener, offchainTransmitter OffchainTransmitter, lggr logger.Logger) (*functionsConnectorHandler, error) { +func NewFunctionsConnectorHandler(pluginConfig *config.PluginConfig, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist fallow.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions fsub.OnchainSubscriptions, listener FunctionsListener, offchainTransmitter OffchainTransmitter, lggr logger.Logger) (*functionsConnectorHandler, error) { if signerKey == nil || storage == nil || allowlist == nil || rateLimiter == nil || subscriptions == nil || listener == nil || offchainTransmitter == nil { return nil, fmt.Errorf("all dependencies must be non-nil") } diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index 9a5a9042693..aadc84ba96a 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -23,7 +23,8 @@ import ( gwconnector "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" gcmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector/mocks" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" - gfmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" + fallowMocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist/mocks" + fsubMocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" "github.com/smartcontractkit/chainlink/v2/core/services/s4" s4mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" @@ -66,9 +67,9 @@ func TestFunctionsConnectorHandler(t *testing.T) { privateKey, addr := testutils.NewPrivateKeyAndAddress(t) storage := s4mocks.NewStorage(t) connector := gcmocks.NewGatewayConnector(t) - allowlist := gfmocks.NewOnchainAllowlist(t) + allowlist := fallowMocks.NewOnchainAllowlist(t) rateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) - subscriptions := gfmocks.NewOnchainSubscriptions(t) + subscriptions := fsubMocks.NewOnchainSubscriptions(t) reportCh := make(chan *functions.OffchainResponse) offchainTransmitter := sfmocks.NewOffchainTransmitter(t) offchainTransmitter.On("ReportChannel", mock.Anything).Return(reportCh) diff --git a/core/services/gateway/handlers/functions/allowlist.go b/core/services/gateway/handlers/functions/allowlist/allowlist.go similarity index 51% rename from core/services/gateway/handlers/functions/allowlist.go rename to core/services/gateway/handlers/functions/allowlist/allowlist.go index 0a18d6e6d87..20dc92ced70 100644 --- a/core/services/gateway/handlers/functions/allowlist.go +++ b/core/services/gateway/handlers/functions/allowlist/allowlist.go @@ -1,4 +1,4 @@ -package functions +package allowlist import ( "context" @@ -22,14 +22,26 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +const ( + defaultStoredAllowlistBatchSize = 1000 + defaultOnchainAllowlistBatchSize = 100 + defaultFetchingDelayInRangeSec = 1 +) + type OnchainAllowlistConfig struct { // ContractAddress is required ContractAddress common.Address `json:"contractAddress"` ContractVersion uint32 `json:"contractVersion"` BlockConfirmations uint `json:"blockConfirmations"` // UpdateFrequencySec can be zero to disable periodic updates - UpdateFrequencySec uint `json:"updateFrequencySec"` - UpdateTimeoutSec uint `json:"updateTimeoutSec"` + UpdateFrequencySec uint `json:"updateFrequencySec"` + UpdateTimeoutSec uint `json:"updateTimeoutSec"` + StoredAllowlistBatchSize uint `json:"storedAllowlistBatchSize"` + OnchainAllowlistBatchSize uint `json:"onchainAllowlistBatchSize"` + // StoreAllowedSendersEnabled is a feature flag that enables storing in db a copy of the allowlist. + StoreAllowedSendersEnabled bool `json:"storeAllowedSendersEnabled"` + // FetchingDelayInRangeSec prevents RPC client being rate limited when fetching the allowlist in ranges. + FetchingDelayInRangeSec uint `json:"fetchingDelayInRangeSec"` } // OnchainAllowlist maintains an allowlist of addresses fetched from the blockchain (EVM-only). @@ -50,6 +62,7 @@ type onchainAllowlist struct { config OnchainAllowlistConfig allowlist atomic.Pointer[map[common.Address]struct{}] + orm ORM client evmclient.Client contractV1 *functions_router.FunctionsRouter blockConfirmations *big.Int @@ -58,7 +71,7 @@ type onchainAllowlist struct { stopCh services.StopChan } -func NewOnchainAllowlist(client evmclient.Client, config OnchainAllowlistConfig, lggr logger.Logger) (OnchainAllowlist, error) { +func NewOnchainAllowlist(client evmclient.Client, config OnchainAllowlistConfig, orm ORM, lggr logger.Logger) (OnchainAllowlist, error) { if client == nil { return nil, errors.New("client is nil") } @@ -68,12 +81,33 @@ func NewOnchainAllowlist(client evmclient.Client, config OnchainAllowlistConfig, if config.ContractVersion != 1 { return nil, fmt.Errorf("unsupported contract version %d", config.ContractVersion) } + + if config.StoredAllowlistBatchSize == 0 { + lggr.Info("StoredAllowlistBatchSize not specified, using default size: ", defaultStoredAllowlistBatchSize) + config.StoredAllowlistBatchSize = defaultStoredAllowlistBatchSize + } + + if config.OnchainAllowlistBatchSize == 0 { + lggr.Info("OnchainAllowlistBatchSize not specified, using default size: ", defaultOnchainAllowlistBatchSize) + config.OnchainAllowlistBatchSize = defaultOnchainAllowlistBatchSize + } + + if config.FetchingDelayInRangeSec == 0 { + lggr.Info("FetchingDelayInRangeSec not specified, using default delay: ", defaultFetchingDelayInRangeSec) + config.FetchingDelayInRangeSec = defaultFetchingDelayInRangeSec + } + + if config.UpdateFrequencySec != 0 && config.FetchingDelayInRangeSec >= config.UpdateFrequencySec { + return nil, fmt.Errorf("to avoid updates overlapping FetchingDelayInRangeSec:%d should be less than UpdateFrequencySec:%d", config.FetchingDelayInRangeSec, config.UpdateFrequencySec) + } + contractV1, err := functions_router.NewFunctionsRouter(config.ContractAddress, client) if err != nil { return nil, fmt.Errorf("unexpected error during functions_router.NewFunctionsRouter: %s", err) } allowlist := &onchainAllowlist{ config: config, + orm: orm, client: client, contractV1: contractV1, blockConfirmations: big.NewInt(int64(config.BlockConfirmations)), @@ -93,6 +127,8 @@ func (a *onchainAllowlist) Start(ctx context.Context) error { return nil } + a.loadStoredAllowedSenderList() + updateOnce := func() { timeoutCtx, cancel := utils.ContextFromChanWithTimeout(a.stopCh, time.Duration(a.config.UpdateTimeoutSec)*time.Second) if err := a.UpdateFromContract(timeoutCtx); err != nil { @@ -172,17 +208,113 @@ func (a *onchainAllowlist) updateFromContractV1(ctx context.Context, blockNum *b if err != nil { return errors.Wrap(err, "unexpected error during functions_allow_list.NewTermsOfServiceAllowList") } - addrList, err := tosContract.GetAllAllowedSenders(&bind.CallOpts{ + + var allowedSenderList []common.Address + if !a.config.StoreAllowedSendersEnabled { + allowedSenderList, err = tosContract.GetAllAllowedSenders(&bind.CallOpts{ + Pending: false, + BlockNumber: blockNum, + Context: ctx, + }) + if err != nil { + return errors.Wrap(err, "error calling GetAllAllowedSenders") + } + } else { + err = a.syncBlockedSenders(ctx, tosContract, blockNum) + if err != nil { + return errors.Wrap(err, "failed to sync the stored allowed and blocked senders") + } + + allowedSenderList, err = a.getAllowedSendersBatched(ctx, tosContract, blockNum) + if err != nil { + return errors.Wrap(err, "failed to get allowed senders in rage") + } + } + + a.update(allowedSenderList) + return nil +} + +func (a *onchainAllowlist) getAllowedSendersBatched(ctx context.Context, tosContract *functions_allow_list.TermsOfServiceAllowList, blockNum *big.Int) ([]common.Address, error) { + allowedSenderList := make([]common.Address, 0) + count, err := tosContract.GetAllowedSendersCount(&bind.CallOpts{ Pending: false, BlockNumber: blockNum, Context: ctx, }) if err != nil { - return errors.Wrap(err, "error calling GetAllAllowedSenders") + return nil, errors.Wrap(err, "unexpected error during functions_allow_list.GetAllowedSendersCount") } - a.update(addrList) + + throttleTicker := time.NewTicker(time.Duration(a.config.FetchingDelayInRangeSec) * time.Second) + for idxStart := uint64(0); idxStart < count; idxStart += uint64(a.config.OnchainAllowlistBatchSize) { + <-throttleTicker.C + + idxEnd := idxStart + uint64(a.config.OnchainAllowlistBatchSize) + if idxEnd >= count { + idxEnd = count - 1 + } + + allowedSendersBatch, err := tosContract.GetAllowedSendersInRange(&bind.CallOpts{ + Pending: false, + BlockNumber: blockNum, + Context: ctx, + }, idxStart, idxEnd) + if err != nil { + return nil, errors.Wrap(err, "error calling GetAllowedSendersInRange") + } + + allowedSenderList = append(allowedSenderList, allowedSendersBatch...) + err = a.orm.CreateAllowedSenders(allowedSendersBatch) + if err != nil { + a.lggr.Errorf("failed to update stored allowedSenderList: %w", err) + } + } + throttleTicker.Stop() + + return allowedSenderList, nil +} + +// syncBlockedSenders fetches the list of blocked addresses from the contract in batches +// and removes the addresses from the functions_allowlist table if present +func (a *onchainAllowlist) syncBlockedSenders(ctx context.Context, tosContract *functions_allow_list.TermsOfServiceAllowList, blockNum *big.Int) error { + count, err := tosContract.GetBlockedSendersCount(&bind.CallOpts{ + Pending: false, + BlockNumber: blockNum, + Context: ctx, + }) + if err != nil { + return errors.Wrap(err, "unexpected error during functions_allow_list.GetBlockedSendersCount") + } + + throttleTicker := time.NewTicker(time.Duration(a.config.FetchingDelayInRangeSec) * time.Second) + for idxStart := uint64(0); idxStart < count; idxStart += uint64(a.config.OnchainAllowlistBatchSize) { + <-throttleTicker.C + + idxEnd := idxStart + uint64(a.config.OnchainAllowlistBatchSize) + if idxEnd >= count { + idxEnd = count - 1 + } + + blockedSendersBatch, err := tosContract.GetBlockedSendersInRange(&bind.CallOpts{ + Pending: false, + BlockNumber: blockNum, + Context: ctx, + }, idxStart, idxEnd) + if err != nil { + return errors.Wrap(err, "error calling GetAllowedSendersInRange") + } + + err = a.orm.DeleteAllowedSenders(blockedSendersBatch) + if err != nil { + a.lggr.Errorf("failed to delete blocked address from allowed list in storage: %w", err) + } + } + throttleTicker.Stop() + return nil } + func (a *onchainAllowlist) update(addrList []common.Address) { newAllowlist := make(map[common.Address]struct{}) for _, addr := range addrList { @@ -191,3 +323,24 @@ func (a *onchainAllowlist) update(addrList []common.Address) { a.allowlist.Store(&newAllowlist) a.lggr.Infow("allowlist updated successfully", "len", len(addrList)) } + +func (a *onchainAllowlist) loadStoredAllowedSenderList() { + allowedList := make([]common.Address, 0) + offset := uint(0) + for { + asBatch, err := a.orm.GetAllowedSenders(offset, a.config.StoredAllowlistBatchSize) + if err != nil { + a.lggr.Errorf("failed to get stored allowed senders: %w", err) + break + } + + allowedList = append(allowedList, asBatch...) + + if len(asBatch) < int(a.config.StoredAllowlistBatchSize) { + break + } + offset += a.config.StoredAllowlistBatchSize + } + + a.update(allowedList) +} diff --git a/core/services/gateway/handlers/functions/allowlist/allowlist_test.go b/core/services/gateway/handlers/functions/allowlist/allowlist_test.go new file mode 100644 index 00000000000..e8cbca80b94 --- /dev/null +++ b/core/services/gateway/handlers/functions/allowlist/allowlist_test.go @@ -0,0 +1,184 @@ +package allowlist_test + +import ( + "context" + "encoding/hex" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist" + amocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist/mocks" +) + +const ( + addr1 = "9ed925d8206a4f88a2f643b28b3035b315753cd6" + addr2 = "ea6721ac65bced841b8ec3fc5fedea6141a0ade4" + addr3 = "84689acc87ff22841b8ec378300da5e141a99911" +) + +func sampleEncodedAllowlist(t *testing.T) []byte { + abiEncodedAddresses := + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "000000000000000000000000" + addr1 + + "000000000000000000000000" + addr2 + rawData, err := hex.DecodeString(abiEncodedAddresses) + require.NoError(t, err) + return rawData +} + +func TestAllowlist_UpdateAndCheck(t *testing.T) { + t.Parallel() + + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(sampleEncodedAllowlist(t), nil) + config := allowlist.OnchainAllowlistConfig{ + ContractVersion: 1, + ContractAddress: common.Address{}, + BlockConfirmations: 1, + } + + orm := amocks.NewORM(t) + allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) + require.NoError(t, err) + + err = allowlist.Start(testutils.Context(t)) + require.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, allowlist.Close()) + }) + + require.NoError(t, allowlist.UpdateFromContract(testutils.Context(t))) + require.False(t, allowlist.Allow(common.Address{})) + require.True(t, allowlist.Allow(common.HexToAddress(addr1))) + require.True(t, allowlist.Allow(common.HexToAddress(addr2))) + require.False(t, allowlist.Allow(common.HexToAddress(addr3))) +} + +func TestAllowlist_UnsupportedVersion(t *testing.T) { + t.Parallel() + + client := mocks.NewClient(t) + config := allowlist.OnchainAllowlistConfig{ + ContractVersion: 0, + ContractAddress: common.Address{}, + BlockConfirmations: 1, + } + + orm := amocks.NewORM(t) + _, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) + require.Error(t, err) +} + +func TestAllowlist_UpdatePeriodically(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(testutils.Context(t)) + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + cancel() + }).Return(sampleEncodedAllowlist(t), nil) + config := allowlist.OnchainAllowlistConfig{ + ContractAddress: common.Address{}, + ContractVersion: 1, + BlockConfirmations: 1, + UpdateFrequencySec: 2, + UpdateTimeoutSec: 1, + } + + orm := amocks.NewORM(t) + orm.On("GetAllowedSenders", uint(0), uint(1000)).Return([]common.Address{}, nil) + + allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) + require.NoError(t, err) + + err = allowlist.Start(ctx) + require.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, allowlist.Close()) + }) + + gomega.NewGomegaWithT(t).Eventually(func() bool { + return allowlist.Allow(common.HexToAddress(addr1)) && !allowlist.Allow(common.HexToAddress(addr3)) + }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) +} +func TestAllowlist_UpdateFromContract(t *testing.T) { + t.Parallel() + + t.Run("OK-iterate_over_list_of_allowed_senders", func(t *testing.T) { + ctx, cancel := context.WithCancel(testutils.Context(t)) + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + cancel() + }).Return(sampleEncodedAllowlist(t), nil) + config := allowlist.OnchainAllowlistConfig{ + ContractAddress: common.HexToAddress(addr3), + ContractVersion: 1, + BlockConfirmations: 1, + UpdateFrequencySec: 2, + UpdateTimeoutSec: 1, + StoredAllowlistBatchSize: 2, + OnchainAllowlistBatchSize: 16, + StoreAllowedSendersEnabled: true, + FetchingDelayInRangeSec: 0, + } + + orm := amocks.NewORM(t) + orm.On("DeleteAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(2).Return(nil) + orm.On("CreateAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(2).Return(nil) + + allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) + require.NoError(t, err) + + err = allowlist.UpdateFromContract(ctx) + require.NoError(t, err) + + gomega.NewGomegaWithT(t).Eventually(func() bool { + return allowlist.Allow(common.HexToAddress(addr1)) && !allowlist.Allow(common.HexToAddress(addr3)) + }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) + }) + + t.Run("OK-fetch_complete_list_of_allowed_senders_without_storing", func(t *testing.T) { + ctx, cancel := context.WithCancel(testutils.Context(t)) + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + cancel() + }).Return(sampleEncodedAllowlist(t), nil) + config := allowlist.OnchainAllowlistConfig{ + ContractAddress: common.HexToAddress(addr3), + ContractVersion: 1, + BlockConfirmations: 1, + UpdateFrequencySec: 2, + UpdateTimeoutSec: 1, + StoredAllowlistBatchSize: 2, + OnchainAllowlistBatchSize: 16, + StoreAllowedSendersEnabled: false, + FetchingDelayInRangeSec: 0, + } + + orm := amocks.NewORM(t) + allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) + require.NoError(t, err) + + err = allowlist.UpdateFromContract(ctx) + require.NoError(t, err) + + gomega.NewGomegaWithT(t).Eventually(func() bool { + return allowlist.Allow(common.HexToAddress(addr1)) && !allowlist.Allow(common.HexToAddress(addr3)) + }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) + }) +} diff --git a/core/services/gateway/handlers/functions/mocks/onchain_allowlist.go b/core/services/gateway/handlers/functions/allowlist/mocks/onchain_allowlist.go similarity index 100% rename from core/services/gateway/handlers/functions/mocks/onchain_allowlist.go rename to core/services/gateway/handlers/functions/allowlist/mocks/onchain_allowlist.go diff --git a/core/services/gateway/handlers/functions/allowlist/mocks/orm.go b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go new file mode 100644 index 00000000000..c2ba27c3a24 --- /dev/null +++ b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go @@ -0,0 +1,116 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + common "github.com/ethereum/go-ethereum/common" + mock "github.com/stretchr/testify/mock" + + pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" +) + +// ORM is an autogenerated mock type for the ORM type +type ORM struct { + mock.Mock +} + +// CreateAllowedSenders provides a mock function with given fields: allowedSenders, qopts +func (_m *ORM) CreateAllowedSenders(allowedSenders []common.Address, qopts ...pg.QOpt) error { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, allowedSenders) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CreateAllowedSenders") + } + + var r0 error + if rf, ok := ret.Get(0).(func([]common.Address, ...pg.QOpt) error); ok { + r0 = rf(allowedSenders, qopts...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteAllowedSenders provides a mock function with given fields: blockedSenders, qopts +func (_m *ORM) DeleteAllowedSenders(blockedSenders []common.Address, qopts ...pg.QOpt) error { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, blockedSenders) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for DeleteAllowedSenders") + } + + var r0 error + if rf, ok := ret.Get(0).(func([]common.Address, ...pg.QOpt) error); ok { + r0 = rf(blockedSenders, qopts...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetAllowedSenders provides a mock function with given fields: offset, limit, qopts +func (_m *ORM) GetAllowedSenders(offset uint, limit uint, qopts ...pg.QOpt) ([]common.Address, error) { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, offset, limit) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetAllowedSenders") + } + + var r0 []common.Address + var r1 error + if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) ([]common.Address, error)); ok { + return rf(offset, limit, qopts...) + } + if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) []common.Address); ok { + r0 = rf(offset, limit, qopts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]common.Address) + } + } + + if rf, ok := ret.Get(1).(func(uint, uint, ...pg.QOpt) error); ok { + r1 = rf(offset, limit, qopts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewORM creates a new instance of ORM. 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 NewORM(t interface { + mock.TestingT + Cleanup(func()) +}) *ORM { + mock := &ORM{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/gateway/handlers/functions/allowlist/orm.go b/core/services/gateway/handlers/functions/allowlist/orm.go new file mode 100644 index 00000000000..07ee1ea3b3b --- /dev/null +++ b/core/services/gateway/handlers/functions/allowlist/orm.go @@ -0,0 +1,123 @@ +package allowlist + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/jmoiron/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" +) + +//go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore +type ORM interface { + GetAllowedSenders(offset, limit uint, qopts ...pg.QOpt) ([]common.Address, error) + CreateAllowedSenders(allowedSenders []common.Address, qopts ...pg.QOpt) error + DeleteAllowedSenders(blockedSenders []common.Address, qopts ...pg.QOpt) error +} + +type orm struct { + q pg.Q + lggr logger.Logger + routerContractAddress common.Address +} + +var _ ORM = (*orm)(nil) +var ( + ErrInvalidParameters = errors.New("invalid parameters provided to create a functions contract cache ORM") +) + +const ( + tableName = "functions_allowlist" +) + +func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, routerContractAddress common.Address) (ORM, error) { + if db == nil || cfg == nil || lggr == nil || routerContractAddress == (common.Address{}) { + return nil, ErrInvalidParameters + } + + return &orm{ + q: pg.NewQ(db, lggr, cfg), + lggr: lggr, + routerContractAddress: routerContractAddress, + }, nil +} + +func (o *orm) GetAllowedSenders(offset, limit uint, qopts ...pg.QOpt) ([]common.Address, error) { + var addresses []common.Address + stmt := fmt.Sprintf(` + SELECT allowed_address + FROM %s + WHERE router_contract_address = $1 + ORDER BY id ASC + OFFSET $2 + LIMIT $3; + `, tableName) + err := o.q.WithOpts(qopts...).Select(&addresses, stmt, o.routerContractAddress, offset, limit) + if err != nil { + return addresses, err + } + o.lggr.Debugf("Successfully fetched allowed sender list from DB. offset: %d, limit: %d, length: %d", offset, limit, len(addresses)) + + return addresses, nil +} + +func (o *orm) CreateAllowedSenders(allowedSenders []common.Address, qopts ...pg.QOpt) error { + var valuesPlaceholder []string + for i := 1; i <= len(allowedSenders)*2; i += 2 { + valuesPlaceholder = append(valuesPlaceholder, fmt.Sprintf("($%d, $%d)", i, i+1)) + } + + stmt := fmt.Sprintf(` + INSERT INTO %s (allowed_address, router_contract_address) + VALUES %s ON CONFLICT (allowed_address, router_contract_address) DO NOTHING;`, tableName, strings.Join(valuesPlaceholder, ", ")) + + var args []interface{} + for _, as := range allowedSenders { + args = append(args, as, o.routerContractAddress) + } + + _, err := o.q.WithOpts(qopts...).Exec(stmt, args...) + if err != nil { + return err + } + + o.lggr.Debugf("Successfully stored allowed senders: %v for routerContractAddress: %s", allowedSenders, o.routerContractAddress) + + return nil +} + +func (o *orm) DeleteAllowedSenders(blockedSenders []common.Address, qopts ...pg.QOpt) error { + var valuesPlaceholder []string + for i := 1; i <= len(blockedSenders); i++ { + valuesPlaceholder = append(valuesPlaceholder, fmt.Sprintf("$%d", i+1)) + } + + stmt := fmt.Sprintf(` + DELETE FROM %s + WHERE router_contract_address = $1 + AND allowed_address IN (%s);`, tableName, strings.Join(valuesPlaceholder, ", ")) + + args := []interface{}{o.routerContractAddress} + for _, bs := range blockedSenders { + args = append(args, bs) + } + + res, err := o.q.WithOpts(qopts...).Exec(stmt, args...) + if err != nil { + return err + } + + rowsAffected, err := res.RowsAffected() + if err != nil { + return err + } + + o.lggr.Debugf("Successfully removed blocked senders from the allowed list: %v for routerContractAddress: %s. rowsAffected: %d", blockedSenders, o.routerContractAddress, rowsAffected) + + return nil +} diff --git a/core/services/gateway/handlers/functions/allowlist/orm_test.go b/core/services/gateway/handlers/functions/allowlist/orm_test.go new file mode 100644 index 00000000000..0f63e83cd5f --- /dev/null +++ b/core/services/gateway/handlers/functions/allowlist/orm_test.go @@ -0,0 +1,190 @@ +package allowlist_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "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/gateway/handlers/functions/allowlist" +) + +func setupORM(t *testing.T) (allowlist.ORM, error) { + t.Helper() + + var ( + db = pgtest.NewSqlxDB(t) + lggr = logger.TestLogger(t) + ) + + return allowlist.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) +} + +func seedAllowedSenders(t *testing.T, orm allowlist.ORM, amount int) []common.Address { + storedAllowedSenders := make([]common.Address, amount) + for i := 0; i < amount; i++ { + address := testutils.NewAddress() + storedAllowedSenders[i] = address + } + + err := orm.CreateAllowedSenders(storedAllowedSenders) + require.NoError(t, err) + + return storedAllowedSenders +} +func TestORM_GetAllowedSenders(t *testing.T) { + t.Parallel() + t.Run("fetch first page", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + storedAllowedSenders := seedAllowedSenders(t, orm, 2) + results, err := orm.GetAllowedSenders(0, 1) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, storedAllowedSenders[0], results[0]) + }) + + t.Run("fetch second page", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + storedAllowedSenders := seedAllowedSenders(t, orm, 2) + results, err := orm.GetAllowedSenders(1, 5) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, storedAllowedSenders[1], results[0]) + }) +} + +func TestORM_CreateAllowedSenders(t *testing.T) { + t.Parallel() + + t.Run("OK-create_an_allowed_sender", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + expected := testutils.NewAddress() + err = orm.CreateAllowedSenders([]common.Address{expected}) + require.NoError(t, err) + + results, err := orm.GetAllowedSenders(0, 1) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, expected, results[0]) + }) + + t.Run("OK-create_an_existing_allowed_sender", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + expected := testutils.NewAddress() + err = orm.CreateAllowedSenders([]common.Address{expected}) + require.NoError(t, err) + + err = orm.CreateAllowedSenders([]common.Address{expected}) + require.NoError(t, err) + + results, err := orm.GetAllowedSenders(0, 5) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, expected, results[0]) + }) + + t.Run("OK-create_multiple_allowed_senders_in_one_query", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + expected := []common.Address{testutils.NewAddress(), testutils.NewAddress()} + err = orm.CreateAllowedSenders(expected) + require.NoError(t, err) + + results, err := orm.GetAllowedSenders(0, 2) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, expected[0], results[0]) + require.Equal(t, expected[1], results[1]) + }) + + t.Run("OK-create_multiple_allowed_senders_with_duplicates", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + addr1 := testutils.NewAddress() + addr2 := testutils.NewAddress() + expected := []common.Address{addr1, addr2} + + duplicatedAddressInput := []common.Address{addr1, addr1, addr1, addr2} + err = orm.CreateAllowedSenders(duplicatedAddressInput) + require.NoError(t, err) + + results, err := orm.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, expected[0], results[0]) + require.Equal(t, expected[1], results[1]) + }) +} + +func TestORM_DeleteAllowedSenders(t *testing.T) { + t.Parallel() + + t.Run("OK-delete_blocked_sender_from_allowed_list", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + add1 := testutils.NewAddress() + add2 := testutils.NewAddress() + add3 := testutils.NewAddress() + err = orm.CreateAllowedSenders([]common.Address{add1, add2, add3}) + require.NoError(t, err) + + results, err := orm.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 3, len(results), "incorrect results length") + require.Equal(t, add1, results[0]) + + err = orm.DeleteAllowedSenders([]common.Address{add1, add3}) + require.NoError(t, err) + + results, err = orm.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 1, len(results), "incorrect results length") + require.Equal(t, add2, results[0]) + }) + + t.Run("OK-delete_non_existing_blocked_sender_from_allowed_list", func(t *testing.T) { + orm, err := setupORM(t) + require.NoError(t, err) + add1 := testutils.NewAddress() + add2 := testutils.NewAddress() + err = orm.CreateAllowedSenders([]common.Address{add1, add2}) + require.NoError(t, err) + + results, err := orm.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, add1, results[0]) + + add3 := testutils.NewAddress() + err = orm.DeleteAllowedSenders([]common.Address{add3}) + require.NoError(t, err) + + results, err = orm.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, add1, results[0]) + require.Equal(t, add2, results[1]) + }) +} + +func Test_NewORM(t *testing.T) { + t.Run("OK-create_ORM", func(t *testing.T) { + _, err := allowlist.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), testutils.NewAddress()) + require.NoError(t, err) + }) + t.Run("NOK-create_ORM_with_nil_fields", func(t *testing.T) { + _, err := allowlist.NewORM(nil, nil, nil, common.Address{}) + require.Error(t, err) + }) + t.Run("NOK-create_ORM_with_empty_address", func(t *testing.T) { + _, err := allowlist.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), common.Address{}) + require.Error(t, err) + }) +} diff --git a/core/services/gateway/handlers/functions/allowlist_test.go b/core/services/gateway/handlers/functions/allowlist_test.go deleted file mode 100644 index fdaea81b2c0..00000000000 --- a/core/services/gateway/handlers/functions/allowlist_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package functions_test - -import ( - "context" - "encoding/hex" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/onsi/gomega" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" -) - -const ( - addr1 = "9ed925d8206a4f88a2f643b28b3035b315753cd6" - addr2 = "ea6721ac65bced841b8ec3fc5fedea6141a0ade4" - addr3 = "84689acc87ff22841b8ec378300da5e141a99911" -) - -func sampleEncodedAllowlist(t *testing.T) []byte { - abiEncodedAddresses := - "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000002" + - "000000000000000000000000" + addr1 + - "000000000000000000000000" + addr2 - rawData, err := hex.DecodeString(abiEncodedAddresses) - require.NoError(t, err) - return rawData -} - -func TestAllowlist_UpdateAndCheck(t *testing.T) { - t.Parallel() - - client := mocks.NewClient(t) - client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) - client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(sampleEncodedAllowlist(t), nil) - config := functions.OnchainAllowlistConfig{ - ContractVersion: 1, - ContractAddress: common.Address{}, - BlockConfirmations: 1, - } - allowlist, err := functions.NewOnchainAllowlist(client, config, logger.TestLogger(t)) - require.NoError(t, err) - - err = allowlist.Start(testutils.Context(t)) - require.NoError(t, err) - t.Cleanup(func() { - assert.NoError(t, allowlist.Close()) - }) - - require.NoError(t, allowlist.UpdateFromContract(testutils.Context(t))) - require.False(t, allowlist.Allow(common.Address{})) - require.True(t, allowlist.Allow(common.HexToAddress(addr1))) - require.True(t, allowlist.Allow(common.HexToAddress(addr2))) - require.False(t, allowlist.Allow(common.HexToAddress(addr3))) -} - -func TestAllowlist_UnsupportedVersion(t *testing.T) { - t.Parallel() - - client := mocks.NewClient(t) - config := functions.OnchainAllowlistConfig{ - ContractVersion: 0, - ContractAddress: common.Address{}, - BlockConfirmations: 1, - } - _, err := functions.NewOnchainAllowlist(client, config, logger.TestLogger(t)) - require.Error(t, err) -} - -func TestAllowlist_UpdatePeriodically(t *testing.T) { - t.Parallel() - - ctx, cancel := context.WithCancel(testutils.Context(t)) - client := mocks.NewClient(t) - client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) - client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - cancel() - }).Return(sampleEncodedAllowlist(t), nil) - config := functions.OnchainAllowlistConfig{ - ContractAddress: common.Address{}, - ContractVersion: 1, - BlockConfirmations: 1, - UpdateFrequencySec: 1, - UpdateTimeoutSec: 1, - } - allowlist, err := functions.NewOnchainAllowlist(client, config, logger.TestLogger(t)) - require.NoError(t, err) - - err = allowlist.Start(ctx) - require.NoError(t, err) - t.Cleanup(func() { - assert.NoError(t, allowlist.Close()) - }) - - gomega.NewGomegaWithT(t).Eventually(func() bool { - return allowlist.Allow(common.HexToAddress(addr1)) && !allowlist.Allow(common.HexToAddress(addr3)) - }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) -} diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 2872c72f761..ff272e4e577 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -23,6 +23,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" + fallow "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist" + fsub "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -60,10 +62,10 @@ var ( type FunctionsHandlerConfig struct { ChainID string `json:"chainId"` // Not specifying OnchainAllowlist config disables allowlist checks - OnchainAllowlist *OnchainAllowlistConfig `json:"onchainAllowlist"` + OnchainAllowlist *fallow.OnchainAllowlistConfig `json:"onchainAllowlist"` // Not specifying OnchainSubscriptions config disables minimum balance checks - OnchainSubscriptions *OnchainSubscriptionsConfig `json:"onchainSubscriptions"` - MinimumSubscriptionBalance *assets.Link `json:"minimumSubscriptionBalance"` + OnchainSubscriptions *fsub.OnchainSubscriptionsConfig `json:"onchainSubscriptions"` + MinimumSubscriptionBalance *assets.Link `json:"minimumSubscriptionBalance"` // Not specifying RateLimiter config disables rate limiting UserRateLimiter *hc.RateLimiterConfig `json:"userRateLimiter"` NodeRateLimiter *hc.RateLimiterConfig `json:"nodeRateLimiter"` @@ -79,8 +81,8 @@ type functionsHandler struct { donConfig *config.DONConfig don handlers.DON pendingRequests hc.RequestCache[PendingRequest] - allowlist OnchainAllowlist - subscriptions OnchainSubscriptions + allowlist fallow.OnchainAllowlist + subscriptions fsub.OnchainSubscriptions minimumBalance *assets.Link userRateLimiter *hc.RateLimiter nodeRateLimiter *hc.RateLimiter @@ -105,13 +107,18 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con return nil, err } lggr = lggr.Named("FunctionsHandler:" + donConfig.DonId) - var allowlist OnchainAllowlist + var allowlist fallow.OnchainAllowlist if cfg.OnchainAllowlist != nil { chain, err2 := legacyChains.Get(cfg.ChainID) if err2 != nil { return nil, err2 } - allowlist, err2 = NewOnchainAllowlist(chain.Client(), *cfg.OnchainAllowlist, lggr) + + orm, err2 := fallow.NewORM(db, lggr, qcfg, cfg.OnchainAllowlist.ContractAddress) + if err2 != nil { + return nil, err2 + } + allowlist, err2 = fallow.NewOnchainAllowlist(chain.Client(), *cfg.OnchainAllowlist, orm, lggr) if err2 != nil { return nil, err2 } @@ -129,19 +136,19 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con return nil, err } } - var subscriptions OnchainSubscriptions + var subscriptions fsub.OnchainSubscriptions if cfg.OnchainSubscriptions != nil { chain, err2 := legacyChains.Get(cfg.ChainID) if err2 != nil { return nil, err2 } - orm, err2 := NewORM(db, lggr, qcfg, cfg.OnchainSubscriptions.ContractAddress) + orm, err2 := fsub.NewORM(db, lggr, qcfg, cfg.OnchainSubscriptions.ContractAddress) if err2 != nil { return nil, err2 } - subscriptions, err2 = NewOnchainSubscriptions(chain.Client(), *cfg.OnchainSubscriptions, orm, lggr) + subscriptions, err2 = fsub.NewOnchainSubscriptions(chain.Client(), *cfg.OnchainSubscriptions, orm, lggr) if err2 != nil { return nil, err2 } @@ -159,8 +166,8 @@ func NewFunctionsHandler( donConfig *config.DONConfig, don handlers.DON, pendingRequestsCache hc.RequestCache[PendingRequest], - allowlist OnchainAllowlist, - subscriptions OnchainSubscriptions, + allowlist fallow.OnchainAllowlist, + subscriptions fsub.OnchainSubscriptions, minimumBalance *assets.Link, userRateLimiter *hc.RateLimiter, nodeRateLimiter *hc.RateLimiter, diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 7e055815c88..2e2c1c77caf 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -22,11 +22,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" - functions_mocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" + allowlist_mocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist/mocks" + subscriptions_mocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions/mocks" handlers_mocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/mocks" ) -func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTimeout time.Duration, heartbeatSender string) (handlers.Handler, *handlers_mocks.DON, *functions_mocks.OnchainAllowlist, *functions_mocks.OnchainSubscriptions) { +func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTimeout time.Duration, heartbeatSender string) (handlers.Handler, *handlers_mocks.DON, *allowlist_mocks.OnchainAllowlist, *subscriptions_mocks.OnchainSubscriptions) { cfg := functions.FunctionsHandlerConfig{} donConfig := &config.DONConfig{ Members: []config.NodeConfig{}, @@ -41,8 +42,8 @@ func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTi } don := handlers_mocks.NewDON(t) - allowlist := functions_mocks.NewOnchainAllowlist(t) - subscriptions := functions_mocks.NewOnchainSubscriptions(t) + allowlist := allowlist_mocks.NewOnchainAllowlist(t) + subscriptions := subscriptions_mocks.NewOnchainSubscriptions(t) minBalance := assets.NewLinkFromJuels(100) userRateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) diff --git a/core/services/gateway/handlers/functions/mocks/onchain_subscriptions.go b/core/services/gateway/handlers/functions/subscriptions/mocks/onchain_subscriptions.go similarity index 100% rename from core/services/gateway/handlers/functions/mocks/onchain_subscriptions.go rename to core/services/gateway/handlers/functions/subscriptions/mocks/onchain_subscriptions.go diff --git a/core/services/gateway/handlers/functions/mocks/orm.go b/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go similarity index 73% rename from core/services/gateway/handlers/functions/mocks/orm.go rename to core/services/gateway/handlers/functions/subscriptions/mocks/orm.go index 110675c8b55..0f278aa49b0 100644 --- a/core/services/gateway/handlers/functions/mocks/orm.go +++ b/core/services/gateway/handlers/functions/subscriptions/mocks/orm.go @@ -3,10 +3,9 @@ package mocks import ( - functions "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" - mock "github.com/stretchr/testify/mock" - + subscriptions "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" pg "github.com/smartcontractkit/chainlink/v2/core/services/pg" + mock "github.com/stretchr/testify/mock" ) // ORM is an autogenerated mock type for the ORM type @@ -15,7 +14,7 @@ type ORM struct { } // GetSubscriptions provides a mock function with given fields: offset, limit, qopts -func (_m *ORM) GetSubscriptions(offset uint, limit uint, qopts ...pg.QOpt) ([]functions.CachedSubscription, error) { +func (_m *ORM) GetSubscriptions(offset uint, limit uint, qopts ...pg.QOpt) ([]subscriptions.StoredSubscription, error) { _va := make([]interface{}, len(qopts)) for _i := range qopts { _va[_i] = qopts[_i] @@ -29,16 +28,16 @@ func (_m *ORM) GetSubscriptions(offset uint, limit uint, qopts ...pg.QOpt) ([]fu panic("no return value specified for GetSubscriptions") } - var r0 []functions.CachedSubscription + var r0 []subscriptions.StoredSubscription var r1 error - if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) ([]functions.CachedSubscription, error)); ok { + if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) ([]subscriptions.StoredSubscription, error)); ok { return rf(offset, limit, qopts...) } - if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) []functions.CachedSubscription); ok { + if rf, ok := ret.Get(0).(func(uint, uint, ...pg.QOpt) []subscriptions.StoredSubscription); ok { r0 = rf(offset, limit, qopts...) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]functions.CachedSubscription) + r0 = ret.Get(0).([]subscriptions.StoredSubscription) } } @@ -52,7 +51,7 @@ func (_m *ORM) GetSubscriptions(offset uint, limit uint, qopts ...pg.QOpt) ([]fu } // UpsertSubscription provides a mock function with given fields: subscription, qopts -func (_m *ORM) UpsertSubscription(subscription functions.CachedSubscription, qopts ...pg.QOpt) error { +func (_m *ORM) UpsertSubscription(subscription subscriptions.StoredSubscription, qopts ...pg.QOpt) error { _va := make([]interface{}, len(qopts)) for _i := range qopts { _va[_i] = qopts[_i] @@ -67,7 +66,7 @@ func (_m *ORM) UpsertSubscription(subscription functions.CachedSubscription, qop } var r0 error - if rf, ok := ret.Get(0).(func(functions.CachedSubscription, ...pg.QOpt) error); ok { + if rf, ok := ret.Get(0).(func(subscriptions.StoredSubscription, ...pg.QOpt) error); ok { r0 = rf(subscription, qopts...) } else { r0 = ret.Error(0) diff --git a/core/services/gateway/handlers/functions/orm.go b/core/services/gateway/handlers/functions/subscriptions/orm.go similarity index 73% rename from core/services/gateway/handlers/functions/orm.go rename to core/services/gateway/handlers/functions/subscriptions/orm.go index b7ec8d865d1..369291ace54 100644 --- a/core/services/gateway/handlers/functions/orm.go +++ b/core/services/gateway/handlers/functions/subscriptions/orm.go @@ -1,4 +1,4 @@ -package functions +package subscriptions import ( "fmt" @@ -16,27 +16,27 @@ import ( ) //go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore - type ORM interface { - GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]CachedSubscription, error) - UpsertSubscription(subscription CachedSubscription, qopts ...pg.QOpt) error + GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]StoredSubscription, error) + UpsertSubscription(subscription StoredSubscription, qopts ...pg.QOpt) error } type orm struct { q pg.Q + lggr logger.Logger routerContractAddress common.Address } var _ ORM = (*orm)(nil) var ( - ErrInvalidParameters = errors.New("invalid parameters provided to create a subscription cache ORM") + ErrInvalidParameters = errors.New("invalid parameters provided to create a subscription contract ORM") ) const ( tableName = "functions_subscriptions" ) -type cachedSubscriptionRow struct { +type storedSubscriptionRow struct { SubscriptionID uint64 Owner common.Address Balance int64 @@ -54,13 +54,14 @@ func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, routerContractAddre return &orm{ q: pg.NewQ(db, lggr, cfg), + lggr: lggr, routerContractAddress: routerContractAddress, }, nil } -func (o *orm) GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]CachedSubscription, error) { - var cacheSubscriptions []CachedSubscription - var cacheSubscriptionRows []cachedSubscriptionRow +func (o *orm) GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]StoredSubscription, error) { + var storedSubscriptions []StoredSubscription + var storedSubscriptionRows []storedSubscriptionRow stmt := fmt.Sprintf(` SELECT subscription_id, owner, balance, blocked_balance, proposed_owner, consumers, flags, router_contract_address FROM %s @@ -69,21 +70,21 @@ func (o *orm) GetSubscriptions(offset, limit uint, qopts ...pg.QOpt) ([]CachedSu OFFSET $2 LIMIT $3; `, tableName) - err := o.q.WithOpts(qopts...).Select(&cacheSubscriptionRows, stmt, o.routerContractAddress, offset, limit) + err := o.q.WithOpts(qopts...).Select(&storedSubscriptionRows, stmt, o.routerContractAddress, offset, limit) if err != nil { - return cacheSubscriptions, err + return storedSubscriptions, err } - for _, cs := range cacheSubscriptionRows { - cacheSubscriptions = append(cacheSubscriptions, cs.encode()) + for _, cs := range storedSubscriptionRows { + storedSubscriptions = append(storedSubscriptions, cs.encode()) } - return cacheSubscriptions, nil + return storedSubscriptions, nil } // UpsertSubscription will update if a subscription exists or create if it does not. // In case a subscription gets deleted we will update it with an owner address equal to 0x0. -func (o *orm) UpsertSubscription(subscription CachedSubscription, qopts ...pg.QOpt) error { +func (o *orm) UpsertSubscription(subscription StoredSubscription, qopts ...pg.QOpt) error { stmt := fmt.Sprintf(` INSERT INTO %s (subscription_id, owner, balance, blocked_balance, proposed_owner, consumers, flags, router_contract_address) VALUES ($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT (subscription_id, router_contract_address) DO UPDATE @@ -97,6 +98,11 @@ func (o *orm) UpsertSubscription(subscription CachedSubscription, qopts ...pg.QO subscription.BlockedBalance = big.NewInt(0) } + var consumers [][]byte + for _, c := range subscription.Consumers { + consumers = append(consumers, c.Bytes()) + } + _, err := o.q.WithOpts(qopts...).Exec( stmt, subscription.SubscriptionID, @@ -104,21 +110,26 @@ func (o *orm) UpsertSubscription(subscription CachedSubscription, qopts ...pg.QO subscription.Balance.Int64(), subscription.BlockedBalance.Int64(), subscription.ProposedOwner, - subscription.Consumers, + consumers, subscription.Flags[:], o.routerContractAddress, ) + if err != nil { + return err + } + + o.lggr.Debugf("Successfully updated subscription: %d for routerContractAddress: %s", subscription.SubscriptionID, o.routerContractAddress) - return err + return nil } -func (cs *cachedSubscriptionRow) encode() CachedSubscription { +func (cs *storedSubscriptionRow) encode() StoredSubscription { consumers := make([]common.Address, 0) for _, csc := range cs.Consumers { consumers = append(consumers, common.BytesToAddress(csc)) } - return CachedSubscription{ + return StoredSubscription{ SubscriptionID: cs.SubscriptionID, IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: big.NewInt(cs.Balance), diff --git a/core/services/gateway/handlers/functions/orm_test.go b/core/services/gateway/handlers/functions/subscriptions/orm_test.go similarity index 81% rename from core/services/gateway/handlers/functions/orm_test.go rename to core/services/gateway/handlers/functions/subscriptions/orm_test.go index 37ec24ea0b1..6cb1146f03c 100644 --- a/core/services/gateway/handlers/functions/orm_test.go +++ b/core/services/gateway/handlers/functions/subscriptions/orm_test.go @@ -1,4 +1,4 @@ -package functions_test +package subscriptions_test import ( "math/big" @@ -12,14 +12,14 @@ import ( "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/gateway/handlers/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" ) var ( defaultFlags = [32]byte{0x1, 0x2, 0x3} ) -func setupORM(t *testing.T) (functions.ORM, error) { +func setupORM(t *testing.T) (subscriptions.ORM, error) { t.Helper() var ( @@ -27,13 +27,13 @@ func setupORM(t *testing.T) (functions.ORM, error) { lggr = logger.TestLogger(t) ) - return functions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) + return subscriptions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) } -func createSubscriptions(t *testing.T, orm functions.ORM, amount int) []functions.CachedSubscription { - cachedSubscriptions := make([]functions.CachedSubscription, 0) +func seedSubscriptions(t *testing.T, orm subscriptions.ORM, amount int) []subscriptions.StoredSubscription { + storedSubscriptions := make([]subscriptions.StoredSubscription, 0) for i := amount; i > 0; i-- { - cs := functions.CachedSubscription{ + cs := subscriptions.StoredSubscription{ SubscriptionID: uint64(i), IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: big.NewInt(10), @@ -44,11 +44,11 @@ func createSubscriptions(t *testing.T, orm functions.ORM, amount int) []function Flags: defaultFlags, }, } - cachedSubscriptions = append(cachedSubscriptions, cs) + storedSubscriptions = append(storedSubscriptions, cs) err := orm.UpsertSubscription(cs) require.NoError(t, err) } - return cachedSubscriptions + return storedSubscriptions } func TestORM_GetSubscriptions(t *testing.T) { @@ -56,21 +56,21 @@ func TestORM_GetSubscriptions(t *testing.T) { t.Run("fetch first page", func(t *testing.T) { orm, err := setupORM(t) require.NoError(t, err) - cachedSubscriptions := createSubscriptions(t, orm, 2) + storedSubscriptions := seedSubscriptions(t, orm, 2) results, err := orm.GetSubscriptions(0, 1) require.NoError(t, err) require.Equal(t, 1, len(results), "incorrect results length") - require.Equal(t, cachedSubscriptions[1], results[0]) + require.Equal(t, storedSubscriptions[1], results[0]) }) t.Run("fetch second page", func(t *testing.T) { orm, err := setupORM(t) require.NoError(t, err) - cachedSubscriptions := createSubscriptions(t, orm, 2) + storedSubscriptions := seedSubscriptions(t, orm, 2) results, err := orm.GetSubscriptions(1, 5) require.NoError(t, err) require.Equal(t, 1, len(results), "incorrect results length") - require.Equal(t, cachedSubscriptions[0], results[0]) + require.Equal(t, storedSubscriptions[0], results[0]) }) } @@ -80,14 +80,14 @@ func TestORM_UpsertSubscription(t *testing.T) { t.Run("create a subscription", func(t *testing.T) { orm, err := setupORM(t) require.NoError(t, err) - expected := functions.CachedSubscription{ + expected := subscriptions.StoredSubscription{ SubscriptionID: uint64(1), IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: big.NewInt(10), Owner: testutils.NewAddress(), BlockedBalance: big.NewInt(20), ProposedOwner: common.Address{}, - Consumers: []common.Address{}, + Consumers: []common.Address{testutils.NewAddress()}, Flags: defaultFlags, }, } @@ -104,7 +104,7 @@ func TestORM_UpsertSubscription(t *testing.T) { orm, err := setupORM(t) require.NoError(t, err) - expectedUpdated := functions.CachedSubscription{ + expectedUpdated := subscriptions.StoredSubscription{ SubscriptionID: uint64(1), IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: big.NewInt(10), @@ -118,7 +118,7 @@ func TestORM_UpsertSubscription(t *testing.T) { err = orm.UpsertSubscription(expectedUpdated) require.NoError(t, err) - expectedNotUpdated := functions.CachedSubscription{ + expectedNotUpdated := subscriptions.StoredSubscription{ SubscriptionID: uint64(2), IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: big.NewInt(10), @@ -148,7 +148,7 @@ func TestORM_UpsertSubscription(t *testing.T) { orm, err := setupORM(t) require.NoError(t, err) - subscription := functions.CachedSubscription{ + subscription := subscriptions.StoredSubscription{ SubscriptionID: uint64(1), IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: big.NewInt(10), @@ -187,12 +187,12 @@ func TestORM_UpsertSubscription(t *testing.T) { lggr = logger.TestLogger(t) ) - orm1, err := functions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) + orm1, err := subscriptions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) require.NoError(t, err) - orm2, err := functions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) + orm2, err := subscriptions.NewORM(db, lggr, pgtest.NewQConfig(true), testutils.NewAddress()) require.NoError(t, err) - subscription := functions.CachedSubscription{ + subscription := subscriptions.StoredSubscription{ SubscriptionID: uint64(1), IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: assets.Ether(10).ToInt(), @@ -229,18 +229,17 @@ func TestORM_UpsertSubscription(t *testing.T) { require.Equal(t, 1, len(results), "incorrect results length") }) } - func Test_NewORM(t *testing.T) { t.Run("OK-create_ORM", func(t *testing.T) { - _, err := functions.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), testutils.NewAddress()) + _, err := subscriptions.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), testutils.NewAddress()) require.NoError(t, err) }) t.Run("NOK-create_ORM_with_nil_fields", func(t *testing.T) { - _, err := functions.NewORM(nil, nil, nil, common.Address{}) + _, err := subscriptions.NewORM(nil, nil, nil, common.Address{}) require.Error(t, err) }) t.Run("NOK-create_ORM_with_empty_address", func(t *testing.T) { - _, err := functions.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), common.Address{}) + _, err := subscriptions.NewORM(pgtest.NewSqlxDB(t), logger.TestLogger(t), pgtest.NewQConfig(true), common.Address{}) require.Error(t, err) }) } diff --git a/core/services/gateway/handlers/functions/subscriptions.go b/core/services/gateway/handlers/functions/subscriptions/subscriptions.go similarity index 90% rename from core/services/gateway/handlers/functions/subscriptions.go rename to core/services/gateway/handlers/functions/subscriptions/subscriptions.go index 8bc9cf09e7b..610aaa1d7f1 100644 --- a/core/services/gateway/handlers/functions/subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions/subscriptions.go @@ -1,4 +1,4 @@ -package functions +package subscriptions import ( "context" @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -const defaultCacheBatchSize = 100 +const defaultStoreBatchSize = 100 type OnchainSubscriptionsConfig struct { ContractAddress common.Address `json:"contractAddress"` @@ -27,7 +27,7 @@ type OnchainSubscriptionsConfig struct { UpdateFrequencySec uint `json:"updateFrequencySec"` UpdateTimeoutSec uint `json:"updateTimeoutSec"` UpdateRangeSize uint `json:"updateRangeSize"` - CacheBatchSize uint `json:"cacheBatchSize"` + StoreBatchSize uint `json:"storeBatchSize"` } // OnchainSubscriptions maintains a mirror of all subscriptions fetched from the blockchain (EVM-only). @@ -68,10 +68,10 @@ func NewOnchainSubscriptions(client evmclient.Client, config OnchainSubscription return nil, fmt.Errorf("unexpected error during functions_router.NewFunctionsRouter: %s", err) } - // if CacheBatchSize is not specified use the default value - if config.CacheBatchSize == 0 { - lggr.Info("CacheBatchSize not specified, using default size: ", defaultCacheBatchSize) - config.CacheBatchSize = defaultCacheBatchSize + // if StoreBatchSize is not specified use the default value + if config.StoreBatchSize == 0 { + lggr.Info("StoreBatchSize not specified, using default size: ", defaultStoreBatchSize) + config.StoreBatchSize = defaultStoreBatchSize } return &onchainSubscriptions{ @@ -99,7 +99,7 @@ func (s *onchainSubscriptions) Start(ctx context.Context) error { return errors.New("OnchainSubscriptionsConfig.UpdateRangeSize must be greater than 0") } - s.loadCachedSubscriptions() + s.loadStoredSubscriptions() s.closeWait.Add(1) go s.queryLoop() @@ -206,11 +206,11 @@ func (s *onchainSubscriptions) querySubscriptionsRange(ctx context.Context, bloc subscription := subscription updated := s.subscriptions.UpdateSubscription(subscriptionId, &subscription) if updated { - if err = s.orm.UpsertSubscription(CachedSubscription{ + if err = s.orm.UpsertSubscription(StoredSubscription{ SubscriptionID: subscriptionId, IFunctionsSubscriptionsSubscription: subscription, }); err != nil { - s.lggr.Errorf("unexpected error updating subscription in the cache: %w", err) + s.lggr.Errorf("unexpected error updating subscription in the db: %w", err) } } } @@ -226,10 +226,10 @@ func (s *onchainSubscriptions) getSubscriptionsCount(ctx context.Context, blockN }) } -func (s *onchainSubscriptions) loadCachedSubscriptions() { +func (s *onchainSubscriptions) loadStoredSubscriptions() { offset := uint(0) for { - csBatch, err := s.orm.GetSubscriptions(offset, s.config.CacheBatchSize) + csBatch, err := s.orm.GetSubscriptions(offset, s.config.StoreBatchSize) if err != nil { break } @@ -244,11 +244,11 @@ func (s *onchainSubscriptions) loadCachedSubscriptions() { Flags: cs.Flags, }) } - s.lggr.Debugw("Loading cached subscriptions", "offset", offset, "batch_length", len(csBatch)) + s.lggr.Debugw("Loading stored subscriptions", "offset", offset, "batch_length", len(csBatch)) - if len(csBatch) != int(s.config.CacheBatchSize) { + if len(csBatch) != int(s.config.StoreBatchSize) { break } - offset += s.config.CacheBatchSize + offset += s.config.StoreBatchSize } } diff --git a/core/services/gateway/handlers/functions/subscriptions_test.go b/core/services/gateway/handlers/functions/subscriptions/subscriptions_test.go similarity index 89% rename from core/services/gateway/handlers/functions/subscriptions_test.go rename to core/services/gateway/handlers/functions/subscriptions/subscriptions_test.go index 782dcc2332d..be1d2520434 100644 --- a/core/services/gateway/handlers/functions/subscriptions_test.go +++ b/core/services/gateway/handlers/functions/subscriptions/subscriptions_test.go @@ -1,4 +1,4 @@ -package functions_test +package subscriptions_test import ( "math/big" @@ -18,14 +18,14 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" - fmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" + smocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions/mocks" ) const ( validUser = "0x9ED925d8206a4f88a2f643b28B3035B315753Cd6" invalidUser = "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C" - cachedUser = "0x3E2dc0F9DB014aE19888F539E59285D2Ea04233G" + storedUser = "0x3E2dc0F9DB014aE19888F539E59285D2Ea04233G" ) func TestSubscriptions_OnePass(t *testing.T) { @@ -43,17 +43,17 @@ func TestSubscriptions_OnePass(t *testing.T) { To: &common.Address{}, Data: hexutil.MustDecode("0xec2454e500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003"), }, mock.Anything).Return(getSubscriptionsInRange, nil) - config := functions.OnchainSubscriptionsConfig{ + config := subscriptions.OnchainSubscriptionsConfig{ ContractAddress: common.Address{}, BlockConfirmations: 1, UpdateFrequencySec: 1, UpdateTimeoutSec: 1, UpdateRangeSize: 3, } - orm := fmocks.NewORM(t) - orm.On("GetSubscriptions", uint(0), uint(100)).Return([]functions.CachedSubscription{}, nil) + orm := smocks.NewORM(t) + orm.On("GetSubscriptions", uint(0), uint(100)).Return([]subscriptions.StoredSubscription{}, nil) orm.On("UpsertSubscription", mock.Anything).Return(nil) - subscriptions, err := functions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) + subscriptions, err := subscriptions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) require.NoError(t, err) err = subscriptions.Start(ctx) @@ -94,17 +94,17 @@ func TestSubscriptions_MultiPass(t *testing.T) { To: &common.Address{}, Data: hexutil.MustDecode("0xec2454e500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006"), }, mock.Anything).Return(getSubscriptionsInRange, nil) - config := functions.OnchainSubscriptionsConfig{ + config := subscriptions.OnchainSubscriptionsConfig{ ContractAddress: common.Address{}, BlockConfirmations: 1, UpdateFrequencySec: 1, UpdateTimeoutSec: 1, UpdateRangeSize: 3, } - orm := fmocks.NewORM(t) - orm.On("GetSubscriptions", uint(0), uint(100)).Return([]functions.CachedSubscription{}, nil) + orm := smocks.NewORM(t) + orm.On("GetSubscriptions", uint(0), uint(100)).Return([]subscriptions.StoredSubscription{}, nil) orm.On("UpsertSubscription", mock.Anything).Return(nil) - subscriptions, err := functions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) + subscriptions, err := subscriptions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) require.NoError(t, err) err = subscriptions.Start(ctx) @@ -118,7 +118,7 @@ func TestSubscriptions_MultiPass(t *testing.T) { }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) } -func TestSubscriptions_Cached(t *testing.T) { +func TestSubscriptions_Stored(t *testing.T) { getSubscriptionCount := hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000003") getSubscriptionsInRange := hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000109e6e1b12098cc8f3a1e9719a817ec53ab9b35c000000000000000000000000000000000000000000000000000034e23f515cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f5340f0968ee8b7dfd97e3327a6139273cc2c4fa000000000000000000000000000000000000000000000001158e460913d000000000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc14b92364c75e20000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005439e5881a529f3ccbffc0e82d49f9db3950aefe") @@ -133,31 +133,31 @@ func TestSubscriptions_Cached(t *testing.T) { To: &common.Address{}, Data: hexutil.MustDecode("0xec2454e500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003"), }, mock.Anything).Return(getSubscriptionsInRange, nil) - config := functions.OnchainSubscriptionsConfig{ + config := subscriptions.OnchainSubscriptionsConfig{ ContractAddress: common.Address{}, BlockConfirmations: 1, UpdateFrequencySec: 1, UpdateTimeoutSec: 1, UpdateRangeSize: 3, - CacheBatchSize: 1, + StoreBatchSize: 1, } expectedBalance := big.NewInt(5) - orm := fmocks.NewORM(t) - orm.On("GetSubscriptions", uint(0), uint(1)).Return([]functions.CachedSubscription{ + orm := smocks.NewORM(t) + orm.On("GetSubscriptions", uint(0), uint(1)).Return([]subscriptions.StoredSubscription{ { SubscriptionID: 1, IFunctionsSubscriptionsSubscription: functions_router.IFunctionsSubscriptionsSubscription{ Balance: expectedBalance, - Owner: common.HexToAddress(cachedUser), + Owner: common.HexToAddress(storedUser), BlockedBalance: big.NewInt(10), }, }, }, nil) - orm.On("GetSubscriptions", uint(1), uint(1)).Return([]functions.CachedSubscription{}, nil) + orm.On("GetSubscriptions", uint(1), uint(1)).Return([]subscriptions.StoredSubscription{}, nil) orm.On("UpsertSubscription", mock.Anything).Return(nil) - subscriptions, err := functions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) + subscriptions, err := subscriptions.NewOnchainSubscriptions(client, config, orm, logger.TestLogger(t)) require.NoError(t, err) err = subscriptions.Start(ctx) @@ -167,7 +167,7 @@ func TestSubscriptions_Cached(t *testing.T) { }) gomega.NewGomegaWithT(t).Eventually(func() bool { - actualBalance, err := subscriptions.GetMaxUserBalance(common.HexToAddress(cachedUser)) + actualBalance, err := subscriptions.GetMaxUserBalance(common.HexToAddress(storedUser)) return err == nil && assert.Equal(t, expectedBalance, actualBalance) }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) } diff --git a/core/services/gateway/handlers/functions/user_subscriptions.go b/core/services/gateway/handlers/functions/subscriptions/user_subscriptions.go similarity index 95% rename from core/services/gateway/handlers/functions/user_subscriptions.go rename to core/services/gateway/handlers/functions/subscriptions/user_subscriptions.go index 7e63720da71..ec506b6a864 100644 --- a/core/services/gateway/handlers/functions/user_subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions/user_subscriptions.go @@ -1,4 +1,4 @@ -package functions +package subscriptions import ( "math/big" @@ -31,8 +31,8 @@ func NewUserSubscriptions() UserSubscriptions { } } -// CachedSubscription is used to populate the user subscription maps from a persistent layer like postgres. -type CachedSubscription struct { +// StoredSubscription is used to populate the user subscription maps from a persistent layer like postgres. +type StoredSubscription struct { SubscriptionID uint64 functions_router.IFunctionsSubscriptionsSubscription } diff --git a/core/services/gateway/handlers/functions/user_subscriptions_test.go b/core/services/gateway/handlers/functions/subscriptions/user_subscriptions_test.go similarity index 92% rename from core/services/gateway/handlers/functions/user_subscriptions_test.go rename to core/services/gateway/handlers/functions/subscriptions/user_subscriptions_test.go index 53827e07e1b..4d58155735f 100644 --- a/core/services/gateway/handlers/functions/user_subscriptions_test.go +++ b/core/services/gateway/handlers/functions/subscriptions/user_subscriptions_test.go @@ -1,4 +1,4 @@ -package functions_test +package subscriptions_test import ( "math/big" @@ -6,7 +6,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" - "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" "github.com/stretchr/testify/assert" ) @@ -14,7 +14,7 @@ import ( func TestUserSubscriptions(t *testing.T) { t.Parallel() - us := functions.NewUserSubscriptions() + us := subscriptions.NewUserSubscriptions() t.Run("GetMaxUserBalance for unknown user", func(t *testing.T) { _, err := us.GetMaxUserBalance(utils.RandomAddress()) @@ -60,7 +60,7 @@ func TestUserSubscriptions_UpdateSubscription(t *testing.T) { t.Parallel() t.Run("update balance", func(t *testing.T) { - us := functions.NewUserSubscriptions() + us := subscriptions.NewUserSubscriptions() owner := utils.RandomAddress() updated := us.UpdateSubscription(1, &functions_router.IFunctionsSubscriptionsSubscription{ @@ -77,7 +77,7 @@ func TestUserSubscriptions_UpdateSubscription(t *testing.T) { }) t.Run("updated proposed owner", func(t *testing.T) { - us := functions.NewUserSubscriptions() + us := subscriptions.NewUserSubscriptions() owner := utils.RandomAddress() updated := us.UpdateSubscription(1, &functions_router.IFunctionsSubscriptionsSubscription{ @@ -94,7 +94,7 @@ func TestUserSubscriptions_UpdateSubscription(t *testing.T) { assert.True(t, updated) }) t.Run("remove subscriptions", func(t *testing.T) { - us := functions.NewUserSubscriptions() + us := subscriptions.NewUserSubscriptions() user2 := utils.RandomAddress() user2Balance1 := big.NewInt(50) user2Balance2 := big.NewInt(70) @@ -126,7 +126,7 @@ func TestUserSubscriptions_UpdateSubscription(t *testing.T) { }) t.Run("remove a non existing subscription", func(t *testing.T) { - us := functions.NewUserSubscriptions() + us := subscriptions.NewUserSubscriptions() updated := us.UpdateSubscription(3, &functions_router.IFunctionsSubscriptionsSubscription{ Owner: utils.ZeroAddress, }) @@ -134,7 +134,7 @@ func TestUserSubscriptions_UpdateSubscription(t *testing.T) { }) t.Run("no actual changes", func(t *testing.T) { - us := functions.NewUserSubscriptions() + us := subscriptions.NewUserSubscriptions() subscription := &functions_router.IFunctionsSubscriptionsSubscription{ Owner: utils.RandomAddress(), Balance: big.NewInt(25), diff --git a/core/services/ocr2/plugins/functions/config/config.go b/core/services/ocr2/plugins/functions/config/config.go index 38af7b8587f..e0e1ba3bfa0 100644 --- a/core/services/ocr2/plugins/functions/config/config.go +++ b/core/services/ocr2/plugins/functions/config/config.go @@ -13,41 +13,42 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" - "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" s4PluginConfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/s4" "github.com/smartcontractkit/chainlink/v2/core/services/s4" ) // This config is part of the job spec and is loaded only once on node boot/job creation. type PluginConfig struct { - EnableRequestSignatureCheck bool `json:"enableRequestSignatureCheck"` - DONID string `json:"donID"` - ContractVersion uint32 `json:"contractVersion"` - MinRequestConfirmations uint32 `json:"minRequestConfirmations"` - MinResponseConfirmations uint32 `json:"minResponseConfirmations"` - MinIncomingConfirmations uint32 `json:"minIncomingConfirmations"` - PastBlocksToPoll uint32 `json:"pastBlocksToPoll"` - LogPollerCacheDurationSec uint32 `json:"logPollerCacheDurationSec"` // Duration to cache previously detected request or response logs such that they can be filtered when calling logpoller_wrapper.LatestEvents() - RequestTimeoutSec uint32 `json:"requestTimeoutSec"` - RequestTimeoutCheckFrequencySec uint32 `json:"requestTimeoutCheckFrequencySec"` - RequestTimeoutBatchLookupSize uint32 `json:"requestTimeoutBatchLookupSize"` - PruneMaxStoredRequests uint32 `json:"pruneMaxStoredRequests"` - PruneCheckFrequencySec uint32 `json:"pruneCheckFrequencySec"` - PruneBatchSize uint32 `json:"pruneBatchSize"` - ListenerEventHandlerTimeoutSec uint32 `json:"listenerEventHandlerTimeoutSec"` - ListenerEventsCheckFrequencyMillis uint32 `json:"listenerEventsCheckFrequencyMillis"` - ContractUpdateCheckFrequencySec uint32 `json:"contractUpdateCheckFrequencySec"` - MaxRequestSizeBytes uint32 `json:"maxRequestSizeBytes"` - MaxRequestSizesList []uint32 `json:"maxRequestSizesList"` - MaxSecretsSizesList []uint32 `json:"maxSecretsSizesList"` - MinimumSubscriptionBalance assets.Link `json:"minimumSubscriptionBalance"` - AllowedHeartbeatInitiators []string `json:"allowedHeartbeatInitiators"` - GatewayConnectorConfig *connector.ConnectorConfig `json:"gatewayConnectorConfig"` - OnchainAllowlist *functions.OnchainAllowlistConfig `json:"onchainAllowlist"` - OnchainSubscriptions *functions.OnchainSubscriptionsConfig `json:"onchainSubscriptions"` - RateLimiter *common.RateLimiterConfig `json:"rateLimiter"` - S4Constraints *s4.Constraints `json:"s4Constraints"` - DecryptionQueueConfig *DecryptionQueueConfig `json:"decryptionQueueConfig"` + EnableRequestSignatureCheck bool `json:"enableRequestSignatureCheck"` + DONID string `json:"donID"` + ContractVersion uint32 `json:"contractVersion"` + MinRequestConfirmations uint32 `json:"minRequestConfirmations"` + MinResponseConfirmations uint32 `json:"minResponseConfirmations"` + MinIncomingConfirmations uint32 `json:"minIncomingConfirmations"` + PastBlocksToPoll uint32 `json:"pastBlocksToPoll"` + LogPollerCacheDurationSec uint32 `json:"logPollerCacheDurationSec"` // Duration to cache previously detected request or response logs such that they can be filtered when calling logpoller_wrapper.LatestEvents() + RequestTimeoutSec uint32 `json:"requestTimeoutSec"` + RequestTimeoutCheckFrequencySec uint32 `json:"requestTimeoutCheckFrequencySec"` + RequestTimeoutBatchLookupSize uint32 `json:"requestTimeoutBatchLookupSize"` + PruneMaxStoredRequests uint32 `json:"pruneMaxStoredRequests"` + PruneCheckFrequencySec uint32 `json:"pruneCheckFrequencySec"` + PruneBatchSize uint32 `json:"pruneBatchSize"` + ListenerEventHandlerTimeoutSec uint32 `json:"listenerEventHandlerTimeoutSec"` + ListenerEventsCheckFrequencyMillis uint32 `json:"listenerEventsCheckFrequencyMillis"` + ContractUpdateCheckFrequencySec uint32 `json:"contractUpdateCheckFrequencySec"` + MaxRequestSizeBytes uint32 `json:"maxRequestSizeBytes"` + MaxRequestSizesList []uint32 `json:"maxRequestSizesList"` + MaxSecretsSizesList []uint32 `json:"maxSecretsSizesList"` + MinimumSubscriptionBalance assets.Link `json:"minimumSubscriptionBalance"` + AllowedHeartbeatInitiators []string `json:"allowedHeartbeatInitiators"` + GatewayConnectorConfig *connector.ConnectorConfig `json:"gatewayConnectorConfig"` + OnchainAllowlist *allowlist.OnchainAllowlistConfig `json:"onchainAllowlist"` + OnchainSubscriptions *subscriptions.OnchainSubscriptionsConfig `json:"onchainSubscriptions"` + RateLimiter *common.RateLimiterConfig `json:"rateLimiter"` + S4Constraints *s4.Constraints `json:"s4Constraints"` + DecryptionQueueConfig *DecryptionQueueConfig `json:"decryptionQueueConfig"` } type DecryptionQueueConfig struct { diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index fd72b6fd38e..15e36f07b09 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -21,7 +21,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/functions" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" - gwFunctions "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" + gwAllowlist "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist" + gwSubscriptions "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -134,7 +135,11 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs allServices = append(allServices, job.NewServiceAdapter(functionsReportingPluginOracle)) if pluginConfig.GatewayConnectorConfig != nil && s4Storage != nil && pluginConfig.OnchainAllowlist != nil && pluginConfig.RateLimiter != nil && pluginConfig.OnchainSubscriptions != nil { - allowlist, err2 := gwFunctions.NewOnchainAllowlist(conf.Chain.Client(), *pluginConfig.OnchainAllowlist, conf.Logger) + allowlistORM, err := gwAllowlist.NewORM(conf.DB, conf.Logger, conf.QConfig, pluginConfig.OnchainAllowlist.ContractAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to create allowlist ORM") + } + allowlist, err2 := gwAllowlist.NewOnchainAllowlist(conf.Chain.Client(), *pluginConfig.OnchainAllowlist, allowlistORM, conf.Logger) if err2 != nil { return nil, errors.Wrap(err, "failed to create OnchainAllowlist") } @@ -142,11 +147,11 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs if err2 != nil { return nil, errors.Wrap(err, "failed to create a RateLimiter") } - gwFunctionsORM, err := gwFunctions.NewORM(conf.DB, conf.Logger, conf.QConfig, pluginConfig.OnchainSubscriptions.ContractAddress) + subscriptionsORM, err := gwSubscriptions.NewORM(conf.DB, conf.Logger, conf.QConfig, pluginConfig.OnchainSubscriptions.ContractAddress) if err != nil { - return nil, errors.Wrap(err, "failed to create functions ORM") + return nil, errors.Wrap(err, "failed to create subscriptions ORM") } - subscriptions, err2 := gwFunctions.NewOnchainSubscriptions(conf.Chain.Client(), *pluginConfig.OnchainSubscriptions, gwFunctionsORM, conf.Logger) + subscriptions, err2 := gwSubscriptions.NewOnchainSubscriptions(conf.Chain.Client(), *pluginConfig.OnchainSubscriptions, subscriptionsORM, conf.Logger) if err2 != nil { return nil, errors.Wrap(err, "failed to create a OnchainSubscriptions") } @@ -178,7 +183,7 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs return allServices, nil } -func NewConnector(pluginConfig *config.PluginConfig, ethKeystore keystore.Eth, chainID *big.Int, s4Storage s4.Storage, allowlist gwFunctions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions gwFunctions.OnchainSubscriptions, listener functions.FunctionsListener, offchainTransmitter functions.OffchainTransmitter, lggr logger.Logger) (connector.GatewayConnector, error) { +func NewConnector(pluginConfig *config.PluginConfig, ethKeystore keystore.Eth, chainID *big.Int, s4Storage s4.Storage, allowlist gwAllowlist.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions gwSubscriptions.OnchainSubscriptions, listener functions.FunctionsListener, offchainTransmitter functions.OffchainTransmitter, lggr logger.Logger) (connector.GatewayConnector, error) { enabledKeys, err := ethKeystore.EnabledKeysForChain(chainID) if err != nil { return nil, err diff --git a/core/services/ocr2/plugins/functions/plugin_test.go b/core/services/ocr2/plugins/functions/plugin_test.go index 6d3f57b086c..cd7956240df 100644 --- a/core/services/ocr2/plugins/functions/plugin_test.go +++ b/core/services/ocr2/plugins/functions/plugin_test.go @@ -12,7 +12,8 @@ import ( sfmocks "github.com/smartcontractkit/chainlink/v2/core/services/functions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" - gfmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" + gfaMocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/allowlist/mocks" + gfsMocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/subscriptions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions" @@ -32,8 +33,8 @@ func TestNewConnector_Success(t *testing.T) { chainID := big.NewInt(80001) ethKeystore := ksmocks.NewEth(t) s4Storage := s4mocks.NewStorage(t) - allowlist := gfmocks.NewOnchainAllowlist(t) - subscriptions := gfmocks.NewOnchainSubscriptions(t) + allowlist := gfaMocks.NewOnchainAllowlist(t) + subscriptions := gfsMocks.NewOnchainSubscriptions(t) rateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) listener := sfmocks.NewFunctionsListener(t) @@ -60,8 +61,8 @@ func TestNewConnector_NoKeyForConfiguredAddress(t *testing.T) { chainID := big.NewInt(80001) ethKeystore := ksmocks.NewEth(t) s4Storage := s4mocks.NewStorage(t) - allowlist := gfmocks.NewOnchainAllowlist(t) - subscriptions := gfmocks.NewOnchainSubscriptions(t) + allowlist := gfaMocks.NewOnchainAllowlist(t) + subscriptions := gfsMocks.NewOnchainSubscriptions(t) rateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) listener := sfmocks.NewFunctionsListener(t) diff --git a/core/store/migrate/migrations/0221_functions_allowlist.sql b/core/store/migrate/migrations/0221_functions_allowlist.sql new file mode 100644 index 00000000000..e97b2fc4077 --- /dev/null +++ b/core/store/migrate/migrations/0221_functions_allowlist.sql @@ -0,0 +1,22 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE functions_allowlist( + id BIGSERIAL, + router_contract_address bytea CHECK (octet_length(router_contract_address) = 20) NOT NULL, + allowed_address bytea CHECK (octet_length(allowed_address) = 20) NOT NULL, + PRIMARY KEY(router_contract_address, allowed_address) +); + +ALTER TABLE functions_subscriptions +ADD CONSTRAINT router_contract_address_octet_length CHECK (octet_length(router_contract_address) = 20); + +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin + +ALTER TABLE functions_subscriptions +DROP CONSTRAINT router_contract_address_octet_length; + +DROP TABLE IF EXISTS functions_allowlist; +-- +goose StatementEnd From 9550bbeeb13a205b3d7851f774d57d23c921f11b Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Wed, 24 Jan 2024 08:48:45 -0800 Subject: [PATCH 203/234] Add CHANGELOG for core v2.7.2 (#11876) --- docs/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4430c75ee4a..113ac50e701 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -108,6 +108,11 @@ Starting in `v2.9.0`: ... +## 2.7.2 - 2023-12-14 + +### Fixed + +- Fixed a bug that caused nodes without OCR or OCR2 enabled to fail config validation if `P2P.V2` was not explicitly disabled. With this fix, NOPs will not have to make changes to their config. ## 2.7.1 - 2023-11-21 From c308d46f3a9c16fc1769d822db1bf716ee6f226d Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Wed, 24 Jan 2024 12:05:31 -0500 Subject: [PATCH 204/234] add 2.2 contracts to go_generate file and fix master generation script (#11874) --- .../generate-automation-master-interface.ts | 12 +- .../v2_2/IAutomationRegistryMaster.sol | 12 +- .../dev/v2_2/AutomationRegistryBase2_2.sol | 8 +- .../automation/AutomationRegistry2_2.test.ts | 40 +- contracts/test/v0.8/automation/helpers.ts | 31 + .../automation_registrar_wrapper2_2.go | 1685 +++++ .../automation_utils_2_2.go | 322 + .../i_keeper_registry_master_wrapper_2_2.go | 6442 +++++++++++++++++ .../keeper_registry_logic_a_wrapper_2_2.go | 4863 +++++++++++++ .../keeper_registry_logic_b_wrapper_2_2.go | 5708 +++++++++++++++ .../keeper_registry_wrapper_2_2.go | 5169 +++++++++++++ ...rapper-dependency-versions-do-not-edit.txt | 6 + core/gethwrappers/go_generate.go | 8 +- 13 files changed, 24265 insertions(+), 41 deletions(-) create mode 100644 core/gethwrappers/generated/automation_registrar_wrapper2_2/automation_registrar_wrapper2_2.go create mode 100644 core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go create mode 100644 core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go create mode 100644 core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go create mode 100644 core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go create mode 100644 core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go diff --git a/contracts/scripts/generate-automation-master-interface.ts b/contracts/scripts/generate-automation-master-interface.ts index 2837fc34923..78c09cf2836 100644 --- a/contracts/scripts/generate-automation-master-interface.ts +++ b/contracts/scripts/generate-automation-master-interface.ts @@ -2,9 +2,9 @@ * @description this script generates a master interface for interacting with the automation registry * @notice run this script with pnpm ts-node ./scripts/generate-automation-master-interface.ts */ -import { KeeperRegistry2_2__factory as KeeperRegistry } from '../typechain/factories/KeeperRegistry2_2__factory' -import { KeeperRegistryLogicA2_2__factory as KeeperRegistryLogicA } from '../typechain/factories/KeeperRegistryLogicA2_2__factory' -import { KeeperRegistryLogicB2_2__factory as KeeperRegistryLogicB } from '../typechain/factories/KeeperRegistryLogicB2_2__factory' +import { AutomationRegistry2_2__factory as Registry } from '../typechain/factories/AutomationRegistry2_2__factory' +import { AutomationRegistryLogicA2_2__factory as RegistryLogicA } from '../typechain/factories/AutomationRegistryLogicA2_2__factory' +import { AutomationRegistryLogicB2_2__factory as RegistryLogicB } from '../typechain/factories/AutomationRegistryLogicB2_2__factory' import { utils } from 'ethers' import fs from 'fs' import { exec } from 'child_process' @@ -15,11 +15,7 @@ const tmpDest = `${dest}/tmp.txt` const combinedABI = [] const abiSet = new Set() -const abis = [ - KeeperRegistry.abi, - KeeperRegistryLogicA.abi, - KeeperRegistryLogicB.abi, -] +const abis = [Registry.abi, RegistryLogicA.abi, RegistryLogicB.abi] for (const abi of abis) { for (const entry of abi) { diff --git a/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol b/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol index b967c7d4b60..a72177bef2f 100644 --- a/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol +++ b/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol @@ -126,7 +126,7 @@ interface IAutomationRegistryMaster { address[] memory signers, address[] memory transmitters, uint8 f, - KeeperRegistryBase2_2.OnchainConfig memory onchainConfig, + AutomationRegistryBase2_2.OnchainConfig memory onchainConfig, uint64 offchainConfigVersion, bytes memory offchainConfig ) external; @@ -229,8 +229,8 @@ interface IAutomationRegistryMaster { external view returns ( - KeeperRegistryBase2_2.State memory state, - KeeperRegistryBase2_2.OnchainConfig memory config, + AutomationRegistryBase2_2.State memory state, + AutomationRegistryBase2_2.OnchainConfig memory config, address[] memory signers, address[] memory transmitters, uint8 f @@ -239,7 +239,7 @@ interface IAutomationRegistryMaster { address query ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee); function getTriggerType(uint256 upkeepId) external pure returns (uint8); - function getUpkeep(uint256 id) external view returns (KeeperRegistryBase2_2.UpkeepInfo memory upkeepInfo); + function getUpkeep(uint256 id) external view returns (AutomationRegistryBase2_2.UpkeepInfo memory upkeepInfo); function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory); function getUpkeepTriggerConfig(uint256 upkeepId) external view returns (bytes memory); function hasDedupKey(bytes32 dedupKey) external view returns (bool); @@ -264,7 +264,7 @@ interface IAutomationRegistryMaster { function withdrawPayment(address from, address to) external; } -interface KeeperRegistryBase2_2 { +interface AutomationRegistryBase2_2 { struct OnchainConfig { uint32 paymentPremiumPPB; uint32 flatFeeMicroLink; @@ -312,5 +312,5 @@ interface KeeperRegistryBase2_2 { // THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON: /* -[{"inputs":[{"internalType":"contract KeeperRegistryLogicB2_2","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MaxCheckDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MaxPerformDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"PaymentGreaterThanAllLINK","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"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":"address[]","name":"signers","type":"address[]"},{"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":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"OwnerFundsWithdrawn","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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","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":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct KeeperRegistryBase2_2.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract KeeperRegistryLogicB2_2","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum KeeperRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum KeeperRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum KeeperRegistryBase2_2.Mode","name":"mode","type":"uint8"},{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkNativeFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkNativeFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum KeeperRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMode","outputs":[{"internalType":"enum KeeperRegistryBase2_2.Mode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum KeeperRegistryBase2_2.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerPerformByteGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct KeeperRegistryBase2_2.State","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct KeeperRegistryBase2_2.OnchainConfig","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum KeeperRegistryBase2_2.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct KeeperRegistryBase2_2.UpkeepInfo","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum KeeperRegistryBase2_2.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepTranscoderVersion","outputs":[{"internalType":"enum UpkeepFormat","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwnerFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] +[{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_2","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MaxCheckDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MaxPerformDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"PaymentGreaterThanAllLINK","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"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":"address[]","name":"signers","type":"address[]"},{"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":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"OwnerFundsWithdrawn","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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","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":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct AutomationRegistryBase2_2.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_2","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum AutomationRegistryBase2_2.Mode","name":"mode","type":"uint8"},{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkNativeFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkNativeFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMode","outputs":[{"internalType":"enum AutomationRegistryBase2_2.Mode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum AutomationRegistryBase2_2.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerPerformByteGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct AutomationRegistryBase2_2.State","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct AutomationRegistryBase2_2.OnchainConfig","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct AutomationRegistryBase2_2.UpkeepInfo","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum AutomationRegistryBase2_2.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepTranscoderVersion","outputs":[{"internalType":"enum UpkeepFormat","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwnerFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] */ diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol index b3e970e6c35..e4b6e61d6f7 100644 --- a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol @@ -740,7 +740,7 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi if (!_validateConditionalTrigger(upkeepId, rawTrigger, transmitInfo)) return (false, dedupID); } else if (transmitInfo.triggerType == Trigger.LOG) { bool valid; - (valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger, transmitInfo); + (valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger); if (!valid) return (false, dedupID); } else { revert InvalidTriggerType(); @@ -789,11 +789,7 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi return true; } - function _validateLogTrigger( - uint256 upkeepId, - bytes memory rawTrigger, - UpkeepTransmitInfo memory transmitInfo - ) internal returns (bool, bytes32) { + function _validateLogTrigger(uint256 upkeepId, bytes memory rawTrigger) internal returns (bool, bytes32) { LogTrigger memory trigger = abi.decode(rawTrigger, (LogTrigger)); bytes32 dedupID = keccak256(abi.encodePacked(upkeepId, trigger.logBlockHash, trigger.txHash, trigger.logIndex)); if ( diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts index 3822d362240..bb460aa60c9 100644 --- a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts +++ b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts @@ -34,17 +34,17 @@ import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder' import { UpkeepAutoFunder } from '../../../typechain' import { CancelledUpkeepReportEvent, - IKeeperRegistryMaster as IKeeperRegistry, + IAutomationRegistryMaster as IAutomationRegistry, InsufficientFundsUpkeepReportEvent, ReorgedUpkeepReportEvent, StaleUpkeepReportEvent, UpkeepPerformedEvent, -} from '../../../typechain/IKeeperRegistryMaster' +} from '../../../typechain/IAutomationRegistryMaster' import { deployMockContract, MockContract, } from '@ethereum-waffle/mock-contract' -import { deployRegistry21 } from './helpers' +import { deployRegistry22 } from './helpers' const describeMaybe = process.env.SKIP_SLOW ? describe.skip : describe const itMaybe = process.env.SKIP_SLOW ? it.skip : it @@ -70,7 +70,7 @@ enum Mode { OPTIMISM, } -// copied from KeeperRegistryBase2_2.sol +// copied from AutomationRegistryBase2_2.sol enum Trigger { CONDITION, LOG, @@ -149,11 +149,11 @@ let personas: Personas let linkToken: Contract let linkEthFeed: MockV3Aggregator let gasPriceFeed: MockV3Aggregator -let registry: IKeeperRegistry // default registry, used for most tests -let arbRegistry: IKeeperRegistry // arbitrum registry -let opRegistry: IKeeperRegistry // optimism registry -let mgRegistry: IKeeperRegistry // "migrate registry" used in migration tests -let blankRegistry: IKeeperRegistry // used to test initial configurations +let registry: IAutomationRegistry // default registry, used for most tests +let arbRegistry: IAutomationRegistry // arbitrum registry +let opRegistry: IAutomationRegistry // optimism registry +let mgRegistry: IAutomationRegistry // "migrate registry" used in migration tests +let blankRegistry: IAutomationRegistry // used to test initial configurations let mock: UpkeepMock let autoFunderUpkeep: UpkeepAutoFunder let ltUpkeep: MockContract @@ -388,7 +388,7 @@ const parseCancelledUpkeepReportLogs = (receipt: ContractReceipt) => { return parsedLogs } -describe('KeeperRegistry2_2', () => { +describe('AutomationRegistry2_2', () => { let owner: Signer let keeper1: Signer let keeper2: Signer @@ -418,7 +418,7 @@ describe('KeeperRegistry2_2', () => { let signers: Wallet[] let signerAddresses: string[] let config: any - let baseConfig: Parameters + let baseConfig: Parameters let upkeepManager: string before(async () => { @@ -564,7 +564,7 @@ describe('KeeperRegistry2_2', () => { } const verifyMaxPayment = async ( - registry: IKeeperRegistry, + registry: IAutomationRegistry, l1CostWei?: BigNumber, ) => { type TestCase = { @@ -708,7 +708,7 @@ describe('KeeperRegistry2_2', () => { } const getTransmitTx = async ( - registry: IKeeperRegistry, + registry: IAutomationRegistry, transmitter: Signer, upkeepIds: BigNumber[], overrides: GetTransmitTXOptions = {}, @@ -794,7 +794,7 @@ describe('KeeperRegistry2_2', () => { } const getTransmitTxWithReport = async ( - registry: IKeeperRegistry, + registry: IAutomationRegistry, transmitter: Signer, report: BytesLike, ) => { @@ -891,7 +891,7 @@ describe('KeeperRegistry2_2', () => { offchainBytes, ] - registry = await deployRegistry21( + registry = await deployRegistry22( owner, Mode.DEFAULT, linkToken.address, @@ -899,7 +899,7 @@ describe('KeeperRegistry2_2', () => { gasPriceFeed.address, ) - arbRegistry = await deployRegistry21( + arbRegistry = await deployRegistry22( owner, Mode.ARBITRUM, linkToken.address, @@ -907,7 +907,7 @@ describe('KeeperRegistry2_2', () => { gasPriceFeed.address, ) - opRegistry = await deployRegistry21( + opRegistry = await deployRegistry22( owner, Mode.OPTIMISM, linkToken.address, @@ -915,7 +915,7 @@ describe('KeeperRegistry2_2', () => { gasPriceFeed.address, ) - mgRegistry = await deployRegistry21( + mgRegistry = await deployRegistry22( owner, Mode.DEFAULT, linkToken.address, @@ -923,7 +923,7 @@ describe('KeeperRegistry2_2', () => { gasPriceFeed.address, ) - blankRegistry = await deployRegistry21( + blankRegistry = await deployRegistry22( owner, Mode.DEFAULT, linkToken.address, @@ -3453,7 +3453,7 @@ describe('KeeperRegistry2_2', () => { describe('#typeAndVersion', () => { it('uses the correct type and version', async () => { const typeAndVersion = await registry.typeAndVersion() - assert.equal(typeAndVersion, 'KeeperRegistry 2.1.0') + assert.equal(typeAndVersion, 'AutomationRegistry 2.2.0') }) }) diff --git a/contracts/test/v0.8/automation/helpers.ts b/contracts/test/v0.8/automation/helpers.ts index e2aa762577a..3490bc72c4d 100644 --- a/contracts/test/v0.8/automation/helpers.ts +++ b/contracts/test/v0.8/automation/helpers.ts @@ -3,6 +3,9 @@ import { ethers } from 'hardhat' import { KeeperRegistryLogicB2_1__factory as KeeperRegistryLogicBFactory } from '../../../typechain/factories/KeeperRegistryLogicB2_1__factory' import { IKeeperRegistryMaster as IKeeperRegistry } from '../../../typechain/IKeeperRegistryMaster' import { IKeeperRegistryMaster__factory as IKeeperRegistryMasterFactory } from '../../../typechain/factories/IKeeperRegistryMaster__factory' +import { AutomationRegistryLogicB2_2__factory as AutomationRegistryLogicBFactory } from '../../../typechain/factories/AutomationRegistryLogicB2_2__factory' +import { IAutomationRegistryMaster as IAutomationRegistry } from '../../../typechain/IAutomationRegistryMaster' +import { IAutomationRegistryMaster__factory as IAutomationRegistryMasterFactory } from '../../../typechain/factories/IAutomationRegistryMaster__factory' export const deployRegistry21 = async ( from: Signer, @@ -29,3 +32,31 @@ export const deployRegistry21 = async ( const master = await registryFactory.connect(from).deploy(logicA.address) return IKeeperRegistryMasterFactory.connect(master.address, from) } + +export const deployRegistry22 = async ( + from: Signer, + mode: Parameters[0], + link: Parameters[1], + linkNative: Parameters[2], + fastgas: Parameters[3], +): Promise => { + const logicBFactory = await ethers.getContractFactory( + 'AutomationRegistryLogicB2_2', + ) + const logicAFactory = await ethers.getContractFactory( + 'AutomationRegistryLogicA2_2', + ) + const registryFactory = await ethers.getContractFactory( + 'AutomationRegistry2_2', + ) + const forwarderLogicFactory = await ethers.getContractFactory( + 'AutomationForwarderLogic', + ) + const forwarderLogic = await forwarderLogicFactory.connect(from).deploy() + const logicB = await logicBFactory + .connect(from) + .deploy(mode, link, linkNative, fastgas, forwarderLogic.address) + const logicA = await logicAFactory.connect(from).deploy(logicB.address) + const master = await registryFactory.connect(from).deploy(logicA.address) + return IAutomationRegistryMasterFactory.connect(master.address, from) +} diff --git a/core/gethwrappers/generated/automation_registrar_wrapper2_2/automation_registrar_wrapper2_2.go b/core/gethwrappers/generated/automation_registrar_wrapper2_2/automation_registrar_wrapper2_2.go new file mode 100644 index 00000000000..c8a84800447 --- /dev/null +++ b/core/gethwrappers/generated/automation_registrar_wrapper2_2/automation_registrar_wrapper2_2.go @@ -0,0 +1,1685 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package automation_registrar_wrapper2_2 + +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 AutomationRegistrar22InitialTriggerConfig struct { + TriggerType uint8 + AutoApproveType uint8 + AutoApproveMaxAllowed uint32 +} + +type AutomationRegistrar22RegistrationParams struct { + Name string + EncryptedEmail []byte + UpkeepContract common.Address + GasLimit uint32 + AdminAddress common.Address + TriggerType uint8 + CheckData []byte + TriggerConfig []byte + OffchainConfig []byte + Amount *big.Int +} + +type AutomationRegistrar22TriggerRegistrationStorage struct { + AutoApproveType uint8 + AutoApproveMaxAllowed uint32 + ApprovedCount uint32 +} + +var AutomationRegistrarMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"LINKAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"AutomationRegistry\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"minLINKJuels\",\"type\":\"uint96\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"enumAutomationRegistrar2_2.AutoApproveType\",\"name\":\"autoApproveType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"autoApproveMaxAllowed\",\"type\":\"uint32\"}],\"internalType\":\"structAutomationRegistrar2_2.InitialTriggerConfig[]\",\"name\":\"triggerConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AmountMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FunctionNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HashMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientPayment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidAdminAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"LinkTransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyAdminOrOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistrationRequestFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RequestNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SenderMismatch\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"name\":\"AutoApproveAllowedSenderSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"AutomationRegistry\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"minLINKJuels\",\"type\":\"uint96\"}],\"name\":\"ConfigChanged\",\"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\":true,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"displayName\",\"type\":\"string\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"RegistrationApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"RegistrationRejected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"encryptedEmail\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"upkeepContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"RegistrationRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"enumAutomationRegistrar2_2.AutoApproveType\",\"name\":\"autoApproveType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"autoApproveMaxAllowed\",\"type\":\"uint32\"}],\"name\":\"TriggerConfigSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"upkeepContract\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"cancel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"}],\"name\":\"getAutoApproveAllowedSender\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"AutomationRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minLINKJuels\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"getPendingRequest\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"}],\"name\":\"getTriggerRegistrationDetails\",\"outputs\":[{\"components\":[{\"internalType\":\"enumAutomationRegistrar2_2.AutoApproveType\",\"name\":\"autoApproveType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"autoApproveMaxAllowed\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"approvedCount\",\"type\":\"uint32\"}],\"internalType\":\"structAutomationRegistrar2_2.TriggerRegistrationStorage\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"encryptedEmail\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"upkeepContract\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"encryptedEmail\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"upkeepContract\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"internalType\":\"structAutomationRegistrar2_2.RegistrationParams\",\"name\":\"requestParams\",\"type\":\"tuple\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"name\":\"setAutoApproveAllowedSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"AutomationRegistry\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"minLINKJuels\",\"type\":\"uint96\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"enumAutomationRegistrar2_2.AutoApproveType\",\"name\":\"autoApproveType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"autoApproveMaxAllowed\",\"type\":\"uint32\"}],\"name\":\"setTriggerConfig\",\"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: "0x60a06040523480156200001157600080fd5b5060405162002d8238038062002d8283398101604081905262000034916200043b565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be816200017a565b5050506001600160a01b038416608052620000da838362000225565b60005b81518110156200016f576200015a82828151811062000100576200010062000598565b60200260200101516000015183838151811062000121576200012162000598565b60200260200101516020015184848151811062000142576200014262000598565b6020026020010151604001516200029e60201b60201c565b806200016681620005ae565b915050620000dd565b50505050506200062f565b336001600160a01b03821603620001d45760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200022f6200034c565b6040805180820182526001600160a01b0384168082526001600160601b0384166020928301819052600160a01b810282176004558351918252918101919091527f39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a910160405180910390a15050565b620002a86200034c565b60ff83166000908152600360205260409020805483919060ff19166001836002811115620002da57620002da620005d6565b021790555060ff831660009081526003602052604090819020805464ffffffff00191661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a3906200033f90859085908590620005ec565b60405180910390a1505050565b6000546001600160a01b03163314620003a85760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b80516001600160a01b0381168114620003c257600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715620004025762000402620003c7565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620004335762000433620003c7565b604052919050565b600080600080608085870312156200045257600080fd5b6200045d85620003aa565b935060206200046e818701620003aa565b604087810151919550906001600160601b03811681146200048e57600080fd5b606088810151919550906001600160401b0380821115620004ae57600080fd5b818a0191508a601f830112620004c357600080fd5b815181811115620004d857620004d8620003c7565b620004e8868260051b0162000408565b818152868101925090840283018601908c8211156200050657600080fd5b928601925b81841015620005875784848e031215620005255760008081fd5b6200052f620003dd565b845160ff81168114620005425760008081fd5b81528488015160038110620005575760008081fd5b818901528487015163ffffffff81168114620005735760008081fd5b81880152835292840192918601916200050b565b999c989b5096995050505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201620005cf57634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052602160045260246000fd5b60ff8416815260608101600384106200061557634e487b7160e01b600052602160045260246000fd5b83602083015263ffffffff83166040830152949350505050565b6080516127146200066e60003960008181610177015281816105d601528181610887015281816109bd01528181610f0e015261171b01526127146000f3fe608060405234801561001057600080fd5b506004361061011b5760003560e01c8063856853e6116100b2578063b5ff5b4111610081578063c4d252f511610066578063c4d252f5146103e3578063e8d4070d146103f6578063f2fde38b1461040957600080fd5b8063b5ff5b4114610369578063c3f909d41461037c57600080fd5b8063856853e61461027857806388b12d551461028b5780638da5cb5b14610338578063a4c0ed361461035657600080fd5b80633f678e11116100ee5780633f678e11146101f35780636c4cdfc31461021457806379ba5097146102275780637e776f7f1461022f57600080fd5b8063181f5a77146101205780631b6b6d2314610172578063212d0884146101be578063367b9b4f146101de575b600080fd5b61015c6040518060400160405280601981526020017f4175746f6d6174696f6e52656769737472617220322e312e300000000000000081525081565b6040516101699190611a74565b60405180910390f35b6101997f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610169565b6101d16101cc366004611aa4565b61041c565b6040516101699190611b29565b6101f16101ec366004611b9d565b6104a9565b005b610206610201366004611bd6565b61053b565b604051908152602001610169565b6101f1610222366004611c2e565b6106d3565b6101f161076d565b61026861023d366004611c63565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205460ff1690565b6040519015158152602001610169565b6101f1610286366004611de1565b61086f565b6102ff610299366004611f40565b60009081526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff169290910182905291565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526bffffffffffffffffffffffff909116602083015201610169565b60005473ffffffffffffffffffffffffffffffffffffffff16610199565b6101f1610364366004611f59565b6109a5565b6101f1610377366004611fb5565b610ce3565b60408051808201825260045473ffffffffffffffffffffffffffffffffffffffff8116808352740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff16602092830181905283519182529181019190915201610169565b6101f16103f1366004611f40565b610dc2565b6101f1610404366004611ffe565b61104c565b6101f1610417366004611c63565b6112d9565b60408051606080820183526000808352602080840182905283850182905260ff86811683526003909152908490208451928301909452835492939192839116600281111561046c5761046c611abf565b600281111561047d5761047d611abf565b8152905463ffffffff610100820481166020840152650100000000009091041660409091015292915050565b6104b16112ed565b73ffffffffffffffffffffffffffffffffffffffff821660008181526005602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527f20c6237dac83526a849285a9f79d08a483291bdd3a056a0ef9ae94ecee1ad356910160405180910390a25050565b6004546000907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1661057961014084016101208501612109565b6bffffffffffffffffffffffff1610156105bf576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166323b872dd333061060f61014087016101208801612109565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526bffffffffffffffffffffffff1660448201526064016020604051808303816000875af1158015610696573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ba9190612124565b506106cd6106c783612141565b33611370565b92915050565b6106db6112ed565b60408051808201825273ffffffffffffffffffffffffffffffffffffffff84168082526bffffffffffffffffffffffff8416602092830181905274010000000000000000000000000000000000000000810282176004558351918252918101919091527f39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a910160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146107f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146108de576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109966040518061014001604052808e81526020018d8d8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525073ffffffffffffffffffffffffffffffffffffffff808d16602083015263ffffffff8c1660408301528a16606082015260ff8916608082015260a0810188905260c0810187905260e081018690526bffffffffffffffffffffffff85166101009091015282611370565b50505050505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a14576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81818080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101517fffffffff0000000000000000000000000000000000000000000000000000000081167f856853e60000000000000000000000000000000000000000000000000000000014610aca576040517fe3d6792100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484846000610adc8260048186612276565b810190610ae991906122a0565b509950505050505050505050806bffffffffffffffffffffffff168414610b3c576040517f55e97b0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8988886000610b4e8260048186612276565b810190610b5b91906122a0565b9a50505050505050505050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610bcc576040517ff8c5638e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff168d1015610c2e576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60003073ffffffffffffffffffffffffffffffffffffffff168d8d604051610c579291906123dd565b600060405180830381855af49150503d8060008114610c92576040519150601f19603f3d011682016040523d82523d6000602084013e610c97565b606091505b5050905080610cd2576040517f649bf81000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050505050565b610ceb6112ed565b60ff8316600090815260036020526040902080548391907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836002811115610d3857610d38611abf565b021790555060ff83166000908152600360205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a390610db5908590859085906123ed565b60405180910390a1505050565b60008181526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff1691830191909152331480610e49575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610e7f576040517f61685c2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16610ecd576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260026020908152604080832083905583519184015190517fa9059cbb0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169263a9059cbb92610f859260040173ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b6020604051808303816000875af1158015610fa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc89190612124565b90508061101c5781516040517fc2e4dce800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107ea565b60405183907f3663fb28ebc87645eb972c9dad8521bf665c623f287e79f1c56f1eb374b82a2290600090a2505050565b6110546112ed565b60008181526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff16918301919091526110ed576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008b8b8b8b8b8b8b8b8b60405160200161111099989796959493929190612461565b604051602081830303815290604052805190602001209050808314611161576040517f3f4d605300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026000848152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556000820160146101000a8154906bffffffffffffffffffffffff021916905550506112c96040518061014001604052808f81526020016040518060200160405280600081525081526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018d63ffffffff1681526020018c73ffffffffffffffffffffffffffffffffffffffff1681526020018b60ff1681526020018a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060208082018a905260408051601f8a0183900483028101830182528981529201919089908990819084018382808284376000920191909152505050908252506020858101516bffffffffffffffffffffffff1691015282611647565b5050505050505050505050505050565b6112e16112ed565b6112ea81611876565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461136e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107ea565b565b608082015160009073ffffffffffffffffffffffffffffffffffffffff166113c4576040517f05bb467c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360400151846060015185608001518660a001518760c001518860e0015189610100015160405160200161140097969594939291906124e7565b604051602081830303815290604052805190602001209050836040015173ffffffffffffffffffffffffffffffffffffffff16817f7684390ebb103102f7f48c71439c2408713f8d437782a6fab2756acc0e42c1b786600001518760200151886060015189608001518a60a001518b60e001518c61010001518d60c001518e610120015160405161149999989796959493929190612569565b60405180910390a360a084015160ff9081166000908152600360205260408082208151606081019092528054929361151c9383911660028111156114df576114df611abf565b60028111156114f0576114f0611abf565b8152905463ffffffff61010082048116602084015265010000000000909104166040909101528561196b565b156115845760a085015160ff166000908152600360205260409020805465010000000000900463ffffffff1690600561155483612653565b91906101000a81548163ffffffff021916908363ffffffff1602179055505061157d8583611647565b905061163f565b61012085015160008381526002602052604081205490916115ca917401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16612676565b604080518082018252608089015173ffffffffffffffffffffffffffffffffffffffff90811682526bffffffffffffffffffffffff9384166020808401918252600089815260029091529390932091519251909316740100000000000000000000000000000000000000000291909216179055505b949350505050565b600480546040808501516060860151608087015160a088015160c089015160e08a01516101008b015196517f28f32f3800000000000000000000000000000000000000000000000000000000815260009973ffffffffffffffffffffffffffffffffffffffff909916988a988a986328f32f38986116d29891979096919590949193909291016124e7565b6020604051808303816000875af11580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171591906126a2565b905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634000aea0848861012001518560405160200161176f91815260200190565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161179c939291906126bb565b6020604051808303816000875af11580156117bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117df9190612124565b905080611830576040517fc2e4dce800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016107ea565b81857fb9a292fb7e3edd920cd2d2829a3615a640c43fd7de0a0820aa0668feb4c37d4b88600001516040516118659190611a74565b60405180910390a350949350505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036118f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107ea565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000808351600281111561198157611981611abf565b0361198e575060006106cd565b6001835160028111156119a3576119a3611abf565b1480156119d6575073ffffffffffffffffffffffffffffffffffffffff821660009081526005602052604090205460ff16155b156119e3575060006106cd565b826020015163ffffffff16836040015163ffffffff161015611a07575060016106cd565b50600092915050565b6000815180845260005b81811015611a3657602081850181015186830182015201611a1a565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611a876020830184611a10565b9392505050565b803560ff81168114611a9f57600080fd5b919050565b600060208284031215611ab657600080fd5b611a8782611a8e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611b25577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000606082019050611b3c828451611aee565b602083015163ffffffff8082166020850152806040860151166040850152505092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146112ea57600080fd5b8035611a9f81611b62565b80151581146112ea57600080fd5b60008060408385031215611bb057600080fd5b8235611bbb81611b62565b91506020830135611bcb81611b8f565b809150509250929050565b600060208284031215611be857600080fd5b813567ffffffffffffffff811115611bff57600080fd5b82016101408185031215611a8757600080fd5b80356bffffffffffffffffffffffff81168114611a9f57600080fd5b60008060408385031215611c4157600080fd5b8235611c4c81611b62565b9150611c5a60208401611c12565b90509250929050565b600060208284031215611c7557600080fd5b8135611a8781611b62565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715611cd357611cd3611c80565b60405290565b600082601f830112611cea57600080fd5b813567ffffffffffffffff80821115611d0557611d05611c80565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715611d4b57611d4b611c80565b81604052838152866020858801011115611d6457600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008083601f840112611d9657600080fd5b50813567ffffffffffffffff811115611dae57600080fd5b602083019150836020828501011115611dc657600080fd5b9250929050565b803563ffffffff81168114611a9f57600080fd5b6000806000806000806000806000806000806101608d8f031215611e0457600080fd5b67ffffffffffffffff8d351115611e1a57600080fd5b611e278e8e358f01611cd9565b9b5067ffffffffffffffff60208e01351115611e4257600080fd5b611e528e60208f01358f01611d84565b909b509950611e6360408e01611b84565b9850611e7160608e01611dcd565b9750611e7f60808e01611b84565b9650611e8d60a08e01611a8e565b955067ffffffffffffffff60c08e01351115611ea857600080fd5b611eb88e60c08f01358f01611cd9565b945067ffffffffffffffff60e08e01351115611ed357600080fd5b611ee38e60e08f01358f01611cd9565b935067ffffffffffffffff6101008e01351115611eff57600080fd5b611f108e6101008f01358f01611cd9565b9250611f1f6101208e01611c12565b9150611f2e6101408e01611b84565b90509295989b509295989b509295989b565b600060208284031215611f5257600080fd5b5035919050565b60008060008060608587031215611f6f57600080fd5b8435611f7a81611b62565b935060208501359250604085013567ffffffffffffffff811115611f9d57600080fd5b611fa987828801611d84565b95989497509550505050565b600080600060608486031215611fca57600080fd5b611fd384611a8e565b9250602084013560038110611fe757600080fd5b9150611ff560408501611dcd565b90509250925092565b60008060008060008060008060008060006101208c8e03121561202057600080fd5b67ffffffffffffffff808d35111561203757600080fd5b6120448e8e358f01611cd9565b9b5061205260208e01611b84565b9a5061206060408e01611dcd565b995061206e60608e01611b84565b985061207c60808e01611a8e565b97508060a08e0135111561208f57600080fd5b61209f8e60a08f01358f01611d84565b909750955060c08d01358110156120b557600080fd5b6120c58e60c08f01358f01611cd9565b94508060e08e013511156120d857600080fd5b506120e98d60e08e01358e01611d84565b81945080935050506101008c013590509295989b509295989b9093969950565b60006020828403121561211b57600080fd5b611a8782611c12565b60006020828403121561213657600080fd5b8151611a8781611b8f565b6000610140823603121561215457600080fd5b61215c611caf565b823567ffffffffffffffff8082111561217457600080fd5b61218036838701611cd9565b8352602085013591508082111561219657600080fd5b6121a236838701611cd9565b60208401526121b360408601611b84565b60408401526121c460608601611dcd565b60608401526121d560808601611b84565b60808401526121e660a08601611a8e565b60a084015260c08501359150808211156121ff57600080fd5b61220b36838701611cd9565b60c084015260e085013591508082111561222457600080fd5b61223036838701611cd9565b60e08401526101009150818501358181111561224b57600080fd5b61225736828801611cd9565b8385015250505061012061226c818501611c12565b9082015292915050565b6000808585111561228657600080fd5b8386111561229357600080fd5b5050820193919092039150565b60008060008060008060008060008060006101608c8e0312156122c257600080fd5b67ffffffffffffffff808d3511156122d957600080fd5b6122e68e8e358f01611cd9565b9b508060208e013511156122f957600080fd5b6123098e60208f01358f01611cd9565b9a5061231760408e01611b84565b995061232560608e01611dcd565b985061233360808e01611b84565b975061234160a08e01611a8e565b96508060c08e0135111561235457600080fd5b6123648e60c08f01358f01611cd9565b95508060e08e0135111561237757600080fd5b6123878e60e08f01358f01611cd9565b9450806101008e0135111561239b57600080fd5b506123ad8d6101008e01358e01611cd9565b92506123bc6101208d01611c12565b91506123cb6101408d01611b84565b90509295989b509295989b9093969950565b8183823760009101908152919050565b60ff84168152606081016124046020830185611aee565b63ffffffff83166040830152949350505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808c16835263ffffffff8b166020840152808a1660408401525060ff8816606083015260e060808301526124b060e083018789612418565b82810360a08401526124c28187611a10565b905082810360c08401526124d7818587612418565b9c9b505050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808a16835263ffffffff8916602084015280881660408401525060ff8616606083015260e0608083015261253560e0830186611a10565b82810360a08401526125478186611a10565b905082810360c084015261255b8185611a10565b9a9950505050505050505050565b600061012080835261257d8184018d611a10565b90508281036020840152612591818c611a10565b905063ffffffff8a16604084015273ffffffffffffffffffffffffffffffffffffffff8916606084015260ff8816608084015282810360a08401526125d68188611a10565b905082810360c08401526125ea8187611a10565b905082810360e08401526125fe8186611a10565b9150506bffffffffffffffffffffffff83166101008301529a9950505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff80831681810361266c5761266c612624565b6001019392505050565b6bffffffffffffffffffffffff81811683821601908082111561269b5761269b612624565b5092915050565b6000602082840312156126b457600080fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff841681526bffffffffffffffffffffffff831660208201526060604082015260006126fe6060830184611a10565b9594505050505056fea164736f6c6343000810000a", +} + +var AutomationRegistrarABI = AutomationRegistrarMetaData.ABI + +var AutomationRegistrarBin = AutomationRegistrarMetaData.Bin + +func DeployAutomationRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, LINKAddress common.Address, AutomationRegistry common.Address, minLINKJuels *big.Int, triggerConfigs []AutomationRegistrar22InitialTriggerConfig) (common.Address, *types.Transaction, *AutomationRegistrar, error) { + parsed, err := AutomationRegistrarMetaData.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(AutomationRegistrarBin), backend, LINKAddress, AutomationRegistry, minLINKJuels, triggerConfigs) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &AutomationRegistrar{address: address, abi: *parsed, AutomationRegistrarCaller: AutomationRegistrarCaller{contract: contract}, AutomationRegistrarTransactor: AutomationRegistrarTransactor{contract: contract}, AutomationRegistrarFilterer: AutomationRegistrarFilterer{contract: contract}}, nil +} + +type AutomationRegistrar struct { + address common.Address + abi abi.ABI + AutomationRegistrarCaller + AutomationRegistrarTransactor + AutomationRegistrarFilterer +} + +type AutomationRegistrarCaller struct { + contract *bind.BoundContract +} + +type AutomationRegistrarTransactor struct { + contract *bind.BoundContract +} + +type AutomationRegistrarFilterer struct { + contract *bind.BoundContract +} + +type AutomationRegistrarSession struct { + Contract *AutomationRegistrar + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type AutomationRegistrarCallerSession struct { + Contract *AutomationRegistrarCaller + CallOpts bind.CallOpts +} + +type AutomationRegistrarTransactorSession struct { + Contract *AutomationRegistrarTransactor + TransactOpts bind.TransactOpts +} + +type AutomationRegistrarRaw struct { + Contract *AutomationRegistrar +} + +type AutomationRegistrarCallerRaw struct { + Contract *AutomationRegistrarCaller +} + +type AutomationRegistrarTransactorRaw struct { + Contract *AutomationRegistrarTransactor +} + +func NewAutomationRegistrar(address common.Address, backend bind.ContractBackend) (*AutomationRegistrar, error) { + abi, err := abi.JSON(strings.NewReader(AutomationRegistrarABI)) + if err != nil { + return nil, err + } + contract, err := bindAutomationRegistrar(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AutomationRegistrar{address: address, abi: abi, AutomationRegistrarCaller: AutomationRegistrarCaller{contract: contract}, AutomationRegistrarTransactor: AutomationRegistrarTransactor{contract: contract}, AutomationRegistrarFilterer: AutomationRegistrarFilterer{contract: contract}}, nil +} + +func NewAutomationRegistrarCaller(address common.Address, caller bind.ContractCaller) (*AutomationRegistrarCaller, error) { + contract, err := bindAutomationRegistrar(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AutomationRegistrarCaller{contract: contract}, nil +} + +func NewAutomationRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*AutomationRegistrarTransactor, error) { + contract, err := bindAutomationRegistrar(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AutomationRegistrarTransactor{contract: contract}, nil +} + +func NewAutomationRegistrarFilterer(address common.Address, filterer bind.ContractFilterer) (*AutomationRegistrarFilterer, error) { + contract, err := bindAutomationRegistrar(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AutomationRegistrarFilterer{contract: contract}, nil +} + +func bindAutomationRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AutomationRegistrarMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_AutomationRegistrar *AutomationRegistrarRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistrar.Contract.AutomationRegistrarCaller.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistrar *AutomationRegistrarRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.AutomationRegistrarTransactor.contract.Transfer(opts) +} + +func (_AutomationRegistrar *AutomationRegistrarRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.AutomationRegistrarTransactor.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistrar.Contract.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.contract.Transfer(opts) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) LINK(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "LINK") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) LINK() (common.Address, error) { + return _AutomationRegistrar.Contract.LINK(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) LINK() (common.Address, error) { + return _AutomationRegistrar.Contract.LINK(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) GetAutoApproveAllowedSender(opts *bind.CallOpts, senderAddress common.Address) (bool, error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "getAutoApproveAllowedSender", senderAddress) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) GetAutoApproveAllowedSender(senderAddress common.Address) (bool, error) { + return _AutomationRegistrar.Contract.GetAutoApproveAllowedSender(&_AutomationRegistrar.CallOpts, senderAddress) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) GetAutoApproveAllowedSender(senderAddress common.Address) (bool, error) { + return _AutomationRegistrar.Contract.GetAutoApproveAllowedSender(&_AutomationRegistrar.CallOpts, senderAddress) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) GetConfig(opts *bind.CallOpts) (GetConfig, + + error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "getConfig") + + outstruct := new(GetConfig) + if err != nil { + return *outstruct, err + } + + outstruct.AutomationRegistry = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + outstruct.MinLINKJuels = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) GetConfig() (GetConfig, + + error) { + return _AutomationRegistrar.Contract.GetConfig(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) GetConfig() (GetConfig, + + error) { + return _AutomationRegistrar.Contract.GetConfig(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) GetPendingRequest(opts *bind.CallOpts, hash [32]byte) (common.Address, *big.Int, error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "getPendingRequest", hash) + + if err != nil { + return *new(common.Address), *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return out0, out1, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) GetPendingRequest(hash [32]byte) (common.Address, *big.Int, error) { + return _AutomationRegistrar.Contract.GetPendingRequest(&_AutomationRegistrar.CallOpts, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) GetPendingRequest(hash [32]byte) (common.Address, *big.Int, error) { + return _AutomationRegistrar.Contract.GetPendingRequest(&_AutomationRegistrar.CallOpts, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) GetTriggerRegistrationDetails(opts *bind.CallOpts, triggerType uint8) (AutomationRegistrar22TriggerRegistrationStorage, error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "getTriggerRegistrationDetails", triggerType) + + if err != nil { + return *new(AutomationRegistrar22TriggerRegistrationStorage), err + } + + out0 := *abi.ConvertType(out[0], new(AutomationRegistrar22TriggerRegistrationStorage)).(*AutomationRegistrar22TriggerRegistrationStorage) + + return out0, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) GetTriggerRegistrationDetails(triggerType uint8) (AutomationRegistrar22TriggerRegistrationStorage, error) { + return _AutomationRegistrar.Contract.GetTriggerRegistrationDetails(&_AutomationRegistrar.CallOpts, triggerType) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) GetTriggerRegistrationDetails(triggerType uint8) (AutomationRegistrar22TriggerRegistrationStorage, error) { + return _AutomationRegistrar.Contract.GetTriggerRegistrationDetails(&_AutomationRegistrar.CallOpts, triggerType) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) Owner() (common.Address, error) { + return _AutomationRegistrar.Contract.Owner(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) Owner() (common.Address, error) { + return _AutomationRegistrar.Contract.Owner(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AutomationRegistrar.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_AutomationRegistrar *AutomationRegistrarSession) TypeAndVersion() (string, error) { + return _AutomationRegistrar.Contract.TypeAndVersion(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarCallerSession) TypeAndVersion() (string, error) { + return _AutomationRegistrar.Contract.TypeAndVersion(&_AutomationRegistrar.CallOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "acceptOwnership") +} + +func (_AutomationRegistrar *AutomationRegistrarSession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistrar.Contract.AcceptOwnership(&_AutomationRegistrar.TransactOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistrar.Contract.AcceptOwnership(&_AutomationRegistrar.TransactOpts) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) Approve(opts *bind.TransactOpts, name string, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, hash [32]byte) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "approve", name, upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) Approve(name string, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, hash [32]byte) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.Approve(&_AutomationRegistrar.TransactOpts, name, upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) Approve(name string, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, hash [32]byte) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.Approve(&_AutomationRegistrar.TransactOpts, name, upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) Cancel(opts *bind.TransactOpts, hash [32]byte) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "cancel", hash) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) Cancel(hash [32]byte) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.Cancel(&_AutomationRegistrar.TransactOpts, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) Cancel(hash [32]byte) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.Cancel(&_AutomationRegistrar.TransactOpts, hash) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "onTokenTransfer", sender, amount, data) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.OnTokenTransfer(&_AutomationRegistrar.TransactOpts, sender, amount, data) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.OnTokenTransfer(&_AutomationRegistrar.TransactOpts, sender, amount, data) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) Register(opts *bind.TransactOpts, name string, encryptedEmail []byte, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, amount *big.Int, sender common.Address) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "register", name, encryptedEmail, upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig, amount, sender) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) Register(name string, encryptedEmail []byte, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, amount *big.Int, sender common.Address) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.Register(&_AutomationRegistrar.TransactOpts, name, encryptedEmail, upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig, amount, sender) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) Register(name string, encryptedEmail []byte, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, amount *big.Int, sender common.Address) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.Register(&_AutomationRegistrar.TransactOpts, name, encryptedEmail, upkeepContract, gasLimit, adminAddress, triggerType, checkData, triggerConfig, offchainConfig, amount, sender) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) RegisterUpkeep(opts *bind.TransactOpts, requestParams AutomationRegistrar22RegistrationParams) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "registerUpkeep", requestParams) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) RegisterUpkeep(requestParams AutomationRegistrar22RegistrationParams) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.RegisterUpkeep(&_AutomationRegistrar.TransactOpts, requestParams) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) RegisterUpkeep(requestParams AutomationRegistrar22RegistrationParams) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.RegisterUpkeep(&_AutomationRegistrar.TransactOpts, requestParams) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) SetAutoApproveAllowedSender(opts *bind.TransactOpts, senderAddress common.Address, allowed bool) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "setAutoApproveAllowedSender", senderAddress, allowed) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) SetAutoApproveAllowedSender(senderAddress common.Address, allowed bool) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.SetAutoApproveAllowedSender(&_AutomationRegistrar.TransactOpts, senderAddress, allowed) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) SetAutoApproveAllowedSender(senderAddress common.Address, allowed bool) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.SetAutoApproveAllowedSender(&_AutomationRegistrar.TransactOpts, senderAddress, allowed) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) SetConfig(opts *bind.TransactOpts, AutomationRegistry common.Address, minLINKJuels *big.Int) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "setConfig", AutomationRegistry, minLINKJuels) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) SetConfig(AutomationRegistry common.Address, minLINKJuels *big.Int) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.SetConfig(&_AutomationRegistrar.TransactOpts, AutomationRegistry, minLINKJuels) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) SetConfig(AutomationRegistry common.Address, minLINKJuels *big.Int) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.SetConfig(&_AutomationRegistrar.TransactOpts, AutomationRegistry, minLINKJuels) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) SetTriggerConfig(opts *bind.TransactOpts, triggerType uint8, autoApproveType uint8, autoApproveMaxAllowed uint32) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "setTriggerConfig", triggerType, autoApproveType, autoApproveMaxAllowed) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) SetTriggerConfig(triggerType uint8, autoApproveType uint8, autoApproveMaxAllowed uint32) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.SetTriggerConfig(&_AutomationRegistrar.TransactOpts, triggerType, autoApproveType, autoApproveMaxAllowed) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) SetTriggerConfig(triggerType uint8, autoApproveType uint8, autoApproveMaxAllowed uint32) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.SetTriggerConfig(&_AutomationRegistrar.TransactOpts, triggerType, autoApproveType, autoApproveMaxAllowed) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _AutomationRegistrar.contract.Transact(opts, "transferOwnership", to) +} + +func (_AutomationRegistrar *AutomationRegistrarSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.TransferOwnership(&_AutomationRegistrar.TransactOpts, to) +} + +func (_AutomationRegistrar *AutomationRegistrarTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistrar.Contract.TransferOwnership(&_AutomationRegistrar.TransactOpts, to) +} + +type AutomationRegistrarAutoApproveAllowedSenderSetIterator struct { + Event *AutomationRegistrarAutoApproveAllowedSenderSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarAutoApproveAllowedSenderSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarAutoApproveAllowedSenderSet) + 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(AutomationRegistrarAutoApproveAllowedSenderSet) + 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 *AutomationRegistrarAutoApproveAllowedSenderSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarAutoApproveAllowedSenderSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarAutoApproveAllowedSenderSet struct { + SenderAddress common.Address + Allowed bool + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterAutoApproveAllowedSenderSet(opts *bind.FilterOpts, senderAddress []common.Address) (*AutomationRegistrarAutoApproveAllowedSenderSetIterator, error) { + + var senderAddressRule []interface{} + for _, senderAddressItem := range senderAddress { + senderAddressRule = append(senderAddressRule, senderAddressItem) + } + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "AutoApproveAllowedSenderSet", senderAddressRule) + if err != nil { + return nil, err + } + return &AutomationRegistrarAutoApproveAllowedSenderSetIterator{contract: _AutomationRegistrar.contract, event: "AutoApproveAllowedSenderSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchAutoApproveAllowedSenderSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarAutoApproveAllowedSenderSet, senderAddress []common.Address) (event.Subscription, error) { + + var senderAddressRule []interface{} + for _, senderAddressItem := range senderAddress { + senderAddressRule = append(senderAddressRule, senderAddressItem) + } + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "AutoApproveAllowedSenderSet", senderAddressRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarAutoApproveAllowedSenderSet) + if err := _AutomationRegistrar.contract.UnpackLog(event, "AutoApproveAllowedSenderSet", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseAutoApproveAllowedSenderSet(log types.Log) (*AutomationRegistrarAutoApproveAllowedSenderSet, error) { + event := new(AutomationRegistrarAutoApproveAllowedSenderSet) + if err := _AutomationRegistrar.contract.UnpackLog(event, "AutoApproveAllowedSenderSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarConfigChangedIterator struct { + Event *AutomationRegistrarConfigChanged + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarConfigChangedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarConfigChanged) + 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(AutomationRegistrarConfigChanged) + 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 *AutomationRegistrarConfigChangedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarConfigChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarConfigChanged struct { + AutomationRegistry common.Address + MinLINKJuels *big.Int + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterConfigChanged(opts *bind.FilterOpts) (*AutomationRegistrarConfigChangedIterator, error) { + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "ConfigChanged") + if err != nil { + return nil, err + } + return &AutomationRegistrarConfigChangedIterator{contract: _AutomationRegistrar.contract, event: "ConfigChanged", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchConfigChanged(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarConfigChanged) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "ConfigChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarConfigChanged) + if err := _AutomationRegistrar.contract.UnpackLog(event, "ConfigChanged", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseConfigChanged(log types.Log) (*AutomationRegistrarConfigChanged, error) { + event := new(AutomationRegistrarConfigChanged) + if err := _AutomationRegistrar.contract.UnpackLog(event, "ConfigChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarOwnershipTransferRequestedIterator struct { + Event *AutomationRegistrarOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarOwnershipTransferRequested) + 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(AutomationRegistrarOwnershipTransferRequested) + 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 *AutomationRegistrarOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistrarOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistrarOwnershipTransferRequestedIterator{contract: _AutomationRegistrar.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarOwnershipTransferRequested) + if err := _AutomationRegistrar.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistrarOwnershipTransferRequested, error) { + event := new(AutomationRegistrarOwnershipTransferRequested) + if err := _AutomationRegistrar.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarOwnershipTransferredIterator struct { + Event *AutomationRegistrarOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarOwnershipTransferred) + 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(AutomationRegistrarOwnershipTransferred) + 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 *AutomationRegistrarOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistrarOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistrarOwnershipTransferredIterator{contract: _AutomationRegistrar.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarOwnershipTransferred) + if err := _AutomationRegistrar.contract.UnpackLog(event, "OwnershipTransferred", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseOwnershipTransferred(log types.Log) (*AutomationRegistrarOwnershipTransferred, error) { + event := new(AutomationRegistrarOwnershipTransferred) + if err := _AutomationRegistrar.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarRegistrationApprovedIterator struct { + Event *AutomationRegistrarRegistrationApproved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarRegistrationApprovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarRegistrationApproved) + 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(AutomationRegistrarRegistrationApproved) + 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 *AutomationRegistrarRegistrationApprovedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarRegistrationApprovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarRegistrationApproved struct { + Hash [32]byte + DisplayName string + UpkeepId *big.Int + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterRegistrationApproved(opts *bind.FilterOpts, hash [][32]byte, upkeepId []*big.Int) (*AutomationRegistrarRegistrationApprovedIterator, error) { + + var hashRule []interface{} + for _, hashItem := range hash { + hashRule = append(hashRule, hashItem) + } + + var upkeepIdRule []interface{} + for _, upkeepIdItem := range upkeepId { + upkeepIdRule = append(upkeepIdRule, upkeepIdItem) + } + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "RegistrationApproved", hashRule, upkeepIdRule) + if err != nil { + return nil, err + } + return &AutomationRegistrarRegistrationApprovedIterator{contract: _AutomationRegistrar.contract, event: "RegistrationApproved", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchRegistrationApproved(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarRegistrationApproved, hash [][32]byte, upkeepId []*big.Int) (event.Subscription, error) { + + var hashRule []interface{} + for _, hashItem := range hash { + hashRule = append(hashRule, hashItem) + } + + var upkeepIdRule []interface{} + for _, upkeepIdItem := range upkeepId { + upkeepIdRule = append(upkeepIdRule, upkeepIdItem) + } + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "RegistrationApproved", hashRule, upkeepIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarRegistrationApproved) + if err := _AutomationRegistrar.contract.UnpackLog(event, "RegistrationApproved", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseRegistrationApproved(log types.Log) (*AutomationRegistrarRegistrationApproved, error) { + event := new(AutomationRegistrarRegistrationApproved) + if err := _AutomationRegistrar.contract.UnpackLog(event, "RegistrationApproved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarRegistrationRejectedIterator struct { + Event *AutomationRegistrarRegistrationRejected + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarRegistrationRejectedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarRegistrationRejected) + 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(AutomationRegistrarRegistrationRejected) + 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 *AutomationRegistrarRegistrationRejectedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarRegistrationRejectedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarRegistrationRejected struct { + Hash [32]byte + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterRegistrationRejected(opts *bind.FilterOpts, hash [][32]byte) (*AutomationRegistrarRegistrationRejectedIterator, error) { + + var hashRule []interface{} + for _, hashItem := range hash { + hashRule = append(hashRule, hashItem) + } + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "RegistrationRejected", hashRule) + if err != nil { + return nil, err + } + return &AutomationRegistrarRegistrationRejectedIterator{contract: _AutomationRegistrar.contract, event: "RegistrationRejected", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchRegistrationRejected(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarRegistrationRejected, hash [][32]byte) (event.Subscription, error) { + + var hashRule []interface{} + for _, hashItem := range hash { + hashRule = append(hashRule, hashItem) + } + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "RegistrationRejected", hashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarRegistrationRejected) + if err := _AutomationRegistrar.contract.UnpackLog(event, "RegistrationRejected", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseRegistrationRejected(log types.Log) (*AutomationRegistrarRegistrationRejected, error) { + event := new(AutomationRegistrarRegistrationRejected) + if err := _AutomationRegistrar.contract.UnpackLog(event, "RegistrationRejected", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarRegistrationRequestedIterator struct { + Event *AutomationRegistrarRegistrationRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarRegistrationRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarRegistrationRequested) + 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(AutomationRegistrarRegistrationRequested) + 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 *AutomationRegistrarRegistrationRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarRegistrationRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarRegistrationRequested struct { + Hash [32]byte + Name string + EncryptedEmail []byte + UpkeepContract common.Address + GasLimit uint32 + AdminAddress common.Address + TriggerType uint8 + TriggerConfig []byte + OffchainConfig []byte + CheckData []byte + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterRegistrationRequested(opts *bind.FilterOpts, hash [][32]byte, upkeepContract []common.Address) (*AutomationRegistrarRegistrationRequestedIterator, error) { + + var hashRule []interface{} + for _, hashItem := range hash { + hashRule = append(hashRule, hashItem) + } + + var upkeepContractRule []interface{} + for _, upkeepContractItem := range upkeepContract { + upkeepContractRule = append(upkeepContractRule, upkeepContractItem) + } + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "RegistrationRequested", hashRule, upkeepContractRule) + if err != nil { + return nil, err + } + return &AutomationRegistrarRegistrationRequestedIterator{contract: _AutomationRegistrar.contract, event: "RegistrationRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchRegistrationRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarRegistrationRequested, hash [][32]byte, upkeepContract []common.Address) (event.Subscription, error) { + + var hashRule []interface{} + for _, hashItem := range hash { + hashRule = append(hashRule, hashItem) + } + + var upkeepContractRule []interface{} + for _, upkeepContractItem := range upkeepContract { + upkeepContractRule = append(upkeepContractRule, upkeepContractItem) + } + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "RegistrationRequested", hashRule, upkeepContractRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarRegistrationRequested) + if err := _AutomationRegistrar.contract.UnpackLog(event, "RegistrationRequested", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseRegistrationRequested(log types.Log) (*AutomationRegistrarRegistrationRequested, error) { + event := new(AutomationRegistrarRegistrationRequested) + if err := _AutomationRegistrar.contract.UnpackLog(event, "RegistrationRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistrarTriggerConfigSetIterator struct { + Event *AutomationRegistrarTriggerConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistrarTriggerConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistrarTriggerConfigSet) + 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(AutomationRegistrarTriggerConfigSet) + 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 *AutomationRegistrarTriggerConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistrarTriggerConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistrarTriggerConfigSet struct { + TriggerType uint8 + AutoApproveType uint8 + AutoApproveMaxAllowed uint32 + Raw types.Log +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) FilterTriggerConfigSet(opts *bind.FilterOpts) (*AutomationRegistrarTriggerConfigSetIterator, error) { + + logs, sub, err := _AutomationRegistrar.contract.FilterLogs(opts, "TriggerConfigSet") + if err != nil { + return nil, err + } + return &AutomationRegistrarTriggerConfigSetIterator{contract: _AutomationRegistrar.contract, event: "TriggerConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistrar *AutomationRegistrarFilterer) WatchTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarTriggerConfigSet) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistrar.contract.WatchLogs(opts, "TriggerConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistrarTriggerConfigSet) + if err := _AutomationRegistrar.contract.UnpackLog(event, "TriggerConfigSet", 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 (_AutomationRegistrar *AutomationRegistrarFilterer) ParseTriggerConfigSet(log types.Log) (*AutomationRegistrarTriggerConfigSet, error) { + event := new(AutomationRegistrarTriggerConfigSet) + if err := _AutomationRegistrar.contract.UnpackLog(event, "TriggerConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetConfig struct { + AutomationRegistry common.Address + MinLINKJuels *big.Int +} + +func (_AutomationRegistrar *AutomationRegistrar) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _AutomationRegistrar.abi.Events["AutoApproveAllowedSenderSet"].ID: + return _AutomationRegistrar.ParseAutoApproveAllowedSenderSet(log) + case _AutomationRegistrar.abi.Events["ConfigChanged"].ID: + return _AutomationRegistrar.ParseConfigChanged(log) + case _AutomationRegistrar.abi.Events["OwnershipTransferRequested"].ID: + return _AutomationRegistrar.ParseOwnershipTransferRequested(log) + case _AutomationRegistrar.abi.Events["OwnershipTransferred"].ID: + return _AutomationRegistrar.ParseOwnershipTransferred(log) + case _AutomationRegistrar.abi.Events["RegistrationApproved"].ID: + return _AutomationRegistrar.ParseRegistrationApproved(log) + case _AutomationRegistrar.abi.Events["RegistrationRejected"].ID: + return _AutomationRegistrar.ParseRegistrationRejected(log) + case _AutomationRegistrar.abi.Events["RegistrationRequested"].ID: + return _AutomationRegistrar.ParseRegistrationRequested(log) + case _AutomationRegistrar.abi.Events["TriggerConfigSet"].ID: + return _AutomationRegistrar.ParseTriggerConfigSet(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (AutomationRegistrarAutoApproveAllowedSenderSet) Topic() common.Hash { + return common.HexToHash("0x20c6237dac83526a849285a9f79d08a483291bdd3a056a0ef9ae94ecee1ad356") +} + +func (AutomationRegistrarConfigChanged) Topic() common.Hash { + return common.HexToHash("0x39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a") +} + +func (AutomationRegistrarOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (AutomationRegistrarOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (AutomationRegistrarRegistrationApproved) Topic() common.Hash { + return common.HexToHash("0xb9a292fb7e3edd920cd2d2829a3615a640c43fd7de0a0820aa0668feb4c37d4b") +} + +func (AutomationRegistrarRegistrationRejected) Topic() common.Hash { + return common.HexToHash("0x3663fb28ebc87645eb972c9dad8521bf665c623f287e79f1c56f1eb374b82a22") +} + +func (AutomationRegistrarRegistrationRequested) Topic() common.Hash { + return common.HexToHash("0x7684390ebb103102f7f48c71439c2408713f8d437782a6fab2756acc0e42c1b7") +} + +func (AutomationRegistrarTriggerConfigSet) Topic() common.Hash { + return common.HexToHash("0x830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a3") +} + +func (_AutomationRegistrar *AutomationRegistrar) Address() common.Address { + return _AutomationRegistrar.address +} + +type AutomationRegistrarInterface interface { + LINK(opts *bind.CallOpts) (common.Address, error) + + GetAutoApproveAllowedSender(opts *bind.CallOpts, senderAddress common.Address) (bool, error) + + GetConfig(opts *bind.CallOpts) (GetConfig, + + error) + + GetPendingRequest(opts *bind.CallOpts, hash [32]byte) (common.Address, *big.Int, error) + + GetTriggerRegistrationDetails(opts *bind.CallOpts, triggerType uint8) (AutomationRegistrar22TriggerRegistrationStorage, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + Approve(opts *bind.TransactOpts, name string, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, hash [32]byte) (*types.Transaction, error) + + Cancel(opts *bind.TransactOpts, hash [32]byte) (*types.Transaction, error) + + OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) + + Register(opts *bind.TransactOpts, name string, encryptedEmail []byte, upkeepContract common.Address, gasLimit uint32, adminAddress common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte, amount *big.Int, sender common.Address) (*types.Transaction, error) + + RegisterUpkeep(opts *bind.TransactOpts, requestParams AutomationRegistrar22RegistrationParams) (*types.Transaction, error) + + SetAutoApproveAllowedSender(opts *bind.TransactOpts, senderAddress common.Address, allowed bool) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, AutomationRegistry common.Address, minLINKJuels *big.Int) (*types.Transaction, error) + + SetTriggerConfig(opts *bind.TransactOpts, triggerType uint8, autoApproveType uint8, autoApproveMaxAllowed uint32) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + FilterAutoApproveAllowedSenderSet(opts *bind.FilterOpts, senderAddress []common.Address) (*AutomationRegistrarAutoApproveAllowedSenderSetIterator, error) + + WatchAutoApproveAllowedSenderSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarAutoApproveAllowedSenderSet, senderAddress []common.Address) (event.Subscription, error) + + ParseAutoApproveAllowedSenderSet(log types.Log) (*AutomationRegistrarAutoApproveAllowedSenderSet, error) + + FilterConfigChanged(opts *bind.FilterOpts) (*AutomationRegistrarConfigChangedIterator, error) + + WatchConfigChanged(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarConfigChanged) (event.Subscription, error) + + ParseConfigChanged(log types.Log) (*AutomationRegistrarConfigChanged, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistrarOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistrarOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistrarOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*AutomationRegistrarOwnershipTransferred, error) + + FilterRegistrationApproved(opts *bind.FilterOpts, hash [][32]byte, upkeepId []*big.Int) (*AutomationRegistrarRegistrationApprovedIterator, error) + + WatchRegistrationApproved(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarRegistrationApproved, hash [][32]byte, upkeepId []*big.Int) (event.Subscription, error) + + ParseRegistrationApproved(log types.Log) (*AutomationRegistrarRegistrationApproved, error) + + FilterRegistrationRejected(opts *bind.FilterOpts, hash [][32]byte) (*AutomationRegistrarRegistrationRejectedIterator, error) + + WatchRegistrationRejected(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarRegistrationRejected, hash [][32]byte) (event.Subscription, error) + + ParseRegistrationRejected(log types.Log) (*AutomationRegistrarRegistrationRejected, error) + + FilterRegistrationRequested(opts *bind.FilterOpts, hash [][32]byte, upkeepContract []common.Address) (*AutomationRegistrarRegistrationRequestedIterator, error) + + WatchRegistrationRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarRegistrationRequested, hash [][32]byte, upkeepContract []common.Address) (event.Subscription, error) + + ParseRegistrationRequested(log types.Log) (*AutomationRegistrarRegistrationRequested, error) + + FilterTriggerConfigSet(opts *bind.FilterOpts) (*AutomationRegistrarTriggerConfigSetIterator, error) + + WatchTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistrarTriggerConfigSet) (event.Subscription, error) + + ParseTriggerConfigSet(log types.Log) (*AutomationRegistrarTriggerConfigSet, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go b/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go new file mode 100644 index 00000000000..5eb64110247 --- /dev/null +++ b/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go @@ -0,0 +1,322 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package automation_utils_2_2 + +import ( + "errors" + "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" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type AutomationRegistryBase22ConditionalTrigger struct { + BlockNum uint32 + BlockHash [32]byte +} + +type AutomationRegistryBase22LogTrigger struct { + LogBlockHash [32]byte + TxHash [32]byte + LogIndex uint32 + BlockNum uint32 + BlockHash [32]byte +} + +type AutomationRegistryBase22OnchainConfig struct { + PaymentPremiumPPB uint32 + FlatFeeMicroLink uint32 + CheckGasLimit uint32 + StalenessSeconds *big.Int + GasCeilingMultiplier uint16 + MinUpkeepSpend *big.Int + MaxPerformGas uint32 + MaxCheckDataSize uint32 + MaxPerformDataSize uint32 + MaxRevertDataSize uint32 + FallbackGasPrice *big.Int + FallbackLinkPrice *big.Int + Transcoder common.Address + Registrars []common.Address + UpkeepPrivilegeManager common.Address +} + +type AutomationRegistryBase22Report struct { + FastGasWei *big.Int + LinkNative *big.Int + UpkeepIds []*big.Int + GasLimits []*big.Int + Triggers [][]byte + PerformDatas [][]byte +} + +type Log struct { + Index *big.Int + Timestamp *big.Int + TxHash [32]byte + BlockNumber *big.Int + BlockHash [32]byte + Source common.Address + Topics [][32]byte + Data []byte +} + +type LogTriggerConfig struct { + ContractAddress common.Address + FilterSelector uint8 + Topic0 [32]byte + Topic1 [32]byte + Topic2 [32]byte + Topic3 [32]byte +} + +var AutomationUtilsMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNum\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"internalType\":\"structAutomationRegistryBase2_2.ConditionalTrigger\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_conditionalTrigger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_log\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"logBlockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"logIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNum\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"internalType\":\"structAutomationRegistryBase2_2.LogTrigger\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_logTrigger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"filterSelector\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"topic0\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic1\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic2\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic3\",\"type\":\"bytes32\"}],\"internalType\":\"structLogTriggerConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_logTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_onChainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"upkeepIds\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"gasLimits\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"triggers\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes[]\",\"name\":\"performDatas\",\"type\":\"bytes[]\"}],\"internalType\":\"structAutomationRegistryBase2_2.Report\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b506108ca806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c8063776f306111610050578063776f3061146100a6578063e65d6546146100b4578063e9720a49146100c257600080fd5b806321f373d7146100775780632ff92a811461008a5780634b6df29414610098575b600080fd5b6100886100853660046101e8565b50565b005b610088610085366004610363565b6100886100853660046104bd565b610088610085366004610514565b6100886100853660046106fb565b6100886100853660046107e8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101e0810167ffffffffffffffff81118282101715610123576101236100d0565b60405290565b60405160c0810167ffffffffffffffff81118282101715610123576101236100d0565b604051610100810167ffffffffffffffff81118282101715610123576101236100d0565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156101b7576101b76100d0565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101e357600080fd5b919050565b600060c082840312156101fa57600080fd5b60405160c0810181811067ffffffffffffffff8211171561021d5761021d6100d0565b604052610229836101bf565b8152602083013560ff8116811461023f57600080fd5b8060208301525060408301356040820152606083013560608201526080830135608082015260a083013560a08201528091505092915050565b803563ffffffff811681146101e357600080fd5b803562ffffff811681146101e357600080fd5b803561ffff811681146101e357600080fd5b80356bffffffffffffffffffffffff811681146101e357600080fd5b600067ffffffffffffffff8211156102e7576102e76100d0565b5060051b60200190565b600082601f83011261030257600080fd5b81356020610317610312836102cd565b610170565b82815260059290921b8401810191818101908684111561033657600080fd5b8286015b848110156103585761034b816101bf565b835291830191830161033a565b509695505050505050565b60006020828403121561037557600080fd5b813567ffffffffffffffff8082111561038d57600080fd5b908301906101e082860312156103a257600080fd5b6103aa6100ff565b6103b383610278565b81526103c160208401610278565b60208201526103d260408401610278565b60408201526103e36060840161028c565b60608201526103f46080840161029f565b608082015261040560a084016102b1565b60a082015261041660c08401610278565b60c082015261042760e08401610278565b60e082015261010061043a818501610278565b9082015261012061044c848201610278565b90820152610140838101359082015261016080840135908201526101806104748185016101bf565b908201526101a0838101358381111561048c57600080fd5b610498888287016102f1565b8284015250506101c091506104ae8284016101bf565b91810191909152949350505050565b6000604082840312156104cf57600080fd5b6040516040810181811067ffffffffffffffff821117156104f2576104f26100d0565b6040526104fe83610278565b8152602083013560208201528091505092915050565b600060a0828403121561052657600080fd5b60405160a0810181811067ffffffffffffffff82111715610549576105496100d0565b8060405250823581526020830135602082015261056860408401610278565b604082015261057960608401610278565b6060820152608083013560808201528091505092915050565b600082601f8301126105a357600080fd5b813560206105b3610312836102cd565b82815260059290921b840181019181810190868411156105d257600080fd5b8286015b8481101561035857803583529183019183016105d6565b600082601f8301126105fe57600080fd5b813567ffffffffffffffff811115610618576106186100d0565b61064960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610170565b81815284602083860101111561065e57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261068c57600080fd5b8135602061069c610312836102cd565b82815260059290921b840181019181810190868411156106bb57600080fd5b8286015b8481101561035857803567ffffffffffffffff8111156106df5760008081fd5b6106ed8986838b01016105ed565b8452509183019183016106bf565b60006020828403121561070d57600080fd5b813567ffffffffffffffff8082111561072557600080fd5b9083019060c0828603121561073957600080fd5b610741610129565b823581526020830135602082015260408301358281111561076157600080fd5b61076d87828601610592565b60408301525060608301358281111561078557600080fd5b61079187828601610592565b6060830152506080830135828111156107a957600080fd5b6107b58782860161067b565b60808301525060a0830135828111156107cd57600080fd5b6107d98782860161067b565b60a08301525095945050505050565b6000602082840312156107fa57600080fd5b813567ffffffffffffffff8082111561081257600080fd5b90830190610100828603121561082757600080fd5b61082f61014c565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261086760a084016101bf565b60a082015260c08301358281111561087e57600080fd5b61088a87828601610592565b60c08301525060e0830135828111156108a257600080fd5b6108ae878286016105ed565b60e0830152509594505050505056fea164736f6c6343000810000a", +} + +var AutomationUtilsABI = AutomationUtilsMetaData.ABI + +var AutomationUtilsBin = AutomationUtilsMetaData.Bin + +func DeployAutomationUtils(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *AutomationUtils, error) { + parsed, err := AutomationUtilsMetaData.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(AutomationUtilsBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &AutomationUtils{address: address, abi: *parsed, AutomationUtilsCaller: AutomationUtilsCaller{contract: contract}, AutomationUtilsTransactor: AutomationUtilsTransactor{contract: contract}, AutomationUtilsFilterer: AutomationUtilsFilterer{contract: contract}}, nil +} + +type AutomationUtils struct { + address common.Address + abi abi.ABI + AutomationUtilsCaller + AutomationUtilsTransactor + AutomationUtilsFilterer +} + +type AutomationUtilsCaller struct { + contract *bind.BoundContract +} + +type AutomationUtilsTransactor struct { + contract *bind.BoundContract +} + +type AutomationUtilsFilterer struct { + contract *bind.BoundContract +} + +type AutomationUtilsSession struct { + Contract *AutomationUtils + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type AutomationUtilsCallerSession struct { + Contract *AutomationUtilsCaller + CallOpts bind.CallOpts +} + +type AutomationUtilsTransactorSession struct { + Contract *AutomationUtilsTransactor + TransactOpts bind.TransactOpts +} + +type AutomationUtilsRaw struct { + Contract *AutomationUtils +} + +type AutomationUtilsCallerRaw struct { + Contract *AutomationUtilsCaller +} + +type AutomationUtilsTransactorRaw struct { + Contract *AutomationUtilsTransactor +} + +func NewAutomationUtils(address common.Address, backend bind.ContractBackend) (*AutomationUtils, error) { + abi, err := abi.JSON(strings.NewReader(AutomationUtilsABI)) + if err != nil { + return nil, err + } + contract, err := bindAutomationUtils(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AutomationUtils{address: address, abi: abi, AutomationUtilsCaller: AutomationUtilsCaller{contract: contract}, AutomationUtilsTransactor: AutomationUtilsTransactor{contract: contract}, AutomationUtilsFilterer: AutomationUtilsFilterer{contract: contract}}, nil +} + +func NewAutomationUtilsCaller(address common.Address, caller bind.ContractCaller) (*AutomationUtilsCaller, error) { + contract, err := bindAutomationUtils(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AutomationUtilsCaller{contract: contract}, nil +} + +func NewAutomationUtilsTransactor(address common.Address, transactor bind.ContractTransactor) (*AutomationUtilsTransactor, error) { + contract, err := bindAutomationUtils(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AutomationUtilsTransactor{contract: contract}, nil +} + +func NewAutomationUtilsFilterer(address common.Address, filterer bind.ContractFilterer) (*AutomationUtilsFilterer, error) { + contract, err := bindAutomationUtils(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AutomationUtilsFilterer{contract: contract}, nil +} + +func bindAutomationUtils(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AutomationUtilsMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_AutomationUtils *AutomationUtilsRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationUtils.Contract.AutomationUtilsCaller.contract.Call(opts, result, method, params...) +} + +func (_AutomationUtils *AutomationUtilsRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationUtils.Contract.AutomationUtilsTransactor.contract.Transfer(opts) +} + +func (_AutomationUtils *AutomationUtilsRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationUtils.Contract.AutomationUtilsTransactor.contract.Transact(opts, method, params...) +} + +func (_AutomationUtils *AutomationUtilsCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationUtils.Contract.contract.Call(opts, result, method, params...) +} + +func (_AutomationUtils *AutomationUtilsTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationUtils.Contract.contract.Transfer(opts) +} + +func (_AutomationUtils *AutomationUtilsTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationUtils.Contract.contract.Transact(opts, method, params...) +} + +func (_AutomationUtils *AutomationUtilsTransactor) ConditionalTrigger(opts *bind.TransactOpts, arg0 AutomationRegistryBase22ConditionalTrigger) (*types.Transaction, error) { + return _AutomationUtils.contract.Transact(opts, "_conditionalTrigger", arg0) +} + +func (_AutomationUtils *AutomationUtilsSession) ConditionalTrigger(arg0 AutomationRegistryBase22ConditionalTrigger) (*types.Transaction, error) { + return _AutomationUtils.Contract.ConditionalTrigger(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactorSession) ConditionalTrigger(arg0 AutomationRegistryBase22ConditionalTrigger) (*types.Transaction, error) { + return _AutomationUtils.Contract.ConditionalTrigger(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactor) Log(opts *bind.TransactOpts, arg0 Log) (*types.Transaction, error) { + return _AutomationUtils.contract.Transact(opts, "_log", arg0) +} + +func (_AutomationUtils *AutomationUtilsSession) Log(arg0 Log) (*types.Transaction, error) { + return _AutomationUtils.Contract.Log(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactorSession) Log(arg0 Log) (*types.Transaction, error) { + return _AutomationUtils.Contract.Log(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactor) LogTrigger(opts *bind.TransactOpts, arg0 AutomationRegistryBase22LogTrigger) (*types.Transaction, error) { + return _AutomationUtils.contract.Transact(opts, "_logTrigger", arg0) +} + +func (_AutomationUtils *AutomationUtilsSession) LogTrigger(arg0 AutomationRegistryBase22LogTrigger) (*types.Transaction, error) { + return _AutomationUtils.Contract.LogTrigger(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactorSession) LogTrigger(arg0 AutomationRegistryBase22LogTrigger) (*types.Transaction, error) { + return _AutomationUtils.Contract.LogTrigger(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactor) LogTriggerConfig(opts *bind.TransactOpts, arg0 LogTriggerConfig) (*types.Transaction, error) { + return _AutomationUtils.contract.Transact(opts, "_logTriggerConfig", arg0) +} + +func (_AutomationUtils *AutomationUtilsSession) LogTriggerConfig(arg0 LogTriggerConfig) (*types.Transaction, error) { + return _AutomationUtils.Contract.LogTriggerConfig(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactorSession) LogTriggerConfig(arg0 LogTriggerConfig) (*types.Transaction, error) { + return _AutomationUtils.Contract.LogTriggerConfig(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactor) OnChainConfig(opts *bind.TransactOpts, arg0 AutomationRegistryBase22OnchainConfig) (*types.Transaction, error) { + return _AutomationUtils.contract.Transact(opts, "_onChainConfig", arg0) +} + +func (_AutomationUtils *AutomationUtilsSession) OnChainConfig(arg0 AutomationRegistryBase22OnchainConfig) (*types.Transaction, error) { + return _AutomationUtils.Contract.OnChainConfig(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactorSession) OnChainConfig(arg0 AutomationRegistryBase22OnchainConfig) (*types.Transaction, error) { + return _AutomationUtils.Contract.OnChainConfig(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactor) Report(opts *bind.TransactOpts, arg0 AutomationRegistryBase22Report) (*types.Transaction, error) { + return _AutomationUtils.contract.Transact(opts, "_report", arg0) +} + +func (_AutomationUtils *AutomationUtilsSession) Report(arg0 AutomationRegistryBase22Report) (*types.Transaction, error) { + return _AutomationUtils.Contract.Report(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtilsTransactorSession) Report(arg0 AutomationRegistryBase22Report) (*types.Transaction, error) { + return _AutomationUtils.Contract.Report(&_AutomationUtils.TransactOpts, arg0) +} + +func (_AutomationUtils *AutomationUtils) Address() common.Address { + return _AutomationUtils.address +} + +type AutomationUtilsInterface interface { + ConditionalTrigger(opts *bind.TransactOpts, arg0 AutomationRegistryBase22ConditionalTrigger) (*types.Transaction, error) + + Log(opts *bind.TransactOpts, arg0 Log) (*types.Transaction, error) + + LogTrigger(opts *bind.TransactOpts, arg0 AutomationRegistryBase22LogTrigger) (*types.Transaction, error) + + LogTriggerConfig(opts *bind.TransactOpts, arg0 LogTriggerConfig) (*types.Transaction, error) + + OnChainConfig(opts *bind.TransactOpts, arg0 AutomationRegistryBase22OnchainConfig) (*types.Transaction, error) + + Report(opts *bind.TransactOpts, arg0 AutomationRegistryBase22Report) (*types.Transaction, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go b/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go new file mode 100644 index 00000000000..964e54f1893 --- /dev/null +++ b/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go @@ -0,0 +1,6442 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package i_keeper_registry_master_wrapper_2_2 + +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 AutomationRegistryBase22OnchainConfig struct { + PaymentPremiumPPB uint32 + FlatFeeMicroLink uint32 + CheckGasLimit uint32 + StalenessSeconds *big.Int + GasCeilingMultiplier uint16 + MinUpkeepSpend *big.Int + MaxPerformGas uint32 + MaxCheckDataSize uint32 + MaxPerformDataSize uint32 + MaxRevertDataSize uint32 + FallbackGasPrice *big.Int + FallbackLinkPrice *big.Int + Transcoder common.Address + Registrars []common.Address + UpkeepPrivilegeManager common.Address +} + +type AutomationRegistryBase22State struct { + Nonce uint32 + OwnerLinkBalance *big.Int + ExpectedLinkBalance *big.Int + TotalPremium *big.Int + NumUpkeeps *big.Int + ConfigCount uint32 + LatestConfigBlockNumber uint32 + LatestConfigDigest [32]byte + LatestEpoch uint32 + Paused bool +} + +type AutomationRegistryBase22UpkeepInfo struct { + Target common.Address + PerformGas uint32 + CheckData []byte + Balance *big.Int + Admin common.Address + MaxValidBlocknumber uint64 + LastPerformedBlockNumber uint32 + AmountSpent *big.Int + Paused bool + OffchainConfig []byte +} + +var IAutomationRegistryMasterMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"addFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancelUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"values\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"checkCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"executeCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkNativeFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMode\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.State\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structAutomationRegistryBase2_2.UpkeepInfo\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"migrateUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedUpkeeps\",\"type\":\"bytes\"}],\"name\":\"receiveUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"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\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepTranscoderVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawOwnerFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +var IAutomationRegistryMasterABI = IAutomationRegistryMasterMetaData.ABI + +type IAutomationRegistryMaster struct { + address common.Address + abi abi.ABI + IAutomationRegistryMasterCaller + IAutomationRegistryMasterTransactor + IAutomationRegistryMasterFilterer +} + +type IAutomationRegistryMasterCaller struct { + contract *bind.BoundContract +} + +type IAutomationRegistryMasterTransactor struct { + contract *bind.BoundContract +} + +type IAutomationRegistryMasterFilterer struct { + contract *bind.BoundContract +} + +type IAutomationRegistryMasterSession struct { + Contract *IAutomationRegistryMaster + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type IAutomationRegistryMasterCallerSession struct { + Contract *IAutomationRegistryMasterCaller + CallOpts bind.CallOpts +} + +type IAutomationRegistryMasterTransactorSession struct { + Contract *IAutomationRegistryMasterTransactor + TransactOpts bind.TransactOpts +} + +type IAutomationRegistryMasterRaw struct { + Contract *IAutomationRegistryMaster +} + +type IAutomationRegistryMasterCallerRaw struct { + Contract *IAutomationRegistryMasterCaller +} + +type IAutomationRegistryMasterTransactorRaw struct { + Contract *IAutomationRegistryMasterTransactor +} + +func NewIAutomationRegistryMaster(address common.Address, backend bind.ContractBackend) (*IAutomationRegistryMaster, error) { + abi, err := abi.JSON(strings.NewReader(IAutomationRegistryMasterABI)) + if err != nil { + return nil, err + } + contract, err := bindIAutomationRegistryMaster(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IAutomationRegistryMaster{address: address, abi: abi, IAutomationRegistryMasterCaller: IAutomationRegistryMasterCaller{contract: contract}, IAutomationRegistryMasterTransactor: IAutomationRegistryMasterTransactor{contract: contract}, IAutomationRegistryMasterFilterer: IAutomationRegistryMasterFilterer{contract: contract}}, nil +} + +func NewIAutomationRegistryMasterCaller(address common.Address, caller bind.ContractCaller) (*IAutomationRegistryMasterCaller, error) { + contract, err := bindIAutomationRegistryMaster(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterCaller{contract: contract}, nil +} + +func NewIAutomationRegistryMasterTransactor(address common.Address, transactor bind.ContractTransactor) (*IAutomationRegistryMasterTransactor, error) { + contract, err := bindIAutomationRegistryMaster(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterTransactor{contract: contract}, nil +} + +func NewIAutomationRegistryMasterFilterer(address common.Address, filterer bind.ContractFilterer) (*IAutomationRegistryMasterFilterer, error) { + contract, err := bindIAutomationRegistryMaster(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterFilterer{contract: contract}, nil +} + +func bindIAutomationRegistryMaster(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IAutomationRegistryMasterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IAutomationRegistryMaster.Contract.IAutomationRegistryMasterCaller.contract.Call(opts, result, method, params...) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.IAutomationRegistryMasterTransactor.contract.Transfer(opts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.IAutomationRegistryMasterTransactor.contract.Transact(opts, method, params...) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IAutomationRegistryMaster.Contract.contract.Call(opts, result, method, params...) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.contract.Transfer(opts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.contract.Transact(opts, method, params...) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) CheckCallback(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (CheckCallback, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "checkCallback", id, values, extraData) + + outstruct := new(CheckCallback) + if err != nil { + return *outstruct, err + } + + outstruct.UpkeepNeeded = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.PerformData = *abi.ConvertType(out[1], new([]byte)).(*[]byte) + outstruct.UpkeepFailureReason = *abi.ConvertType(out[2], new(uint8)).(*uint8) + outstruct.GasUsed = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) CheckCallback(id *big.Int, values [][]byte, extraData []byte) (CheckCallback, + + error) { + return _IAutomationRegistryMaster.Contract.CheckCallback(&_IAutomationRegistryMaster.CallOpts, id, values, extraData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) CheckCallback(id *big.Int, values [][]byte, extraData []byte) (CheckCallback, + + error) { + return _IAutomationRegistryMaster.Contract.CheckCallback(&_IAutomationRegistryMaster.CallOpts, id, values, extraData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) CheckUpkeep(opts *bind.CallOpts, id *big.Int, triggerData []byte) (CheckUpkeep, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "checkUpkeep", id, triggerData) + + outstruct := new(CheckUpkeep) + if err != nil { + return *outstruct, err + } + + outstruct.UpkeepNeeded = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.PerformData = *abi.ConvertType(out[1], new([]byte)).(*[]byte) + outstruct.UpkeepFailureReason = *abi.ConvertType(out[2], new(uint8)).(*uint8) + outstruct.GasUsed = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.GasLimit = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + outstruct.FastGasWei = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) + outstruct.LinkNative = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) CheckUpkeep(id *big.Int, triggerData []byte) (CheckUpkeep, + + error) { + return _IAutomationRegistryMaster.Contract.CheckUpkeep(&_IAutomationRegistryMaster.CallOpts, id, triggerData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) CheckUpkeep(id *big.Int, triggerData []byte) (CheckUpkeep, + + error) { + return _IAutomationRegistryMaster.Contract.CheckUpkeep(&_IAutomationRegistryMaster.CallOpts, id, triggerData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) CheckUpkeep0(opts *bind.CallOpts, id *big.Int) (CheckUpkeep0, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "checkUpkeep0", id) + + outstruct := new(CheckUpkeep0) + if err != nil { + return *outstruct, err + } + + outstruct.UpkeepNeeded = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.PerformData = *abi.ConvertType(out[1], new([]byte)).(*[]byte) + outstruct.UpkeepFailureReason = *abi.ConvertType(out[2], new(uint8)).(*uint8) + outstruct.GasUsed = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.GasLimit = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + outstruct.FastGasWei = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) + outstruct.LinkNative = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) CheckUpkeep0(id *big.Int) (CheckUpkeep0, + + error) { + return _IAutomationRegistryMaster.Contract.CheckUpkeep0(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) CheckUpkeep0(id *big.Int) (CheckUpkeep0, + + error) { + return _IAutomationRegistryMaster.Contract.CheckUpkeep0(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) FallbackTo(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "fallbackTo") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) FallbackTo() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.FallbackTo(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) FallbackTo() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.FallbackTo(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getActiveUpkeepIDs", startIndex, maxCount) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetActiveUpkeepIDs(startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetActiveUpkeepIDs(&_IAutomationRegistryMaster.CallOpts, startIndex, maxCount) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetActiveUpkeepIDs(startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetActiveUpkeepIDs(&_IAutomationRegistryMaster.CallOpts, startIndex, maxCount) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetAdminPrivilegeConfig(opts *bind.CallOpts, admin common.Address) ([]byte, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getAdminPrivilegeConfig", admin) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetAdminPrivilegeConfig(admin common.Address) ([]byte, error) { + return _IAutomationRegistryMaster.Contract.GetAdminPrivilegeConfig(&_IAutomationRegistryMaster.CallOpts, admin) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetAdminPrivilegeConfig(admin common.Address) ([]byte, error) { + return _IAutomationRegistryMaster.Contract.GetAdminPrivilegeConfig(&_IAutomationRegistryMaster.CallOpts, admin) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetAutomationForwarderLogic(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getAutomationForwarderLogic") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetAutomationForwarderLogic() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetAutomationForwarderLogic(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetAutomationForwarderLogic() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetAutomationForwarderLogic(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getBalance", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetBalance(id *big.Int) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetBalance(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetBalance(id *big.Int) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetBalance(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetCancellationDelay(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getCancellationDelay") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetCancellationDelay() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetCancellationDelay(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetCancellationDelay() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetCancellationDelay(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetConditionalGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getConditionalGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetConditionalGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetConditionalGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetConditionalGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetConditionalGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetFastGasFeedAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getFastGasFeedAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetFastGasFeedAddress() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetFastGasFeedAddress(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetFastGasFeedAddress() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetFastGasFeedAddress(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetForwarder(opts *bind.CallOpts, upkeepID *big.Int) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getForwarder", upkeepID) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetForwarder(upkeepID *big.Int) (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetForwarder(&_IAutomationRegistryMaster.CallOpts, upkeepID) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetForwarder(upkeepID *big.Int) (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetForwarder(&_IAutomationRegistryMaster.CallOpts, upkeepID) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetLinkAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getLinkAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetLinkAddress() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetLinkAddress(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetLinkAddress() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetLinkAddress(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetLinkNativeFeedAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getLinkNativeFeedAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetLinkNativeFeedAddress() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetLinkNativeFeedAddress(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetLinkNativeFeedAddress() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.GetLinkNativeFeedAddress(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetLogGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getLogGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetLogGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetLogGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetLogGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetLogGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetMaxPaymentForGas(opts *bind.CallOpts, triggerType uint8, gasLimit uint32) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getMaxPaymentForGas", triggerType, gasLimit) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetMaxPaymentForGas(triggerType uint8, gasLimit uint32) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetMaxPaymentForGas(&_IAutomationRegistryMaster.CallOpts, triggerType, gasLimit) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetMaxPaymentForGas(triggerType uint8, gasLimit uint32) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetMaxPaymentForGas(&_IAutomationRegistryMaster.CallOpts, triggerType, gasLimit) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetMinBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getMinBalance", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetMinBalance(id *big.Int) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetMinBalance(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetMinBalance(id *big.Int) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetMinBalance(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetMinBalanceForUpkeep(opts *bind.CallOpts, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getMinBalanceForUpkeep", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetMinBalanceForUpkeep(id *big.Int) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetMinBalanceForUpkeep(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetMinBalanceForUpkeep(id *big.Int) (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetMinBalanceForUpkeep(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetMode(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getMode") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetMode() (uint8, error) { + return _IAutomationRegistryMaster.Contract.GetMode(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetMode() (uint8, error) { + return _IAutomationRegistryMaster.Contract.GetMode(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetPeerRegistryMigrationPermission(opts *bind.CallOpts, peer common.Address) (uint8, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getPeerRegistryMigrationPermission", peer) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetPeerRegistryMigrationPermission(peer common.Address) (uint8, error) { + return _IAutomationRegistryMaster.Contract.GetPeerRegistryMigrationPermission(&_IAutomationRegistryMaster.CallOpts, peer) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetPeerRegistryMigrationPermission(peer common.Address) (uint8, error) { + return _IAutomationRegistryMaster.Contract.GetPeerRegistryMigrationPermission(&_IAutomationRegistryMaster.CallOpts, peer) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetPerPerformByteGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getPerPerformByteGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetPerPerformByteGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetPerPerformByteGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetPerPerformByteGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetPerPerformByteGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetPerSignerGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getPerSignerGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetPerSignerGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetPerSignerGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetPerSignerGasOverhead() (*big.Int, error) { + return _IAutomationRegistryMaster.Contract.GetPerSignerGasOverhead(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetSignerInfo(opts *bind.CallOpts, query common.Address) (GetSignerInfo, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getSignerInfo", query) + + outstruct := new(GetSignerInfo) + if err != nil { + return *outstruct, err + } + + outstruct.Active = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.Index = *abi.ConvertType(out[1], new(uint8)).(*uint8) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetSignerInfo(query common.Address) (GetSignerInfo, + + error) { + return _IAutomationRegistryMaster.Contract.GetSignerInfo(&_IAutomationRegistryMaster.CallOpts, query) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetSignerInfo(query common.Address) (GetSignerInfo, + + error) { + return _IAutomationRegistryMaster.Contract.GetSignerInfo(&_IAutomationRegistryMaster.CallOpts, query) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetState(opts *bind.CallOpts) (GetState, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getState") + + outstruct := new(GetState) + if err != nil { + return *outstruct, err + } + + outstruct.State = *abi.ConvertType(out[0], new(AutomationRegistryBase22State)).(*AutomationRegistryBase22State) + outstruct.Config = *abi.ConvertType(out[1], new(AutomationRegistryBase22OnchainConfig)).(*AutomationRegistryBase22OnchainConfig) + outstruct.Signers = *abi.ConvertType(out[2], new([]common.Address)).(*[]common.Address) + outstruct.Transmitters = *abi.ConvertType(out[3], new([]common.Address)).(*[]common.Address) + outstruct.F = *abi.ConvertType(out[4], new(uint8)).(*uint8) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetState() (GetState, + + error) { + return _IAutomationRegistryMaster.Contract.GetState(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetState() (GetState, + + error) { + return _IAutomationRegistryMaster.Contract.GetState(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetTransmitterInfo(opts *bind.CallOpts, query common.Address) (GetTransmitterInfo, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getTransmitterInfo", query) + + outstruct := new(GetTransmitterInfo) + if err != nil { + return *outstruct, err + } + + outstruct.Active = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.Index = *abi.ConvertType(out[1], new(uint8)).(*uint8) + outstruct.Balance = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.LastCollected = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.Payee = *abi.ConvertType(out[4], new(common.Address)).(*common.Address) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetTransmitterInfo(query common.Address) (GetTransmitterInfo, + + error) { + return _IAutomationRegistryMaster.Contract.GetTransmitterInfo(&_IAutomationRegistryMaster.CallOpts, query) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetTransmitterInfo(query common.Address) (GetTransmitterInfo, + + error) { + return _IAutomationRegistryMaster.Contract.GetTransmitterInfo(&_IAutomationRegistryMaster.CallOpts, query) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetTriggerType(opts *bind.CallOpts, upkeepId *big.Int) (uint8, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getTriggerType", upkeepId) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetTriggerType(upkeepId *big.Int) (uint8, error) { + return _IAutomationRegistryMaster.Contract.GetTriggerType(&_IAutomationRegistryMaster.CallOpts, upkeepId) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetTriggerType(upkeepId *big.Int) (uint8, error) { + return _IAutomationRegistryMaster.Contract.GetTriggerType(&_IAutomationRegistryMaster.CallOpts, upkeepId) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetUpkeep(opts *bind.CallOpts, id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getUpkeep", id) + + if err != nil { + return *new(AutomationRegistryBase22UpkeepInfo), err + } + + out0 := *abi.ConvertType(out[0], new(AutomationRegistryBase22UpkeepInfo)).(*AutomationRegistryBase22UpkeepInfo) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetUpkeep(id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) { + return _IAutomationRegistryMaster.Contract.GetUpkeep(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetUpkeep(id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) { + return _IAutomationRegistryMaster.Contract.GetUpkeep(&_IAutomationRegistryMaster.CallOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getUpkeepPrivilegeConfig", upkeepId) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return _IAutomationRegistryMaster.Contract.GetUpkeepPrivilegeConfig(&_IAutomationRegistryMaster.CallOpts, upkeepId) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return _IAutomationRegistryMaster.Contract.GetUpkeepPrivilegeConfig(&_IAutomationRegistryMaster.CallOpts, upkeepId) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "getUpkeepTriggerConfig", upkeepId) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) GetUpkeepTriggerConfig(upkeepId *big.Int) ([]byte, error) { + return _IAutomationRegistryMaster.Contract.GetUpkeepTriggerConfig(&_IAutomationRegistryMaster.CallOpts, upkeepId) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) GetUpkeepTriggerConfig(upkeepId *big.Int) ([]byte, error) { + return _IAutomationRegistryMaster.Contract.GetUpkeepTriggerConfig(&_IAutomationRegistryMaster.CallOpts, upkeepId) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) HasDedupKey(opts *bind.CallOpts, dedupKey [32]byte) (bool, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "hasDedupKey", dedupKey) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) HasDedupKey(dedupKey [32]byte) (bool, error) { + return _IAutomationRegistryMaster.Contract.HasDedupKey(&_IAutomationRegistryMaster.CallOpts, dedupKey) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) HasDedupKey(dedupKey [32]byte) (bool, error) { + return _IAutomationRegistryMaster.Contract.HasDedupKey(&_IAutomationRegistryMaster.CallOpts, dedupKey) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) LatestConfigDetails(opts *bind.CallOpts) (LatestConfigDetails, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "latestConfigDetails") + + outstruct := new(LatestConfigDetails) + if err != nil { + return *outstruct, err + } + + outstruct.ConfigCount = *abi.ConvertType(out[0], new(uint32)).(*uint32) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.ConfigDigest = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) LatestConfigDetails() (LatestConfigDetails, + + error) { + return _IAutomationRegistryMaster.Contract.LatestConfigDetails(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) LatestConfigDetails() (LatestConfigDetails, + + error) { + return _IAutomationRegistryMaster.Contract.LatestConfigDetails(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) LatestConfigDigestAndEpoch(opts *bind.CallOpts) (LatestConfigDigestAndEpoch, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "latestConfigDigestAndEpoch") + + outstruct := new(LatestConfigDigestAndEpoch) + if err != nil { + return *outstruct, err + } + + outstruct.ScanLogs = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.ConfigDigest = *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + outstruct.Epoch = *abi.ConvertType(out[2], new(uint32)).(*uint32) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) LatestConfigDigestAndEpoch() (LatestConfigDigestAndEpoch, + + error) { + return _IAutomationRegistryMaster.Contract.LatestConfigDigestAndEpoch(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) LatestConfigDigestAndEpoch() (LatestConfigDigestAndEpoch, + + error) { + return _IAutomationRegistryMaster.Contract.LatestConfigDigestAndEpoch(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) Owner() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.Owner(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) Owner() (common.Address, error) { + return _IAutomationRegistryMaster.Contract.Owner(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) SimulatePerformUpkeep(opts *bind.CallOpts, id *big.Int, performData []byte) (SimulatePerformUpkeep, + + error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "simulatePerformUpkeep", id, performData) + + outstruct := new(SimulatePerformUpkeep) + if err != nil { + return *outstruct, err + } + + outstruct.Success = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.GasUsed = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SimulatePerformUpkeep(id *big.Int, performData []byte) (SimulatePerformUpkeep, + + error) { + return _IAutomationRegistryMaster.Contract.SimulatePerformUpkeep(&_IAutomationRegistryMaster.CallOpts, id, performData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) SimulatePerformUpkeep(id *big.Int, performData []byte) (SimulatePerformUpkeep, + + error) { + return _IAutomationRegistryMaster.Contract.SimulatePerformUpkeep(&_IAutomationRegistryMaster.CallOpts, id, performData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) TypeAndVersion() (string, error) { + return _IAutomationRegistryMaster.Contract.TypeAndVersion(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) TypeAndVersion() (string, error) { + return _IAutomationRegistryMaster.Contract.TypeAndVersion(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) UpkeepTranscoderVersion(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "upkeepTranscoderVersion") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) UpkeepTranscoderVersion() (uint8, error) { + return _IAutomationRegistryMaster.Contract.UpkeepTranscoderVersion(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) UpkeepTranscoderVersion() (uint8, error) { + return _IAutomationRegistryMaster.Contract.UpkeepTranscoderVersion(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCaller) UpkeepVersion(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _IAutomationRegistryMaster.contract.Call(opts, &out, "upkeepVersion") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) UpkeepVersion() (uint8, error) { + return _IAutomationRegistryMaster.Contract.UpkeepVersion(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterCallerSession) UpkeepVersion() (uint8, error) { + return _IAutomationRegistryMaster.Contract.UpkeepVersion(&_IAutomationRegistryMaster.CallOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "acceptOwnership") +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) AcceptOwnership() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AcceptOwnership(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AcceptOwnership(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) AcceptPayeeship(opts *bind.TransactOpts, transmitter common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "acceptPayeeship", transmitter) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) AcceptPayeeship(transmitter common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AcceptPayeeship(&_IAutomationRegistryMaster.TransactOpts, transmitter) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) AcceptPayeeship(transmitter common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AcceptPayeeship(&_IAutomationRegistryMaster.TransactOpts, transmitter) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) AcceptUpkeepAdmin(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "acceptUpkeepAdmin", id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) AcceptUpkeepAdmin(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AcceptUpkeepAdmin(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) AcceptUpkeepAdmin(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AcceptUpkeepAdmin(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) AddFunds(opts *bind.TransactOpts, id *big.Int, amount *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "addFunds", id, amount) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) AddFunds(id *big.Int, amount *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AddFunds(&_IAutomationRegistryMaster.TransactOpts, id, amount) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) AddFunds(id *big.Int, amount *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.AddFunds(&_IAutomationRegistryMaster.TransactOpts, id, amount) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) CancelUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "cancelUpkeep", id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) CancelUpkeep(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.CancelUpkeep(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) CancelUpkeep(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.CancelUpkeep(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) ExecuteCallback(opts *bind.TransactOpts, id *big.Int, payload []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "executeCallback", id, payload) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) ExecuteCallback(id *big.Int, payload []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.ExecuteCallback(&_IAutomationRegistryMaster.TransactOpts, id, payload) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) ExecuteCallback(id *big.Int, payload []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.ExecuteCallback(&_IAutomationRegistryMaster.TransactOpts, id, payload) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) MigrateUpkeeps(opts *bind.TransactOpts, ids []*big.Int, destination common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "migrateUpkeeps", ids, destination) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) MigrateUpkeeps(ids []*big.Int, destination common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.MigrateUpkeeps(&_IAutomationRegistryMaster.TransactOpts, ids, destination) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) MigrateUpkeeps(ids []*big.Int, destination common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.MigrateUpkeeps(&_IAutomationRegistryMaster.TransactOpts, ids, destination) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "onTokenTransfer", sender, amount, data) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.OnTokenTransfer(&_IAutomationRegistryMaster.TransactOpts, sender, amount, data) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.OnTokenTransfer(&_IAutomationRegistryMaster.TransactOpts, sender, amount, data) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "pause") +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) Pause() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Pause(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) Pause() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Pause(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) PauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "pauseUpkeep", id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) PauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.PauseUpkeep(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) PauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.PauseUpkeep(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) ReceiveUpkeeps(opts *bind.TransactOpts, encodedUpkeeps []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "receiveUpkeeps", encodedUpkeeps) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) ReceiveUpkeeps(encodedUpkeeps []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.ReceiveUpkeeps(&_IAutomationRegistryMaster.TransactOpts, encodedUpkeeps) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) ReceiveUpkeeps(encodedUpkeeps []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.ReceiveUpkeeps(&_IAutomationRegistryMaster.TransactOpts, encodedUpkeeps) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) RecoverFunds(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "recoverFunds") +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) RecoverFunds() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.RecoverFunds(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) RecoverFunds() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.RecoverFunds(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) RegisterUpkeep(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "registerUpkeep", target, gasLimit, admin, triggerType, checkData, triggerConfig, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) RegisterUpkeep(target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.RegisterUpkeep(&_IAutomationRegistryMaster.TransactOpts, target, gasLimit, admin, triggerType, checkData, triggerConfig, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) RegisterUpkeep(target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.RegisterUpkeep(&_IAutomationRegistryMaster.TransactOpts, target, gasLimit, admin, triggerType, checkData, triggerConfig, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) RegisterUpkeep0(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "registerUpkeep0", target, gasLimit, admin, checkData, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) RegisterUpkeep0(target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.RegisterUpkeep0(&_IAutomationRegistryMaster.TransactOpts, target, gasLimit, admin, checkData, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) RegisterUpkeep0(target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.RegisterUpkeep0(&_IAutomationRegistryMaster.TransactOpts, target, gasLimit, admin, checkData, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetAdminPrivilegeConfig(opts *bind.TransactOpts, admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setAdminPrivilegeConfig", admin, newPrivilegeConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetAdminPrivilegeConfig(admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetAdminPrivilegeConfig(&_IAutomationRegistryMaster.TransactOpts, admin, newPrivilegeConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetAdminPrivilegeConfig(admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetAdminPrivilegeConfig(&_IAutomationRegistryMaster.TransactOpts, admin, newPrivilegeConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetConfig(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setConfig", signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetConfig(signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetConfig(&_IAutomationRegistryMaster.TransactOpts, signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetConfig(signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetConfig(&_IAutomationRegistryMaster.TransactOpts, signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetConfigTypeSafe(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setConfigTypeSafe", signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetConfigTypeSafe(signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetConfigTypeSafe(&_IAutomationRegistryMaster.TransactOpts, signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetConfigTypeSafe(signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetConfigTypeSafe(&_IAutomationRegistryMaster.TransactOpts, signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetPayees(opts *bind.TransactOpts, payees []common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setPayees", payees) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetPayees(payees []common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetPayees(&_IAutomationRegistryMaster.TransactOpts, payees) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetPayees(payees []common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetPayees(&_IAutomationRegistryMaster.TransactOpts, payees) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetPeerRegistryMigrationPermission(opts *bind.TransactOpts, peer common.Address, permission uint8) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setPeerRegistryMigrationPermission", peer, permission) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetPeerRegistryMigrationPermission(peer common.Address, permission uint8) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetPeerRegistryMigrationPermission(&_IAutomationRegistryMaster.TransactOpts, peer, permission) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetPeerRegistryMigrationPermission(peer common.Address, permission uint8) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetPeerRegistryMigrationPermission(&_IAutomationRegistryMaster.TransactOpts, peer, permission) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetUpkeepCheckData(opts *bind.TransactOpts, id *big.Int, newCheckData []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setUpkeepCheckData", id, newCheckData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetUpkeepCheckData(id *big.Int, newCheckData []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepCheckData(&_IAutomationRegistryMaster.TransactOpts, id, newCheckData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetUpkeepCheckData(id *big.Int, newCheckData []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepCheckData(&_IAutomationRegistryMaster.TransactOpts, id, newCheckData) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetUpkeepGasLimit(opts *bind.TransactOpts, id *big.Int, gasLimit uint32) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setUpkeepGasLimit", id, gasLimit) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetUpkeepGasLimit(id *big.Int, gasLimit uint32) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepGasLimit(&_IAutomationRegistryMaster.TransactOpts, id, gasLimit) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetUpkeepGasLimit(id *big.Int, gasLimit uint32) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepGasLimit(&_IAutomationRegistryMaster.TransactOpts, id, gasLimit) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetUpkeepOffchainConfig(opts *bind.TransactOpts, id *big.Int, config []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setUpkeepOffchainConfig", id, config) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetUpkeepOffchainConfig(id *big.Int, config []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepOffchainConfig(&_IAutomationRegistryMaster.TransactOpts, id, config) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetUpkeepOffchainConfig(id *big.Int, config []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepOffchainConfig(&_IAutomationRegistryMaster.TransactOpts, id, config) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetUpkeepPrivilegeConfig(opts *bind.TransactOpts, upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setUpkeepPrivilegeConfig", upkeepId, newPrivilegeConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetUpkeepPrivilegeConfig(upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepPrivilegeConfig(&_IAutomationRegistryMaster.TransactOpts, upkeepId, newPrivilegeConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetUpkeepPrivilegeConfig(upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepPrivilegeConfig(&_IAutomationRegistryMaster.TransactOpts, upkeepId, newPrivilegeConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) SetUpkeepTriggerConfig(opts *bind.TransactOpts, id *big.Int, triggerConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "setUpkeepTriggerConfig", id, triggerConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) SetUpkeepTriggerConfig(id *big.Int, triggerConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepTriggerConfig(&_IAutomationRegistryMaster.TransactOpts, id, triggerConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) SetUpkeepTriggerConfig(id *big.Int, triggerConfig []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.SetUpkeepTriggerConfig(&_IAutomationRegistryMaster.TransactOpts, id, triggerConfig) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "transferOwnership", to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.TransferOwnership(&_IAutomationRegistryMaster.TransactOpts, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.TransferOwnership(&_IAutomationRegistryMaster.TransactOpts, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) TransferPayeeship(opts *bind.TransactOpts, transmitter common.Address, proposed common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "transferPayeeship", transmitter, proposed) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) TransferPayeeship(transmitter common.Address, proposed common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.TransferPayeeship(&_IAutomationRegistryMaster.TransactOpts, transmitter, proposed) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) TransferPayeeship(transmitter common.Address, proposed common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.TransferPayeeship(&_IAutomationRegistryMaster.TransactOpts, transmitter, proposed) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) TransferUpkeepAdmin(opts *bind.TransactOpts, id *big.Int, proposed common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "transferUpkeepAdmin", id, proposed) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) TransferUpkeepAdmin(id *big.Int, proposed common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.TransferUpkeepAdmin(&_IAutomationRegistryMaster.TransactOpts, id, proposed) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) TransferUpkeepAdmin(id *big.Int, proposed common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.TransferUpkeepAdmin(&_IAutomationRegistryMaster.TransactOpts, id, proposed) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "transmit", reportContext, rawReport, rs, ss, rawVs) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) Transmit(reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Transmit(&_IAutomationRegistryMaster.TransactOpts, reportContext, rawReport, rs, ss, rawVs) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) Transmit(reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Transmit(&_IAutomationRegistryMaster.TransactOpts, reportContext, rawReport, rs, ss, rawVs) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "unpause") +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) Unpause() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Unpause(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) Unpause() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Unpause(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) UnpauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "unpauseUpkeep", id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) UnpauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.UnpauseUpkeep(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) UnpauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.UnpauseUpkeep(&_IAutomationRegistryMaster.TransactOpts, id) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) WithdrawFunds(opts *bind.TransactOpts, id *big.Int, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "withdrawFunds", id, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) WithdrawFunds(id *big.Int, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.WithdrawFunds(&_IAutomationRegistryMaster.TransactOpts, id, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) WithdrawFunds(id *big.Int, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.WithdrawFunds(&_IAutomationRegistryMaster.TransactOpts, id, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) WithdrawOwnerFunds(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "withdrawOwnerFunds") +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) WithdrawOwnerFunds() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.WithdrawOwnerFunds(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) WithdrawOwnerFunds() (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.WithdrawOwnerFunds(&_IAutomationRegistryMaster.TransactOpts) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) WithdrawPayment(opts *bind.TransactOpts, from common.Address, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.Transact(opts, "withdrawPayment", from, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) WithdrawPayment(from common.Address, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.WithdrawPayment(&_IAutomationRegistryMaster.TransactOpts, from, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) WithdrawPayment(from common.Address, to common.Address) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.WithdrawPayment(&_IAutomationRegistryMaster.TransactOpts, from, to) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.contract.RawTransact(opts, calldata) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Fallback(&_IAutomationRegistryMaster.TransactOpts, calldata) +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _IAutomationRegistryMaster.Contract.Fallback(&_IAutomationRegistryMaster.TransactOpts, calldata) +} + +type IAutomationRegistryMasterAdminPrivilegeConfigSetIterator struct { + Event *IAutomationRegistryMasterAdminPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterAdminPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterAdminPrivilegeConfigSet) + 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(IAutomationRegistryMasterAdminPrivilegeConfigSet) + 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 *IAutomationRegistryMasterAdminPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterAdminPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterAdminPrivilegeConfigSet struct { + Admin common.Address + PrivilegeConfig []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*IAutomationRegistryMasterAdminPrivilegeConfigSetIterator, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterAdminPrivilegeConfigSetIterator{contract: _IAutomationRegistryMaster.contract, event: "AdminPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterAdminPrivilegeConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "AdminPrivilegeConfigSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseAdminPrivilegeConfigSet(log types.Log) (*IAutomationRegistryMasterAdminPrivilegeConfigSet, error) { + event := new(IAutomationRegistryMasterAdminPrivilegeConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "AdminPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterCancelledUpkeepReportIterator struct { + Event *IAutomationRegistryMasterCancelledUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterCancelledUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterCancelledUpkeepReport) + 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(IAutomationRegistryMasterCancelledUpkeepReport) + 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 *IAutomationRegistryMasterCancelledUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterCancelledUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterCancelledUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterCancelledUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterCancelledUpkeepReportIterator{contract: _IAutomationRegistryMaster.contract, event: "CancelledUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterCancelledUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterCancelledUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "CancelledUpkeepReport", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseCancelledUpkeepReport(log types.Log) (*IAutomationRegistryMasterCancelledUpkeepReport, error) { + event := new(IAutomationRegistryMasterCancelledUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "CancelledUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterConfigSetIterator struct { + Event *IAutomationRegistryMasterConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterConfigSet) + 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(IAutomationRegistryMasterConfigSet) + 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 *IAutomationRegistryMasterConfigSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterConfigSet struct { + PreviousConfigBlockNumber uint32 + ConfigDigest [32]byte + ConfigCount uint64 + Signers []common.Address + Transmitters []common.Address + F uint8 + OnchainConfig []byte + OffchainConfigVersion uint64 + OffchainConfig []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterConfigSet(opts *bind.FilterOpts) (*IAutomationRegistryMasterConfigSetIterator, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterConfigSetIterator{contract: _IAutomationRegistryMaster.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterConfigSet) (event.Subscription, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "ConfigSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseConfigSet(log types.Log) (*IAutomationRegistryMasterConfigSet, error) { + event := new(IAutomationRegistryMasterConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterDedupKeyAddedIterator struct { + Event *IAutomationRegistryMasterDedupKeyAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterDedupKeyAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterDedupKeyAdded) + 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(IAutomationRegistryMasterDedupKeyAdded) + 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 *IAutomationRegistryMasterDedupKeyAddedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterDedupKeyAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterDedupKeyAdded struct { + DedupKey [32]byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*IAutomationRegistryMasterDedupKeyAddedIterator, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterDedupKeyAddedIterator{contract: _IAutomationRegistryMaster.contract, event: "DedupKeyAdded", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterDedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterDedupKeyAdded) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "DedupKeyAdded", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseDedupKeyAdded(log types.Log) (*IAutomationRegistryMasterDedupKeyAdded, error) { + event := new(IAutomationRegistryMasterDedupKeyAdded) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "DedupKeyAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterFundsAddedIterator struct { + Event *IAutomationRegistryMasterFundsAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterFundsAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterFundsAdded) + 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(IAutomationRegistryMasterFundsAdded) + 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 *IAutomationRegistryMasterFundsAddedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterFundsAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterFundsAdded struct { + Id *big.Int + From common.Address + Amount *big.Int + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*IAutomationRegistryMasterFundsAddedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterFundsAddedIterator{contract: _IAutomationRegistryMaster.contract, event: "FundsAdded", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterFundsAdded) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "FundsAdded", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseFundsAdded(log types.Log) (*IAutomationRegistryMasterFundsAdded, error) { + event := new(IAutomationRegistryMasterFundsAdded) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "FundsAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterFundsWithdrawnIterator struct { + Event *IAutomationRegistryMasterFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterFundsWithdrawn) + 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(IAutomationRegistryMasterFundsWithdrawn) + 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 *IAutomationRegistryMasterFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterFundsWithdrawn struct { + Id *big.Int + Amount *big.Int + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterFundsWithdrawnIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterFundsWithdrawnIterator{contract: _IAutomationRegistryMaster.contract, event: "FundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterFundsWithdrawn, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterFundsWithdrawn) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "FundsWithdrawn", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseFundsWithdrawn(log types.Log) (*IAutomationRegistryMasterFundsWithdrawn, error) { + event := new(IAutomationRegistryMasterFundsWithdrawn) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "FundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator struct { + Event *IAutomationRegistryMasterInsufficientFundsUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterInsufficientFundsUpkeepReport) + 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(IAutomationRegistryMasterInsufficientFundsUpkeepReport) + 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 *IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterInsufficientFundsUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator{contract: _IAutomationRegistryMaster.contract, event: "InsufficientFundsUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterInsufficientFundsUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseInsufficientFundsUpkeepReport(log types.Log) (*IAutomationRegistryMasterInsufficientFundsUpkeepReport, error) { + event := new(IAutomationRegistryMasterInsufficientFundsUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterOwnerFundsWithdrawnIterator struct { + Event *IAutomationRegistryMasterOwnerFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterOwnerFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterOwnerFundsWithdrawn) + 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(IAutomationRegistryMasterOwnerFundsWithdrawn) + 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 *IAutomationRegistryMasterOwnerFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterOwnerFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterOwnerFundsWithdrawn struct { + Amount *big.Int + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*IAutomationRegistryMasterOwnerFundsWithdrawnIterator, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterOwnerFundsWithdrawnIterator{contract: _IAutomationRegistryMaster.contract, event: "OwnerFundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterOwnerFundsWithdrawn) (event.Subscription, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterOwnerFundsWithdrawn) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "OwnerFundsWithdrawn", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseOwnerFundsWithdrawn(log types.Log) (*IAutomationRegistryMasterOwnerFundsWithdrawn, error) { + event := new(IAutomationRegistryMasterOwnerFundsWithdrawn) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "OwnerFundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterOwnershipTransferRequestedIterator struct { + Event *IAutomationRegistryMasterOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterOwnershipTransferRequested) + 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(IAutomationRegistryMasterOwnershipTransferRequested) + 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 *IAutomationRegistryMasterOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*IAutomationRegistryMasterOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterOwnershipTransferRequestedIterator{contract: _IAutomationRegistryMaster.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterOwnershipTransferRequested) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseOwnershipTransferRequested(log types.Log) (*IAutomationRegistryMasterOwnershipTransferRequested, error) { + event := new(IAutomationRegistryMasterOwnershipTransferRequested) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterOwnershipTransferredIterator struct { + Event *IAutomationRegistryMasterOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterOwnershipTransferred) + 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(IAutomationRegistryMasterOwnershipTransferred) + 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 *IAutomationRegistryMasterOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*IAutomationRegistryMasterOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterOwnershipTransferredIterator{contract: _IAutomationRegistryMaster.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterOwnershipTransferred) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "OwnershipTransferred", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseOwnershipTransferred(log types.Log) (*IAutomationRegistryMasterOwnershipTransferred, error) { + event := new(IAutomationRegistryMasterOwnershipTransferred) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterPausedIterator struct { + Event *IAutomationRegistryMasterPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterPaused) + 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(IAutomationRegistryMasterPaused) + 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 *IAutomationRegistryMasterPausedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterPaused struct { + Account common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterPaused(opts *bind.FilterOpts) (*IAutomationRegistryMasterPausedIterator, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "Paused") + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterPausedIterator{contract: _IAutomationRegistryMaster.contract, event: "Paused", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchPaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPaused) (event.Subscription, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "Paused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterPaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "Paused", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParsePaused(log types.Log) (*IAutomationRegistryMasterPaused, error) { + event := new(IAutomationRegistryMasterPaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "Paused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterPayeesUpdatedIterator struct { + Event *IAutomationRegistryMasterPayeesUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterPayeesUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterPayeesUpdated) + 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(IAutomationRegistryMasterPayeesUpdated) + 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 *IAutomationRegistryMasterPayeesUpdatedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterPayeesUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterPayeesUpdated struct { + Transmitters []common.Address + Payees []common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterPayeesUpdated(opts *bind.FilterOpts) (*IAutomationRegistryMasterPayeesUpdatedIterator, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterPayeesUpdatedIterator{contract: _IAutomationRegistryMaster.contract, event: "PayeesUpdated", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPayeesUpdated) (event.Subscription, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterPayeesUpdated) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PayeesUpdated", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParsePayeesUpdated(log types.Log) (*IAutomationRegistryMasterPayeesUpdated, error) { + event := new(IAutomationRegistryMasterPayeesUpdated) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PayeesUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterPayeeshipTransferRequestedIterator struct { + Event *IAutomationRegistryMasterPayeeshipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterPayeeshipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterPayeeshipTransferRequested) + 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(IAutomationRegistryMasterPayeeshipTransferRequested) + 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 *IAutomationRegistryMasterPayeeshipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterPayeeshipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterPayeeshipTransferRequested struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*IAutomationRegistryMasterPayeeshipTransferRequestedIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterPayeeshipTransferRequestedIterator{contract: _IAutomationRegistryMaster.contract, event: "PayeeshipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterPayeeshipTransferRequested) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PayeeshipTransferRequested", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParsePayeeshipTransferRequested(log types.Log) (*IAutomationRegistryMasterPayeeshipTransferRequested, error) { + event := new(IAutomationRegistryMasterPayeeshipTransferRequested) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PayeeshipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterPayeeshipTransferredIterator struct { + Event *IAutomationRegistryMasterPayeeshipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterPayeeshipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterPayeeshipTransferred) + 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(IAutomationRegistryMasterPayeeshipTransferred) + 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 *IAutomationRegistryMasterPayeeshipTransferredIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterPayeeshipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterPayeeshipTransferred struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*IAutomationRegistryMasterPayeeshipTransferredIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterPayeeshipTransferredIterator{contract: _IAutomationRegistryMaster.contract, event: "PayeeshipTransferred", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterPayeeshipTransferred) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PayeeshipTransferred", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParsePayeeshipTransferred(log types.Log) (*IAutomationRegistryMasterPayeeshipTransferred, error) { + event := new(IAutomationRegistryMasterPayeeshipTransferred) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PayeeshipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterPaymentWithdrawnIterator struct { + Event *IAutomationRegistryMasterPaymentWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterPaymentWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterPaymentWithdrawn) + 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(IAutomationRegistryMasterPaymentWithdrawn) + 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 *IAutomationRegistryMasterPaymentWithdrawnIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterPaymentWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterPaymentWithdrawn struct { + Transmitter common.Address + Amount *big.Int + To common.Address + Payee common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*IAutomationRegistryMasterPaymentWithdrawnIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterPaymentWithdrawnIterator{contract: _IAutomationRegistryMaster.contract, event: "PaymentWithdrawn", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterPaymentWithdrawn) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PaymentWithdrawn", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParsePaymentWithdrawn(log types.Log) (*IAutomationRegistryMasterPaymentWithdrawn, error) { + event := new(IAutomationRegistryMasterPaymentWithdrawn) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "PaymentWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterReorgedUpkeepReportIterator struct { + Event *IAutomationRegistryMasterReorgedUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterReorgedUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterReorgedUpkeepReport) + 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(IAutomationRegistryMasterReorgedUpkeepReport) + 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 *IAutomationRegistryMasterReorgedUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterReorgedUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterReorgedUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterReorgedUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterReorgedUpkeepReportIterator{contract: _IAutomationRegistryMaster.contract, event: "ReorgedUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterReorgedUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "ReorgedUpkeepReport", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseReorgedUpkeepReport(log types.Log) (*IAutomationRegistryMasterReorgedUpkeepReport, error) { + event := new(IAutomationRegistryMasterReorgedUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "ReorgedUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterStaleUpkeepReportIterator struct { + Event *IAutomationRegistryMasterStaleUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterStaleUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterStaleUpkeepReport) + 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(IAutomationRegistryMasterStaleUpkeepReport) + 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 *IAutomationRegistryMasterStaleUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterStaleUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterStaleUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterStaleUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterStaleUpkeepReportIterator{contract: _IAutomationRegistryMaster.contract, event: "StaleUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterStaleUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterStaleUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "StaleUpkeepReport", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseStaleUpkeepReport(log types.Log) (*IAutomationRegistryMasterStaleUpkeepReport, error) { + event := new(IAutomationRegistryMasterStaleUpkeepReport) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "StaleUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterTransmittedIterator struct { + Event *IAutomationRegistryMasterTransmitted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterTransmittedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterTransmitted) + 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(IAutomationRegistryMasterTransmitted) + 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 *IAutomationRegistryMasterTransmittedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterTransmittedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterTransmitted struct { + ConfigDigest [32]byte + Epoch uint32 + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterTransmitted(opts *bind.FilterOpts) (*IAutomationRegistryMasterTransmittedIterator, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "Transmitted") + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterTransmittedIterator{contract: _IAutomationRegistryMaster.contract, event: "Transmitted", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchTransmitted(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterTransmitted) (event.Subscription, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "Transmitted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterTransmitted) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "Transmitted", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseTransmitted(log types.Log) (*IAutomationRegistryMasterTransmitted, error) { + event := new(IAutomationRegistryMasterTransmitted) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "Transmitted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUnpausedIterator struct { + Event *IAutomationRegistryMasterUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUnpaused) + 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(IAutomationRegistryMasterUnpaused) + 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 *IAutomationRegistryMasterUnpausedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUnpaused struct { + Account common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUnpaused(opts *bind.FilterOpts) (*IAutomationRegistryMasterUnpausedIterator, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUnpausedIterator{contract: _IAutomationRegistryMaster.contract, event: "Unpaused", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUnpaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUnpaused) (event.Subscription, error) { + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUnpaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "Unpaused", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUnpaused(log types.Log) (*IAutomationRegistryMasterUnpaused, error) { + event := new(IAutomationRegistryMasterUnpaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "Unpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator struct { + Event *IAutomationRegistryMasterUpkeepAdminTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepAdminTransferRequested) + 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(IAutomationRegistryMasterUpkeepAdminTransferRequested) + 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 *IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepAdminTransferRequested struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepAdminTransferRequested", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepAdminTransferRequested) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepAdminTransferRequested", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepAdminTransferRequested(log types.Log) (*IAutomationRegistryMasterUpkeepAdminTransferRequested, error) { + event := new(IAutomationRegistryMasterUpkeepAdminTransferRequested) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepAdminTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepAdminTransferredIterator struct { + Event *IAutomationRegistryMasterUpkeepAdminTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepAdminTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepAdminTransferred) + 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(IAutomationRegistryMasterUpkeepAdminTransferred) + 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 *IAutomationRegistryMasterUpkeepAdminTransferredIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepAdminTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepAdminTransferred struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*IAutomationRegistryMasterUpkeepAdminTransferredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepAdminTransferredIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepAdminTransferred", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepAdminTransferred) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepAdminTransferred", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepAdminTransferred(log types.Log) (*IAutomationRegistryMasterUpkeepAdminTransferred, error) { + event := new(IAutomationRegistryMasterUpkeepAdminTransferred) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepAdminTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepCanceledIterator struct { + Event *IAutomationRegistryMasterUpkeepCanceled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepCanceledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepCanceled) + 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(IAutomationRegistryMasterUpkeepCanceled) + 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 *IAutomationRegistryMasterUpkeepCanceledIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepCanceled struct { + Id *big.Int + AtBlockHeight uint64 + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*IAutomationRegistryMasterUpkeepCanceledIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepCanceledIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepCanceled", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepCanceled) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepCanceled", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepCanceled(log types.Log) (*IAutomationRegistryMasterUpkeepCanceled, error) { + event := new(IAutomationRegistryMasterUpkeepCanceled) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepCheckDataSetIterator struct { + Event *IAutomationRegistryMasterUpkeepCheckDataSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepCheckDataSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepCheckDataSet) + 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(IAutomationRegistryMasterUpkeepCheckDataSet) + 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 *IAutomationRegistryMasterUpkeepCheckDataSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepCheckDataSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepCheckDataSet struct { + Id *big.Int + NewCheckData []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepCheckDataSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepCheckDataSetIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepCheckDataSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepCheckDataSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepCheckDataSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepCheckDataSet(log types.Log) (*IAutomationRegistryMasterUpkeepCheckDataSet, error) { + event := new(IAutomationRegistryMasterUpkeepCheckDataSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepCheckDataSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepGasLimitSetIterator struct { + Event *IAutomationRegistryMasterUpkeepGasLimitSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepGasLimitSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepGasLimitSet) + 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(IAutomationRegistryMasterUpkeepGasLimitSet) + 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 *IAutomationRegistryMasterUpkeepGasLimitSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepGasLimitSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepGasLimitSet struct { + Id *big.Int + GasLimit *big.Int + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepGasLimitSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepGasLimitSetIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepGasLimitSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepGasLimitSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepGasLimitSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepGasLimitSet(log types.Log) (*IAutomationRegistryMasterUpkeepGasLimitSet, error) { + event := new(IAutomationRegistryMasterUpkeepGasLimitSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepGasLimitSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepMigratedIterator struct { + Event *IAutomationRegistryMasterUpkeepMigrated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepMigratedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepMigrated) + 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(IAutomationRegistryMasterUpkeepMigrated) + 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 *IAutomationRegistryMasterUpkeepMigratedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepMigratedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepMigrated struct { + Id *big.Int + RemainingBalance *big.Int + Destination common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepMigratedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepMigratedIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepMigrated", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepMigrated, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepMigrated) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepMigrated", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepMigrated(log types.Log) (*IAutomationRegistryMasterUpkeepMigrated, error) { + event := new(IAutomationRegistryMasterUpkeepMigrated) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepMigrated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepOffchainConfigSetIterator struct { + Event *IAutomationRegistryMasterUpkeepOffchainConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepOffchainConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepOffchainConfigSet) + 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(IAutomationRegistryMasterUpkeepOffchainConfigSet) + 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 *IAutomationRegistryMasterUpkeepOffchainConfigSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepOffchainConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepOffchainConfigSet struct { + Id *big.Int + OffchainConfig []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepOffchainConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepOffchainConfigSetIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepOffchainConfigSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepOffchainConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepOffchainConfigSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepOffchainConfigSet(log types.Log) (*IAutomationRegistryMasterUpkeepOffchainConfigSet, error) { + event := new(IAutomationRegistryMasterUpkeepOffchainConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepOffchainConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepPausedIterator struct { + Event *IAutomationRegistryMasterUpkeepPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepPaused) + 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(IAutomationRegistryMasterUpkeepPaused) + 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 *IAutomationRegistryMasterUpkeepPausedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepPaused struct { + Id *big.Int + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepPausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepPausedIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepPaused", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepPaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepPaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepPaused", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepPaused(log types.Log) (*IAutomationRegistryMasterUpkeepPaused, error) { + event := new(IAutomationRegistryMasterUpkeepPaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepPaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepPerformedIterator struct { + Event *IAutomationRegistryMasterUpkeepPerformed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepPerformedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepPerformed) + 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(IAutomationRegistryMasterUpkeepPerformed) + 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 *IAutomationRegistryMasterUpkeepPerformedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepPerformedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepPerformed struct { + Id *big.Int + Success bool + TotalPayment *big.Int + GasUsed *big.Int + GasOverhead *big.Int + Trigger []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*IAutomationRegistryMasterUpkeepPerformedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepPerformedIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepPerformed", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepPerformed) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepPerformed", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepPerformed(log types.Log) (*IAutomationRegistryMasterUpkeepPerformed, error) { + event := new(IAutomationRegistryMasterUpkeepPerformed) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepPerformed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator struct { + Event *IAutomationRegistryMasterUpkeepPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepPrivilegeConfigSet) + 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(IAutomationRegistryMasterUpkeepPrivilegeConfigSet) + 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 *IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepPrivilegeConfigSet struct { + Id *big.Int + PrivilegeConfig []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepPrivilegeConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepPrivilegeConfigSet(log types.Log) (*IAutomationRegistryMasterUpkeepPrivilegeConfigSet, error) { + event := new(IAutomationRegistryMasterUpkeepPrivilegeConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepReceivedIterator struct { + Event *IAutomationRegistryMasterUpkeepReceived + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepReceivedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepReceived) + 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(IAutomationRegistryMasterUpkeepReceived) + 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 *IAutomationRegistryMasterUpkeepReceivedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepReceivedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepReceived struct { + Id *big.Int + StartingBalance *big.Int + ImportedFrom common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepReceivedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepReceivedIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepReceived", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepReceived, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepReceived) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepReceived", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepReceived(log types.Log) (*IAutomationRegistryMasterUpkeepReceived, error) { + event := new(IAutomationRegistryMasterUpkeepReceived) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepReceived", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepRegisteredIterator struct { + Event *IAutomationRegistryMasterUpkeepRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepRegistered) + 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(IAutomationRegistryMasterUpkeepRegistered) + 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 *IAutomationRegistryMasterUpkeepRegisteredIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepRegistered struct { + Id *big.Int + PerformGas uint32 + Admin common.Address + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepRegisteredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepRegisteredIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepRegistered", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepRegistered, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepRegistered) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepRegistered", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepRegistered(log types.Log) (*IAutomationRegistryMasterUpkeepRegistered, error) { + event := new(IAutomationRegistryMasterUpkeepRegistered) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepTriggerConfigSetIterator struct { + Event *IAutomationRegistryMasterUpkeepTriggerConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepTriggerConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepTriggerConfigSet) + 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(IAutomationRegistryMasterUpkeepTriggerConfigSet) + 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 *IAutomationRegistryMasterUpkeepTriggerConfigSetIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepTriggerConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepTriggerConfigSet struct { + Id *big.Int + TriggerConfig []byte + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepTriggerConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepTriggerConfigSetIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepTriggerConfigSet", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepTriggerConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepTriggerConfigSet", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepTriggerConfigSet(log types.Log) (*IAutomationRegistryMasterUpkeepTriggerConfigSet, error) { + event := new(IAutomationRegistryMasterUpkeepTriggerConfigSet) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepTriggerConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type IAutomationRegistryMasterUpkeepUnpausedIterator struct { + Event *IAutomationRegistryMasterUpkeepUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *IAutomationRegistryMasterUpkeepUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(IAutomationRegistryMasterUpkeepUnpaused) + 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(IAutomationRegistryMasterUpkeepUnpaused) + 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 *IAutomationRegistryMasterUpkeepUnpausedIterator) Error() error { + return it.fail +} + +func (it *IAutomationRegistryMasterUpkeepUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type IAutomationRegistryMasterUpkeepUnpaused struct { + Id *big.Int + Raw types.Log +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepUnpausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.FilterLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return &IAutomationRegistryMasterUpkeepUnpausedIterator{contract: _IAutomationRegistryMaster.contract, event: "UpkeepUnpaused", logs: logs, sub: sub}, nil +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepUnpaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _IAutomationRegistryMaster.contract.WatchLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(IAutomationRegistryMasterUpkeepUnpaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepUnpaused", 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 (_IAutomationRegistryMaster *IAutomationRegistryMasterFilterer) ParseUpkeepUnpaused(log types.Log) (*IAutomationRegistryMasterUpkeepUnpaused, error) { + event := new(IAutomationRegistryMasterUpkeepUnpaused) + if err := _IAutomationRegistryMaster.contract.UnpackLog(event, "UpkeepUnpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type CheckCallback struct { + UpkeepNeeded bool + PerformData []byte + UpkeepFailureReason uint8 + GasUsed *big.Int +} +type CheckUpkeep struct { + UpkeepNeeded bool + PerformData []byte + UpkeepFailureReason uint8 + GasUsed *big.Int + GasLimit *big.Int + FastGasWei *big.Int + LinkNative *big.Int +} +type CheckUpkeep0 struct { + UpkeepNeeded bool + PerformData []byte + UpkeepFailureReason uint8 + GasUsed *big.Int + GasLimit *big.Int + FastGasWei *big.Int + LinkNative *big.Int +} +type GetSignerInfo struct { + Active bool + Index uint8 +} +type GetState struct { + State AutomationRegistryBase22State + Config AutomationRegistryBase22OnchainConfig + Signers []common.Address + Transmitters []common.Address + F uint8 +} +type GetTransmitterInfo struct { + Active bool + Index uint8 + Balance *big.Int + LastCollected *big.Int + Payee common.Address +} +type LatestConfigDetails struct { + ConfigCount uint32 + BlockNumber uint32 + ConfigDigest [32]byte +} +type LatestConfigDigestAndEpoch struct { + ScanLogs bool + ConfigDigest [32]byte + Epoch uint32 +} +type SimulatePerformUpkeep struct { + Success bool + GasUsed *big.Int +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMaster) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _IAutomationRegistryMaster.abi.Events["AdminPrivilegeConfigSet"].ID: + return _IAutomationRegistryMaster.ParseAdminPrivilegeConfigSet(log) + case _IAutomationRegistryMaster.abi.Events["CancelledUpkeepReport"].ID: + return _IAutomationRegistryMaster.ParseCancelledUpkeepReport(log) + case _IAutomationRegistryMaster.abi.Events["ConfigSet"].ID: + return _IAutomationRegistryMaster.ParseConfigSet(log) + case _IAutomationRegistryMaster.abi.Events["DedupKeyAdded"].ID: + return _IAutomationRegistryMaster.ParseDedupKeyAdded(log) + case _IAutomationRegistryMaster.abi.Events["FundsAdded"].ID: + return _IAutomationRegistryMaster.ParseFundsAdded(log) + case _IAutomationRegistryMaster.abi.Events["FundsWithdrawn"].ID: + return _IAutomationRegistryMaster.ParseFundsWithdrawn(log) + case _IAutomationRegistryMaster.abi.Events["InsufficientFundsUpkeepReport"].ID: + return _IAutomationRegistryMaster.ParseInsufficientFundsUpkeepReport(log) + case _IAutomationRegistryMaster.abi.Events["OwnerFundsWithdrawn"].ID: + return _IAutomationRegistryMaster.ParseOwnerFundsWithdrawn(log) + case _IAutomationRegistryMaster.abi.Events["OwnershipTransferRequested"].ID: + return _IAutomationRegistryMaster.ParseOwnershipTransferRequested(log) + case _IAutomationRegistryMaster.abi.Events["OwnershipTransferred"].ID: + return _IAutomationRegistryMaster.ParseOwnershipTransferred(log) + case _IAutomationRegistryMaster.abi.Events["Paused"].ID: + return _IAutomationRegistryMaster.ParsePaused(log) + case _IAutomationRegistryMaster.abi.Events["PayeesUpdated"].ID: + return _IAutomationRegistryMaster.ParsePayeesUpdated(log) + case _IAutomationRegistryMaster.abi.Events["PayeeshipTransferRequested"].ID: + return _IAutomationRegistryMaster.ParsePayeeshipTransferRequested(log) + case _IAutomationRegistryMaster.abi.Events["PayeeshipTransferred"].ID: + return _IAutomationRegistryMaster.ParsePayeeshipTransferred(log) + case _IAutomationRegistryMaster.abi.Events["PaymentWithdrawn"].ID: + return _IAutomationRegistryMaster.ParsePaymentWithdrawn(log) + case _IAutomationRegistryMaster.abi.Events["ReorgedUpkeepReport"].ID: + return _IAutomationRegistryMaster.ParseReorgedUpkeepReport(log) + case _IAutomationRegistryMaster.abi.Events["StaleUpkeepReport"].ID: + return _IAutomationRegistryMaster.ParseStaleUpkeepReport(log) + case _IAutomationRegistryMaster.abi.Events["Transmitted"].ID: + return _IAutomationRegistryMaster.ParseTransmitted(log) + case _IAutomationRegistryMaster.abi.Events["Unpaused"].ID: + return _IAutomationRegistryMaster.ParseUnpaused(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepAdminTransferRequested"].ID: + return _IAutomationRegistryMaster.ParseUpkeepAdminTransferRequested(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepAdminTransferred"].ID: + return _IAutomationRegistryMaster.ParseUpkeepAdminTransferred(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepCanceled"].ID: + return _IAutomationRegistryMaster.ParseUpkeepCanceled(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepCheckDataSet"].ID: + return _IAutomationRegistryMaster.ParseUpkeepCheckDataSet(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepGasLimitSet"].ID: + return _IAutomationRegistryMaster.ParseUpkeepGasLimitSet(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepMigrated"].ID: + return _IAutomationRegistryMaster.ParseUpkeepMigrated(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepOffchainConfigSet"].ID: + return _IAutomationRegistryMaster.ParseUpkeepOffchainConfigSet(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepPaused"].ID: + return _IAutomationRegistryMaster.ParseUpkeepPaused(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepPerformed"].ID: + return _IAutomationRegistryMaster.ParseUpkeepPerformed(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepPrivilegeConfigSet"].ID: + return _IAutomationRegistryMaster.ParseUpkeepPrivilegeConfigSet(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepReceived"].ID: + return _IAutomationRegistryMaster.ParseUpkeepReceived(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepRegistered"].ID: + return _IAutomationRegistryMaster.ParseUpkeepRegistered(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepTriggerConfigSet"].ID: + return _IAutomationRegistryMaster.ParseUpkeepTriggerConfigSet(log) + case _IAutomationRegistryMaster.abi.Events["UpkeepUnpaused"].ID: + return _IAutomationRegistryMaster.ParseUpkeepUnpaused(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (IAutomationRegistryMasterAdminPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d2") +} + +func (IAutomationRegistryMasterCancelledUpkeepReport) Topic() common.Hash { + return common.HexToHash("0xc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636") +} + +func (IAutomationRegistryMasterConfigSet) Topic() common.Hash { + return common.HexToHash("0x1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05") +} + +func (IAutomationRegistryMasterDedupKeyAdded) Topic() common.Hash { + return common.HexToHash("0xa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f2") +} + +func (IAutomationRegistryMasterFundsAdded) Topic() common.Hash { + return common.HexToHash("0xafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa734891506203") +} + +func (IAutomationRegistryMasterFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0xf3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318") +} + +func (IAutomationRegistryMasterInsufficientFundsUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e02") +} + +func (IAutomationRegistryMasterOwnerFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0x1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f1") +} + +func (IAutomationRegistryMasterOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (IAutomationRegistryMasterOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (IAutomationRegistryMasterPaused) Topic() common.Hash { + return common.HexToHash("0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258") +} + +func (IAutomationRegistryMasterPayeesUpdated) Topic() common.Hash { + return common.HexToHash("0xa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725") +} + +func (IAutomationRegistryMasterPayeeshipTransferRequested) Topic() common.Hash { + return common.HexToHash("0x84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367") +} + +func (IAutomationRegistryMasterPayeeshipTransferred) Topic() common.Hash { + return common.HexToHash("0x78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b3") +} + +func (IAutomationRegistryMasterPaymentWithdrawn) Topic() common.Hash { + return common.HexToHash("0x9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f40698") +} + +func (IAutomationRegistryMasterReorgedUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301") +} + +func (IAutomationRegistryMasterStaleUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8") +} + +func (IAutomationRegistryMasterTransmitted) Topic() common.Hash { + return common.HexToHash("0xb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62") +} + +func (IAutomationRegistryMasterUnpaused) Topic() common.Hash { + return common.HexToHash("0x5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa") +} + +func (IAutomationRegistryMasterUpkeepAdminTransferRequested) Topic() common.Hash { + return common.HexToHash("0xb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b35") +} + +func (IAutomationRegistryMasterUpkeepAdminTransferred) Topic() common.Hash { + return common.HexToHash("0x5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c") +} + +func (IAutomationRegistryMasterUpkeepCanceled) Topic() common.Hash { + return common.HexToHash("0x91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f791181") +} + +func (IAutomationRegistryMasterUpkeepCheckDataSet) Topic() common.Hash { + return common.HexToHash("0xcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d") +} + +func (IAutomationRegistryMasterUpkeepGasLimitSet) Topic() common.Hash { + return common.HexToHash("0xc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c") +} + +func (IAutomationRegistryMasterUpkeepMigrated) Topic() common.Hash { + return common.HexToHash("0xb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff") +} + +func (IAutomationRegistryMasterUpkeepOffchainConfigSet) Topic() common.Hash { + return common.HexToHash("0x3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf4850") +} + +func (IAutomationRegistryMasterUpkeepPaused) Topic() common.Hash { + return common.HexToHash("0x8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f") +} + +func (IAutomationRegistryMasterUpkeepPerformed) Topic() common.Hash { + return common.HexToHash("0xad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b") +} + +func (IAutomationRegistryMasterUpkeepPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae7769") +} + +func (IAutomationRegistryMasterUpkeepReceived) Topic() common.Hash { + return common.HexToHash("0x74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a71") +} + +func (IAutomationRegistryMasterUpkeepRegistered) Topic() common.Hash { + return common.HexToHash("0xbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d012") +} + +func (IAutomationRegistryMasterUpkeepTriggerConfigSet) Topic() common.Hash { + return common.HexToHash("0x2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664") +} + +func (IAutomationRegistryMasterUpkeepUnpaused) Topic() common.Hash { + return common.HexToHash("0x7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a47456") +} + +func (_IAutomationRegistryMaster *IAutomationRegistryMaster) Address() common.Address { + return _IAutomationRegistryMaster.address +} + +type IAutomationRegistryMasterInterface interface { + CheckCallback(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (CheckCallback, + + error) + + CheckUpkeep(opts *bind.CallOpts, id *big.Int, triggerData []byte) (CheckUpkeep, + + error) + + CheckUpkeep0(opts *bind.CallOpts, id *big.Int) (CheckUpkeep0, + + error) + + FallbackTo(opts *bind.CallOpts) (common.Address, error) + + GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) + + GetAdminPrivilegeConfig(opts *bind.CallOpts, admin common.Address) ([]byte, error) + + GetAutomationForwarderLogic(opts *bind.CallOpts) (common.Address, error) + + GetBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) + + GetCancellationDelay(opts *bind.CallOpts) (*big.Int, error) + + GetConditionalGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetFastGasFeedAddress(opts *bind.CallOpts) (common.Address, error) + + GetForwarder(opts *bind.CallOpts, upkeepID *big.Int) (common.Address, error) + + GetLinkAddress(opts *bind.CallOpts) (common.Address, error) + + GetLinkNativeFeedAddress(opts *bind.CallOpts) (common.Address, error) + + GetLogGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetMaxPaymentForGas(opts *bind.CallOpts, triggerType uint8, gasLimit uint32) (*big.Int, error) + + GetMinBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) + + GetMinBalanceForUpkeep(opts *bind.CallOpts, id *big.Int) (*big.Int, error) + + GetMode(opts *bind.CallOpts) (uint8, error) + + GetPeerRegistryMigrationPermission(opts *bind.CallOpts, peer common.Address) (uint8, error) + + GetPerPerformByteGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetPerSignerGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetSignerInfo(opts *bind.CallOpts, query common.Address) (GetSignerInfo, + + error) + + GetState(opts *bind.CallOpts) (GetState, + + error) + + GetTransmitterInfo(opts *bind.CallOpts, query common.Address) (GetTransmitterInfo, + + error) + + GetTriggerType(opts *bind.CallOpts, upkeepId *big.Int) (uint8, error) + + GetUpkeep(opts *bind.CallOpts, id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) + + GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + + GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + + HasDedupKey(opts *bind.CallOpts, dedupKey [32]byte) (bool, error) + + LatestConfigDetails(opts *bind.CallOpts) (LatestConfigDetails, + + error) + + LatestConfigDigestAndEpoch(opts *bind.CallOpts) (LatestConfigDigestAndEpoch, + + error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + SimulatePerformUpkeep(opts *bind.CallOpts, id *big.Int, performData []byte) (SimulatePerformUpkeep, + + error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + UpkeepTranscoderVersion(opts *bind.CallOpts) (uint8, error) + + UpkeepVersion(opts *bind.CallOpts) (uint8, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AcceptPayeeship(opts *bind.TransactOpts, transmitter common.Address) (*types.Transaction, error) + + AcceptUpkeepAdmin(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + AddFunds(opts *bind.TransactOpts, id *big.Int, amount *big.Int) (*types.Transaction, error) + + CancelUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + ExecuteCallback(opts *bind.TransactOpts, id *big.Int, payload []byte) (*types.Transaction, error) + + MigrateUpkeeps(opts *bind.TransactOpts, ids []*big.Int, destination common.Address) (*types.Transaction, error) + + OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) + + Pause(opts *bind.TransactOpts) (*types.Transaction, error) + + PauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + ReceiveUpkeeps(opts *bind.TransactOpts, encodedUpkeeps []byte) (*types.Transaction, error) + + RecoverFunds(opts *bind.TransactOpts) (*types.Transaction, error) + + RegisterUpkeep(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) + + RegisterUpkeep0(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) + + SetAdminPrivilegeConfig(opts *bind.TransactOpts, admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) + + SetConfigTypeSafe(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) + + SetPayees(opts *bind.TransactOpts, payees []common.Address) (*types.Transaction, error) + + SetPeerRegistryMigrationPermission(opts *bind.TransactOpts, peer common.Address, permission uint8) (*types.Transaction, error) + + SetUpkeepCheckData(opts *bind.TransactOpts, id *big.Int, newCheckData []byte) (*types.Transaction, error) + + SetUpkeepGasLimit(opts *bind.TransactOpts, id *big.Int, gasLimit uint32) (*types.Transaction, error) + + SetUpkeepOffchainConfig(opts *bind.TransactOpts, id *big.Int, config []byte) (*types.Transaction, error) + + SetUpkeepPrivilegeConfig(opts *bind.TransactOpts, upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) + + SetUpkeepTriggerConfig(opts *bind.TransactOpts, id *big.Int, triggerConfig []byte) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + TransferPayeeship(opts *bind.TransactOpts, transmitter common.Address, proposed common.Address) (*types.Transaction, error) + + TransferUpkeepAdmin(opts *bind.TransactOpts, id *big.Int, proposed common.Address) (*types.Transaction, error) + + Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) + + Unpause(opts *bind.TransactOpts) (*types.Transaction, error) + + UnpauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + WithdrawFunds(opts *bind.TransactOpts, id *big.Int, to common.Address) (*types.Transaction, error) + + WithdrawOwnerFunds(opts *bind.TransactOpts) (*types.Transaction, error) + + WithdrawPayment(opts *bind.TransactOpts, from common.Address, to common.Address) (*types.Transaction, error) + + Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) + + FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*IAutomationRegistryMasterAdminPrivilegeConfigSetIterator, error) + + WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) + + ParseAdminPrivilegeConfigSet(log types.Log) (*IAutomationRegistryMasterAdminPrivilegeConfigSet, error) + + FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterCancelledUpkeepReportIterator, error) + + WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterCancelledUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseCancelledUpkeepReport(log types.Log) (*IAutomationRegistryMasterCancelledUpkeepReport, error) + + FilterConfigSet(opts *bind.FilterOpts) (*IAutomationRegistryMasterConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterConfigSet) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*IAutomationRegistryMasterConfigSet, error) + + FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*IAutomationRegistryMasterDedupKeyAddedIterator, error) + + WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterDedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) + + ParseDedupKeyAdded(log types.Log) (*IAutomationRegistryMasterDedupKeyAdded, error) + + FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*IAutomationRegistryMasterFundsAddedIterator, error) + + WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) + + ParseFundsAdded(log types.Log) (*IAutomationRegistryMasterFundsAdded, error) + + FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterFundsWithdrawnIterator, error) + + WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterFundsWithdrawn, id []*big.Int) (event.Subscription, error) + + ParseFundsWithdrawn(log types.Log) (*IAutomationRegistryMasterFundsWithdrawn, error) + + FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterInsufficientFundsUpkeepReportIterator, error) + + WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseInsufficientFundsUpkeepReport(log types.Log) (*IAutomationRegistryMasterInsufficientFundsUpkeepReport, error) + + FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*IAutomationRegistryMasterOwnerFundsWithdrawnIterator, error) + + WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterOwnerFundsWithdrawn) (event.Subscription, error) + + ParseOwnerFundsWithdrawn(log types.Log) (*IAutomationRegistryMasterOwnerFundsWithdrawn, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*IAutomationRegistryMasterOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*IAutomationRegistryMasterOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*IAutomationRegistryMasterOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*IAutomationRegistryMasterOwnershipTransferred, error) + + FilterPaused(opts *bind.FilterOpts) (*IAutomationRegistryMasterPausedIterator, error) + + WatchPaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPaused) (event.Subscription, error) + + ParsePaused(log types.Log) (*IAutomationRegistryMasterPaused, error) + + FilterPayeesUpdated(opts *bind.FilterOpts) (*IAutomationRegistryMasterPayeesUpdatedIterator, error) + + WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPayeesUpdated) (event.Subscription, error) + + ParsePayeesUpdated(log types.Log) (*IAutomationRegistryMasterPayeesUpdated, error) + + FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*IAutomationRegistryMasterPayeeshipTransferRequestedIterator, error) + + WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferRequested(log types.Log) (*IAutomationRegistryMasterPayeeshipTransferRequested, error) + + FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*IAutomationRegistryMasterPayeeshipTransferredIterator, error) + + WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferred(log types.Log) (*IAutomationRegistryMasterPayeeshipTransferred, error) + + FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*IAutomationRegistryMasterPaymentWithdrawnIterator, error) + + WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) + + ParsePaymentWithdrawn(log types.Log) (*IAutomationRegistryMasterPaymentWithdrawn, error) + + FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterReorgedUpkeepReportIterator, error) + + WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseReorgedUpkeepReport(log types.Log) (*IAutomationRegistryMasterReorgedUpkeepReport, error) + + FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterStaleUpkeepReportIterator, error) + + WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterStaleUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseStaleUpkeepReport(log types.Log) (*IAutomationRegistryMasterStaleUpkeepReport, error) + + FilterTransmitted(opts *bind.FilterOpts) (*IAutomationRegistryMasterTransmittedIterator, error) + + WatchTransmitted(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterTransmitted) (event.Subscription, error) + + ParseTransmitted(log types.Log) (*IAutomationRegistryMasterTransmitted, error) + + FilterUnpaused(opts *bind.FilterOpts) (*IAutomationRegistryMasterUnpausedIterator, error) + + WatchUnpaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUnpaused) (event.Subscription, error) + + ParseUnpaused(log types.Log) (*IAutomationRegistryMasterUnpaused, error) + + FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*IAutomationRegistryMasterUpkeepAdminTransferRequestedIterator, error) + + WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferRequested(log types.Log) (*IAutomationRegistryMasterUpkeepAdminTransferRequested, error) + + FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*IAutomationRegistryMasterUpkeepAdminTransferredIterator, error) + + WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferred(log types.Log) (*IAutomationRegistryMasterUpkeepAdminTransferred, error) + + FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*IAutomationRegistryMasterUpkeepCanceledIterator, error) + + WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) + + ParseUpkeepCanceled(log types.Log) (*IAutomationRegistryMasterUpkeepCanceled, error) + + FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepCheckDataSetIterator, error) + + WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepCheckDataSet(log types.Log) (*IAutomationRegistryMasterUpkeepCheckDataSet, error) + + FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepGasLimitSetIterator, error) + + WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepGasLimitSet(log types.Log) (*IAutomationRegistryMasterUpkeepGasLimitSet, error) + + FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepMigratedIterator, error) + + WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepMigrated, id []*big.Int) (event.Subscription, error) + + ParseUpkeepMigrated(log types.Log) (*IAutomationRegistryMasterUpkeepMigrated, error) + + FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepOffchainConfigSetIterator, error) + + WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepOffchainConfigSet(log types.Log) (*IAutomationRegistryMasterUpkeepOffchainConfigSet, error) + + FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepPausedIterator, error) + + WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepPaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPaused(log types.Log) (*IAutomationRegistryMasterUpkeepPaused, error) + + FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*IAutomationRegistryMasterUpkeepPerformedIterator, error) + + WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) + + ParseUpkeepPerformed(log types.Log) (*IAutomationRegistryMasterUpkeepPerformed, error) + + FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepPrivilegeConfigSetIterator, error) + + WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPrivilegeConfigSet(log types.Log) (*IAutomationRegistryMasterUpkeepPrivilegeConfigSet, error) + + FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepReceivedIterator, error) + + WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepReceived, id []*big.Int) (event.Subscription, error) + + ParseUpkeepReceived(log types.Log) (*IAutomationRegistryMasterUpkeepReceived, error) + + FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepRegisteredIterator, error) + + WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepRegistered, id []*big.Int) (event.Subscription, error) + + ParseUpkeepRegistered(log types.Log) (*IAutomationRegistryMasterUpkeepRegistered, error) + + FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepTriggerConfigSetIterator, error) + + WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepTriggerConfigSet(log types.Log) (*IAutomationRegistryMasterUpkeepTriggerConfigSet, error) + + FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*IAutomationRegistryMasterUpkeepUnpausedIterator, error) + + WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *IAutomationRegistryMasterUpkeepUnpaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepUnpaused(log types.Log) (*IAutomationRegistryMasterUpkeepUnpaused, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go b/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go new file mode 100644 index 00000000000..31aa106358c --- /dev/null +++ b/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go @@ -0,0 +1,4863 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package keeper_registry_logic_a_wrapper_2_2 + +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 +) + +var AutomationRegistryLogicAMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicB2_2\",\"name\":\"logicB\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"addFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancelUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"values\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"checkCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"executeCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"migrateUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedUpkeeps\",\"type\":\"bytes\"}],\"name\":\"receiveUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101406040523480156200001257600080fd5b50604051620061d1380380620061d18339810160408190526200003591620003df565b80816001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b919062000406565b826001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003df565b836001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003df565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003df565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003df565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b9816200031b565b505050846002811115620002d157620002d162000429565b60e0816002811115620002e857620002e862000429565b9052506001600160a01b0393841660805291831660a052821660c0528116610100529190911661012052506200043f9050565b336001600160a01b03821603620003755760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003dc57600080fd5b50565b600060208284031215620003f257600080fd5b8151620003ff81620003c6565b9392505050565b6000602082840312156200041957600080fd5b815160038110620003ff57600080fd5b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e0516101005161012051615d18620004b96000396000818161010e01526101a90152600081816103e10152611fa10152600081816135370152818161376d015281816139b50152613b5d015260006130e1015260006131c5015260008181611de301526123af0152615d186000f3fe60806040523480156200001157600080fd5b50600436106200010c5760003560e01c806385c1b0ba11620000a5578063c8048022116200006f578063c804802214620002b7578063ce7dc5b414620002ce578063f2fde38b14620002e5578063f7d334ba14620002fc576200010c565b806385c1b0ba14620002535780638da5cb5b146200026a5780638e86139b1462000289578063948108f714620002a0576200010c565b80634ee88d3511620000e75780634ee88d3514620001ef5780636ded9eae146200020657806371791aa0146200021d57806379ba50971462000249576200010c565b806328f32f38146200015457806329c5efad146200017e578063349e8cca14620001a7575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156200014d573d6000f35b3d6000fd5b005b6200016b62000165366004620041ea565b62000313565b6040519081526020015b60405180910390f35b620001956200018f366004620042d0565b6200068c565b604051620001759493929190620043f8565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200162000175565b620001526200020036600462004435565b62000930565b6200016b6200021736600462004485565b62000998565b620002346200022e366004620042d0565b620009fe565b60405162000175979695949392919062004538565b62000152620010f0565b62000152620002643660046200458a565b620011f3565b60005473ffffffffffffffffffffffffffffffffffffffff16620001c9565b620001526200029a36600462004617565b62001e64565b62000152620002b13660046200467a565b620021ec565b62000152620002c8366004620046a9565b6200247f565b62000195620002df3660046200477f565b62002846565b62000152620002f6366004620047f6565b62002916565b620002346200030d366004620046a9565b6200292e565b6000805473ffffffffffffffffffffffffffffffffffffffff163314801590620003475750620003456009336200296c565b155b156200037f576040517fd48b678b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff89163b620003ce576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b620003d986620029a0565b9050600089307f00000000000000000000000000000000000000000000000000000000000000006040516200040e9062003f7b565b73ffffffffffffffffffffffffffffffffffffffff938416815291831660208301529091166040820152606001604051809103906000f08015801562000458573d6000803e3d6000fd5b5090506200051f826040518060e001604052806000151581526020018c63ffffffff16815260200163ffffffff801681526020018473ffffffffffffffffffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff168152602001600063ffffffff168152508a89898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b92508a915062002b449050565b6014805474010000000000000000000000000000000000000000900463ffffffff1690806200054e8362004845565b91906101000a81548163ffffffff021916908363ffffffff16021790555050817fbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d0128a8a604051620005c792919063ffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a2817fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d878760405162000603929190620048b4565b60405180910390a2817f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664856040516200063d9190620048ca565b60405180910390a2817f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf485084604051620006779190620048ca565b60405180910390a25098975050505050505050565b600060606000806200069d62002f1f565b600086815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff90811694830194909452650100000000008104841694820194909452690100000000000000000090930473ffffffffffffffffffffffffffffffffffffffff166060840152600101546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a0840152780100000000000000000000000000000000000000000000000090041660c08201525a9150600080826060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620007b7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007dd9190620048ec565b73ffffffffffffffffffffffffffffffffffffffff166013600101600c9054906101000a900463ffffffff1663ffffffff16896040516200081f91906200490c565b60006040518083038160008787f1925050503d80600081146200085f576040519150601f19603f3d011682016040523d82523d6000602084013e62000864565b606091505b50915091505a6200087690856200492a565b935081620008a157600060405180602001604052806000815250600796509650965050505062000927565b80806020019051810190620008b791906200499b565b909750955086620008e557600060405180602001604052806000815250600496509650965050505062000927565b601554865164010000000090910463ffffffff1610156200092357600060405180602001604052806000815250600596509650965050505062000927565b5050505b92959194509250565b6200093b8362002f5a565b6000838152601a602052604090206200095682848362004a90565b50827f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d566483836040516200098b929190620048b4565b60405180910390a2505050565b6000620009f288888860008989604051806020016040528060008152508a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506200031392505050565b98975050505050505050565b60006060600080600080600062000a1462002f1f565b600062000a218a62003010565b905060006012604051806101200160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160059054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160099054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600c9054906101000a900461ffff1661ffff1661ffff16815260200160008201600e9054906101000a900460ff1615151515815260200160008201600f9054906101000a900460ff161515151581526020016000820160109054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160008201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090506000600460008d81526020019081526020016000206040518060e00160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160059054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160099054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160018201600c9054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff1681526020016001820160189054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090508160a001511562000d45576000604051806020016040528060008152506009600084602001516000808263ffffffff1692509950995099509950995099509950505050620010e4565b604081015163ffffffff9081161462000d96576000604051806020016040528060008152506001600084602001516000808263ffffffff1692509950995099509950995099509950505050620010e4565b80511562000ddc576000604051806020016040528060008152506002600084602001516000808263ffffffff1692509950995099509950995099509950505050620010e4565b62000de782620030be565b602083015160155492975090955060009162000e19918591879190640100000000900463ffffffff168a8a87620032b0565b9050806bffffffffffffffffffffffff168260a001516bffffffffffffffffffffffff16101562000e83576000604051806020016040528060008152506006600085602001516000808263ffffffff1692509a509a509a509a509a509a509a5050505050620010e4565b600062000e928e868f62003301565b90505a9850600080846060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000eea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000f109190620048ec565b73ffffffffffffffffffffffffffffffffffffffff166013600101600c9054906101000a900463ffffffff1663ffffffff168460405162000f5291906200490c565b60006040518083038160008787f1925050503d806000811462000f92576040519150601f19603f3d011682016040523d82523d6000602084013e62000f97565b606091505b50915091505a62000fa9908c6200492a565b9a5081620010295760155481516801000000000000000090910463ffffffff1610156200100657505060408051602080820190925260008082529490910151939c509a50600899505063ffffffff9091169550620010e492505050565b602090940151939b5060039a505063ffffffff9092169650620010e49350505050565b808060200190518101906200103f91906200499b565b909e509c508d6200108057505060408051602080820190925260008082529490910151939c509a50600499505063ffffffff9091169550620010e492505050565b6015548d5164010000000090910463ffffffff161015620010d157505060408051602080820190925260008082529490910151939c509a50600599505063ffffffff9091169550620010e492505050565b505050506020015163ffffffff16945050505b92959891949750929550565b60015473ffffffffffffffffffffffffffffffffffffffff16331462001177576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600173ffffffffffffffffffffffffffffffffffffffff821660009081526019602052604090205460ff1660038111156200123257620012326200438d565b141580156200127e5750600373ffffffffffffffffffffffffffffffffffffffff821660009081526019602052604090205460ff1660038111156200127b576200127b6200438d565b14155b15620012b6576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6013546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1662001316576040517fd12d7d8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082900362001352576040517f2c2fc94100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290526000808567ffffffffffffffff811115620013a957620013a962004071565b604051908082528060200260200182016040528015620013d3578160200160208202803683370190505b50905060008667ffffffffffffffff811115620013f457620013f462004071565b6040519080825280602002602001820160405280156200147b57816020015b6040805160e08101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181620014135790505b50905060008767ffffffffffffffff8111156200149c576200149c62004071565b604051908082528060200260200182016040528015620014d157816020015b6060815260200190600190039081620014bb5790505b50905060008867ffffffffffffffff811115620014f257620014f262004071565b6040519080825280602002602001820160405280156200152757816020015b6060815260200190600190039081620015115790505b50905060008967ffffffffffffffff81111562001548576200154862004071565b6040519080825280602002602001820160405280156200157d57816020015b6060815260200190600190039081620015675790505b50905060005b8a81101562001b61578b8b82818110620015a157620015a162004bb8565b60209081029290920135600081815260048452604090819020815160e081018352815460ff811615158252610100810463ffffffff90811697830197909752650100000000008104871693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490931660c08401529a509098506200168090508962002f5a565b60608801516040517f1a5da6c800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c8116600483015290911690631a5da6c890602401600060405180830381600087803b158015620016f057600080fd5b505af115801562001705573d6000803e3d6000fd5b50505050878582815181106200171f576200171f62004bb8565b6020026020010181905250600560008a815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1686828151811062001773576200177362004bb8565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910182015260008a81526007909152604090208054620017b290620049e8565b80601f0160208091040260200160405190810160405280929190818152602001828054620017e090620049e8565b8015620018315780601f10620018055761010080835404028352916020019162001831565b820191906000526020600020905b8154815290600101906020018083116200181357829003601f168201915b50505050508482815181106200184b576200184b62004bb8565b6020026020010181905250601a60008a815260200190815260200160002080546200187690620049e8565b80601f0160208091040260200160405190810160405280929190818152602001828054620018a490620049e8565b8015620018f55780601f10620018c957610100808354040283529160200191620018f5565b820191906000526020600020905b815481529060010190602001808311620018d757829003601f168201915b50505050508382815181106200190f576200190f62004bb8565b6020026020010181905250601b60008a815260200190815260200160002080546200193a90620049e8565b80601f01602080910402602001604051908101604052809291908181526020018280546200196890620049e8565b8015620019b95780601f106200198d57610100808354040283529160200191620019b9565b820191906000526020600020905b8154815290600101906020018083116200199b57829003601f168201915b5050505050828281518110620019d357620019d362004bb8565b60200260200101819052508760a001516bffffffffffffffffffffffff1687620019fe919062004be7565b60008a815260046020908152604080832080547fffffff000000000000000000000000000000000000000000000000000000000016815560010180547fffffffff000000000000000000000000000000000000000000000000000000001690556007909152812091985062001a74919062003f89565b6000898152601a6020526040812062001a8d9162003f89565b6000898152601b6020526040812062001aa69162003f89565b600089815260066020526040902080547fffffffffffffffffffffffff000000000000000000000000000000000000000016905562001ae760028a62003523565b5060a0880151604080516bffffffffffffffffffffffff909216825273ffffffffffffffffffffffffffffffffffffffff8c1660208301528a917fb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff910160405180910390a28062001b588162004bfd565b91505062001583565b508560185462001b7291906200492a565b60185560008b8b868167ffffffffffffffff81111562001b965762001b9662004071565b60405190808252806020026020018201604052801562001bc0578160200160208202803683370190505b508988888860405160200162001bde98979695949392919062004da3565b60405160208183030381529060405290508973ffffffffffffffffffffffffffffffffffffffff16638e86139b6013600001600c9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c71249ab60038e73ffffffffffffffffffffffffffffffffffffffff1663aab9edd66040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001c9a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cc0919062004e73565b866040518463ffffffff1660e01b815260040162001ce19392919062004e98565b600060405180830381865afa15801562001cff573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405262001d47919081019062004ebf565b6040518263ffffffff1660e01b815260040162001d659190620048ca565b600060405180830381600087803b15801562001d8057600080fd5b505af115801562001d95573d6000803e3d6000fd5b50506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018b90527f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb91506044016020604051808303816000875af115801562001e2f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001e55919062004ef8565b50505050505050505050505050565b60023360009081526019602052604090205460ff16600381111562001e8d5762001e8d6200438d565b1415801562001ec3575060033360009081526019602052604090205460ff16600381111562001ec05762001ec06200438d565b14155b1562001efb576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080808080808062001f11888a018a620050f0565b965096509650965096509650965060005b8751811015620021e057600073ffffffffffffffffffffffffffffffffffffffff1687828151811062001f595762001f5962004bb8565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff16036200206d5785818151811062001f965762001f9662004bb8565b6020026020010151307f000000000000000000000000000000000000000000000000000000000000000060405162001fce9062003f7b565b73ffffffffffffffffffffffffffffffffffffffff938416815291831660208301529091166040820152606001604051809103906000f08015801562002018573d6000803e3d6000fd5b508782815181106200202e576200202e62004bb8565b60200260200101516060019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b6200212588828151811062002086576200208662004bb8565b6020026020010151888381518110620020a357620020a362004bb8565b6020026020010151878481518110620020c057620020c062004bb8565b6020026020010151878581518110620020dd57620020dd62004bb8565b6020026020010151878681518110620020fa57620020fa62004bb8565b602002602001015187878151811062002117576200211762004bb8565b602002602001015162002b44565b8781815181106200213a576200213a62004bb8565b60200260200101517f74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a7188838151811062002178576200217862004bb8565b602002602001015160a0015133604051620021c39291906bffffffffffffffffffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a280620021d78162004bfd565b91505062001f22565b50505050505050505050565b600082815260046020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452650100000000008104841694820185905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004821660c08201529114620022ea576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160a00151620022fc919062005221565b600084815260046020526040902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff166c010000000000000000000000006bffffffffffffffffffffffff93841602179055601854620023649184169062004be7565b6018556040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526bffffffffffffffffffffffff831660448201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af11580156200240e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002434919062004ef8565b506040516bffffffffffffffffffffffff83168152339084907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a3505050565b6000818152600460209081526040808320815160e081018352815460ff81161515825263ffffffff6101008204811695830195909552650100000000008104851693820184905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004831660c082015292911415906200256860005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16149050818015620025c35750808015620025c15750620025b462003531565b836040015163ffffffff16115b155b15620025fb576040517ffbc0357800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156200262e575060008481526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b1562002666576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200267262003531565b9050816200268a576200268760328262004be7565b90505b6000858152600460205260409020805463ffffffff80841665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff90921691909117909155620026e69060029087906200352316565b5060135460808501516bffffffffffffffffffffffff91821691600091168211156200274f5760808601516200271d908362005249565b90508560a001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff1611156200274f575060a08501515b808660a0015162002761919062005249565b600088815260046020526040902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff166c010000000000000000000000006bffffffffffffffffffffffff93841602179055601454620027c99183911662005221565b601480547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff9290921691909117905560405167ffffffffffffffff84169088907f91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f79118190600090a350505050505050565b600060606000806200285762002f1f565b6000634b56a42e60e01b888888604051602401620028789392919062005271565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506200290389826200068c565b929c919b50995090975095505050505050565b62002920620035ed565b6200292b8162003670565b50565b600060606000806000806000620029558860405180602001604052806000815250620009fe565b959e949d50929b5090995097509550909350915050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415155b90505b92915050565b6000806000620029c76001620029b562003531565b620029c191906200492a565b62003767565b601454604080516020810193909352309083015274010000000000000000000000000000000000000000900463ffffffff166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083015201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060045b600f81101562002ad3578282828151811062002a8f5762002a8f62004bb8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508062002aca8162004bfd565b91505062002a6f565b5083600181111562002ae95762002ae96200438d565b60f81b81600f8151811062002b025762002b0262004bb8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535062002b3c81620052a5565b949350505050565b6012546e010000000000000000000000000000900460ff161562002b94576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601554835163ffffffff909116101562002bda576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108fc856020015163ffffffff16108062002c185750601454602086015163ffffffff70010000000000000000000000000000000090920482169116115b1562002c50576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600460205260409020546901000000000000000000900473ffffffffffffffffffffffffffffffffffffffff161562002cba576040517f6e3b930b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600460209081526040808320885181548a8501518b85015160608d01517fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009093169315157fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff169390931761010063ffffffff92831602177fffffff000000000000000000000000000000000000000000000000ffffffffff1665010000000000938216939093027fffffff0000000000000000000000000000000000000000ffffffffffffffffff1692909217690100000000000000000073ffffffffffffffffffffffffffffffffffffffff9283160217835560808b01516001909301805460a08d015160c08e01516bffffffffffffffffffffffff9687167fffffffffffffffff000000000000000000000000000000000000000000000000909316929092176c010000000000000000000000009690911695909502949094177fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000009490931693909302919091179091556005835281842080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169189169190911790556007909152902062002ead8482620052e8565b508460a001516bffffffffffffffffffffffff1660185462002ed0919062004be7565b6018556000868152601a6020526040902062002eed8382620052e8565b506000868152601b6020526040902062002f088282620052e8565b5062002f16600287620038cf565b50505050505050565b321562002f58576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff16331462002fb8576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090205465010000000000900463ffffffff908116146200292b576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818160045b600f811015620030a5577fff00000000000000000000000000000000000000000000000000000000000000821683826020811062003059576200305962004bb8565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146200309057506000949350505050565b806200309c8162004bfd565b91505062003017565b5081600f1a600181111562002b3c5762002b3c6200438d565b6000806000836060015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156200314b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200317191906200542a565b50945090925050506000811315806200318957508142105b80620031ae5750828015620031ae5750620031a582426200492a565b8463ffffffff16105b15620031bf576016549550620031c3565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156200322f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200325591906200542a565b50945090925050506000811315806200326d57508142105b806200329257508280156200329257506200328982426200492a565b8463ffffffff16105b15620032a3576017549450620032a7565b8094505b50505050915091565b600080620032c488878b60000151620038dd565b9050600080620032e18b8a63ffffffff16858a8a60018b6200397c565b9092509050620032f2818362005221565b9b9a5050505050505050505050565b606060008360018111156200331a576200331a6200438d565b03620033e7576000848152600760205260409081902090517f6e04ff0d0000000000000000000000000000000000000000000000000000000091620033629160240162005522565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506200351c565b6001836001811115620033fe57620033fe6200438d565b03620034ea576000828060200190518101906200341c919062005599565b6000868152600760205260409081902090519192507f40691db4000000000000000000000000000000000000000000000000000000009162003463918491602401620056ad565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291506200351c9050565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b600062002997838362003e1e565b600060017f000000000000000000000000000000000000000000000000000000000000000060028111156200356a576200356a6200438d565b03620035e857606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620035bd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620035e3919062005775565b905090565b504390565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016200116e565b3373ffffffffffffffffffffffffffffffffffffffff821603620036f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200116e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115620037a057620037a06200438d565b03620038c5576000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620037f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200381b919062005775565b905080831015806200383957506101006200383784836200492a565b115b15620038485750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa1580156200389f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200351c919062005775565b504090565b919050565b600062002997838362003f29565b60008080856001811115620038f657620038f66200438d565b0362003907575062015f906200392a565b60018560018111156200391e576200391e6200438d565b03620034ea57506201adb05b6200393d63ffffffff851660146200578f565b6200394a846001620057cf565b6200395b9060ff16611d4c6200578f565b62003967908362004be7565b62003973919062004be7565b95945050505050565b6000806000896080015161ffff16876200399791906200578f565b9050838015620039a65750803a105b15620039af57503a5b600060027f00000000000000000000000000000000000000000000000000000000000000006002811115620039e857620039e86200438d565b0362003b5957604080516000815260208101909152851562003a4c5760003660405180608001604052806048815260200162005cc46048913960405160200162003a3593929190620057eb565b604051602081830303815290604052905062003aba565b60155462003a6a90640100000000900463ffffffff16600462005814565b63ffffffff1667ffffffffffffffff81111562003a8b5762003a8b62004071565b6040519080825280601f01601f19166020018201604052801562003ab6576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e9062003b0c908490600401620048ca565b602060405180830381865afa15801562003b2a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003b50919062005775565b91505062003cc3565b60017f0000000000000000000000000000000000000000000000000000000000000000600281111562003b905762003b906200438d565b0362003cc357841562003c1857606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562003bea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003c10919062005775565b905062003cc3565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa15801562003c67573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003c8d919062005843565b505060155492945062003cb293505050640100000000900463ffffffff16826200578f565b62003cbf9060106200578f565b9150505b8462003ce257808b6080015161ffff1662003cdf91906200578f565b90505b62003cf261ffff8716826200588e565b90506000878262003d048c8e62004be7565b62003d1090866200578f565b62003d1c919062004be7565b62003d3090670de0b6b3a76400006200578f565b62003d3c91906200588e565b905060008c6040015163ffffffff1664e8d4a5100062003d5d91906200578f565b898e6020015163ffffffff16858f8862003d7891906200578f565b62003d84919062004be7565b62003d9490633b9aca006200578f565b62003da091906200578f565b62003dac91906200588e565b62003db8919062004be7565b90506b033b2e3c9fd0803ce800000062003dd3828462004be7565b111562003e0c576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b6000818152600183016020526040812054801562003f1757600062003e456001836200492a565b855490915060009062003e5b906001906200492a565b905081811462003ec757600086600001828154811062003e7f5762003e7f62004bb8565b906000526020600020015490508087600001848154811062003ea55762003ea562004bb8565b6000918252602080832090910192909255918252600188019052604090208390555b855486908062003edb5762003edb620058ca565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506200299a565b60009150506200299a565b5092915050565b600081815260018301602052604081205462003f72575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200299a565b5060006200299a565b6103ca80620058fa83390190565b50805462003f9790620049e8565b6000825580601f1062003fa8575050565b601f0160209004906000526020600020908101906200292b91905b8082111562003fd9576000815560010162003fc3565b5090565b73ffffffffffffffffffffffffffffffffffffffff811681146200292b57600080fd5b803563ffffffff81168114620038ca57600080fd5b803560028110620038ca57600080fd5b60008083601f8401126200403857600080fd5b50813567ffffffffffffffff8111156200405157600080fd5b6020830191508360208285010111156200406a57600080fd5b9250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715620040c657620040c662004071565b60405290565b604051610100810167ffffffffffffffff81118282101715620040c657620040c662004071565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156200413d576200413d62004071565b604052919050565b600067ffffffffffffffff82111562004162576200416262004071565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112620041a057600080fd5b8135620041b7620041b18262004145565b620040f3565b818152846020838601011115620041cd57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060008060e0898b0312156200420757600080fd5b8835620042148162003fdd565b97506200422460208a0162004000565b96506040890135620042368162003fdd565b95506200424660608a0162004015565b9450608089013567ffffffffffffffff808211156200426457600080fd5b620042728c838d0162004025565b909650945060a08b01359150808211156200428c57600080fd5b6200429a8c838d016200418e565b935060c08b0135915080821115620042b157600080fd5b50620042c08b828c016200418e565b9150509295985092959890939650565b60008060408385031215620042e457600080fd5b82359150602083013567ffffffffffffffff8111156200430357600080fd5b62004311858286016200418e565b9150509250929050565b60005b83811015620043385781810151838201526020016200431e565b50506000910152565b600081518084526200435b8160208601602086016200431b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600a8110620043f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b841515815260806020820152600062004415608083018662004341565b9050620044266040830185620043bc565b82606083015295945050505050565b6000806000604084860312156200444b57600080fd5b83359250602084013567ffffffffffffffff8111156200446a57600080fd5b620044788682870162004025565b9497909650939450505050565b600080600080600080600060a0888a031215620044a157600080fd5b8735620044ae8162003fdd565b9650620044be6020890162004000565b95506040880135620044d08162003fdd565b9450606088013567ffffffffffffffff80821115620044ee57600080fd5b620044fc8b838c0162004025565b909650945060808a01359150808211156200451657600080fd5b50620045258a828b0162004025565b989b979a50959850939692959293505050565b871515815260e0602082015260006200455560e083018962004341565b9050620045666040830188620043bc565b8560608301528460808301528360a08301528260c083015298975050505050505050565b600080600060408486031215620045a057600080fd5b833567ffffffffffffffff80821115620045b957600080fd5b818601915086601f830112620045ce57600080fd5b813581811115620045de57600080fd5b8760208260051b8501011115620045f457600080fd5b602092830195509350508401356200460c8162003fdd565b809150509250925092565b600080602083850312156200462b57600080fd5b823567ffffffffffffffff8111156200464357600080fd5b620046518582860162004025565b90969095509350505050565b80356bffffffffffffffffffffffff81168114620038ca57600080fd5b600080604083850312156200468e57600080fd5b82359150620046a0602084016200465d565b90509250929050565b600060208284031215620046bc57600080fd5b5035919050565b600067ffffffffffffffff821115620046e057620046e062004071565b5060051b60200190565b600082601f830112620046fc57600080fd5b813560206200470f620041b183620046c3565b82815260059290921b840181019181810190868411156200472f57600080fd5b8286015b848110156200477457803567ffffffffffffffff811115620047555760008081fd5b620047658986838b01016200418e565b84525091830191830162004733565b509695505050505050565b600080600080606085870312156200479657600080fd5b84359350602085013567ffffffffffffffff80821115620047b657600080fd5b620047c488838901620046ea565b94506040870135915080821115620047db57600080fd5b50620047ea8782880162004025565b95989497509550505050565b6000602082840312156200480957600080fd5b81356200351c8162003fdd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff80831681810362004861576200486162004816565b6001019392505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60208152600062002b3c6020830184866200486b565b60208152600062002997602083018462004341565b8051620038ca8162003fdd565b600060208284031215620048ff57600080fd5b81516200351c8162003fdd565b60008251620049208184602087016200431b565b9190910192915050565b818103818111156200299a576200299a62004816565b80151581146200292b57600080fd5b600082601f8301126200496157600080fd5b815162004972620041b18262004145565b8181528460208386010111156200498857600080fd5b62002b3c8260208301602087016200431b565b60008060408385031215620049af57600080fd5b8251620049bc8162004940565b602084015190925067ffffffffffffffff811115620049da57600080fd5b62004311858286016200494f565b600181811c90821680620049fd57607f821691505b60208210810362004a37577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111562004a8b57600081815260208120601f850160051c8101602086101562004a665750805b601f850160051c820191505b8181101562004a875782815560010162004a72565b5050505b505050565b67ffffffffffffffff83111562004aab5762004aab62004071565b62004ac38362004abc8354620049e8565b8362004a3d565b6000601f84116001811462004b18576000851562004ae15750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835562004bb1565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101562004b69578685013582556020948501946001909201910162004b47565b508682101562004ba5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808201808211156200299a576200299a62004816565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362004c315762004c3162004816565b5060010190565b600081518084526020808501945080840160005b8381101562004cf75781518051151588528381015163ffffffff908116858a01526040808301519091169089015260608082015173ffffffffffffffffffffffffffffffffffffffff16908901526080808201516bffffffffffffffffffffffff169089015260a08082015162004cd2828b01826bffffffffffffffffffffffff169052565b505060c09081015163ffffffff169088015260e0909601959082019060010162004c4c565b509495945050505050565b600081518084526020808501945080840160005b8381101562004cf757815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010162004d16565b600081518084526020808501808196508360051b8101915082860160005b8581101562004d9657828403895262004d8384835162004341565b9885019893509084019060010162004d68565b5091979650505050505050565b60e081528760e082015260006101007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a111562004de057600080fd5b8960051b808c8386013783018381038201602085015262004e048282018b62004c38565b915050828103604084015262004e1b818962004d02565b9050828103606084015262004e31818862004d02565b9050828103608084015262004e47818762004d4a565b905082810360a084015262004e5d818662004d4a565b905082810360c0840152620032f2818562004d4a565b60006020828403121562004e8657600080fd5b815160ff811681146200351c57600080fd5b60ff8416815260ff8316602082015260606040820152600062003973606083018462004341565b60006020828403121562004ed257600080fd5b815167ffffffffffffffff81111562004eea57600080fd5b62002b3c848285016200494f565b60006020828403121562004f0b57600080fd5b81516200351c8162004940565b600082601f83011262004f2a57600080fd5b8135602062004f3d620041b183620046c3565b82815260059290921b8401810191818101908684111562004f5d57600080fd5b8286015b8481101562004774578035835291830191830162004f61565b600082601f83011262004f8c57600080fd5b8135602062004f9f620041b183620046c3565b82815260e0928302850182019282820191908785111562004fbf57600080fd5b8387015b85811015620050765781818a03121562004fdd5760008081fd5b62004fe7620040a0565b813562004ff48162004940565b81526200500382870162004000565b8682015260406200501681840162004000565b908201526060828101356200502b8162003fdd565b9082015260806200503e8382016200465d565b9082015260a0620050518382016200465d565b9082015260c06200506483820162004000565b90820152845292840192810162004fc3565b5090979650505050505050565b600082601f8301126200509557600080fd5b81356020620050a8620041b183620046c3565b82815260059290921b84018101918181019086841115620050c857600080fd5b8286015b8481101562004774578035620050e28162003fdd565b8352918301918301620050cc565b600080600080600080600060e0888a0312156200510c57600080fd5b873567ffffffffffffffff808211156200512557600080fd5b620051338b838c0162004f18565b985060208a01359150808211156200514a57600080fd5b620051588b838c0162004f7a565b975060408a01359150808211156200516f57600080fd5b6200517d8b838c0162005083565b965060608a01359150808211156200519457600080fd5b620051a28b838c0162005083565b955060808a0135915080821115620051b957600080fd5b620051c78b838c01620046ea565b945060a08a0135915080821115620051de57600080fd5b620051ec8b838c01620046ea565b935060c08a01359150808211156200520357600080fd5b50620052128a828b01620046ea565b91505092959891949750929550565b6bffffffffffffffffffffffff81811683821601908082111562003f225762003f2262004816565b6bffffffffffffffffffffffff82811682821603908082111562003f225762003f2262004816565b60408152600062005286604083018662004d4a565b82810360208401526200529b8185876200486b565b9695505050505050565b8051602080830151919081101562004a37577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b16919050565b815167ffffffffffffffff81111562005305576200530562004071565b6200531d81620053168454620049e8565b8462004a3d565b602080601f8311600181146200537357600084156200533c5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855562004a87565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015620053c257888601518255948401946001909101908401620053a1565b5085821015620053ff57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b805169ffffffffffffffffffff81168114620038ca57600080fd5b600080600080600060a086880312156200544357600080fd5b6200544e866200540f565b945060208601519350604086015192506060860151915062005473608087016200540f565b90509295509295909350565b600081546200548e81620049e8565b808552602060018381168015620054ae5760018114620054e75762005517565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b890101955062005517565b866000528260002060005b858110156200550f5781548a8201860152908301908401620054f2565b890184019650505b505050505092915050565b6020815260006200299760208301846200547f565b600082601f8301126200554957600080fd5b815160206200555c620041b183620046c3565b82815260059290921b840181019181810190868411156200557c57600080fd5b8286015b8481101562004774578051835291830191830162005580565b600060208284031215620055ac57600080fd5b815167ffffffffffffffff80821115620055c557600080fd5b908301906101008286031215620055db57600080fd5b620055e5620040cc565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201526200561f60a08401620048df565b60a082015260c0830151828111156200563757600080fd5b620056458782860162005537565b60c08301525060e0830151828111156200565e57600080fd5b6200566c878286016200494f565b60e08301525095945050505050565b600081518084526020808501945080840160005b8381101562004cf7578151875295820195908201906001016200568f565b60408152825160408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015273ffffffffffffffffffffffffffffffffffffffff60a08401511660e0820152600060c0840151610100808185015250620057206101408401826200567b565b905060e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0848303016101208501526200575e828262004341565b91505082810360208401526200397381856200547f565b6000602082840312156200578857600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615620057ca57620057ca62004816565b500290565b60ff81811683821601908111156200299a576200299a62004816565b8284823760008382016000815283516200580a8183602088016200431b565b0195945050505050565b600063ffffffff808316818516818304811182151516156200583a576200583a62004816565b02949350505050565b60008060008060008060c087890312156200585d57600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600082620058c5577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe60c060405234801561001057600080fd5b506040516103ca3803806103ca83398101604081905261002f91610076565b600080546001600160a01b0319166001600160a01b039384161790559181166080521660a0526100b9565b80516001600160a01b038116811461007157600080fd5b919050565b60008060006060848603121561008b57600080fd5b6100948461005a565b92506100a26020850161005a565b91506100b06040850161005a565b90509250925092565b60805160a0516102e76100e36000396000603801526000818160c4015261011701526102e76000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806379188d161461007b578063f00e6a2a146100aa575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610076573d6000f35b3d6000fd5b61008e6100893660046101c1565b6100ee565b6040805192151583526020830191909152015b60405180910390f35b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526020016100a1565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331461011557600080fd5b7f00000000000000000000000000000000000000000000000000000000000000005a91505a61138881101561014957600080fd5b61138881039050856040820482031161016157600080fd5b50803b61016d57600080fd5b6000808551602087016000858af192505a610188908361029a565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080604083850312156101d457600080fd5b82359150602083013567ffffffffffffffff808211156101f357600080fd5b818501915085601f83011261020757600080fd5b81358181111561021957610219610192565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561025f5761025f610192565b8160405282815288602084870101111561027857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b818103818111156102d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000810000a307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", +} + +var AutomationRegistryLogicAABI = AutomationRegistryLogicAMetaData.ABI + +var AutomationRegistryLogicABin = AutomationRegistryLogicAMetaData.Bin + +func DeployAutomationRegistryLogicA(auth *bind.TransactOpts, backend bind.ContractBackend, logicB common.Address) (common.Address, *types.Transaction, *AutomationRegistryLogicA, error) { + parsed, err := AutomationRegistryLogicAMetaData.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(AutomationRegistryLogicABin), backend, logicB) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &AutomationRegistryLogicA{address: address, abi: *parsed, AutomationRegistryLogicACaller: AutomationRegistryLogicACaller{contract: contract}, AutomationRegistryLogicATransactor: AutomationRegistryLogicATransactor{contract: contract}, AutomationRegistryLogicAFilterer: AutomationRegistryLogicAFilterer{contract: contract}}, nil +} + +type AutomationRegistryLogicA struct { + address common.Address + abi abi.ABI + AutomationRegistryLogicACaller + AutomationRegistryLogicATransactor + AutomationRegistryLogicAFilterer +} + +type AutomationRegistryLogicACaller struct { + contract *bind.BoundContract +} + +type AutomationRegistryLogicATransactor struct { + contract *bind.BoundContract +} + +type AutomationRegistryLogicAFilterer struct { + contract *bind.BoundContract +} + +type AutomationRegistryLogicASession struct { + Contract *AutomationRegistryLogicA + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type AutomationRegistryLogicACallerSession struct { + Contract *AutomationRegistryLogicACaller + CallOpts bind.CallOpts +} + +type AutomationRegistryLogicATransactorSession struct { + Contract *AutomationRegistryLogicATransactor + TransactOpts bind.TransactOpts +} + +type AutomationRegistryLogicARaw struct { + Contract *AutomationRegistryLogicA +} + +type AutomationRegistryLogicACallerRaw struct { + Contract *AutomationRegistryLogicACaller +} + +type AutomationRegistryLogicATransactorRaw struct { + Contract *AutomationRegistryLogicATransactor +} + +func NewAutomationRegistryLogicA(address common.Address, backend bind.ContractBackend) (*AutomationRegistryLogicA, error) { + abi, err := abi.JSON(strings.NewReader(AutomationRegistryLogicAABI)) + if err != nil { + return nil, err + } + contract, err := bindAutomationRegistryLogicA(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicA{address: address, abi: abi, AutomationRegistryLogicACaller: AutomationRegistryLogicACaller{contract: contract}, AutomationRegistryLogicATransactor: AutomationRegistryLogicATransactor{contract: contract}, AutomationRegistryLogicAFilterer: AutomationRegistryLogicAFilterer{contract: contract}}, nil +} + +func NewAutomationRegistryLogicACaller(address common.Address, caller bind.ContractCaller) (*AutomationRegistryLogicACaller, error) { + contract, err := bindAutomationRegistryLogicA(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicACaller{contract: contract}, nil +} + +func NewAutomationRegistryLogicATransactor(address common.Address, transactor bind.ContractTransactor) (*AutomationRegistryLogicATransactor, error) { + contract, err := bindAutomationRegistryLogicA(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicATransactor{contract: contract}, nil +} + +func NewAutomationRegistryLogicAFilterer(address common.Address, filterer bind.ContractFilterer) (*AutomationRegistryLogicAFilterer, error) { + contract, err := bindAutomationRegistryLogicA(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAFilterer{contract: contract}, nil +} + +func bindAutomationRegistryLogicA(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AutomationRegistryLogicAMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicARaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistryLogicA.Contract.AutomationRegistryLogicACaller.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicARaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.AutomationRegistryLogicATransactor.contract.Transfer(opts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicARaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.AutomationRegistryLogicATransactor.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicACallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistryLogicA.Contract.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.contract.Transfer(opts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicACaller) FallbackTo(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicA.contract.Call(opts, &out, "fallbackTo") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) FallbackTo() (common.Address, error) { + return _AutomationRegistryLogicA.Contract.FallbackTo(&_AutomationRegistryLogicA.CallOpts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicACallerSession) FallbackTo() (common.Address, error) { + return _AutomationRegistryLogicA.Contract.FallbackTo(&_AutomationRegistryLogicA.CallOpts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicACaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicA.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) Owner() (common.Address, error) { + return _AutomationRegistryLogicA.Contract.Owner(&_AutomationRegistryLogicA.CallOpts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicACallerSession) Owner() (common.Address, error) { + return _AutomationRegistryLogicA.Contract.Owner(&_AutomationRegistryLogicA.CallOpts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "acceptOwnership") +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.AcceptOwnership(&_AutomationRegistryLogicA.TransactOpts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.AcceptOwnership(&_AutomationRegistryLogicA.TransactOpts) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) AddFunds(opts *bind.TransactOpts, id *big.Int, amount *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "addFunds", id, amount) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) AddFunds(id *big.Int, amount *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.AddFunds(&_AutomationRegistryLogicA.TransactOpts, id, amount) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) AddFunds(id *big.Int, amount *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.AddFunds(&_AutomationRegistryLogicA.TransactOpts, id, amount) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) CancelUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "cancelUpkeep", id) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) CancelUpkeep(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CancelUpkeep(&_AutomationRegistryLogicA.TransactOpts, id) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) CancelUpkeep(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CancelUpkeep(&_AutomationRegistryLogicA.TransactOpts, id) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) CheckCallback(opts *bind.TransactOpts, id *big.Int, values [][]byte, extraData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "checkCallback", id, values, extraData) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) CheckCallback(id *big.Int, values [][]byte, extraData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CheckCallback(&_AutomationRegistryLogicA.TransactOpts, id, values, extraData) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) CheckCallback(id *big.Int, values [][]byte, extraData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CheckCallback(&_AutomationRegistryLogicA.TransactOpts, id, values, extraData) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) CheckUpkeep(opts *bind.TransactOpts, id *big.Int, triggerData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "checkUpkeep", id, triggerData) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) CheckUpkeep(id *big.Int, triggerData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CheckUpkeep(&_AutomationRegistryLogicA.TransactOpts, id, triggerData) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) CheckUpkeep(id *big.Int, triggerData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CheckUpkeep(&_AutomationRegistryLogicA.TransactOpts, id, triggerData) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) CheckUpkeep0(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "checkUpkeep0", id) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) CheckUpkeep0(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CheckUpkeep0(&_AutomationRegistryLogicA.TransactOpts, id) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) CheckUpkeep0(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.CheckUpkeep0(&_AutomationRegistryLogicA.TransactOpts, id) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) ExecuteCallback(opts *bind.TransactOpts, id *big.Int, payload []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "executeCallback", id, payload) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) ExecuteCallback(id *big.Int, payload []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.ExecuteCallback(&_AutomationRegistryLogicA.TransactOpts, id, payload) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) ExecuteCallback(id *big.Int, payload []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.ExecuteCallback(&_AutomationRegistryLogicA.TransactOpts, id, payload) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) MigrateUpkeeps(opts *bind.TransactOpts, ids []*big.Int, destination common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "migrateUpkeeps", ids, destination) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) MigrateUpkeeps(ids []*big.Int, destination common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.MigrateUpkeeps(&_AutomationRegistryLogicA.TransactOpts, ids, destination) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) MigrateUpkeeps(ids []*big.Int, destination common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.MigrateUpkeeps(&_AutomationRegistryLogicA.TransactOpts, ids, destination) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) ReceiveUpkeeps(opts *bind.TransactOpts, encodedUpkeeps []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "receiveUpkeeps", encodedUpkeeps) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) ReceiveUpkeeps(encodedUpkeeps []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.ReceiveUpkeeps(&_AutomationRegistryLogicA.TransactOpts, encodedUpkeeps) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) ReceiveUpkeeps(encodedUpkeeps []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.ReceiveUpkeeps(&_AutomationRegistryLogicA.TransactOpts, encodedUpkeeps) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) RegisterUpkeep(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "registerUpkeep", target, gasLimit, admin, triggerType, checkData, triggerConfig, offchainConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) RegisterUpkeep(target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.RegisterUpkeep(&_AutomationRegistryLogicA.TransactOpts, target, gasLimit, admin, triggerType, checkData, triggerConfig, offchainConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) RegisterUpkeep(target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.RegisterUpkeep(&_AutomationRegistryLogicA.TransactOpts, target, gasLimit, admin, triggerType, checkData, triggerConfig, offchainConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) RegisterUpkeep0(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "registerUpkeep0", target, gasLimit, admin, checkData, offchainConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) RegisterUpkeep0(target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.RegisterUpkeep0(&_AutomationRegistryLogicA.TransactOpts, target, gasLimit, admin, checkData, offchainConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) RegisterUpkeep0(target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.RegisterUpkeep0(&_AutomationRegistryLogicA.TransactOpts, target, gasLimit, admin, checkData, offchainConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) SetUpkeepTriggerConfig(opts *bind.TransactOpts, id *big.Int, triggerConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "setUpkeepTriggerConfig", id, triggerConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) SetUpkeepTriggerConfig(id *big.Int, triggerConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.SetUpkeepTriggerConfig(&_AutomationRegistryLogicA.TransactOpts, id, triggerConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) SetUpkeepTriggerConfig(id *big.Int, triggerConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.SetUpkeepTriggerConfig(&_AutomationRegistryLogicA.TransactOpts, id, triggerConfig) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.Transact(opts, "transferOwnership", to) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.TransferOwnership(&_AutomationRegistryLogicA.TransactOpts, to) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.TransferOwnership(&_AutomationRegistryLogicA.TransactOpts, to) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.contract.RawTransact(opts, calldata) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicASession) Fallback(calldata []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.Fallback(&_AutomationRegistryLogicA.TransactOpts, calldata) +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicATransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicA.Contract.Fallback(&_AutomationRegistryLogicA.TransactOpts, calldata) +} + +type AutomationRegistryLogicAAdminPrivilegeConfigSetIterator struct { + Event *AutomationRegistryLogicAAdminPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAAdminPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAAdminPrivilegeConfigSet) + 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(AutomationRegistryLogicAAdminPrivilegeConfigSet) + 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 *AutomationRegistryLogicAAdminPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAAdminPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAAdminPrivilegeConfigSet struct { + Admin common.Address + PrivilegeConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*AutomationRegistryLogicAAdminPrivilegeConfigSetIterator, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAAdminPrivilegeConfigSetIterator{contract: _AutomationRegistryLogicA.contract, event: "AdminPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAAdminPrivilegeConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "AdminPrivilegeConfigSet", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseAdminPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicAAdminPrivilegeConfigSet, error) { + event := new(AutomationRegistryLogicAAdminPrivilegeConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "AdminPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicACancelledUpkeepReportIterator struct { + Event *AutomationRegistryLogicACancelledUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicACancelledUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicACancelledUpkeepReport) + 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(AutomationRegistryLogicACancelledUpkeepReport) + 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 *AutomationRegistryLogicACancelledUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicACancelledUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicACancelledUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicACancelledUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicACancelledUpkeepReportIterator{contract: _AutomationRegistryLogicA.contract, event: "CancelledUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicACancelledUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicACancelledUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "CancelledUpkeepReport", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseCancelledUpkeepReport(log types.Log) (*AutomationRegistryLogicACancelledUpkeepReport, error) { + event := new(AutomationRegistryLogicACancelledUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "CancelledUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicADedupKeyAddedIterator struct { + Event *AutomationRegistryLogicADedupKeyAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicADedupKeyAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicADedupKeyAdded) + 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(AutomationRegistryLogicADedupKeyAdded) + 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 *AutomationRegistryLogicADedupKeyAddedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicADedupKeyAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicADedupKeyAdded struct { + DedupKey [32]byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*AutomationRegistryLogicADedupKeyAddedIterator, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicADedupKeyAddedIterator{contract: _AutomationRegistryLogicA.contract, event: "DedupKeyAdded", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicADedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicADedupKeyAdded) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "DedupKeyAdded", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseDedupKeyAdded(log types.Log) (*AutomationRegistryLogicADedupKeyAdded, error) { + event := new(AutomationRegistryLogicADedupKeyAdded) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "DedupKeyAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAFundsAddedIterator struct { + Event *AutomationRegistryLogicAFundsAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAFundsAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAFundsAdded) + 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(AutomationRegistryLogicAFundsAdded) + 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 *AutomationRegistryLogicAFundsAddedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAFundsAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAFundsAdded struct { + Id *big.Int + From common.Address + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*AutomationRegistryLogicAFundsAddedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAFundsAddedIterator{contract: _AutomationRegistryLogicA.contract, event: "FundsAdded", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAFundsAdded) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "FundsAdded", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseFundsAdded(log types.Log) (*AutomationRegistryLogicAFundsAdded, error) { + event := new(AutomationRegistryLogicAFundsAdded) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "FundsAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAFundsWithdrawnIterator struct { + Event *AutomationRegistryLogicAFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAFundsWithdrawn) + 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(AutomationRegistryLogicAFundsWithdrawn) + 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 *AutomationRegistryLogicAFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAFundsWithdrawn struct { + Id *big.Int + Amount *big.Int + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAFundsWithdrawnIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAFundsWithdrawnIterator{contract: _AutomationRegistryLogicA.contract, event: "FundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAFundsWithdrawn, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAFundsWithdrawn) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "FundsWithdrawn", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseFundsWithdrawn(log types.Log) (*AutomationRegistryLogicAFundsWithdrawn, error) { + event := new(AutomationRegistryLogicAFundsWithdrawn) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "FundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator struct { + Event *AutomationRegistryLogicAInsufficientFundsUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAInsufficientFundsUpkeepReport) + 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(AutomationRegistryLogicAInsufficientFundsUpkeepReport) + 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 *AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAInsufficientFundsUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator{contract: _AutomationRegistryLogicA.contract, event: "InsufficientFundsUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAInsufficientFundsUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseInsufficientFundsUpkeepReport(log types.Log) (*AutomationRegistryLogicAInsufficientFundsUpkeepReport, error) { + event := new(AutomationRegistryLogicAInsufficientFundsUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAOwnerFundsWithdrawnIterator struct { + Event *AutomationRegistryLogicAOwnerFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAOwnerFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAOwnerFundsWithdrawn) + 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(AutomationRegistryLogicAOwnerFundsWithdrawn) + 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 *AutomationRegistryLogicAOwnerFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAOwnerFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAOwnerFundsWithdrawn struct { + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*AutomationRegistryLogicAOwnerFundsWithdrawnIterator, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAOwnerFundsWithdrawnIterator{contract: _AutomationRegistryLogicA.contract, event: "OwnerFundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAOwnerFundsWithdrawn) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAOwnerFundsWithdrawn) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "OwnerFundsWithdrawn", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseOwnerFundsWithdrawn(log types.Log) (*AutomationRegistryLogicAOwnerFundsWithdrawn, error) { + event := new(AutomationRegistryLogicAOwnerFundsWithdrawn) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "OwnerFundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAOwnershipTransferRequestedIterator struct { + Event *AutomationRegistryLogicAOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAOwnershipTransferRequested) + 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(AutomationRegistryLogicAOwnershipTransferRequested) + 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 *AutomationRegistryLogicAOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicAOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAOwnershipTransferRequestedIterator{contract: _AutomationRegistryLogicA.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAOwnershipTransferRequested) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistryLogicAOwnershipTransferRequested, error) { + event := new(AutomationRegistryLogicAOwnershipTransferRequested) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAOwnershipTransferredIterator struct { + Event *AutomationRegistryLogicAOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAOwnershipTransferred) + 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(AutomationRegistryLogicAOwnershipTransferred) + 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 *AutomationRegistryLogicAOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicAOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAOwnershipTransferredIterator{contract: _AutomationRegistryLogicA.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAOwnershipTransferred) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "OwnershipTransferred", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseOwnershipTransferred(log types.Log) (*AutomationRegistryLogicAOwnershipTransferred, error) { + event := new(AutomationRegistryLogicAOwnershipTransferred) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAPausedIterator struct { + Event *AutomationRegistryLogicAPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAPaused) + 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(AutomationRegistryLogicAPaused) + 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 *AutomationRegistryLogicAPausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAPaused struct { + Account common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterPaused(opts *bind.FilterOpts) (*AutomationRegistryLogicAPausedIterator, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "Paused") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAPausedIterator{contract: _AutomationRegistryLogicA.contract, event: "Paused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPaused) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "Paused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAPaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "Paused", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParsePaused(log types.Log) (*AutomationRegistryLogicAPaused, error) { + event := new(AutomationRegistryLogicAPaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "Paused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAPayeesUpdatedIterator struct { + Event *AutomationRegistryLogicAPayeesUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAPayeesUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAPayeesUpdated) + 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(AutomationRegistryLogicAPayeesUpdated) + 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 *AutomationRegistryLogicAPayeesUpdatedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAPayeesUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAPayeesUpdated struct { + Transmitters []common.Address + Payees []common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterPayeesUpdated(opts *bind.FilterOpts) (*AutomationRegistryLogicAPayeesUpdatedIterator, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAPayeesUpdatedIterator{contract: _AutomationRegistryLogicA.contract, event: "PayeesUpdated", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPayeesUpdated) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAPayeesUpdated) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PayeesUpdated", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParsePayeesUpdated(log types.Log) (*AutomationRegistryLogicAPayeesUpdated, error) { + event := new(AutomationRegistryLogicAPayeesUpdated) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PayeesUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAPayeeshipTransferRequestedIterator struct { + Event *AutomationRegistryLogicAPayeeshipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAPayeeshipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAPayeeshipTransferRequested) + 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(AutomationRegistryLogicAPayeeshipTransferRequested) + 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 *AutomationRegistryLogicAPayeeshipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAPayeeshipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAPayeeshipTransferRequested struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicAPayeeshipTransferRequestedIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAPayeeshipTransferRequestedIterator{contract: _AutomationRegistryLogicA.contract, event: "PayeeshipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAPayeeshipTransferRequested) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PayeeshipTransferRequested", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParsePayeeshipTransferRequested(log types.Log) (*AutomationRegistryLogicAPayeeshipTransferRequested, error) { + event := new(AutomationRegistryLogicAPayeeshipTransferRequested) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PayeeshipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAPayeeshipTransferredIterator struct { + Event *AutomationRegistryLogicAPayeeshipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAPayeeshipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAPayeeshipTransferred) + 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(AutomationRegistryLogicAPayeeshipTransferred) + 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 *AutomationRegistryLogicAPayeeshipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAPayeeshipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAPayeeshipTransferred struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicAPayeeshipTransferredIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAPayeeshipTransferredIterator{contract: _AutomationRegistryLogicA.contract, event: "PayeeshipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAPayeeshipTransferred) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PayeeshipTransferred", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParsePayeeshipTransferred(log types.Log) (*AutomationRegistryLogicAPayeeshipTransferred, error) { + event := new(AutomationRegistryLogicAPayeeshipTransferred) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PayeeshipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAPaymentWithdrawnIterator struct { + Event *AutomationRegistryLogicAPaymentWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAPaymentWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAPaymentWithdrawn) + 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(AutomationRegistryLogicAPaymentWithdrawn) + 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 *AutomationRegistryLogicAPaymentWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAPaymentWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAPaymentWithdrawn struct { + Transmitter common.Address + Amount *big.Int + To common.Address + Payee common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*AutomationRegistryLogicAPaymentWithdrawnIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAPaymentWithdrawnIterator{contract: _AutomationRegistryLogicA.contract, event: "PaymentWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAPaymentWithdrawn) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PaymentWithdrawn", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParsePaymentWithdrawn(log types.Log) (*AutomationRegistryLogicAPaymentWithdrawn, error) { + event := new(AutomationRegistryLogicAPaymentWithdrawn) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "PaymentWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAReorgedUpkeepReportIterator struct { + Event *AutomationRegistryLogicAReorgedUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAReorgedUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAReorgedUpkeepReport) + 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(AutomationRegistryLogicAReorgedUpkeepReport) + 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 *AutomationRegistryLogicAReorgedUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAReorgedUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAReorgedUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAReorgedUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAReorgedUpkeepReportIterator{contract: _AutomationRegistryLogicA.contract, event: "ReorgedUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAReorgedUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "ReorgedUpkeepReport", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseReorgedUpkeepReport(log types.Log) (*AutomationRegistryLogicAReorgedUpkeepReport, error) { + event := new(AutomationRegistryLogicAReorgedUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "ReorgedUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAStaleUpkeepReportIterator struct { + Event *AutomationRegistryLogicAStaleUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAStaleUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAStaleUpkeepReport) + 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(AutomationRegistryLogicAStaleUpkeepReport) + 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 *AutomationRegistryLogicAStaleUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAStaleUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAStaleUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAStaleUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAStaleUpkeepReportIterator{contract: _AutomationRegistryLogicA.contract, event: "StaleUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAStaleUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAStaleUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "StaleUpkeepReport", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseStaleUpkeepReport(log types.Log) (*AutomationRegistryLogicAStaleUpkeepReport, error) { + event := new(AutomationRegistryLogicAStaleUpkeepReport) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "StaleUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUnpausedIterator struct { + Event *AutomationRegistryLogicAUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUnpaused) + 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(AutomationRegistryLogicAUnpaused) + 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 *AutomationRegistryLogicAUnpausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUnpaused struct { + Account common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUnpaused(opts *bind.FilterOpts) (*AutomationRegistryLogicAUnpausedIterator, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUnpausedIterator{contract: _AutomationRegistryLogicA.contract, event: "Unpaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUnpaused) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUnpaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "Unpaused", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUnpaused(log types.Log) (*AutomationRegistryLogicAUnpaused, error) { + event := new(AutomationRegistryLogicAUnpaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "Unpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator struct { + Event *AutomationRegistryLogicAUpkeepAdminTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepAdminTransferRequested) + 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(AutomationRegistryLogicAUpkeepAdminTransferRequested) + 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 *AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepAdminTransferRequested struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepAdminTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepAdminTransferRequested) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepAdminTransferRequested", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepAdminTransferRequested(log types.Log) (*AutomationRegistryLogicAUpkeepAdminTransferRequested, error) { + event := new(AutomationRegistryLogicAUpkeepAdminTransferRequested) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepAdminTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepAdminTransferredIterator struct { + Event *AutomationRegistryLogicAUpkeepAdminTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepAdminTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepAdminTransferred) + 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(AutomationRegistryLogicAUpkeepAdminTransferred) + 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 *AutomationRegistryLogicAUpkeepAdminTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepAdminTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepAdminTransferred struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicAUpkeepAdminTransferredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepAdminTransferredIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepAdminTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepAdminTransferred) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepAdminTransferred", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepAdminTransferred(log types.Log) (*AutomationRegistryLogicAUpkeepAdminTransferred, error) { + event := new(AutomationRegistryLogicAUpkeepAdminTransferred) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepAdminTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepCanceledIterator struct { + Event *AutomationRegistryLogicAUpkeepCanceled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepCanceledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepCanceled) + 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(AutomationRegistryLogicAUpkeepCanceled) + 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 *AutomationRegistryLogicAUpkeepCanceledIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepCanceled struct { + Id *big.Int + AtBlockHeight uint64 + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*AutomationRegistryLogicAUpkeepCanceledIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepCanceledIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepCanceled", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepCanceled) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepCanceled", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepCanceled(log types.Log) (*AutomationRegistryLogicAUpkeepCanceled, error) { + event := new(AutomationRegistryLogicAUpkeepCanceled) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepCheckDataSetIterator struct { + Event *AutomationRegistryLogicAUpkeepCheckDataSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepCheckDataSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepCheckDataSet) + 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(AutomationRegistryLogicAUpkeepCheckDataSet) + 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 *AutomationRegistryLogicAUpkeepCheckDataSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepCheckDataSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepCheckDataSet struct { + Id *big.Int + NewCheckData []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepCheckDataSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepCheckDataSetIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepCheckDataSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepCheckDataSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepCheckDataSet", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepCheckDataSet(log types.Log) (*AutomationRegistryLogicAUpkeepCheckDataSet, error) { + event := new(AutomationRegistryLogicAUpkeepCheckDataSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepCheckDataSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepGasLimitSetIterator struct { + Event *AutomationRegistryLogicAUpkeepGasLimitSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepGasLimitSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepGasLimitSet) + 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(AutomationRegistryLogicAUpkeepGasLimitSet) + 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 *AutomationRegistryLogicAUpkeepGasLimitSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepGasLimitSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepGasLimitSet struct { + Id *big.Int + GasLimit *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepGasLimitSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepGasLimitSetIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepGasLimitSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepGasLimitSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepGasLimitSet", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepGasLimitSet(log types.Log) (*AutomationRegistryLogicAUpkeepGasLimitSet, error) { + event := new(AutomationRegistryLogicAUpkeepGasLimitSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepGasLimitSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepMigratedIterator struct { + Event *AutomationRegistryLogicAUpkeepMigrated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepMigratedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepMigrated) + 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(AutomationRegistryLogicAUpkeepMigrated) + 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 *AutomationRegistryLogicAUpkeepMigratedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepMigratedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepMigrated struct { + Id *big.Int + RemainingBalance *big.Int + Destination common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepMigratedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepMigratedIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepMigrated", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepMigrated, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepMigrated) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepMigrated", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepMigrated(log types.Log) (*AutomationRegistryLogicAUpkeepMigrated, error) { + event := new(AutomationRegistryLogicAUpkeepMigrated) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepMigrated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepOffchainConfigSetIterator struct { + Event *AutomationRegistryLogicAUpkeepOffchainConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepOffchainConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepOffchainConfigSet) + 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(AutomationRegistryLogicAUpkeepOffchainConfigSet) + 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 *AutomationRegistryLogicAUpkeepOffchainConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepOffchainConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepOffchainConfigSet struct { + Id *big.Int + OffchainConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepOffchainConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepOffchainConfigSetIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepOffchainConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepOffchainConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepOffchainConfigSet", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepOffchainConfigSet(log types.Log) (*AutomationRegistryLogicAUpkeepOffchainConfigSet, error) { + event := new(AutomationRegistryLogicAUpkeepOffchainConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepOffchainConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepPausedIterator struct { + Event *AutomationRegistryLogicAUpkeepPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepPaused) + 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(AutomationRegistryLogicAUpkeepPaused) + 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 *AutomationRegistryLogicAUpkeepPausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepPaused struct { + Id *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepPausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepPausedIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepPaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepPaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepPaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepPaused", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepPaused(log types.Log) (*AutomationRegistryLogicAUpkeepPaused, error) { + event := new(AutomationRegistryLogicAUpkeepPaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepPaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepPerformedIterator struct { + Event *AutomationRegistryLogicAUpkeepPerformed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepPerformedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepPerformed) + 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(AutomationRegistryLogicAUpkeepPerformed) + 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 *AutomationRegistryLogicAUpkeepPerformedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepPerformedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepPerformed struct { + Id *big.Int + Success bool + TotalPayment *big.Int + GasUsed *big.Int + GasOverhead *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*AutomationRegistryLogicAUpkeepPerformedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepPerformedIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepPerformed", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepPerformed) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepPerformed", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepPerformed(log types.Log) (*AutomationRegistryLogicAUpkeepPerformed, error) { + event := new(AutomationRegistryLogicAUpkeepPerformed) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepPerformed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator struct { + Event *AutomationRegistryLogicAUpkeepPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepPrivilegeConfigSet) + 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(AutomationRegistryLogicAUpkeepPrivilegeConfigSet) + 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 *AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepPrivilegeConfigSet struct { + Id *big.Int + PrivilegeConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepPrivilegeConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicAUpkeepPrivilegeConfigSet, error) { + event := new(AutomationRegistryLogicAUpkeepPrivilegeConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepReceivedIterator struct { + Event *AutomationRegistryLogicAUpkeepReceived + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepReceivedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepReceived) + 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(AutomationRegistryLogicAUpkeepReceived) + 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 *AutomationRegistryLogicAUpkeepReceivedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepReceivedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepReceived struct { + Id *big.Int + StartingBalance *big.Int + ImportedFrom common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepReceivedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepReceivedIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepReceived", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepReceived, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepReceived) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepReceived", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepReceived(log types.Log) (*AutomationRegistryLogicAUpkeepReceived, error) { + event := new(AutomationRegistryLogicAUpkeepReceived) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepReceived", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepRegisteredIterator struct { + Event *AutomationRegistryLogicAUpkeepRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepRegistered) + 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(AutomationRegistryLogicAUpkeepRegistered) + 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 *AutomationRegistryLogicAUpkeepRegisteredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepRegistered struct { + Id *big.Int + PerformGas uint32 + Admin common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepRegisteredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepRegisteredIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepRegistered", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepRegistered, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepRegistered) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepRegistered", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepRegistered(log types.Log) (*AutomationRegistryLogicAUpkeepRegistered, error) { + event := new(AutomationRegistryLogicAUpkeepRegistered) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepTriggerConfigSetIterator struct { + Event *AutomationRegistryLogicAUpkeepTriggerConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepTriggerConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepTriggerConfigSet) + 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(AutomationRegistryLogicAUpkeepTriggerConfigSet) + 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 *AutomationRegistryLogicAUpkeepTriggerConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepTriggerConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepTriggerConfigSet struct { + Id *big.Int + TriggerConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepTriggerConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepTriggerConfigSetIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepTriggerConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepTriggerConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepTriggerConfigSet", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepTriggerConfigSet(log types.Log) (*AutomationRegistryLogicAUpkeepTriggerConfigSet, error) { + event := new(AutomationRegistryLogicAUpkeepTriggerConfigSet) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepTriggerConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicAUpkeepUnpausedIterator struct { + Event *AutomationRegistryLogicAUpkeepUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicAUpkeepUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicAUpkeepUnpaused) + 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(AutomationRegistryLogicAUpkeepUnpaused) + 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 *AutomationRegistryLogicAUpkeepUnpausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicAUpkeepUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicAUpkeepUnpaused struct { + Id *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepUnpausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.FilterLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicAUpkeepUnpausedIterator{contract: _AutomationRegistryLogicA.contract, event: "UpkeepUnpaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepUnpaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicA.contract.WatchLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicAUpkeepUnpaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepUnpaused", 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 (_AutomationRegistryLogicA *AutomationRegistryLogicAFilterer) ParseUpkeepUnpaused(log types.Log) (*AutomationRegistryLogicAUpkeepUnpaused, error) { + event := new(AutomationRegistryLogicAUpkeepUnpaused) + if err := _AutomationRegistryLogicA.contract.UnpackLog(event, "UpkeepUnpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicA) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _AutomationRegistryLogicA.abi.Events["AdminPrivilegeConfigSet"].ID: + return _AutomationRegistryLogicA.ParseAdminPrivilegeConfigSet(log) + case _AutomationRegistryLogicA.abi.Events["CancelledUpkeepReport"].ID: + return _AutomationRegistryLogicA.ParseCancelledUpkeepReport(log) + case _AutomationRegistryLogicA.abi.Events["DedupKeyAdded"].ID: + return _AutomationRegistryLogicA.ParseDedupKeyAdded(log) + case _AutomationRegistryLogicA.abi.Events["FundsAdded"].ID: + return _AutomationRegistryLogicA.ParseFundsAdded(log) + case _AutomationRegistryLogicA.abi.Events["FundsWithdrawn"].ID: + return _AutomationRegistryLogicA.ParseFundsWithdrawn(log) + case _AutomationRegistryLogicA.abi.Events["InsufficientFundsUpkeepReport"].ID: + return _AutomationRegistryLogicA.ParseInsufficientFundsUpkeepReport(log) + case _AutomationRegistryLogicA.abi.Events["OwnerFundsWithdrawn"].ID: + return _AutomationRegistryLogicA.ParseOwnerFundsWithdrawn(log) + case _AutomationRegistryLogicA.abi.Events["OwnershipTransferRequested"].ID: + return _AutomationRegistryLogicA.ParseOwnershipTransferRequested(log) + case _AutomationRegistryLogicA.abi.Events["OwnershipTransferred"].ID: + return _AutomationRegistryLogicA.ParseOwnershipTransferred(log) + case _AutomationRegistryLogicA.abi.Events["Paused"].ID: + return _AutomationRegistryLogicA.ParsePaused(log) + case _AutomationRegistryLogicA.abi.Events["PayeesUpdated"].ID: + return _AutomationRegistryLogicA.ParsePayeesUpdated(log) + case _AutomationRegistryLogicA.abi.Events["PayeeshipTransferRequested"].ID: + return _AutomationRegistryLogicA.ParsePayeeshipTransferRequested(log) + case _AutomationRegistryLogicA.abi.Events["PayeeshipTransferred"].ID: + return _AutomationRegistryLogicA.ParsePayeeshipTransferred(log) + case _AutomationRegistryLogicA.abi.Events["PaymentWithdrawn"].ID: + return _AutomationRegistryLogicA.ParsePaymentWithdrawn(log) + case _AutomationRegistryLogicA.abi.Events["ReorgedUpkeepReport"].ID: + return _AutomationRegistryLogicA.ParseReorgedUpkeepReport(log) + case _AutomationRegistryLogicA.abi.Events["StaleUpkeepReport"].ID: + return _AutomationRegistryLogicA.ParseStaleUpkeepReport(log) + case _AutomationRegistryLogicA.abi.Events["Unpaused"].ID: + return _AutomationRegistryLogicA.ParseUnpaused(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepAdminTransferRequested"].ID: + return _AutomationRegistryLogicA.ParseUpkeepAdminTransferRequested(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepAdminTransferred"].ID: + return _AutomationRegistryLogicA.ParseUpkeepAdminTransferred(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepCanceled"].ID: + return _AutomationRegistryLogicA.ParseUpkeepCanceled(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepCheckDataSet"].ID: + return _AutomationRegistryLogicA.ParseUpkeepCheckDataSet(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepGasLimitSet"].ID: + return _AutomationRegistryLogicA.ParseUpkeepGasLimitSet(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepMigrated"].ID: + return _AutomationRegistryLogicA.ParseUpkeepMigrated(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepOffchainConfigSet"].ID: + return _AutomationRegistryLogicA.ParseUpkeepOffchainConfigSet(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepPaused"].ID: + return _AutomationRegistryLogicA.ParseUpkeepPaused(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepPerformed"].ID: + return _AutomationRegistryLogicA.ParseUpkeepPerformed(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepPrivilegeConfigSet"].ID: + return _AutomationRegistryLogicA.ParseUpkeepPrivilegeConfigSet(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepReceived"].ID: + return _AutomationRegistryLogicA.ParseUpkeepReceived(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepRegistered"].ID: + return _AutomationRegistryLogicA.ParseUpkeepRegistered(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepTriggerConfigSet"].ID: + return _AutomationRegistryLogicA.ParseUpkeepTriggerConfigSet(log) + case _AutomationRegistryLogicA.abi.Events["UpkeepUnpaused"].ID: + return _AutomationRegistryLogicA.ParseUpkeepUnpaused(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (AutomationRegistryLogicAAdminPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d2") +} + +func (AutomationRegistryLogicACancelledUpkeepReport) Topic() common.Hash { + return common.HexToHash("0xc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636") +} + +func (AutomationRegistryLogicADedupKeyAdded) Topic() common.Hash { + return common.HexToHash("0xa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f2") +} + +func (AutomationRegistryLogicAFundsAdded) Topic() common.Hash { + return common.HexToHash("0xafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa734891506203") +} + +func (AutomationRegistryLogicAFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0xf3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318") +} + +func (AutomationRegistryLogicAInsufficientFundsUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e02") +} + +func (AutomationRegistryLogicAOwnerFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0x1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f1") +} + +func (AutomationRegistryLogicAOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (AutomationRegistryLogicAOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (AutomationRegistryLogicAPaused) Topic() common.Hash { + return common.HexToHash("0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258") +} + +func (AutomationRegistryLogicAPayeesUpdated) Topic() common.Hash { + return common.HexToHash("0xa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725") +} + +func (AutomationRegistryLogicAPayeeshipTransferRequested) Topic() common.Hash { + return common.HexToHash("0x84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367") +} + +func (AutomationRegistryLogicAPayeeshipTransferred) Topic() common.Hash { + return common.HexToHash("0x78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b3") +} + +func (AutomationRegistryLogicAPaymentWithdrawn) Topic() common.Hash { + return common.HexToHash("0x9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f40698") +} + +func (AutomationRegistryLogicAReorgedUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301") +} + +func (AutomationRegistryLogicAStaleUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8") +} + +func (AutomationRegistryLogicAUnpaused) Topic() common.Hash { + return common.HexToHash("0x5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa") +} + +func (AutomationRegistryLogicAUpkeepAdminTransferRequested) Topic() common.Hash { + return common.HexToHash("0xb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b35") +} + +func (AutomationRegistryLogicAUpkeepAdminTransferred) Topic() common.Hash { + return common.HexToHash("0x5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c") +} + +func (AutomationRegistryLogicAUpkeepCanceled) Topic() common.Hash { + return common.HexToHash("0x91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f791181") +} + +func (AutomationRegistryLogicAUpkeepCheckDataSet) Topic() common.Hash { + return common.HexToHash("0xcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d") +} + +func (AutomationRegistryLogicAUpkeepGasLimitSet) Topic() common.Hash { + return common.HexToHash("0xc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c") +} + +func (AutomationRegistryLogicAUpkeepMigrated) Topic() common.Hash { + return common.HexToHash("0xb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff") +} + +func (AutomationRegistryLogicAUpkeepOffchainConfigSet) Topic() common.Hash { + return common.HexToHash("0x3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf4850") +} + +func (AutomationRegistryLogicAUpkeepPaused) Topic() common.Hash { + return common.HexToHash("0x8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f") +} + +func (AutomationRegistryLogicAUpkeepPerformed) Topic() common.Hash { + return common.HexToHash("0xad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b") +} + +func (AutomationRegistryLogicAUpkeepPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae7769") +} + +func (AutomationRegistryLogicAUpkeepReceived) Topic() common.Hash { + return common.HexToHash("0x74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a71") +} + +func (AutomationRegistryLogicAUpkeepRegistered) Topic() common.Hash { + return common.HexToHash("0xbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d012") +} + +func (AutomationRegistryLogicAUpkeepTriggerConfigSet) Topic() common.Hash { + return common.HexToHash("0x2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664") +} + +func (AutomationRegistryLogicAUpkeepUnpaused) Topic() common.Hash { + return common.HexToHash("0x7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a47456") +} + +func (_AutomationRegistryLogicA *AutomationRegistryLogicA) Address() common.Address { + return _AutomationRegistryLogicA.address +} + +type AutomationRegistryLogicAInterface interface { + FallbackTo(opts *bind.CallOpts) (common.Address, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AddFunds(opts *bind.TransactOpts, id *big.Int, amount *big.Int) (*types.Transaction, error) + + CancelUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + CheckCallback(opts *bind.TransactOpts, id *big.Int, values [][]byte, extraData []byte) (*types.Transaction, error) + + CheckUpkeep(opts *bind.TransactOpts, id *big.Int, triggerData []byte) (*types.Transaction, error) + + CheckUpkeep0(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + ExecuteCallback(opts *bind.TransactOpts, id *big.Int, payload []byte) (*types.Transaction, error) + + MigrateUpkeeps(opts *bind.TransactOpts, ids []*big.Int, destination common.Address) (*types.Transaction, error) + + ReceiveUpkeeps(opts *bind.TransactOpts, encodedUpkeeps []byte) (*types.Transaction, error) + + RegisterUpkeep(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, triggerType uint8, checkData []byte, triggerConfig []byte, offchainConfig []byte) (*types.Transaction, error) + + RegisterUpkeep0(opts *bind.TransactOpts, target common.Address, gasLimit uint32, admin common.Address, checkData []byte, offchainConfig []byte) (*types.Transaction, error) + + SetUpkeepTriggerConfig(opts *bind.TransactOpts, id *big.Int, triggerConfig []byte) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) + + FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*AutomationRegistryLogicAAdminPrivilegeConfigSetIterator, error) + + WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) + + ParseAdminPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicAAdminPrivilegeConfigSet, error) + + FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicACancelledUpkeepReportIterator, error) + + WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicACancelledUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseCancelledUpkeepReport(log types.Log) (*AutomationRegistryLogicACancelledUpkeepReport, error) + + FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*AutomationRegistryLogicADedupKeyAddedIterator, error) + + WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicADedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) + + ParseDedupKeyAdded(log types.Log) (*AutomationRegistryLogicADedupKeyAdded, error) + + FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*AutomationRegistryLogicAFundsAddedIterator, error) + + WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) + + ParseFundsAdded(log types.Log) (*AutomationRegistryLogicAFundsAdded, error) + + FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAFundsWithdrawnIterator, error) + + WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAFundsWithdrawn, id []*big.Int) (event.Subscription, error) + + ParseFundsWithdrawn(log types.Log) (*AutomationRegistryLogicAFundsWithdrawn, error) + + FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAInsufficientFundsUpkeepReportIterator, error) + + WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseInsufficientFundsUpkeepReport(log types.Log) (*AutomationRegistryLogicAInsufficientFundsUpkeepReport, error) + + FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*AutomationRegistryLogicAOwnerFundsWithdrawnIterator, error) + + WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAOwnerFundsWithdrawn) (event.Subscription, error) + + ParseOwnerFundsWithdrawn(log types.Log) (*AutomationRegistryLogicAOwnerFundsWithdrawn, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicAOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistryLogicAOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicAOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*AutomationRegistryLogicAOwnershipTransferred, error) + + FilterPaused(opts *bind.FilterOpts) (*AutomationRegistryLogicAPausedIterator, error) + + WatchPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPaused) (event.Subscription, error) + + ParsePaused(log types.Log) (*AutomationRegistryLogicAPaused, error) + + FilterPayeesUpdated(opts *bind.FilterOpts) (*AutomationRegistryLogicAPayeesUpdatedIterator, error) + + WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPayeesUpdated) (event.Subscription, error) + + ParsePayeesUpdated(log types.Log) (*AutomationRegistryLogicAPayeesUpdated, error) + + FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicAPayeeshipTransferRequestedIterator, error) + + WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferRequested(log types.Log) (*AutomationRegistryLogicAPayeeshipTransferRequested, error) + + FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicAPayeeshipTransferredIterator, error) + + WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferred(log types.Log) (*AutomationRegistryLogicAPayeeshipTransferred, error) + + FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*AutomationRegistryLogicAPaymentWithdrawnIterator, error) + + WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) + + ParsePaymentWithdrawn(log types.Log) (*AutomationRegistryLogicAPaymentWithdrawn, error) + + FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAReorgedUpkeepReportIterator, error) + + WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseReorgedUpkeepReport(log types.Log) (*AutomationRegistryLogicAReorgedUpkeepReport, error) + + FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAStaleUpkeepReportIterator, error) + + WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAStaleUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseStaleUpkeepReport(log types.Log) (*AutomationRegistryLogicAStaleUpkeepReport, error) + + FilterUnpaused(opts *bind.FilterOpts) (*AutomationRegistryLogicAUnpausedIterator, error) + + WatchUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUnpaused) (event.Subscription, error) + + ParseUnpaused(log types.Log) (*AutomationRegistryLogicAUnpaused, error) + + FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicAUpkeepAdminTransferRequestedIterator, error) + + WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferRequested(log types.Log) (*AutomationRegistryLogicAUpkeepAdminTransferRequested, error) + + FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicAUpkeepAdminTransferredIterator, error) + + WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferred(log types.Log) (*AutomationRegistryLogicAUpkeepAdminTransferred, error) + + FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*AutomationRegistryLogicAUpkeepCanceledIterator, error) + + WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) + + ParseUpkeepCanceled(log types.Log) (*AutomationRegistryLogicAUpkeepCanceled, error) + + FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepCheckDataSetIterator, error) + + WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepCheckDataSet(log types.Log) (*AutomationRegistryLogicAUpkeepCheckDataSet, error) + + FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepGasLimitSetIterator, error) + + WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepGasLimitSet(log types.Log) (*AutomationRegistryLogicAUpkeepGasLimitSet, error) + + FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepMigratedIterator, error) + + WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepMigrated, id []*big.Int) (event.Subscription, error) + + ParseUpkeepMigrated(log types.Log) (*AutomationRegistryLogicAUpkeepMigrated, error) + + FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepOffchainConfigSetIterator, error) + + WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepOffchainConfigSet(log types.Log) (*AutomationRegistryLogicAUpkeepOffchainConfigSet, error) + + FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepPausedIterator, error) + + WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepPaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPaused(log types.Log) (*AutomationRegistryLogicAUpkeepPaused, error) + + FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*AutomationRegistryLogicAUpkeepPerformedIterator, error) + + WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) + + ParseUpkeepPerformed(log types.Log) (*AutomationRegistryLogicAUpkeepPerformed, error) + + FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepPrivilegeConfigSetIterator, error) + + WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicAUpkeepPrivilegeConfigSet, error) + + FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepReceivedIterator, error) + + WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepReceived, id []*big.Int) (event.Subscription, error) + + ParseUpkeepReceived(log types.Log) (*AutomationRegistryLogicAUpkeepReceived, error) + + FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepRegisteredIterator, error) + + WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepRegistered, id []*big.Int) (event.Subscription, error) + + ParseUpkeepRegistered(log types.Log) (*AutomationRegistryLogicAUpkeepRegistered, error) + + FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepTriggerConfigSetIterator, error) + + WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepTriggerConfigSet(log types.Log) (*AutomationRegistryLogicAUpkeepTriggerConfigSet, error) + + FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicAUpkeepUnpausedIterator, error) + + WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicAUpkeepUnpaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepUnpaused(log types.Log) (*AutomationRegistryLogicAUpkeepUnpaused, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go b/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go new file mode 100644 index 00000000000..1805d535680 --- /dev/null +++ b/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go @@ -0,0 +1,5708 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package keeper_registry_logic_b_wrapper_2_2 + +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 AutomationRegistryBase22OnchainConfig struct { + PaymentPremiumPPB uint32 + FlatFeeMicroLink uint32 + CheckGasLimit uint32 + StalenessSeconds *big.Int + GasCeilingMultiplier uint16 + MinUpkeepSpend *big.Int + MaxPerformGas uint32 + MaxCheckDataSize uint32 + MaxPerformDataSize uint32 + MaxRevertDataSize uint32 + FallbackGasPrice *big.Int + FallbackLinkPrice *big.Int + Transcoder common.Address + Registrars []common.Address + UpkeepPrivilegeManager common.Address +} + +type AutomationRegistryBase22State struct { + Nonce uint32 + OwnerLinkBalance *big.Int + ExpectedLinkBalance *big.Int + TotalPremium *big.Int + NumUpkeeps *big.Int + ConfigCount uint32 + LatestConfigBlockNumber uint32 + LatestConfigDigest [32]byte + LatestEpoch uint32 + Paused bool +} + +type AutomationRegistryBase22UpkeepInfo struct { + Target common.Address + PerformGas uint32 + CheckData []byte + Balance *big.Int + Admin common.Address + MaxValidBlocknumber uint64 + LastPerformedBlockNumber uint32 + AmountSpent *big.Int + Paused bool + OffchainConfig []byte +} + +var AutomationRegistryLogicBMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Mode\",\"name\":\"mode\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"fastGasFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"automationForwarderLogic\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"contractIAutomationForwarder\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkNativeFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMode\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Mode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.MigrationPermission\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.State\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structAutomationRegistryBase2_2.UpkeepInfo\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_2.MigrationPermission\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepTranscoderVersion\",\"outputs\":[{\"internalType\":\"enumUpkeepFormat\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawOwnerFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101206040523480156200001257600080fd5b5060405162004fbc38038062004fbc8339810160408190526200003591620001e9565b84848484843380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000121565b505050846002811115620000dc57620000dc6200025e565b60e0816002811115620000f357620000f36200025e565b9052506001600160a01b0393841660805291831660a052821660c05216610100525062000274945050505050565b336001600160a01b038216036200017b5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001e457600080fd5b919050565b600080600080600060a086880312156200020257600080fd5b8551600381106200021257600080fd5b94506200022260208701620001cc565b93506200023260408701620001cc565b92506200024260608701620001cc565b91506200025260808701620001cc565b90509295509295909350565b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e05161010051614cbd620002ff600039600061058701526000818161052501528181613352015281816138d50152613a680152600081816105f4015261314601526000818161071c01526132200152600081816107aa01528181611bab01528181611e81015281816122ee0152818161280101526128850152614cbd6000f3fe608060405234801561001057600080fd5b50600436106103365760003560e01c806379ba5097116101b2578063b121e147116100f9578063ca30e603116100a2578063eb5dcd6c1161007c578063eb5dcd6c146107f4578063ed56b3e114610807578063f2fde38b1461087a578063faa3e9961461088d57600080fd5b8063ca30e603146107a8578063cd7f71b5146107ce578063d7632648146107e157600080fd5b8063b657bc9c116100d3578063b657bc9c1461076d578063b79550be14610780578063c7c3a19a1461078857600080fd5b8063b121e14714610740578063b148ab6b14610753578063b6511a2a1461076657600080fd5b80638dcf0fe71161015b578063aab9edd611610135578063aab9edd614610703578063abc76ae014610712578063b10b673c1461071a57600080fd5b80638dcf0fe7146106ca578063a710b221146106dd578063a72aa27e146106f057600080fd5b80638456cb591161018c5780638456cb59146106915780638765ecbe146106995780638da5cb5b146106ac57600080fd5b806379ba50971461063e57806379ea9943146106465780637d9b97e01461068957600080fd5b8063421d183b116102815780635165f2f51161022a5780636209e1e9116102045780636209e1e9146105df5780636709d0e5146105f2578063671d36ed14610618578063744bfe611461062b57600080fd5b80635165f2f5146105725780635425d8ac146105855780635b6aa71c146105cc57600080fd5b80634b4fd03b1161025b5780634b4fd03b146105235780634ca16c52146105495780635147cd591461055257600080fd5b8063421d183b1461047a57806344cb70b8146104e057806348013d7b1461051357600080fd5b80631a2af011116102e3578063232c1cc5116102bd578063232c1cc5146104585780633b9cce591461045f5780633f4ba83a1461047257600080fd5b80631a2af011146103d45780631e010439146103e7578063207b65161461044557600080fd5b80631865c57d116103145780631865c57d14610388578063187256e8146103a157806319d97a94146103b457600080fd5b8063050ee65d1461033b57806306e3b632146103535780630b7d33e614610373575b600080fd5b6201adb05b6040519081526020015b60405180910390f35b610366610361366004613df3565b6108d3565b60405161034a9190613e15565b610386610381366004613ea2565b6109f0565b005b610390610aaa565b60405161034a9594939291906140a5565b6103866103af3660046141dc565b610ec3565b6103c76103c2366004614219565b610f34565b60405161034a91906142a0565b6103866103e23660046142b3565b610fd6565b6104286103f5366004614219565b6000908152600460205260409020600101546c0100000000000000000000000090046bffffffffffffffffffffffff1690565b6040516bffffffffffffffffffffffff909116815260200161034a565b6103c7610453366004614219565b6110dc565b6014610340565b61038661046d3660046142d8565b6110f9565b61038661134f565b61048d61048836600461434d565b6113b5565b60408051951515865260ff90941660208601526bffffffffffffffffffffffff9283169385019390935216606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00161034a565b6105036104ee366004614219565b60009081526008602052604090205460ff1690565b604051901515815260200161034a565b60005b60405161034a91906143a9565b7f0000000000000000000000000000000000000000000000000000000000000000610516565b62015f90610340565b610565610560366004614219565b6114e8565b60405161034a91906143bc565b610386610580366004614219565b6114f3565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161034a565b6104286105da3660046143e9565b61166a565b6103c76105ed36600461434d565b61179c565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b610386610626366004614422565b6117d0565b6103866106393660046142b3565b6118aa565b610386611ca7565b6105a7610654366004614219565b6000908152600460205260409020546901000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b610386611da9565b610386611f04565b6103866106a7366004614219565b611f75565b60005473ffffffffffffffffffffffffffffffffffffffff166105a7565b6103866106d8366004613ea2565b6120ef565b6103866106eb36600461445e565b612144565b6103866106fe36600461448c565b6123c0565b6040516003815260200161034a565b611d4c610340565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b61038661074e36600461434d565b6124b5565b610386610761366004614219565b6125ad565b6032610340565b61042861077b366004614219565b61279b565b6103866127c8565b61079b610796366004614219565b612924565b60405161034a91906144af565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b6103866107dc366004613ea2565b612cf7565b6104286107ef366004614219565b612d8e565b61038661080236600461445e565b612d99565b61086161081536600461434d565b73ffffffffffffffffffffffffffffffffffffffff166000908152600c602090815260409182902082518084019093525460ff8082161515808552610100909204169290910182905291565b60408051921515835260ff90911660208301520161034a565b61038661088836600461434d565b612ef7565b6108c661089b36600461434d565b73ffffffffffffffffffffffffffffffffffffffff1660009081526019602052604090205460ff1690565b60405161034a91906145e6565b606060006108e16002612f0b565b905080841061091c576040517f1390f2a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109288486614629565b905081811180610936575083155b6109405780610942565b815b90506000610950868361463c565b67ffffffffffffffff8111156109685761096861464f565b604051908082528060200260200182016040528015610991578160200160208202803683370190505b50905060005b81518110156109e4576109b56109ad8883614629565b600290612f15565b8282815181106109c7576109c761467e565b6020908102919091010152806109dc816146ad565b915050610997565b50925050505b92915050565b6015546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314610a51576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152601c60205260409020610a6a828483614787565b50827f2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae77698383604051610a9d9291906148a2565b60405180910390a2505050565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152604080516101e08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a08201526101c0810191909152604080516101408101825260145463ffffffff7401000000000000000000000000000000000000000082041682526bffffffffffffffffffffffff908116602083015260185492820192909252601254700100000000000000000000000000000000900490911660608281019190915290819060009060808101610bf76002612f0b565b81526014547801000000000000000000000000000000000000000000000000810463ffffffff9081166020808501919091527c0100000000000000000000000000000000000000000000000000000000808404831660408087019190915260115460608088019190915260125492830485166080808901919091526e010000000000000000000000000000840460ff16151560a09889015282516101e081018452610100808604881682526501000000000086048816968201969096526c010000000000000000000000008089048816948201949094526901000000000000000000850462ffffff16928101929092529282900461ffff16928101929092526013546bffffffffffffffffffffffff811696830196909652700100000000000000000000000000000000909404831660c082015260155480841660e0830152640100000000810484169282019290925268010000000000000000909104909116610120820152601654610140820152601754610160820152910473ffffffffffffffffffffffffffffffffffffffff166101808201529095506101a08101610d9f6009612f28565b81526015546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16602091820152601254600d80546040805182860281018601909152818152949850899489949293600e9360ff909116928591830182828015610e4257602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e17575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015610eab57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e80575b50505050509150945094509450945094509091929394565b610ecb612f35565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260196020526040902080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115610f2b57610f2b61436a565b02179055505050565b6000818152601c60205260409020805460609190610f51906146e5565b80601f0160208091040260200160405190810160405280929190818152602001828054610f7d906146e5565b8015610fca5780601f10610f9f57610100808354040283529160200191610fca565b820191906000526020600020905b815481529060010190602001808311610fad57829003601f168201915b50505050509050919050565b610fdf82612fb8565b3373ffffffffffffffffffffffffffffffffffffffff82160361102e576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116146110d85760008281526006602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915590519091339185917fb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b3591a45b5050565b6000818152601a60205260409020805460609190610f51906146e5565b611101612f35565b600e54811461113c576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b600e5481101561130e576000600e828154811061115e5761115e61467e565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff908116808452600f909252604083205491935016908585858181106111a8576111a861467e565b90506020020160208101906111bd919061434d565b905073ffffffffffffffffffffffffffffffffffffffff81161580611250575073ffffffffffffffffffffffffffffffffffffffff82161580159061122e57508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b8015611250575073ffffffffffffffffffffffffffffffffffffffff81811614155b15611287576040517fb387a23800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff818116146112f85773ffffffffffffffffffffffffffffffffffffffff8381166000908152600f6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169183169190911790555b5050508080611306906146ad565b91505061113f565b507fa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725600e8383604051611343939291906148ef565b60405180910390a15050565b611357612f35565b601280547fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff1690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152829182918291829190829061148f57606082015160125460009161147b9170010000000000000000000000000000000090046bffffffffffffffffffffffff166149a1565b600e5490915061148b90826149f5565b9150505b8151602083015160408401516114a6908490614a20565b6060949094015173ffffffffffffffffffffffffffffffffffffffff9a8b166000908152600f6020526040902054929b919a9499509750921694509092505050565b60006109ea8261306c565b6114fc81612fb8565b600081815260046020908152604091829020825160e081018452815460ff8116151580835263ffffffff610100830481169584019590955265010000000000820485169583019590955273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c0820152906115fb576040517f1b88a78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905561163a600283613117565b5060405182907f7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a4745690600090a25050565b604080516101208101825260125460ff808216835263ffffffff6101008084048216602086015265010000000000840482169585019590955262ffffff6901000000000000000000840416606085015261ffff6c0100000000000000000000000084041660808501526e01000000000000000000000000000083048216151560a08501526f010000000000000000000000000000008304909116151560c08401526bffffffffffffffffffffffff70010000000000000000000000000000000083041660e08401527c01000000000000000000000000000000000000000000000000000000009091041691810191909152600090818061176983613123565b91509150611792838787601360020160049054906101000a900463ffffffff1686866000613301565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152601d60205260409020805460609190610f51906146e5565b6015546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314611831576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152601d60205260409020611861828483614787565b508273ffffffffffffffffffffffffffffffffffffffff167f7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d28383604051610a9d9291906148a2565b6012546f01000000000000000000000000000000900460ff16156118fa576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff166f0100000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff8116611981576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600460209081526040808320815160e081018352815460ff81161515825263ffffffff610100820481168387015265010000000000820481168386015273ffffffffffffffffffffffffffffffffffffffff6901000000000000000000909204821660608401526001909301546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a08401527801000000000000000000000000000000000000000000000000900490921660c082015286855260059093529220549091163314611a88576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a9061334c565b816040015163ffffffff161115611ad3576040517fff84e5dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600460205260409020600101546018546c010000000000000000000000009091046bffffffffffffffffffffffff1690611b1390829061463c565b60185560008481526004602081905260409182902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff16905590517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116928201929092526bffffffffffffffffffffffff831660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015611bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1a9190614a45565b50604080516bffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff8516602082015285917ff3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318910160405180910390a25050601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff1690555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d2d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611db1612f35565b6014546018546bffffffffffffffffffffffff90911690611dd390829061463c565b601855601480547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690556040516bffffffffffffffffffffffff821681527f1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f19060200160405180910390a16040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526bffffffffffffffffffffffff821660248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044015b6020604051808303816000875af1158015611ee0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d89190614a45565b611f0c612f35565b601280547fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff166e0100000000000000000000000000001790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016113ab565b611f7e81612fb8565b600081815260046020908152604091829020825160e081018452815460ff8116158015835263ffffffff610100830481169584019590955265010000000000820485169583019590955273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c08201529061207d576040517f514b6c2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556120bf600283613401565b5060405182907f8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f90600090a25050565b6120f883612fb8565b6000838152601b60205260409020612111828483614787565b50827f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf48508383604051610a9d9291906148a2565b73ffffffffffffffffffffffffffffffffffffffff8116612191576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600f60205260409020541633146121f1576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e5460009161222891859170010000000000000000000000000000000090046bffffffffffffffffffffffff169061340d565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600b6020526040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601854909150612292906bffffffffffffffffffffffff83169061463c565b6018556040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526bffffffffffffffffffffffff831660248301527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612337573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061235b9190614a45565b5060405133815273ffffffffffffffffffffffffffffffffffffffff808416916bffffffffffffffffffffffff8416918616907f9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f406989060200160405180910390a4505050565b6108fc8163ffffffff1610806123f5575060145463ffffffff7001000000000000000000000000000000009091048116908216115b1561242c576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61243582612fb8565b60008281526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff861690810291909117909155915191825283917fc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c910160405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff818116600090815260106020526040902054163314612515576040517f6752e7aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600f602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556010909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b600081815260046020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452650100000000008104841694820185905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004821660c082015291146126aa576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314612707576040517f6352a85300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602090815260408083208054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821790935560069094528285208054909216909155905173ffffffffffffffffffffffffffffffffffffffff90911692839186917f5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c91a4505050565b60006109ea6127a98361306c565b600084815260046020526040902054610100900463ffffffff1661166a565b6127d0612f35565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa15801561285d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128819190614a67565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33601854846128ce919061463c565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401611ec1565b604080516101408101825260008082526020820181905260609282018390528282018190526080820181905260a0820181905260c0820181905260e082018190526101008201526101208101919091526000828152600460209081526040808320815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606083018190526001909101546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a08401527801000000000000000000000000000000000000000000000000900490921660c0820152919015612abc57816060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab79190614a80565b612abf565b60005b90506040518061014001604052808273ffffffffffffffffffffffffffffffffffffffff168152602001836020015163ffffffff168152602001600760008781526020019081526020016000208054612b17906146e5565b80601f0160208091040260200160405190810160405280929190818152602001828054612b43906146e5565b8015612b905780601f10612b6557610100808354040283529160200191612b90565b820191906000526020600020905b815481529060010190602001808311612b7357829003601f168201915b505050505081526020018360a001516bffffffffffffffffffffffff1681526020016005600087815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001836040015163ffffffff1667ffffffffffffffff1681526020018360c0015163ffffffff16815260200183608001516bffffffffffffffffffffffff168152602001836000015115158152602001601b60008781526020019081526020016000208054612c6d906146e5565b80601f0160208091040260200160405190810160405280929190818152602001828054612c99906146e5565b8015612ce65780601f10612cbb57610100808354040283529160200191612ce6565b820191906000526020600020905b815481529060010190602001808311612cc957829003601f168201915b505050505081525092505050919050565b612d0083612fb8565b60155463ffffffff16811115612d42576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600760205260409020612d5b828483614787565b50827fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d8383604051610a9d9291906148a2565b60006109ea8261279b565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600f6020526040902054163314612df9576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821603612e48576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601060205260409020548116908216146110d85773ffffffffffffffffffffffffffffffffffffffff82811660008181526010602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055513392917f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836791a45050565b612eff612f35565b612f0881613615565b50565b60006109ea825490565b6000612f21838361370a565b9392505050565b60606000612f2183613734565b60005473ffffffffffffffffffffffffffffffffffffffff163314612fb6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401611d24565b565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314613015576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090205465010000000000900463ffffffff90811614612f08576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818160045b600f8110156130f9577fff0000000000000000000000000000000000000000000000000000000000000082168382602081106130b1576130b161467e565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146130e757506000949350505050565b806130f1816146ad565b915050613073565b5081600f1a600181111561310f5761310f61436a565b949350505050565b6000612f21838361378f565b6000806000836060015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156131af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d39190614ab7565b50945090925050506000811315806131ea57508142105b8061320b575082801561320b5750613202824261463c565b8463ffffffff16105b1561321a57601654955061321e565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613289573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ad9190614ab7565b50945090925050506000811315806132c457508142105b806132e557508280156132e557506132dc824261463c565b8463ffffffff16105b156132f45760175494506132f8565b8094505b50505050915091565b60008061331388878b600001516137de565b905060008061332e8b8a63ffffffff16858a8a60018b6138a0565b909250905061333d8183614a20565b9b9a5050505050505050505050565b600060017f000000000000000000000000000000000000000000000000000000000000000060028111156133825761338261436a565b036133fc57606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f79190614a67565b905090565b504390565b6000612f218383613cf9565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152906136095760008160600151856134a591906149a1565b905060006134b385836149f5565b905080836040018181516134c79190614a20565b6bffffffffffffffffffffffff169052506134e28582614b07565b836060018181516134f39190614a20565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613694576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401611d24565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008260000182815481106137215761372161467e565b9060005260206000200154905092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015610fca57602002820191906000526020600020905b8154815260200190600101908083116137705750505050509050919050565b60008181526001830160205260408120546137d6575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109ea565b5060006109ea565b600080808560018111156137f4576137f461436a565b03613803575062015f90613858565b60018560018111156138175761381761436a565b0361382657506201adb0613858565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61386963ffffffff85166014614b3b565b613874846001614b78565b6138839060ff16611d4c614b3b565b61388d9083614629565b6138979190614629565b95945050505050565b6000806000896080015161ffff16876138b99190614b3b565b90508380156138c75750803a105b156138cf57503a5b600060027f000000000000000000000000000000000000000000000000000000000000000060028111156139055761390561436a565b03613a6457604080516000815260208101909152851561396357600036604051806080016040528060488152602001614c696048913960405160200161394d93929190614b91565b60405160208183030381529060405290506139cb565b60155461397f90640100000000900463ffffffff166004614bb8565b63ffffffff1667ffffffffffffffff81111561399d5761399d61464f565b6040519080825280601f01601f1916602001820160405280156139c7576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e90613a1b9084906004016142a0565b602060405180830381865afa158015613a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5c9190614a67565b915050613bbe565b60017f00000000000000000000000000000000000000000000000000000000000000006002811115613a9857613a9861436a565b03613bbe578415613b1a57606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613aef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b139190614a67565b9050613bbe565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa158015613b68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8c9190614bdb565b5050601554929450613baf93505050640100000000900463ffffffff1682614b3b565b613bba906010614b3b565b9150505b84613bda57808b6080015161ffff16613bd79190614b3b565b90505b613be861ffff871682614c25565b905060008782613bf88c8e614629565b613c029086614b3b565b613c0c9190614629565b613c1e90670de0b6b3a7640000614b3b565b613c289190614c25565b905060008c6040015163ffffffff1664e8d4a51000613c479190614b3b565b898e6020015163ffffffff16858f88613c609190614b3b565b613c6a9190614629565b613c7890633b9aca00614b3b565b613c829190614b3b565b613c8c9190614c25565b613c969190614629565b90506b033b2e3c9fd0803ce8000000613caf8284614629565b1115613ce7576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b60008181526001830160205260408120548015613de2576000613d1d60018361463c565b8554909150600090613d319060019061463c565b9050818114613d96576000866000018281548110613d5157613d5161467e565b9060005260206000200154905080876000018481548110613d7457613d7461467e565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613da757613da7614c39565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109ea565b60009150506109ea565b5092915050565b60008060408385031215613e0657600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015613e4d57835183529284019291840191600101613e31565b50909695505050505050565b60008083601f840112613e6b57600080fd5b50813567ffffffffffffffff811115613e8357600080fd5b602083019150836020828501011115613e9b57600080fd5b9250929050565b600080600060408486031215613eb757600080fd5b83359250602084013567ffffffffffffffff811115613ed557600080fd5b613ee186828701613e59565b9497909650939450505050565b600081518084526020808501945080840160005b83811015613f3457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613f02565b509495945050505050565b805163ffffffff16825260006101e06020830151613f65602086018263ffffffff169052565b506040830151613f7d604086018263ffffffff169052565b506060830151613f94606086018262ffffff169052565b506080830151613faa608086018261ffff169052565b5060a0830151613fca60a08601826bffffffffffffffffffffffff169052565b5060c0830151613fe260c086018263ffffffff169052565b5060e0830151613ffa60e086018263ffffffff169052565b506101008381015163ffffffff908116918601919091526101208085015190911690850152610140808401519085015261016080840151908501526101808084015173ffffffffffffffffffffffffffffffffffffffff16908501526101a08084015181860183905261406f83870182613eee565b925050506101c08084015161409b8287018273ffffffffffffffffffffffffffffffffffffffff169052565b5090949350505050565b855163ffffffff16815260006101c060208801516140d360208501826bffffffffffffffffffffffff169052565b506040880151604084015260608801516140fd60608501826bffffffffffffffffffffffff169052565b506080880151608084015260a088015161411f60a085018263ffffffff169052565b5060c088015161413760c085018263ffffffff169052565b5060e088015160e08401526101008089015161415a8286018263ffffffff169052565b505061012088810151151590840152610140830181905261417d81840188613f3f565b90508281036101608401526141928187613eee565b90508281036101808401526141a78186613eee565b9150506117926101a083018460ff169052565b73ffffffffffffffffffffffffffffffffffffffff81168114612f0857600080fd5b600080604083850312156141ef57600080fd5b82356141fa816141ba565b915060208301356004811061420e57600080fd5b809150509250929050565b60006020828403121561422b57600080fd5b5035919050565b60005b8381101561424d578181015183820152602001614235565b50506000910152565b6000815180845261426e816020860160208601614232565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612f216020830184614256565b600080604083850312156142c657600080fd5b82359150602083013561420e816141ba565b600080602083850312156142eb57600080fd5b823567ffffffffffffffff8082111561430357600080fd5b818501915085601f83011261431757600080fd5b81358181111561432657600080fd5b8660208260051b850101111561433b57600080fd5b60209290920196919550909350505050565b60006020828403121561435f57600080fd5b8135612f21816141ba565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110612f0857612f0861436a565b602081016143b683614399565b91905290565b60208101600283106143b6576143b661436a565b803563ffffffff811681146143e457600080fd5b919050565b600080604083850312156143fc57600080fd5b82356002811061440b57600080fd5b9150614419602084016143d0565b90509250929050565b60008060006040848603121561443757600080fd5b8335614442816141ba565b9250602084013567ffffffffffffffff811115613ed557600080fd5b6000806040838503121561447157600080fd5b823561447c816141ba565b9150602083013561420e816141ba565b6000806040838503121561449f57600080fd5b82359150614419602084016143d0565b602081526144d660208201835173ffffffffffffffffffffffffffffffffffffffff169052565b600060208301516144ef604084018263ffffffff169052565b50604083015161014080606085015261450c610160850183614256565b9150606085015161452d60808601826bffffffffffffffffffffffff169052565b50608085015173ffffffffffffffffffffffffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015163ffffffff811660e08601525060e0850151610100614599818701836bffffffffffffffffffffffff169052565b86015190506101206145ae8682018315159052565b8601518584037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018387015290506117928382614256565b60208101600483106143b6576143b661436a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156109ea576109ea6145fa565b818103818111156109ea576109ea6145fa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036146de576146de6145fa565b5060010190565b600181811c908216806146f957607f821691505b602082108103614732577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111561478257600081815260208120601f850160051c8101602086101561475f5750805b601f850160051c820191505b8181101561477e5782815560010161476b565b5050505b505050565b67ffffffffffffffff83111561479f5761479f61464f565b6147b3836147ad83546146e5565b83614738565b6000601f84116001811461480557600085156147cf5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561489b565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156148545786850135825560209485019460019092019101614834565b508682101561488f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b6000604082016040835280865480835260608501915087600052602092508260002060005b8281101561494657815473ffffffffffffffffffffffffffffffffffffffff1684529284019260019182019101614914565b505050838103828501528481528590820160005b8681101561499557823561496d816141ba565b73ffffffffffffffffffffffffffffffffffffffff168252918301919083019060010161495a565b50979650505050505050565b6bffffffffffffffffffffffff828116828216039080821115613dec57613dec6145fa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff80841680614a1457614a146149c6565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115613dec57613dec6145fa565b600060208284031215614a5757600080fd5b81518015158114612f2157600080fd5b600060208284031215614a7957600080fd5b5051919050565b600060208284031215614a9257600080fd5b8151612f21816141ba565b805169ffffffffffffffffffff811681146143e457600080fd5b600080600080600060a08688031215614acf57600080fd5b614ad886614a9d565b9450602086015193506040860151925060608601519150614afb60808701614a9d565b90509295509295909350565b60006bffffffffffffffffffffffff80831681851681830481118215151615614b3257614b326145fa565b02949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615614b7357614b736145fa565b500290565b60ff81811683821601908111156109ea576109ea6145fa565b828482376000838201600081528351614bae818360208801614232565b0195945050505050565b600063ffffffff80831681851681830481118215151615614b3257614b326145fa565b60008060008060008060c08789031215614bf457600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600082614c3457614c346149c6565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", +} + +var AutomationRegistryLogicBABI = AutomationRegistryLogicBMetaData.ABI + +var AutomationRegistryLogicBBin = AutomationRegistryLogicBMetaData.Bin + +func DeployAutomationRegistryLogicB(auth *bind.TransactOpts, backend bind.ContractBackend, mode uint8, link common.Address, linkNativeFeed common.Address, fastGasFeed common.Address, automationForwarderLogic common.Address) (common.Address, *types.Transaction, *AutomationRegistryLogicB, error) { + parsed, err := AutomationRegistryLogicBMetaData.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(AutomationRegistryLogicBBin), backend, mode, link, linkNativeFeed, fastGasFeed, automationForwarderLogic) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &AutomationRegistryLogicB{address: address, abi: *parsed, AutomationRegistryLogicBCaller: AutomationRegistryLogicBCaller{contract: contract}, AutomationRegistryLogicBTransactor: AutomationRegistryLogicBTransactor{contract: contract}, AutomationRegistryLogicBFilterer: AutomationRegistryLogicBFilterer{contract: contract}}, nil +} + +type AutomationRegistryLogicB struct { + address common.Address + abi abi.ABI + AutomationRegistryLogicBCaller + AutomationRegistryLogicBTransactor + AutomationRegistryLogicBFilterer +} + +type AutomationRegistryLogicBCaller struct { + contract *bind.BoundContract +} + +type AutomationRegistryLogicBTransactor struct { + contract *bind.BoundContract +} + +type AutomationRegistryLogicBFilterer struct { + contract *bind.BoundContract +} + +type AutomationRegistryLogicBSession struct { + Contract *AutomationRegistryLogicB + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type AutomationRegistryLogicBCallerSession struct { + Contract *AutomationRegistryLogicBCaller + CallOpts bind.CallOpts +} + +type AutomationRegistryLogicBTransactorSession struct { + Contract *AutomationRegistryLogicBTransactor + TransactOpts bind.TransactOpts +} + +type AutomationRegistryLogicBRaw struct { + Contract *AutomationRegistryLogicB +} + +type AutomationRegistryLogicBCallerRaw struct { + Contract *AutomationRegistryLogicBCaller +} + +type AutomationRegistryLogicBTransactorRaw struct { + Contract *AutomationRegistryLogicBTransactor +} + +func NewAutomationRegistryLogicB(address common.Address, backend bind.ContractBackend) (*AutomationRegistryLogicB, error) { + abi, err := abi.JSON(strings.NewReader(AutomationRegistryLogicBABI)) + if err != nil { + return nil, err + } + contract, err := bindAutomationRegistryLogicB(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicB{address: address, abi: abi, AutomationRegistryLogicBCaller: AutomationRegistryLogicBCaller{contract: contract}, AutomationRegistryLogicBTransactor: AutomationRegistryLogicBTransactor{contract: contract}, AutomationRegistryLogicBFilterer: AutomationRegistryLogicBFilterer{contract: contract}}, nil +} + +func NewAutomationRegistryLogicBCaller(address common.Address, caller bind.ContractCaller) (*AutomationRegistryLogicBCaller, error) { + contract, err := bindAutomationRegistryLogicB(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBCaller{contract: contract}, nil +} + +func NewAutomationRegistryLogicBTransactor(address common.Address, transactor bind.ContractTransactor) (*AutomationRegistryLogicBTransactor, error) { + contract, err := bindAutomationRegistryLogicB(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBTransactor{contract: contract}, nil +} + +func NewAutomationRegistryLogicBFilterer(address common.Address, filterer bind.ContractFilterer) (*AutomationRegistryLogicBFilterer, error) { + contract, err := bindAutomationRegistryLogicB(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBFilterer{contract: contract}, nil +} + +func bindAutomationRegistryLogicB(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AutomationRegistryLogicBMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistryLogicB.Contract.AutomationRegistryLogicBCaller.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AutomationRegistryLogicBTransactor.contract.Transfer(opts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AutomationRegistryLogicBTransactor.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistryLogicB.Contract.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.contract.Transfer(opts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getActiveUpkeepIDs", startIndex, maxCount) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetActiveUpkeepIDs(startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetActiveUpkeepIDs(&_AutomationRegistryLogicB.CallOpts, startIndex, maxCount) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetActiveUpkeepIDs(startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetActiveUpkeepIDs(&_AutomationRegistryLogicB.CallOpts, startIndex, maxCount) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetAdminPrivilegeConfig(opts *bind.CallOpts, admin common.Address) ([]byte, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getAdminPrivilegeConfig", admin) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetAdminPrivilegeConfig(admin common.Address) ([]byte, error) { + return _AutomationRegistryLogicB.Contract.GetAdminPrivilegeConfig(&_AutomationRegistryLogicB.CallOpts, admin) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetAdminPrivilegeConfig(admin common.Address) ([]byte, error) { + return _AutomationRegistryLogicB.Contract.GetAdminPrivilegeConfig(&_AutomationRegistryLogicB.CallOpts, admin) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetAutomationForwarderLogic(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getAutomationForwarderLogic") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetAutomationForwarderLogic() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetAutomationForwarderLogic(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetAutomationForwarderLogic() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetAutomationForwarderLogic(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getBalance", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetBalance(id *big.Int) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetBalance(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetBalance(id *big.Int) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetBalance(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetCancellationDelay(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getCancellationDelay") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetCancellationDelay() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetCancellationDelay(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetCancellationDelay() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetCancellationDelay(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetConditionalGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getConditionalGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetConditionalGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetConditionalGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetConditionalGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetConditionalGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetFastGasFeedAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getFastGasFeedAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetFastGasFeedAddress() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetFastGasFeedAddress(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetFastGasFeedAddress() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetFastGasFeedAddress(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetForwarder(opts *bind.CallOpts, upkeepID *big.Int) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getForwarder", upkeepID) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetForwarder(upkeepID *big.Int) (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetForwarder(&_AutomationRegistryLogicB.CallOpts, upkeepID) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetForwarder(upkeepID *big.Int) (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetForwarder(&_AutomationRegistryLogicB.CallOpts, upkeepID) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetLinkAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getLinkAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetLinkAddress() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetLinkAddress(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetLinkAddress() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetLinkAddress(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetLinkNativeFeedAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getLinkNativeFeedAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetLinkNativeFeedAddress() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetLinkNativeFeedAddress(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetLinkNativeFeedAddress() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.GetLinkNativeFeedAddress(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetLogGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getLogGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetLogGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetLogGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetLogGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetLogGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetMaxPaymentForGas(opts *bind.CallOpts, triggerType uint8, gasLimit uint32) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getMaxPaymentForGas", triggerType, gasLimit) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetMaxPaymentForGas(triggerType uint8, gasLimit uint32) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetMaxPaymentForGas(&_AutomationRegistryLogicB.CallOpts, triggerType, gasLimit) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetMaxPaymentForGas(triggerType uint8, gasLimit uint32) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetMaxPaymentForGas(&_AutomationRegistryLogicB.CallOpts, triggerType, gasLimit) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetMinBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getMinBalance", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetMinBalance(id *big.Int) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetMinBalance(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetMinBalance(id *big.Int) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetMinBalance(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetMinBalanceForUpkeep(opts *bind.CallOpts, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getMinBalanceForUpkeep", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetMinBalanceForUpkeep(id *big.Int) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetMinBalanceForUpkeep(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetMinBalanceForUpkeep(id *big.Int) (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetMinBalanceForUpkeep(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetMode(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getMode") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetMode() (uint8, error) { + return _AutomationRegistryLogicB.Contract.GetMode(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetMode() (uint8, error) { + return _AutomationRegistryLogicB.Contract.GetMode(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetPeerRegistryMigrationPermission(opts *bind.CallOpts, peer common.Address) (uint8, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getPeerRegistryMigrationPermission", peer) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetPeerRegistryMigrationPermission(peer common.Address) (uint8, error) { + return _AutomationRegistryLogicB.Contract.GetPeerRegistryMigrationPermission(&_AutomationRegistryLogicB.CallOpts, peer) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetPeerRegistryMigrationPermission(peer common.Address) (uint8, error) { + return _AutomationRegistryLogicB.Contract.GetPeerRegistryMigrationPermission(&_AutomationRegistryLogicB.CallOpts, peer) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetPerPerformByteGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getPerPerformByteGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetPerPerformByteGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetPerPerformByteGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetPerPerformByteGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetPerPerformByteGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetPerSignerGasOverhead(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getPerSignerGasOverhead") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetPerSignerGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetPerSignerGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetPerSignerGasOverhead() (*big.Int, error) { + return _AutomationRegistryLogicB.Contract.GetPerSignerGasOverhead(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetSignerInfo(opts *bind.CallOpts, query common.Address) (GetSignerInfo, + + error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getSignerInfo", query) + + outstruct := new(GetSignerInfo) + if err != nil { + return *outstruct, err + } + + outstruct.Active = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.Index = *abi.ConvertType(out[1], new(uint8)).(*uint8) + + return *outstruct, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetSignerInfo(query common.Address) (GetSignerInfo, + + error) { + return _AutomationRegistryLogicB.Contract.GetSignerInfo(&_AutomationRegistryLogicB.CallOpts, query) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetSignerInfo(query common.Address) (GetSignerInfo, + + error) { + return _AutomationRegistryLogicB.Contract.GetSignerInfo(&_AutomationRegistryLogicB.CallOpts, query) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetState(opts *bind.CallOpts) (GetState, + + error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getState") + + outstruct := new(GetState) + if err != nil { + return *outstruct, err + } + + outstruct.State = *abi.ConvertType(out[0], new(AutomationRegistryBase22State)).(*AutomationRegistryBase22State) + outstruct.Config = *abi.ConvertType(out[1], new(AutomationRegistryBase22OnchainConfig)).(*AutomationRegistryBase22OnchainConfig) + outstruct.Signers = *abi.ConvertType(out[2], new([]common.Address)).(*[]common.Address) + outstruct.Transmitters = *abi.ConvertType(out[3], new([]common.Address)).(*[]common.Address) + outstruct.F = *abi.ConvertType(out[4], new(uint8)).(*uint8) + + return *outstruct, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetState() (GetState, + + error) { + return _AutomationRegistryLogicB.Contract.GetState(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetState() (GetState, + + error) { + return _AutomationRegistryLogicB.Contract.GetState(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetTransmitterInfo(opts *bind.CallOpts, query common.Address) (GetTransmitterInfo, + + error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getTransmitterInfo", query) + + outstruct := new(GetTransmitterInfo) + if err != nil { + return *outstruct, err + } + + outstruct.Active = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.Index = *abi.ConvertType(out[1], new(uint8)).(*uint8) + outstruct.Balance = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.LastCollected = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.Payee = *abi.ConvertType(out[4], new(common.Address)).(*common.Address) + + return *outstruct, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetTransmitterInfo(query common.Address) (GetTransmitterInfo, + + error) { + return _AutomationRegistryLogicB.Contract.GetTransmitterInfo(&_AutomationRegistryLogicB.CallOpts, query) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetTransmitterInfo(query common.Address) (GetTransmitterInfo, + + error) { + return _AutomationRegistryLogicB.Contract.GetTransmitterInfo(&_AutomationRegistryLogicB.CallOpts, query) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetTriggerType(opts *bind.CallOpts, upkeepId *big.Int) (uint8, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getTriggerType", upkeepId) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetTriggerType(upkeepId *big.Int) (uint8, error) { + return _AutomationRegistryLogicB.Contract.GetTriggerType(&_AutomationRegistryLogicB.CallOpts, upkeepId) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetTriggerType(upkeepId *big.Int) (uint8, error) { + return _AutomationRegistryLogicB.Contract.GetTriggerType(&_AutomationRegistryLogicB.CallOpts, upkeepId) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetUpkeep(opts *bind.CallOpts, id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getUpkeep", id) + + if err != nil { + return *new(AutomationRegistryBase22UpkeepInfo), err + } + + out0 := *abi.ConvertType(out[0], new(AutomationRegistryBase22UpkeepInfo)).(*AutomationRegistryBase22UpkeepInfo) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetUpkeep(id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) { + return _AutomationRegistryLogicB.Contract.GetUpkeep(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetUpkeep(id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) { + return _AutomationRegistryLogicB.Contract.GetUpkeep(&_AutomationRegistryLogicB.CallOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getUpkeepPrivilegeConfig", upkeepId) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return _AutomationRegistryLogicB.Contract.GetUpkeepPrivilegeConfig(&_AutomationRegistryLogicB.CallOpts, upkeepId) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return _AutomationRegistryLogicB.Contract.GetUpkeepPrivilegeConfig(&_AutomationRegistryLogicB.CallOpts, upkeepId) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "getUpkeepTriggerConfig", upkeepId) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) GetUpkeepTriggerConfig(upkeepId *big.Int) ([]byte, error) { + return _AutomationRegistryLogicB.Contract.GetUpkeepTriggerConfig(&_AutomationRegistryLogicB.CallOpts, upkeepId) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) GetUpkeepTriggerConfig(upkeepId *big.Int) ([]byte, error) { + return _AutomationRegistryLogicB.Contract.GetUpkeepTriggerConfig(&_AutomationRegistryLogicB.CallOpts, upkeepId) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) HasDedupKey(opts *bind.CallOpts, dedupKey [32]byte) (bool, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "hasDedupKey", dedupKey) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) HasDedupKey(dedupKey [32]byte) (bool, error) { + return _AutomationRegistryLogicB.Contract.HasDedupKey(&_AutomationRegistryLogicB.CallOpts, dedupKey) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) HasDedupKey(dedupKey [32]byte) (bool, error) { + return _AutomationRegistryLogicB.Contract.HasDedupKey(&_AutomationRegistryLogicB.CallOpts, dedupKey) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) Owner() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.Owner(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) Owner() (common.Address, error) { + return _AutomationRegistryLogicB.Contract.Owner(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) UpkeepTranscoderVersion(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "upkeepTranscoderVersion") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) UpkeepTranscoderVersion() (uint8, error) { + return _AutomationRegistryLogicB.Contract.UpkeepTranscoderVersion(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) UpkeepTranscoderVersion() (uint8, error) { + return _AutomationRegistryLogicB.Contract.UpkeepTranscoderVersion(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCaller) UpkeepVersion(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _AutomationRegistryLogicB.contract.Call(opts, &out, "upkeepVersion") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) UpkeepVersion() (uint8, error) { + return _AutomationRegistryLogicB.Contract.UpkeepVersion(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBCallerSession) UpkeepVersion() (uint8, error) { + return _AutomationRegistryLogicB.Contract.UpkeepVersion(&_AutomationRegistryLogicB.CallOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "acceptOwnership") +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AcceptOwnership(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AcceptOwnership(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) AcceptPayeeship(opts *bind.TransactOpts, transmitter common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "acceptPayeeship", transmitter) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) AcceptPayeeship(transmitter common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AcceptPayeeship(&_AutomationRegistryLogicB.TransactOpts, transmitter) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) AcceptPayeeship(transmitter common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AcceptPayeeship(&_AutomationRegistryLogicB.TransactOpts, transmitter) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) AcceptUpkeepAdmin(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "acceptUpkeepAdmin", id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) AcceptUpkeepAdmin(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AcceptUpkeepAdmin(&_AutomationRegistryLogicB.TransactOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) AcceptUpkeepAdmin(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.AcceptUpkeepAdmin(&_AutomationRegistryLogicB.TransactOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "pause") +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) Pause() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.Pause(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) Pause() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.Pause(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) PauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "pauseUpkeep", id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) PauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.PauseUpkeep(&_AutomationRegistryLogicB.TransactOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) PauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.PauseUpkeep(&_AutomationRegistryLogicB.TransactOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) RecoverFunds(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "recoverFunds") +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) RecoverFunds() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.RecoverFunds(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) RecoverFunds() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.RecoverFunds(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetAdminPrivilegeConfig(opts *bind.TransactOpts, admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setAdminPrivilegeConfig", admin, newPrivilegeConfig) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetAdminPrivilegeConfig(admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetAdminPrivilegeConfig(&_AutomationRegistryLogicB.TransactOpts, admin, newPrivilegeConfig) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetAdminPrivilegeConfig(admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetAdminPrivilegeConfig(&_AutomationRegistryLogicB.TransactOpts, admin, newPrivilegeConfig) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetPayees(opts *bind.TransactOpts, payees []common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setPayees", payees) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetPayees(payees []common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetPayees(&_AutomationRegistryLogicB.TransactOpts, payees) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetPayees(payees []common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetPayees(&_AutomationRegistryLogicB.TransactOpts, payees) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetPeerRegistryMigrationPermission(opts *bind.TransactOpts, peer common.Address, permission uint8) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setPeerRegistryMigrationPermission", peer, permission) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetPeerRegistryMigrationPermission(peer common.Address, permission uint8) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetPeerRegistryMigrationPermission(&_AutomationRegistryLogicB.TransactOpts, peer, permission) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetPeerRegistryMigrationPermission(peer common.Address, permission uint8) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetPeerRegistryMigrationPermission(&_AutomationRegistryLogicB.TransactOpts, peer, permission) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetUpkeepCheckData(opts *bind.TransactOpts, id *big.Int, newCheckData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setUpkeepCheckData", id, newCheckData) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetUpkeepCheckData(id *big.Int, newCheckData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepCheckData(&_AutomationRegistryLogicB.TransactOpts, id, newCheckData) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetUpkeepCheckData(id *big.Int, newCheckData []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepCheckData(&_AutomationRegistryLogicB.TransactOpts, id, newCheckData) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetUpkeepGasLimit(opts *bind.TransactOpts, id *big.Int, gasLimit uint32) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setUpkeepGasLimit", id, gasLimit) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetUpkeepGasLimit(id *big.Int, gasLimit uint32) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepGasLimit(&_AutomationRegistryLogicB.TransactOpts, id, gasLimit) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetUpkeepGasLimit(id *big.Int, gasLimit uint32) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepGasLimit(&_AutomationRegistryLogicB.TransactOpts, id, gasLimit) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetUpkeepOffchainConfig(opts *bind.TransactOpts, id *big.Int, config []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setUpkeepOffchainConfig", id, config) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetUpkeepOffchainConfig(id *big.Int, config []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepOffchainConfig(&_AutomationRegistryLogicB.TransactOpts, id, config) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetUpkeepOffchainConfig(id *big.Int, config []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepOffchainConfig(&_AutomationRegistryLogicB.TransactOpts, id, config) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) SetUpkeepPrivilegeConfig(opts *bind.TransactOpts, upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "setUpkeepPrivilegeConfig", upkeepId, newPrivilegeConfig) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) SetUpkeepPrivilegeConfig(upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepPrivilegeConfig(&_AutomationRegistryLogicB.TransactOpts, upkeepId, newPrivilegeConfig) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) SetUpkeepPrivilegeConfig(upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.SetUpkeepPrivilegeConfig(&_AutomationRegistryLogicB.TransactOpts, upkeepId, newPrivilegeConfig) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "transferOwnership", to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.TransferOwnership(&_AutomationRegistryLogicB.TransactOpts, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.TransferOwnership(&_AutomationRegistryLogicB.TransactOpts, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) TransferPayeeship(opts *bind.TransactOpts, transmitter common.Address, proposed common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "transferPayeeship", transmitter, proposed) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) TransferPayeeship(transmitter common.Address, proposed common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.TransferPayeeship(&_AutomationRegistryLogicB.TransactOpts, transmitter, proposed) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) TransferPayeeship(transmitter common.Address, proposed common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.TransferPayeeship(&_AutomationRegistryLogicB.TransactOpts, transmitter, proposed) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) TransferUpkeepAdmin(opts *bind.TransactOpts, id *big.Int, proposed common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "transferUpkeepAdmin", id, proposed) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) TransferUpkeepAdmin(id *big.Int, proposed common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.TransferUpkeepAdmin(&_AutomationRegistryLogicB.TransactOpts, id, proposed) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) TransferUpkeepAdmin(id *big.Int, proposed common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.TransferUpkeepAdmin(&_AutomationRegistryLogicB.TransactOpts, id, proposed) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "unpause") +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) Unpause() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.Unpause(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) Unpause() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.Unpause(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) UnpauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "unpauseUpkeep", id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) UnpauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.UnpauseUpkeep(&_AutomationRegistryLogicB.TransactOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) UnpauseUpkeep(id *big.Int) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.UnpauseUpkeep(&_AutomationRegistryLogicB.TransactOpts, id) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) WithdrawFunds(opts *bind.TransactOpts, id *big.Int, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "withdrawFunds", id, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) WithdrawFunds(id *big.Int, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.WithdrawFunds(&_AutomationRegistryLogicB.TransactOpts, id, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) WithdrawFunds(id *big.Int, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.WithdrawFunds(&_AutomationRegistryLogicB.TransactOpts, id, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) WithdrawOwnerFunds(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "withdrawOwnerFunds") +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) WithdrawOwnerFunds() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.WithdrawOwnerFunds(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) WithdrawOwnerFunds() (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.WithdrawOwnerFunds(&_AutomationRegistryLogicB.TransactOpts) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactor) WithdrawPayment(opts *bind.TransactOpts, from common.Address, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.contract.Transact(opts, "withdrawPayment", from, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBSession) WithdrawPayment(from common.Address, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.WithdrawPayment(&_AutomationRegistryLogicB.TransactOpts, from, to) +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBTransactorSession) WithdrawPayment(from common.Address, to common.Address) (*types.Transaction, error) { + return _AutomationRegistryLogicB.Contract.WithdrawPayment(&_AutomationRegistryLogicB.TransactOpts, from, to) +} + +type AutomationRegistryLogicBAdminPrivilegeConfigSetIterator struct { + Event *AutomationRegistryLogicBAdminPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBAdminPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBAdminPrivilegeConfigSet) + 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(AutomationRegistryLogicBAdminPrivilegeConfigSet) + 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 *AutomationRegistryLogicBAdminPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBAdminPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBAdminPrivilegeConfigSet struct { + Admin common.Address + PrivilegeConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*AutomationRegistryLogicBAdminPrivilegeConfigSetIterator, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBAdminPrivilegeConfigSetIterator{contract: _AutomationRegistryLogicB.contract, event: "AdminPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBAdminPrivilegeConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "AdminPrivilegeConfigSet", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseAdminPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicBAdminPrivilegeConfigSet, error) { + event := new(AutomationRegistryLogicBAdminPrivilegeConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "AdminPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBCancelledUpkeepReportIterator struct { + Event *AutomationRegistryLogicBCancelledUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBCancelledUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBCancelledUpkeepReport) + 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(AutomationRegistryLogicBCancelledUpkeepReport) + 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 *AutomationRegistryLogicBCancelledUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBCancelledUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBCancelledUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBCancelledUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBCancelledUpkeepReportIterator{contract: _AutomationRegistryLogicB.contract, event: "CancelledUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBCancelledUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBCancelledUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "CancelledUpkeepReport", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseCancelledUpkeepReport(log types.Log) (*AutomationRegistryLogicBCancelledUpkeepReport, error) { + event := new(AutomationRegistryLogicBCancelledUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "CancelledUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBDedupKeyAddedIterator struct { + Event *AutomationRegistryLogicBDedupKeyAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBDedupKeyAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBDedupKeyAdded) + 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(AutomationRegistryLogicBDedupKeyAdded) + 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 *AutomationRegistryLogicBDedupKeyAddedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBDedupKeyAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBDedupKeyAdded struct { + DedupKey [32]byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*AutomationRegistryLogicBDedupKeyAddedIterator, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBDedupKeyAddedIterator{contract: _AutomationRegistryLogicB.contract, event: "DedupKeyAdded", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBDedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBDedupKeyAdded) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "DedupKeyAdded", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseDedupKeyAdded(log types.Log) (*AutomationRegistryLogicBDedupKeyAdded, error) { + event := new(AutomationRegistryLogicBDedupKeyAdded) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "DedupKeyAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBFundsAddedIterator struct { + Event *AutomationRegistryLogicBFundsAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBFundsAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBFundsAdded) + 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(AutomationRegistryLogicBFundsAdded) + 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 *AutomationRegistryLogicBFundsAddedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBFundsAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBFundsAdded struct { + Id *big.Int + From common.Address + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*AutomationRegistryLogicBFundsAddedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBFundsAddedIterator{contract: _AutomationRegistryLogicB.contract, event: "FundsAdded", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBFundsAdded) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "FundsAdded", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseFundsAdded(log types.Log) (*AutomationRegistryLogicBFundsAdded, error) { + event := new(AutomationRegistryLogicBFundsAdded) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "FundsAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBFundsWithdrawnIterator struct { + Event *AutomationRegistryLogicBFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBFundsWithdrawn) + 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(AutomationRegistryLogicBFundsWithdrawn) + 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 *AutomationRegistryLogicBFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBFundsWithdrawn struct { + Id *big.Int + Amount *big.Int + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBFundsWithdrawnIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBFundsWithdrawnIterator{contract: _AutomationRegistryLogicB.contract, event: "FundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBFundsWithdrawn, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBFundsWithdrawn) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "FundsWithdrawn", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseFundsWithdrawn(log types.Log) (*AutomationRegistryLogicBFundsWithdrawn, error) { + event := new(AutomationRegistryLogicBFundsWithdrawn) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "FundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator struct { + Event *AutomationRegistryLogicBInsufficientFundsUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBInsufficientFundsUpkeepReport) + 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(AutomationRegistryLogicBInsufficientFundsUpkeepReport) + 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 *AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBInsufficientFundsUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator{contract: _AutomationRegistryLogicB.contract, event: "InsufficientFundsUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBInsufficientFundsUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseInsufficientFundsUpkeepReport(log types.Log) (*AutomationRegistryLogicBInsufficientFundsUpkeepReport, error) { + event := new(AutomationRegistryLogicBInsufficientFundsUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBOwnerFundsWithdrawnIterator struct { + Event *AutomationRegistryLogicBOwnerFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBOwnerFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBOwnerFundsWithdrawn) + 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(AutomationRegistryLogicBOwnerFundsWithdrawn) + 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 *AutomationRegistryLogicBOwnerFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBOwnerFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBOwnerFundsWithdrawn struct { + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*AutomationRegistryLogicBOwnerFundsWithdrawnIterator, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBOwnerFundsWithdrawnIterator{contract: _AutomationRegistryLogicB.contract, event: "OwnerFundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBOwnerFundsWithdrawn) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBOwnerFundsWithdrawn) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "OwnerFundsWithdrawn", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseOwnerFundsWithdrawn(log types.Log) (*AutomationRegistryLogicBOwnerFundsWithdrawn, error) { + event := new(AutomationRegistryLogicBOwnerFundsWithdrawn) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "OwnerFundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBOwnershipTransferRequestedIterator struct { + Event *AutomationRegistryLogicBOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBOwnershipTransferRequested) + 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(AutomationRegistryLogicBOwnershipTransferRequested) + 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 *AutomationRegistryLogicBOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicBOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBOwnershipTransferRequestedIterator{contract: _AutomationRegistryLogicB.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBOwnershipTransferRequested) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistryLogicBOwnershipTransferRequested, error) { + event := new(AutomationRegistryLogicBOwnershipTransferRequested) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBOwnershipTransferredIterator struct { + Event *AutomationRegistryLogicBOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBOwnershipTransferred) + 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(AutomationRegistryLogicBOwnershipTransferred) + 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 *AutomationRegistryLogicBOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicBOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBOwnershipTransferredIterator{contract: _AutomationRegistryLogicB.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBOwnershipTransferred) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "OwnershipTransferred", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseOwnershipTransferred(log types.Log) (*AutomationRegistryLogicBOwnershipTransferred, error) { + event := new(AutomationRegistryLogicBOwnershipTransferred) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBPausedIterator struct { + Event *AutomationRegistryLogicBPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBPaused) + 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(AutomationRegistryLogicBPaused) + 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 *AutomationRegistryLogicBPausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBPaused struct { + Account common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterPaused(opts *bind.FilterOpts) (*AutomationRegistryLogicBPausedIterator, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "Paused") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBPausedIterator{contract: _AutomationRegistryLogicB.contract, event: "Paused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPaused) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "Paused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBPaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "Paused", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParsePaused(log types.Log) (*AutomationRegistryLogicBPaused, error) { + event := new(AutomationRegistryLogicBPaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "Paused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBPayeesUpdatedIterator struct { + Event *AutomationRegistryLogicBPayeesUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBPayeesUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBPayeesUpdated) + 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(AutomationRegistryLogicBPayeesUpdated) + 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 *AutomationRegistryLogicBPayeesUpdatedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBPayeesUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBPayeesUpdated struct { + Transmitters []common.Address + Payees []common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterPayeesUpdated(opts *bind.FilterOpts) (*AutomationRegistryLogicBPayeesUpdatedIterator, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBPayeesUpdatedIterator{contract: _AutomationRegistryLogicB.contract, event: "PayeesUpdated", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPayeesUpdated) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBPayeesUpdated) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PayeesUpdated", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParsePayeesUpdated(log types.Log) (*AutomationRegistryLogicBPayeesUpdated, error) { + event := new(AutomationRegistryLogicBPayeesUpdated) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PayeesUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBPayeeshipTransferRequestedIterator struct { + Event *AutomationRegistryLogicBPayeeshipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBPayeeshipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBPayeeshipTransferRequested) + 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(AutomationRegistryLogicBPayeeshipTransferRequested) + 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 *AutomationRegistryLogicBPayeeshipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBPayeeshipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBPayeeshipTransferRequested struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicBPayeeshipTransferRequestedIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBPayeeshipTransferRequestedIterator{contract: _AutomationRegistryLogicB.contract, event: "PayeeshipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBPayeeshipTransferRequested) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PayeeshipTransferRequested", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParsePayeeshipTransferRequested(log types.Log) (*AutomationRegistryLogicBPayeeshipTransferRequested, error) { + event := new(AutomationRegistryLogicBPayeeshipTransferRequested) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PayeeshipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBPayeeshipTransferredIterator struct { + Event *AutomationRegistryLogicBPayeeshipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBPayeeshipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBPayeeshipTransferred) + 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(AutomationRegistryLogicBPayeeshipTransferred) + 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 *AutomationRegistryLogicBPayeeshipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBPayeeshipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBPayeeshipTransferred struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicBPayeeshipTransferredIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBPayeeshipTransferredIterator{contract: _AutomationRegistryLogicB.contract, event: "PayeeshipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBPayeeshipTransferred) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PayeeshipTransferred", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParsePayeeshipTransferred(log types.Log) (*AutomationRegistryLogicBPayeeshipTransferred, error) { + event := new(AutomationRegistryLogicBPayeeshipTransferred) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PayeeshipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBPaymentWithdrawnIterator struct { + Event *AutomationRegistryLogicBPaymentWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBPaymentWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBPaymentWithdrawn) + 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(AutomationRegistryLogicBPaymentWithdrawn) + 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 *AutomationRegistryLogicBPaymentWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBPaymentWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBPaymentWithdrawn struct { + Transmitter common.Address + Amount *big.Int + To common.Address + Payee common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*AutomationRegistryLogicBPaymentWithdrawnIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBPaymentWithdrawnIterator{contract: _AutomationRegistryLogicB.contract, event: "PaymentWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBPaymentWithdrawn) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PaymentWithdrawn", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParsePaymentWithdrawn(log types.Log) (*AutomationRegistryLogicBPaymentWithdrawn, error) { + event := new(AutomationRegistryLogicBPaymentWithdrawn) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "PaymentWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBReorgedUpkeepReportIterator struct { + Event *AutomationRegistryLogicBReorgedUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBReorgedUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBReorgedUpkeepReport) + 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(AutomationRegistryLogicBReorgedUpkeepReport) + 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 *AutomationRegistryLogicBReorgedUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBReorgedUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBReorgedUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBReorgedUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBReorgedUpkeepReportIterator{contract: _AutomationRegistryLogicB.contract, event: "ReorgedUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBReorgedUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "ReorgedUpkeepReport", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseReorgedUpkeepReport(log types.Log) (*AutomationRegistryLogicBReorgedUpkeepReport, error) { + event := new(AutomationRegistryLogicBReorgedUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "ReorgedUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBStaleUpkeepReportIterator struct { + Event *AutomationRegistryLogicBStaleUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBStaleUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBStaleUpkeepReport) + 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(AutomationRegistryLogicBStaleUpkeepReport) + 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 *AutomationRegistryLogicBStaleUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBStaleUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBStaleUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBStaleUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBStaleUpkeepReportIterator{contract: _AutomationRegistryLogicB.contract, event: "StaleUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBStaleUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBStaleUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "StaleUpkeepReport", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseStaleUpkeepReport(log types.Log) (*AutomationRegistryLogicBStaleUpkeepReport, error) { + event := new(AutomationRegistryLogicBStaleUpkeepReport) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "StaleUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUnpausedIterator struct { + Event *AutomationRegistryLogicBUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUnpaused) + 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(AutomationRegistryLogicBUnpaused) + 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 *AutomationRegistryLogicBUnpausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUnpaused struct { + Account common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUnpaused(opts *bind.FilterOpts) (*AutomationRegistryLogicBUnpausedIterator, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUnpausedIterator{contract: _AutomationRegistryLogicB.contract, event: "Unpaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUnpaused) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUnpaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "Unpaused", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUnpaused(log types.Log) (*AutomationRegistryLogicBUnpaused, error) { + event := new(AutomationRegistryLogicBUnpaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "Unpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator struct { + Event *AutomationRegistryLogicBUpkeepAdminTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepAdminTransferRequested) + 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(AutomationRegistryLogicBUpkeepAdminTransferRequested) + 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 *AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepAdminTransferRequested struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepAdminTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepAdminTransferRequested) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepAdminTransferRequested", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepAdminTransferRequested(log types.Log) (*AutomationRegistryLogicBUpkeepAdminTransferRequested, error) { + event := new(AutomationRegistryLogicBUpkeepAdminTransferRequested) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepAdminTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepAdminTransferredIterator struct { + Event *AutomationRegistryLogicBUpkeepAdminTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepAdminTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepAdminTransferred) + 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(AutomationRegistryLogicBUpkeepAdminTransferred) + 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 *AutomationRegistryLogicBUpkeepAdminTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepAdminTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepAdminTransferred struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicBUpkeepAdminTransferredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepAdminTransferredIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepAdminTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepAdminTransferred) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepAdminTransferred", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepAdminTransferred(log types.Log) (*AutomationRegistryLogicBUpkeepAdminTransferred, error) { + event := new(AutomationRegistryLogicBUpkeepAdminTransferred) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepAdminTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepCanceledIterator struct { + Event *AutomationRegistryLogicBUpkeepCanceled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepCanceledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepCanceled) + 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(AutomationRegistryLogicBUpkeepCanceled) + 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 *AutomationRegistryLogicBUpkeepCanceledIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepCanceled struct { + Id *big.Int + AtBlockHeight uint64 + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*AutomationRegistryLogicBUpkeepCanceledIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepCanceledIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepCanceled", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepCanceled) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepCanceled", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepCanceled(log types.Log) (*AutomationRegistryLogicBUpkeepCanceled, error) { + event := new(AutomationRegistryLogicBUpkeepCanceled) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepCheckDataSetIterator struct { + Event *AutomationRegistryLogicBUpkeepCheckDataSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepCheckDataSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepCheckDataSet) + 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(AutomationRegistryLogicBUpkeepCheckDataSet) + 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 *AutomationRegistryLogicBUpkeepCheckDataSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepCheckDataSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepCheckDataSet struct { + Id *big.Int + NewCheckData []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepCheckDataSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepCheckDataSetIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepCheckDataSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepCheckDataSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepCheckDataSet", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepCheckDataSet(log types.Log) (*AutomationRegistryLogicBUpkeepCheckDataSet, error) { + event := new(AutomationRegistryLogicBUpkeepCheckDataSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepCheckDataSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepGasLimitSetIterator struct { + Event *AutomationRegistryLogicBUpkeepGasLimitSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepGasLimitSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepGasLimitSet) + 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(AutomationRegistryLogicBUpkeepGasLimitSet) + 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 *AutomationRegistryLogicBUpkeepGasLimitSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepGasLimitSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepGasLimitSet struct { + Id *big.Int + GasLimit *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepGasLimitSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepGasLimitSetIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepGasLimitSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepGasLimitSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepGasLimitSet", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepGasLimitSet(log types.Log) (*AutomationRegistryLogicBUpkeepGasLimitSet, error) { + event := new(AutomationRegistryLogicBUpkeepGasLimitSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepGasLimitSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepMigratedIterator struct { + Event *AutomationRegistryLogicBUpkeepMigrated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepMigratedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepMigrated) + 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(AutomationRegistryLogicBUpkeepMigrated) + 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 *AutomationRegistryLogicBUpkeepMigratedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepMigratedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepMigrated struct { + Id *big.Int + RemainingBalance *big.Int + Destination common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepMigratedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepMigratedIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepMigrated", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepMigrated, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepMigrated) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepMigrated", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepMigrated(log types.Log) (*AutomationRegistryLogicBUpkeepMigrated, error) { + event := new(AutomationRegistryLogicBUpkeepMigrated) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepMigrated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepOffchainConfigSetIterator struct { + Event *AutomationRegistryLogicBUpkeepOffchainConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepOffchainConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepOffchainConfigSet) + 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(AutomationRegistryLogicBUpkeepOffchainConfigSet) + 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 *AutomationRegistryLogicBUpkeepOffchainConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepOffchainConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepOffchainConfigSet struct { + Id *big.Int + OffchainConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepOffchainConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepOffchainConfigSetIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepOffchainConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepOffchainConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepOffchainConfigSet", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepOffchainConfigSet(log types.Log) (*AutomationRegistryLogicBUpkeepOffchainConfigSet, error) { + event := new(AutomationRegistryLogicBUpkeepOffchainConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepOffchainConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepPausedIterator struct { + Event *AutomationRegistryLogicBUpkeepPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepPaused) + 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(AutomationRegistryLogicBUpkeepPaused) + 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 *AutomationRegistryLogicBUpkeepPausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepPaused struct { + Id *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepPausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepPausedIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepPaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepPaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepPaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepPaused", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepPaused(log types.Log) (*AutomationRegistryLogicBUpkeepPaused, error) { + event := new(AutomationRegistryLogicBUpkeepPaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepPaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepPerformedIterator struct { + Event *AutomationRegistryLogicBUpkeepPerformed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepPerformedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepPerformed) + 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(AutomationRegistryLogicBUpkeepPerformed) + 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 *AutomationRegistryLogicBUpkeepPerformedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepPerformedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepPerformed struct { + Id *big.Int + Success bool + TotalPayment *big.Int + GasUsed *big.Int + GasOverhead *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*AutomationRegistryLogicBUpkeepPerformedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepPerformedIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepPerformed", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepPerformed) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepPerformed", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepPerformed(log types.Log) (*AutomationRegistryLogicBUpkeepPerformed, error) { + event := new(AutomationRegistryLogicBUpkeepPerformed) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepPerformed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator struct { + Event *AutomationRegistryLogicBUpkeepPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepPrivilegeConfigSet) + 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(AutomationRegistryLogicBUpkeepPrivilegeConfigSet) + 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 *AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepPrivilegeConfigSet struct { + Id *big.Int + PrivilegeConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepPrivilegeConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicBUpkeepPrivilegeConfigSet, error) { + event := new(AutomationRegistryLogicBUpkeepPrivilegeConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepReceivedIterator struct { + Event *AutomationRegistryLogicBUpkeepReceived + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepReceivedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepReceived) + 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(AutomationRegistryLogicBUpkeepReceived) + 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 *AutomationRegistryLogicBUpkeepReceivedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepReceivedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepReceived struct { + Id *big.Int + StartingBalance *big.Int + ImportedFrom common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepReceivedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepReceivedIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepReceived", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepReceived, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepReceived) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepReceived", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepReceived(log types.Log) (*AutomationRegistryLogicBUpkeepReceived, error) { + event := new(AutomationRegistryLogicBUpkeepReceived) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepReceived", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepRegisteredIterator struct { + Event *AutomationRegistryLogicBUpkeepRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepRegistered) + 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(AutomationRegistryLogicBUpkeepRegistered) + 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 *AutomationRegistryLogicBUpkeepRegisteredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepRegistered struct { + Id *big.Int + PerformGas uint32 + Admin common.Address + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepRegisteredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepRegisteredIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepRegistered", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepRegistered, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepRegistered) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepRegistered", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepRegistered(log types.Log) (*AutomationRegistryLogicBUpkeepRegistered, error) { + event := new(AutomationRegistryLogicBUpkeepRegistered) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepTriggerConfigSetIterator struct { + Event *AutomationRegistryLogicBUpkeepTriggerConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepTriggerConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepTriggerConfigSet) + 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(AutomationRegistryLogicBUpkeepTriggerConfigSet) + 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 *AutomationRegistryLogicBUpkeepTriggerConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepTriggerConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepTriggerConfigSet struct { + Id *big.Int + TriggerConfig []byte + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepTriggerConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepTriggerConfigSetIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepTriggerConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepTriggerConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepTriggerConfigSet", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepTriggerConfigSet(log types.Log) (*AutomationRegistryLogicBUpkeepTriggerConfigSet, error) { + event := new(AutomationRegistryLogicBUpkeepTriggerConfigSet) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepTriggerConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryLogicBUpkeepUnpausedIterator struct { + Event *AutomationRegistryLogicBUpkeepUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryLogicBUpkeepUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryLogicBUpkeepUnpaused) + 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(AutomationRegistryLogicBUpkeepUnpaused) + 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 *AutomationRegistryLogicBUpkeepUnpausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryLogicBUpkeepUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryLogicBUpkeepUnpaused struct { + Id *big.Int + Raw types.Log +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepUnpausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.FilterLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryLogicBUpkeepUnpausedIterator{contract: _AutomationRegistryLogicB.contract, event: "UpkeepUnpaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepUnpaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistryLogicB.contract.WatchLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryLogicBUpkeepUnpaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepUnpaused", 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 (_AutomationRegistryLogicB *AutomationRegistryLogicBFilterer) ParseUpkeepUnpaused(log types.Log) (*AutomationRegistryLogicBUpkeepUnpaused, error) { + event := new(AutomationRegistryLogicBUpkeepUnpaused) + if err := _AutomationRegistryLogicB.contract.UnpackLog(event, "UpkeepUnpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetSignerInfo struct { + Active bool + Index uint8 +} +type GetState struct { + State AutomationRegistryBase22State + Config AutomationRegistryBase22OnchainConfig + Signers []common.Address + Transmitters []common.Address + F uint8 +} +type GetTransmitterInfo struct { + Active bool + Index uint8 + Balance *big.Int + LastCollected *big.Int + Payee common.Address +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicB) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _AutomationRegistryLogicB.abi.Events["AdminPrivilegeConfigSet"].ID: + return _AutomationRegistryLogicB.ParseAdminPrivilegeConfigSet(log) + case _AutomationRegistryLogicB.abi.Events["CancelledUpkeepReport"].ID: + return _AutomationRegistryLogicB.ParseCancelledUpkeepReport(log) + case _AutomationRegistryLogicB.abi.Events["DedupKeyAdded"].ID: + return _AutomationRegistryLogicB.ParseDedupKeyAdded(log) + case _AutomationRegistryLogicB.abi.Events["FundsAdded"].ID: + return _AutomationRegistryLogicB.ParseFundsAdded(log) + case _AutomationRegistryLogicB.abi.Events["FundsWithdrawn"].ID: + return _AutomationRegistryLogicB.ParseFundsWithdrawn(log) + case _AutomationRegistryLogicB.abi.Events["InsufficientFundsUpkeepReport"].ID: + return _AutomationRegistryLogicB.ParseInsufficientFundsUpkeepReport(log) + case _AutomationRegistryLogicB.abi.Events["OwnerFundsWithdrawn"].ID: + return _AutomationRegistryLogicB.ParseOwnerFundsWithdrawn(log) + case _AutomationRegistryLogicB.abi.Events["OwnershipTransferRequested"].ID: + return _AutomationRegistryLogicB.ParseOwnershipTransferRequested(log) + case _AutomationRegistryLogicB.abi.Events["OwnershipTransferred"].ID: + return _AutomationRegistryLogicB.ParseOwnershipTransferred(log) + case _AutomationRegistryLogicB.abi.Events["Paused"].ID: + return _AutomationRegistryLogicB.ParsePaused(log) + case _AutomationRegistryLogicB.abi.Events["PayeesUpdated"].ID: + return _AutomationRegistryLogicB.ParsePayeesUpdated(log) + case _AutomationRegistryLogicB.abi.Events["PayeeshipTransferRequested"].ID: + return _AutomationRegistryLogicB.ParsePayeeshipTransferRequested(log) + case _AutomationRegistryLogicB.abi.Events["PayeeshipTransferred"].ID: + return _AutomationRegistryLogicB.ParsePayeeshipTransferred(log) + case _AutomationRegistryLogicB.abi.Events["PaymentWithdrawn"].ID: + return _AutomationRegistryLogicB.ParsePaymentWithdrawn(log) + case _AutomationRegistryLogicB.abi.Events["ReorgedUpkeepReport"].ID: + return _AutomationRegistryLogicB.ParseReorgedUpkeepReport(log) + case _AutomationRegistryLogicB.abi.Events["StaleUpkeepReport"].ID: + return _AutomationRegistryLogicB.ParseStaleUpkeepReport(log) + case _AutomationRegistryLogicB.abi.Events["Unpaused"].ID: + return _AutomationRegistryLogicB.ParseUnpaused(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepAdminTransferRequested"].ID: + return _AutomationRegistryLogicB.ParseUpkeepAdminTransferRequested(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepAdminTransferred"].ID: + return _AutomationRegistryLogicB.ParseUpkeepAdminTransferred(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepCanceled"].ID: + return _AutomationRegistryLogicB.ParseUpkeepCanceled(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepCheckDataSet"].ID: + return _AutomationRegistryLogicB.ParseUpkeepCheckDataSet(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepGasLimitSet"].ID: + return _AutomationRegistryLogicB.ParseUpkeepGasLimitSet(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepMigrated"].ID: + return _AutomationRegistryLogicB.ParseUpkeepMigrated(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepOffchainConfigSet"].ID: + return _AutomationRegistryLogicB.ParseUpkeepOffchainConfigSet(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepPaused"].ID: + return _AutomationRegistryLogicB.ParseUpkeepPaused(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepPerformed"].ID: + return _AutomationRegistryLogicB.ParseUpkeepPerformed(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepPrivilegeConfigSet"].ID: + return _AutomationRegistryLogicB.ParseUpkeepPrivilegeConfigSet(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepReceived"].ID: + return _AutomationRegistryLogicB.ParseUpkeepReceived(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepRegistered"].ID: + return _AutomationRegistryLogicB.ParseUpkeepRegistered(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepTriggerConfigSet"].ID: + return _AutomationRegistryLogicB.ParseUpkeepTriggerConfigSet(log) + case _AutomationRegistryLogicB.abi.Events["UpkeepUnpaused"].ID: + return _AutomationRegistryLogicB.ParseUpkeepUnpaused(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (AutomationRegistryLogicBAdminPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d2") +} + +func (AutomationRegistryLogicBCancelledUpkeepReport) Topic() common.Hash { + return common.HexToHash("0xc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636") +} + +func (AutomationRegistryLogicBDedupKeyAdded) Topic() common.Hash { + return common.HexToHash("0xa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f2") +} + +func (AutomationRegistryLogicBFundsAdded) Topic() common.Hash { + return common.HexToHash("0xafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa734891506203") +} + +func (AutomationRegistryLogicBFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0xf3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318") +} + +func (AutomationRegistryLogicBInsufficientFundsUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e02") +} + +func (AutomationRegistryLogicBOwnerFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0x1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f1") +} + +func (AutomationRegistryLogicBOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (AutomationRegistryLogicBOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (AutomationRegistryLogicBPaused) Topic() common.Hash { + return common.HexToHash("0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258") +} + +func (AutomationRegistryLogicBPayeesUpdated) Topic() common.Hash { + return common.HexToHash("0xa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725") +} + +func (AutomationRegistryLogicBPayeeshipTransferRequested) Topic() common.Hash { + return common.HexToHash("0x84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367") +} + +func (AutomationRegistryLogicBPayeeshipTransferred) Topic() common.Hash { + return common.HexToHash("0x78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b3") +} + +func (AutomationRegistryLogicBPaymentWithdrawn) Topic() common.Hash { + return common.HexToHash("0x9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f40698") +} + +func (AutomationRegistryLogicBReorgedUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301") +} + +func (AutomationRegistryLogicBStaleUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8") +} + +func (AutomationRegistryLogicBUnpaused) Topic() common.Hash { + return common.HexToHash("0x5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa") +} + +func (AutomationRegistryLogicBUpkeepAdminTransferRequested) Topic() common.Hash { + return common.HexToHash("0xb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b35") +} + +func (AutomationRegistryLogicBUpkeepAdminTransferred) Topic() common.Hash { + return common.HexToHash("0x5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c") +} + +func (AutomationRegistryLogicBUpkeepCanceled) Topic() common.Hash { + return common.HexToHash("0x91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f791181") +} + +func (AutomationRegistryLogicBUpkeepCheckDataSet) Topic() common.Hash { + return common.HexToHash("0xcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d") +} + +func (AutomationRegistryLogicBUpkeepGasLimitSet) Topic() common.Hash { + return common.HexToHash("0xc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c") +} + +func (AutomationRegistryLogicBUpkeepMigrated) Topic() common.Hash { + return common.HexToHash("0xb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff") +} + +func (AutomationRegistryLogicBUpkeepOffchainConfigSet) Topic() common.Hash { + return common.HexToHash("0x3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf4850") +} + +func (AutomationRegistryLogicBUpkeepPaused) Topic() common.Hash { + return common.HexToHash("0x8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f") +} + +func (AutomationRegistryLogicBUpkeepPerformed) Topic() common.Hash { + return common.HexToHash("0xad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b") +} + +func (AutomationRegistryLogicBUpkeepPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae7769") +} + +func (AutomationRegistryLogicBUpkeepReceived) Topic() common.Hash { + return common.HexToHash("0x74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a71") +} + +func (AutomationRegistryLogicBUpkeepRegistered) Topic() common.Hash { + return common.HexToHash("0xbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d012") +} + +func (AutomationRegistryLogicBUpkeepTriggerConfigSet) Topic() common.Hash { + return common.HexToHash("0x2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664") +} + +func (AutomationRegistryLogicBUpkeepUnpaused) Topic() common.Hash { + return common.HexToHash("0x7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a47456") +} + +func (_AutomationRegistryLogicB *AutomationRegistryLogicB) Address() common.Address { + return _AutomationRegistryLogicB.address +} + +type AutomationRegistryLogicBInterface interface { + GetActiveUpkeepIDs(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) + + GetAdminPrivilegeConfig(opts *bind.CallOpts, admin common.Address) ([]byte, error) + + GetAutomationForwarderLogic(opts *bind.CallOpts) (common.Address, error) + + GetBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) + + GetCancellationDelay(opts *bind.CallOpts) (*big.Int, error) + + GetConditionalGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetFastGasFeedAddress(opts *bind.CallOpts) (common.Address, error) + + GetForwarder(opts *bind.CallOpts, upkeepID *big.Int) (common.Address, error) + + GetLinkAddress(opts *bind.CallOpts) (common.Address, error) + + GetLinkNativeFeedAddress(opts *bind.CallOpts) (common.Address, error) + + GetLogGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetMaxPaymentForGas(opts *bind.CallOpts, triggerType uint8, gasLimit uint32) (*big.Int, error) + + GetMinBalance(opts *bind.CallOpts, id *big.Int) (*big.Int, error) + + GetMinBalanceForUpkeep(opts *bind.CallOpts, id *big.Int) (*big.Int, error) + + GetMode(opts *bind.CallOpts) (uint8, error) + + GetPeerRegistryMigrationPermission(opts *bind.CallOpts, peer common.Address) (uint8, error) + + GetPerPerformByteGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetPerSignerGasOverhead(opts *bind.CallOpts) (*big.Int, error) + + GetSignerInfo(opts *bind.CallOpts, query common.Address) (GetSignerInfo, + + error) + + GetState(opts *bind.CallOpts) (GetState, + + error) + + GetTransmitterInfo(opts *bind.CallOpts, query common.Address) (GetTransmitterInfo, + + error) + + GetTriggerType(opts *bind.CallOpts, upkeepId *big.Int) (uint8, error) + + GetUpkeep(opts *bind.CallOpts, id *big.Int) (AutomationRegistryBase22UpkeepInfo, error) + + GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + + GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + + HasDedupKey(opts *bind.CallOpts, dedupKey [32]byte) (bool, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + UpkeepTranscoderVersion(opts *bind.CallOpts) (uint8, error) + + UpkeepVersion(opts *bind.CallOpts) (uint8, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AcceptPayeeship(opts *bind.TransactOpts, transmitter common.Address) (*types.Transaction, error) + + AcceptUpkeepAdmin(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + Pause(opts *bind.TransactOpts) (*types.Transaction, error) + + PauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + RecoverFunds(opts *bind.TransactOpts) (*types.Transaction, error) + + SetAdminPrivilegeConfig(opts *bind.TransactOpts, admin common.Address, newPrivilegeConfig []byte) (*types.Transaction, error) + + SetPayees(opts *bind.TransactOpts, payees []common.Address) (*types.Transaction, error) + + SetPeerRegistryMigrationPermission(opts *bind.TransactOpts, peer common.Address, permission uint8) (*types.Transaction, error) + + SetUpkeepCheckData(opts *bind.TransactOpts, id *big.Int, newCheckData []byte) (*types.Transaction, error) + + SetUpkeepGasLimit(opts *bind.TransactOpts, id *big.Int, gasLimit uint32) (*types.Transaction, error) + + SetUpkeepOffchainConfig(opts *bind.TransactOpts, id *big.Int, config []byte) (*types.Transaction, error) + + SetUpkeepPrivilegeConfig(opts *bind.TransactOpts, upkeepId *big.Int, newPrivilegeConfig []byte) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + TransferPayeeship(opts *bind.TransactOpts, transmitter common.Address, proposed common.Address) (*types.Transaction, error) + + TransferUpkeepAdmin(opts *bind.TransactOpts, id *big.Int, proposed common.Address) (*types.Transaction, error) + + Unpause(opts *bind.TransactOpts) (*types.Transaction, error) + + UnpauseUpkeep(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) + + WithdrawFunds(opts *bind.TransactOpts, id *big.Int, to common.Address) (*types.Transaction, error) + + WithdrawOwnerFunds(opts *bind.TransactOpts) (*types.Transaction, error) + + WithdrawPayment(opts *bind.TransactOpts, from common.Address, to common.Address) (*types.Transaction, error) + + FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*AutomationRegistryLogicBAdminPrivilegeConfigSetIterator, error) + + WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) + + ParseAdminPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicBAdminPrivilegeConfigSet, error) + + FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBCancelledUpkeepReportIterator, error) + + WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBCancelledUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseCancelledUpkeepReport(log types.Log) (*AutomationRegistryLogicBCancelledUpkeepReport, error) + + FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*AutomationRegistryLogicBDedupKeyAddedIterator, error) + + WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBDedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) + + ParseDedupKeyAdded(log types.Log) (*AutomationRegistryLogicBDedupKeyAdded, error) + + FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*AutomationRegistryLogicBFundsAddedIterator, error) + + WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) + + ParseFundsAdded(log types.Log) (*AutomationRegistryLogicBFundsAdded, error) + + FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBFundsWithdrawnIterator, error) + + WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBFundsWithdrawn, id []*big.Int) (event.Subscription, error) + + ParseFundsWithdrawn(log types.Log) (*AutomationRegistryLogicBFundsWithdrawn, error) + + FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBInsufficientFundsUpkeepReportIterator, error) + + WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseInsufficientFundsUpkeepReport(log types.Log) (*AutomationRegistryLogicBInsufficientFundsUpkeepReport, error) + + FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*AutomationRegistryLogicBOwnerFundsWithdrawnIterator, error) + + WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBOwnerFundsWithdrawn) (event.Subscription, error) + + ParseOwnerFundsWithdrawn(log types.Log) (*AutomationRegistryLogicBOwnerFundsWithdrawn, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicBOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistryLogicBOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryLogicBOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*AutomationRegistryLogicBOwnershipTransferred, error) + + FilterPaused(opts *bind.FilterOpts) (*AutomationRegistryLogicBPausedIterator, error) + + WatchPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPaused) (event.Subscription, error) + + ParsePaused(log types.Log) (*AutomationRegistryLogicBPaused, error) + + FilterPayeesUpdated(opts *bind.FilterOpts) (*AutomationRegistryLogicBPayeesUpdatedIterator, error) + + WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPayeesUpdated) (event.Subscription, error) + + ParsePayeesUpdated(log types.Log) (*AutomationRegistryLogicBPayeesUpdated, error) + + FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicBPayeeshipTransferRequestedIterator, error) + + WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferRequested(log types.Log) (*AutomationRegistryLogicBPayeeshipTransferRequested, error) + + FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryLogicBPayeeshipTransferredIterator, error) + + WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferred(log types.Log) (*AutomationRegistryLogicBPayeeshipTransferred, error) + + FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*AutomationRegistryLogicBPaymentWithdrawnIterator, error) + + WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) + + ParsePaymentWithdrawn(log types.Log) (*AutomationRegistryLogicBPaymentWithdrawn, error) + + FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBReorgedUpkeepReportIterator, error) + + WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseReorgedUpkeepReport(log types.Log) (*AutomationRegistryLogicBReorgedUpkeepReport, error) + + FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBStaleUpkeepReportIterator, error) + + WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBStaleUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseStaleUpkeepReport(log types.Log) (*AutomationRegistryLogicBStaleUpkeepReport, error) + + FilterUnpaused(opts *bind.FilterOpts) (*AutomationRegistryLogicBUnpausedIterator, error) + + WatchUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUnpaused) (event.Subscription, error) + + ParseUnpaused(log types.Log) (*AutomationRegistryLogicBUnpaused, error) + + FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicBUpkeepAdminTransferRequestedIterator, error) + + WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferRequested(log types.Log) (*AutomationRegistryLogicBUpkeepAdminTransferRequested, error) + + FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryLogicBUpkeepAdminTransferredIterator, error) + + WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferred(log types.Log) (*AutomationRegistryLogicBUpkeepAdminTransferred, error) + + FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*AutomationRegistryLogicBUpkeepCanceledIterator, error) + + WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) + + ParseUpkeepCanceled(log types.Log) (*AutomationRegistryLogicBUpkeepCanceled, error) + + FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepCheckDataSetIterator, error) + + WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepCheckDataSet(log types.Log) (*AutomationRegistryLogicBUpkeepCheckDataSet, error) + + FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepGasLimitSetIterator, error) + + WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepGasLimitSet(log types.Log) (*AutomationRegistryLogicBUpkeepGasLimitSet, error) + + FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepMigratedIterator, error) + + WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepMigrated, id []*big.Int) (event.Subscription, error) + + ParseUpkeepMigrated(log types.Log) (*AutomationRegistryLogicBUpkeepMigrated, error) + + FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepOffchainConfigSetIterator, error) + + WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepOffchainConfigSet(log types.Log) (*AutomationRegistryLogicBUpkeepOffchainConfigSet, error) + + FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepPausedIterator, error) + + WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepPaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPaused(log types.Log) (*AutomationRegistryLogicBUpkeepPaused, error) + + FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*AutomationRegistryLogicBUpkeepPerformedIterator, error) + + WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) + + ParseUpkeepPerformed(log types.Log) (*AutomationRegistryLogicBUpkeepPerformed, error) + + FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepPrivilegeConfigSetIterator, error) + + WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPrivilegeConfigSet(log types.Log) (*AutomationRegistryLogicBUpkeepPrivilegeConfigSet, error) + + FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepReceivedIterator, error) + + WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepReceived, id []*big.Int) (event.Subscription, error) + + ParseUpkeepReceived(log types.Log) (*AutomationRegistryLogicBUpkeepReceived, error) + + FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepRegisteredIterator, error) + + WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepRegistered, id []*big.Int) (event.Subscription, error) + + ParseUpkeepRegistered(log types.Log) (*AutomationRegistryLogicBUpkeepRegistered, error) + + FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepTriggerConfigSetIterator, error) + + WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepTriggerConfigSet(log types.Log) (*AutomationRegistryLogicBUpkeepTriggerConfigSet, error) + + FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryLogicBUpkeepUnpausedIterator, error) + + WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryLogicBUpkeepUnpaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepUnpaused(log types.Log) (*AutomationRegistryLogicBUpkeepUnpaused, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go b/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go new file mode 100644 index 00000000000..89da6bc9584 --- /dev/null +++ b/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go @@ -0,0 +1,5169 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package keeper_registry_wrapper_2_2 + +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 AutomationRegistryBase22OnchainConfig struct { + PaymentPremiumPPB uint32 + FlatFeeMicroLink uint32 + CheckGasLimit uint32 + StalenessSeconds *big.Int + GasCeilingMultiplier uint16 + MinUpkeepSpend *big.Int + MaxPerformGas uint32 + MaxCheckDataSize uint32 + MaxPerformDataSize uint32 + MaxRevertDataSize uint32 + FallbackGasPrice *big.Int + FallbackLinkPrice *big.Int + Transcoder common.Address + Registrars []common.Address + UpkeepPrivilegeManager common.Address +} + +var AutomationRegistryMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicB2_2\",\"name\":\"logicA\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"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\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6101406040523480156200001257600080fd5b50604051620054c9380380620054c98339810160408190526200003591620003df565b80816001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b919062000406565b826001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003df565b836001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003df565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003df565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003df565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b9816200031b565b505050846002811115620002d157620002d162000429565b60e0816002811115620002e857620002e862000429565b9052506001600160a01b0393841660805291831660a052821660c0528116610100529190911661012052506200043f9050565b336001600160a01b03821603620003755760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003dc57600080fd5b50565b600060208284031215620003f257600080fd5b8151620003ff81620003c6565b9392505050565b6000602082840312156200041957600080fd5b815160038110620003ff57600080fd5b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e0516101005161012051615028620004a16000396000818160d6015261016f01526000505060008181612eb60152818161321f015281816133b20152613a4901526000505060005050600061043b01526150286000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063aed2e92911610081578063e29b753c1161005b578063e29b753c146102e8578063e3d0e712146102fb578063f2fde38b1461030e576100d4565b8063aed2e92914610262578063afcb95d71461028c578063b1dc65a4146102d5576100d4565b806381ff7048116100b257806381ff7048146101bc5780638da5cb5b14610231578063a4c0ed361461024f576100d4565b8063181f5a771461011b578063349e8cca1461016d57806379ba5097146101b4575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610114573d6000f35b3d6000fd5b005b6101576040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e322e30000000000000000081525081565b6040516101649190613cc8565b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610164565b610119610321565b61020e60145460115463ffffffff780100000000000000000000000000000000000000000000000083048116937c01000000000000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610164565b60005473ffffffffffffffffffffffffffffffffffffffff1661018f565b61011961025d366004613d4a565b610423565b610275610270366004613da6565b61063f565b604080519215158352602083019190915201610164565b601154601254604080516000815260208101939093527c010000000000000000000000000000000000000000000000000000000090910463ffffffff1690820152606001610164565b6101196102e3366004613e37565b6107a7565b6101196102f63660046142b2565b6112ea565b61011961030936600461437f565b6121e6565b61011961031c36600461440e565b61220f565b60015473ffffffffffffffffffffffffffffffffffffffff1633146103a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610492576040517fc8bad78d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081146104cc576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006104da8284018461442b565b60008181526004602052604090205490915065010000000000900463ffffffff90811614610534576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090206001015461056f9085906c0100000000000000000000000090046bffffffffffffffffffffffff16614473565b600082815260046020526040902060010180546bffffffffffffffffffffffff929092166c01000000000000000000000000027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff9092169190911790556018546105da908590614498565b6018556040516bffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff86169082907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a35050505050565b60008061064a612223565b6012546e010000000000000000000000000000900460ff1615610699576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff908116838601819052650100000000008304821684880152690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606084018190526001909401546bffffffffffffffffffffffff80821660808601526c0100000000000000000000000082041660a0850152780100000000000000000000000000000000000000000000000090041660c08301528451601f8901859004850281018501909552878552909361079893899089908190840183828082843760009201919091525061225d92505050565b9093509150505b935093915050565b60005a604080516101208101825260125460ff808216835261010080830463ffffffff90811660208601526501000000000084048116958501959095526901000000000000000000830462ffffff1660608501526c01000000000000000000000000830461ffff1660808501526e0100000000000000000000000000008304821615801560a08601526f010000000000000000000000000000008404909216151560c085015270010000000000000000000000000000000083046bffffffffffffffffffffffff1660e08501527c0100000000000000000000000000000000000000000000000000000000909204909316908201529192506108d5576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff1661091e576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011548a351461095a576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516109679060016144da565b60ff16861415806109785750858414155b156109af576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109bf8a8a8a8a8a8a8a8a612468565b60006109cb8a8a6126d1565b9050600081604001515167ffffffffffffffff8111156109ed576109ed613eee565b604051908082528060200260200182016040528015610ab157816020015b604080516101e0810182526000610100820181815261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610a0b5790505b5090506000805b836040015151811015610efa576004600085604001518381518110610adf57610adf6144ab565b6020908102919091018101518252818101929092526040908101600020815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c08201528351849083908110610bc457610bc46144ab565b602002602001015160000181905250610bf984604001518281518110610bec57610bec6144ab565b602002602001015161278c565b838281518110610c0b57610c0b6144ab565b6020026020010151608001906001811115610c2857610c286144f3565b90816001811115610c3b57610c3b6144f3565b81525050610caf85848381518110610c5557610c556144ab565b60200260200101516080015186606001518481518110610c7757610c776144ab565b60200260200101518760a001518581518110610c9557610c956144ab565b602002602001015151886000015189602001516001612837565b838281518110610cc157610cc16144ab565b6020026020010151604001906bffffffffffffffffffffffff1690816bffffffffffffffffffffffff1681525050610d4d84604001518281518110610d0857610d086144ab565b602002602001015185608001518381518110610d2657610d266144ab565b6020026020010151858481518110610d4057610d406144ab565b6020026020010151612882565b848381518110610d5f57610d5f6144ab565b6020026020010151602001858481518110610d7c57610d7c6144ab565b602002602001015160e0018281525082151515158152505050828181518110610da757610da76144ab565b60200260200101516020015115610dca57610dc3600183614522565b9150610dcf565b610ee8565b610e35838281518110610de457610de46144ab565b6020026020010151600001516060015185606001518381518110610e0a57610e0a6144ab565b60200260200101518660a001518481518110610e2857610e286144ab565b602002602001015161225d565b848381518110610e4757610e476144ab565b6020026020010151606001858481518110610e6457610e646144ab565b602002602001015160a0018281525082151515158152505050828181518110610e8f57610e8f6144ab565b602002602001015160a0015186610ea6919061453d565b9550610ee884604001518281518110610ec157610ec16144ab565b6020026020010151848381518110610edb57610edb6144ab565b6020026020010151612a00565b80610ef281614550565b915050610ab8565b508061ffff16600003610f115750505050506112e0565b8351610f1e9060016144da565b610f2d9060ff1661044c614588565b616b6c610f3b8d6010614588565b5a610f46908961453d565b610f509190614498565b610f5a9190614498565b610f649190614498565b9450611b58610f7761ffff8316876145f4565b610f819190614498565b945060008060008060005b87604001515181101561118257868181518110610fab57610fab6144ab565b60200260200101516020015115611170576110078a888381518110610fd257610fd26144ab565b6020026020010151608001518a60a001518481518110610ff457610ff46144ab565b6020026020010151518c60000151612b12565b878281518110611019576110196144ab565b602002602001015160c00181815250506110758989604001518381518110611043576110436144ab565b602002602001015189848151811061105d5761105d6144ab565b60200260200101518b600001518c602001518b612b32565b90935091506110848285614473565b93506110908386614473565b94508681815181106110a4576110a46144ab565b6020026020010151606001511515886040015182815181106110c8576110c86144ab565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b84866110fd9190614473565b8a858151811061110f5761110f6144ab565b602002602001015160a001518b868151811061112d5761112d6144ab565b602002602001015160c001518d60800151878151811061114f5761114f6144ab565b60200260200101516040516111679493929190614608565b60405180910390a35b8061117a81614550565b915050610f8c565b5050336000908152600b6020526040902080548492506002906111ba9084906201000090046bffffffffffffffffffffffff16614473565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080601260000160108282829054906101000a90046bffffffffffffffffffffffff166112149190614473565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060008f600160038110611257576112576144ab565b602002013560001c9050600060088264ffffffffff16901c905087610100015163ffffffff168163ffffffff1611156112d657601280547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff8416021790555b5050505050505050505b5050505050505050565b6112f2612c25565b601f8651111561132e576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff1660000361136b576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114158061138a5750611382846003614645565b60ff16865111155b156113c1576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e547001000000000000000000000000000000009091046bffffffffffffffffffffffff169060005b816bffffffffffffffffffffffff1681101561145657611443600e828154811061141a5761141a6144ab565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff168484612ca6565b508061144e81614550565b9150506113ee565b5060008060005b836bffffffffffffffffffffffff1681101561155f57600d8181548110611486576114866144ab565b600091825260209091200154600e805473ffffffffffffffffffffffffffffffffffffffff909216945090829081106114c1576114c16144ab565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff8681168452600c8352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559116808452600b90925290912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905591508061155781614550565b91505061145d565b5061156c600d6000613b9d565b611578600e6000613b9d565b604080516080810182526000808252602082018190529181018290526060810182905290805b8c518110156119e157600c60008e83815181106115bd576115bd6144ab565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615611628576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168d8281518110611652576116526144ab565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036116a7576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008f84815181106116d8576116d86144ab565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558b518c9082908110611780576117806144ab565b60200260200101519150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036117f0576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e010000000000000000000000000000900490921660608301529093506118ab576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001835260ff80821660208086019182526bffffffffffffffffffffffff808b166060880190815273ffffffffffffffffffffffffffffffffffffffff87166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090951694909417179190911692909217919091179055806119d981614550565b91505061159e565b50508a516119f79150600d9060208d0190613bbb565b508851611a0b90600e9060208c0190613bbb565b506040518061012001604052808960ff168152602001886000015163ffffffff168152602001886020015163ffffffff168152602001886060015162ffffff168152602001886080015161ffff1681526020016012600001600e9054906101000a900460ff16151581526020016012600001600f9054906101000a900460ff1615158152602001856bffffffffffffffffffffffff168152602001600063ffffffff16815250601260008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160056101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160096101000a81548162ffffff021916908362ffffff160217905550608082015181600001600c6101000a81548161ffff021916908361ffff16021790555060a082015181600001600e6101000a81548160ff02191690831515021790555060c082015181600001600f6101000a81548160ff02191690831515021790555060e08201518160000160106101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061010082015181600001601c6101000a81548163ffffffff021916908363ffffffff1602179055509050506040518061018001604052808860a001516bffffffffffffffffffffffff16815260200188610180015173ffffffffffffffffffffffffffffffffffffffff168152602001601360010160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001886040015163ffffffff1681526020018860c0015163ffffffff168152602001601360010160149054906101000a900463ffffffff1663ffffffff168152602001601360010160189054906101000a900463ffffffff1663ffffffff1681526020016013600101601c9054906101000a900463ffffffff1663ffffffff1681526020018860e0015163ffffffff16815260200188610100015163ffffffff16815260200188610120015163ffffffff168152602001886101c0015173ffffffffffffffffffffffffffffffffffffffff16815250601360008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550606082015181600101600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160106101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548163ffffffff021916908363ffffffff1602179055506101208201518160020160046101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160086101000a81548163ffffffff021916908363ffffffff16021790555061016082015181600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555090505086610140015160168190555086610160015160178190555060006013600101601c9054906101000a900463ffffffff169050611fcd612eb0565b601480547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff9384160217808255600192601891612048918591780100000000000000000000000000000000000000000000000090041661466e565b92506101000a81548163ffffffff021916908363ffffffff16021790555060008860405160200161207991906146dc565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181529190526014549091506120e290469030907801000000000000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f612f65565b60115560005b6120f2600961300f565b8110156121225761210f612107600983613019565b600990613025565b508061211a81614550565b9150506120e8565b5060005b896101a0015151811015612179576121668a6101a00151828151811061214e5761214e6144ab565b6020026020010151600961304790919063ffffffff16565b508061217181614550565b915050612126565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0582601154601360010160189054906101000a900463ffffffff168f8f8f878f8f6040516121d099989796959493929190614840565b60405180910390a1505050505050505050505050565b612207868686868060200190518101906122009190614971565b86866112ea565b505050505050565b612217612c25565b61222081613069565b50565b321561225b576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60125460009081906f01000000000000000000000000000000900460ff16156122b2576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff166f010000000000000000000000000000001790556040517f4585e33b000000000000000000000000000000000000000000000000000000009061231f908590602401613cc8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d16906123f29087908790600401614acb565b60408051808303816000875af1158015612410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124349190614ae4565b601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff16905590969095509350505050565b6000878760405161247a929190614b17565b604051908190038120612491918b90602001614b27565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b88811015612668576001858783602081106124fd576124fd6144ab565b61250a91901a601b6144da565b8c8c8581811061251c5761251c6144ab565b905060200201358b8b86818110612535576125356144ab565b9050602002013560405160008152602001604052604051612572949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612594573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612642576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b84019350808061266090614550565b9150506124e0565b50827e010101010101010101010101010101010101010101010101010101010101018416146126c3576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b61270a6040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b600061271883850185614c18565b604081015151606082015151919250908114158061273b57508082608001515114155b8061274b5750808260a001515114155b15612782576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090505b92915050565b6000818160045b600f811015612819577fff0000000000000000000000000000000000000000000000000000000000000082168382602081106127d1576127d16144ab565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461280757506000949350505050565b8061281181614550565b915050612793565b5081600f1a600181111561282f5761282f6144f3565b949350505050565b60008061284988878b6000015161315e565b90506000806128648b8a63ffffffff16858a8a60018b6131ea565b90925090506128738183614473565b9b9a5050505050505050505050565b60008080808460800151600181111561289d5761289d6144f3565b036128c1576128ad868686613643565b6128bc5760009250905061079f565b612937565b6001846080015160018111156128d9576128d96144f3565b036129055760006128ea8787613737565b92509050806128ff575060009250905061079f565b50612937565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61293f612eb0565b84516040015163ffffffff161161299357857fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636866040516129809190613cc8565b60405180910390a260009250905061079f565b83604001516bffffffffffffffffffffffff16846000015160a001516bffffffffffffffffffffffff1610156129f357857f377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e02866040516129809190613cc8565b6001969095509350505050565b600081608001516001811115612a1857612a186144f3565b03612a8a57612a25612eb0565b6000838152600460205260409020600101805463ffffffff929092167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9092169190911790555050565b600181608001516001811115612aa257612aa26144f3565b03612b0e5760e08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b5050565b6000612b1f84848461315e565b90508085101561282f5750929392505050565b600080612b4d888760a001518860c0015188888860016131ea565b90925090506000612b5e8284614473565b600089815260046020526040902060010180549192508291600c90612ba29084906c0100000000000000000000000090046bffffffffffffffffffffffff16614d05565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008a815260046020526040812060010180548594509092612beb91859116614473565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461225b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161039e565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015290612ea2576000816060015185612d3e9190614d05565b90506000612d4c8583614d2a565b90508083604001818151612d609190614473565b6bffffffffffffffffffffffff16905250612d7b8582614d55565b83606001818151612d8c9190614473565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b6040015190505b9392505050565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115612ee657612ee66144f3565b03612f6057606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5b9190614d89565b905090565b504390565b6000808a8a8a8a8a8a8a8a8a604051602001612f8999989796959493929190614da2565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b6000612786825490565b6000612ea983836138d0565b6000612ea98373ffffffffffffffffffffffffffffffffffffffff84166138fa565b6000612ea98373ffffffffffffffffffffffffffffffffffffffff84166139f4565b3373ffffffffffffffffffffffffffffffffffffffff8216036130e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161039e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008080856001811115613174576131746144f3565b03613183575062015f906131a2565b6001856001811115613197576131976144f3565b0361290557506201adb05b6131b363ffffffff85166014614588565b6131be8460016144da565b6131cd9060ff16611d4c614588565b6131d79083614498565b6131e19190614498565b95945050505050565b6000806000896080015161ffff16876132039190614588565b90508380156132115750803a105b1561321957503a5b600060027f0000000000000000000000000000000000000000000000000000000000000000600281111561324f5761324f6144f3565b036133ae5760408051600081526020810190915285156132ad57600036604051806080016040528060488152602001614fd46048913960405160200161329793929190614e37565b6040516020818303038152906040529050613315565b6015546132c990640100000000900463ffffffff166004614e5e565b63ffffffff1667ffffffffffffffff8111156132e7576132e7613eee565b6040519080825280601f01601f191660200182016040528015613311576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e90613365908490600401613cc8565b602060405180830381865afa158015613382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133a69190614d89565b915050613508565b60017f000000000000000000000000000000000000000000000000000000000000000060028111156133e2576133e26144f3565b0361350857841561346457606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613439573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061345d9190614d89565b9050613508565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa1580156134b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134d69190614e81565b50506015549294506134f993505050640100000000900463ffffffff1682614588565b613504906010614588565b9150505b8461352457808b6080015161ffff166135219190614588565b90505b61353261ffff8716826145f4565b9050600087826135428c8e614498565b61354c9086614588565b6135569190614498565b61356890670de0b6b3a7640000614588565b61357291906145f4565b905060008c6040015163ffffffff1664e8d4a510006135919190614588565b898e6020015163ffffffff16858f886135aa9190614588565b6135b49190614498565b6135c290633b9aca00614588565b6135cc9190614588565b6135d691906145f4565b6135e09190614498565b90506b033b2e3c9fd0803ce80000006135f98284614498565b1115613631576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b6000808380602001905181019061365a9190614ecb565b835160c00151815191925063ffffffff908116911610156136b757847f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8856040516136a59190613cc8565b60405180910390a26000915050612ea9565b6020810151158015906136de5750602081015181516136db9063ffffffff16613a43565b14155b806136f757506136ec612eb0565b815163ffffffff1610155b1561372c57847f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301856040516136a59190613cc8565b506001949350505050565b6000806000838060200190518101906137509190614f23565b90506000858260000151836020015184604001516040516020016137b294939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012060808301519091501580159061381457508160800151613811836060015163ffffffff16613a43565b14155b806138305750613822612eb0565b826060015163ffffffff1610155b1561387a57857f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301866040516138659190613cc8565b60405180910390a26000935091506138c99050565b60008181526008602052604090205460ff16156138c157857f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8866040516138659190613cc8565b600193509150505b9250929050565b60008260000182815481106138e7576138e76144ab565b9060005260206000200154905092915050565b600081815260018301602052604081205480156139e357600061391e60018361453d565b85549091506000906139329060019061453d565b9050818114613997576000866000018281548110613952576139526144ab565b9060005260206000200154905080876000018481548110613975576139756144ab565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806139a8576139a8614fa4565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612786565b6000915050612786565b5092915050565b6000818152600183016020526040812054613a3b57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612786565b506000612786565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115613a7957613a796144f3565b03613b93576000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613acc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af09190614d89565b90508083101580613b0b5750610100613b09848361453d565b115b15613b195750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa158015613b6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea99190614d89565b504090565b919050565b50805460008255906000526020600020908101906122209190613c45565b828054828255906000526020600020908101928215613c35579160200282015b82811115613c3557825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190613bdb565b50613c41929150613c45565b5090565b5b80821115613c415760008155600101613c46565b60005b83811015613c75578181015183820152602001613c5d565b50506000910152565b60008151808452613c96816020860160208601613c5a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612ea96020830184613c7e565b73ffffffffffffffffffffffffffffffffffffffff8116811461222057600080fd5b8035613b9881613cdb565b60008083601f840112613d1a57600080fd5b50813567ffffffffffffffff811115613d3257600080fd5b6020830191508360208285010111156138c957600080fd5b60008060008060608587031215613d6057600080fd5b8435613d6b81613cdb565b935060208501359250604085013567ffffffffffffffff811115613d8e57600080fd5b613d9a87828801613d08565b95989497509550505050565b600080600060408486031215613dbb57600080fd5b83359250602084013567ffffffffffffffff811115613dd957600080fd5b613de586828701613d08565b9497909650939450505050565b60008083601f840112613e0457600080fd5b50813567ffffffffffffffff811115613e1c57600080fd5b6020830191508360208260051b85010111156138c957600080fd5b60008060008060008060008060e0898b031215613e5357600080fd5b606089018a811115613e6457600080fd5b8998503567ffffffffffffffff80821115613e7e57600080fd5b613e8a8c838d01613d08565b909950975060808b0135915080821115613ea357600080fd5b613eaf8c838d01613df2565b909750955060a08b0135915080821115613ec857600080fd5b50613ed58b828c01613df2565b999c989b50969995989497949560c00135949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101e0810167ffffffffffffffff81118282101715613f4157613f41613eee565b60405290565b60405160c0810167ffffffffffffffff81118282101715613f4157613f41613eee565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613fb157613fb1613eee565b604052919050565b600067ffffffffffffffff821115613fd357613fd3613eee565b5060051b60200190565b600082601f830112613fee57600080fd5b81356020614003613ffe83613fb9565b613f6a565b82815260059290921b8401810191818101908684111561402257600080fd5b8286015b8481101561404657803561403981613cdb565b8352918301918301614026565b509695505050505050565b803560ff81168114613b9857600080fd5b63ffffffff8116811461222057600080fd5b8035613b9881614062565b62ffffff8116811461222057600080fd5b8035613b988161407f565b61ffff8116811461222057600080fd5b8035613b988161409b565b6bffffffffffffffffffffffff8116811461222057600080fd5b8035613b98816140b6565b60006101e082840312156140ee57600080fd5b6140f6613f1d565b905061410182614074565b815261410f60208301614074565b602082015261412060408301614074565b604082015261413160608301614090565b6060820152614142608083016140ab565b608082015261415360a083016140d0565b60a082015261416460c08301614074565b60c082015261417560e08301614074565b60e0820152610100614188818401614074565b9082015261012061419a838201614074565b90820152610140828101359082015261016080830135908201526101806141c2818401613cfd565b908201526101a08281013567ffffffffffffffff8111156141e257600080fd5b6141ee85828601613fdd565b8284015250506101c0614202818401613cfd565b9082015292915050565b803567ffffffffffffffff81168114613b9857600080fd5b600082601f83011261423557600080fd5b813567ffffffffffffffff81111561424f5761424f613eee565b61428060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613f6a565b81815284602083860101111561429557600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c087890312156142cb57600080fd5b863567ffffffffffffffff808211156142e357600080fd5b6142ef8a838b01613fdd565b9750602089013591508082111561430557600080fd5b6143118a838b01613fdd565b965061431f60408a01614051565b9550606089013591508082111561433557600080fd5b6143418a838b016140db565b945061434f60808a0161420c565b935060a089013591508082111561436557600080fd5b5061437289828a01614224565b9150509295509295509295565b60008060008060008060c0878903121561439857600080fd5b863567ffffffffffffffff808211156143b057600080fd5b6143bc8a838b01613fdd565b975060208901359150808211156143d257600080fd5b6143de8a838b01613fdd565b96506143ec60408a01614051565b9550606089013591508082111561440257600080fd5b6143418a838b01614224565b60006020828403121561442057600080fd5b8135612ea981613cdb565b60006020828403121561443d57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff8181168382160190808211156139ed576139ed614444565b8082018082111561278657612786614444565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60ff818116838216019081111561278657612786614444565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b61ffff8181168382160190808211156139ed576139ed614444565b8181038181111561278657612786614444565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361458157614581614444565b5060010190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156145c0576145c0614444565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614603576146036145c5565b500490565b6bffffffffffffffffffffffff8516815283602082015282604082015260806060820152600061463b6080830184613c7e565b9695505050505050565b600060ff821660ff84168160ff048111821515161561466657614666614444565b029392505050565b63ffffffff8181168382160190808211156139ed576139ed614444565b600081518084526020808501945080840160005b838110156146d157815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010161469f565b509495945050505050565b602081526146f360208201835163ffffffff169052565b6000602083015161470c604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015162ffffff8116608084015250608083015161ffff811660a08401525060a08301516bffffffffffffffffffffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006147858185018363ffffffff169052565b840151905061012061479e8482018363ffffffff169052565b84015190506101406147b78482018363ffffffff169052565b840151610160848101919091528401516101808085019190915284015190506101a06147fa8185018373ffffffffffffffffffffffffffffffffffffffff169052565b808501519150506101e06101c0818186015261481a61020086018461468b565b95015173ffffffffffffffffffffffffffffffffffffffff169301929092525090919050565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526148708184018a61468b565b90508281036080840152614884818961468b565b905060ff871660a084015282810360c08401526148a18187613c7e565b905067ffffffffffffffff851660e08401528281036101008401526148c68185613c7e565b9c9b505050505050505050505050565b8051613b9881614062565b8051613b988161407f565b8051613b988161409b565b8051613b98816140b6565b8051613b9881613cdb565b600082601f83011261491e57600080fd5b8151602061492e613ffe83613fb9565b82815260059290921b8401810191818101908684111561494d57600080fd5b8286015b8481101561404657805161496481613cdb565b8352918301918301614951565b60006020828403121561498357600080fd5b815167ffffffffffffffff8082111561499b57600080fd5b908301906101e082860312156149b057600080fd5b6149b8613f1d565b6149c1836148d6565b81526149cf602084016148d6565b60208201526149e0604084016148d6565b60408201526149f1606084016148e1565b6060820152614a02608084016148ec565b6080820152614a1360a084016148f7565b60a0820152614a2460c084016148d6565b60c0820152614a3560e084016148d6565b60e0820152610100614a488185016148d6565b90820152610120614a5a8482016148d6565b9082015261014083810151908201526101608084015190820152610180614a82818501614902565b908201526101a08381015183811115614a9a57600080fd5b614aa68882870161490d565b8284015250506101c09150614abc828401614902565b91810191909152949350505050565b82815260406020820152600061282f6040830184613c7e565b60008060408385031215614af757600080fd5b82518015158114614b0757600080fd5b6020939093015192949293505050565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f830112614b4e57600080fd5b81356020614b5e613ffe83613fb9565b82815260059290921b84018101918181019086841115614b7d57600080fd5b8286015b848110156140465780358352918301918301614b81565b600082601f830112614ba957600080fd5b81356020614bb9613ffe83613fb9565b82815260059290921b84018101918181019086841115614bd857600080fd5b8286015b8481101561404657803567ffffffffffffffff811115614bfc5760008081fd5b614c0a8986838b0101614224565b845250918301918301614bdc565b600060208284031215614c2a57600080fd5b813567ffffffffffffffff80821115614c4257600080fd5b9083019060c08286031215614c5657600080fd5b614c5e613f47565b8235815260208301356020820152604083013582811115614c7e57600080fd5b614c8a87828601614b3d565b604083015250606083013582811115614ca257600080fd5b614cae87828601614b3d565b606083015250608083013582811115614cc657600080fd5b614cd287828601614b98565b60808301525060a083013582811115614cea57600080fd5b614cf687828601614b98565b60a08301525095945050505050565b6bffffffffffffffffffffffff8281168282160390808211156139ed576139ed614444565b60006bffffffffffffffffffffffff80841680614d4957614d496145c5565b92169190910492915050565b60006bffffffffffffffffffffffff80831681851681830481118215151615614d8057614d80614444565b02949350505050565b600060208284031215614d9b57600080fd5b5051919050565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614de98285018b61468b565b91508382036080850152614dfd828a61468b565b915060ff881660a085015283820360c0850152614e1a8288613c7e565b90861660e085015283810361010085015290506148c68185613c7e565b828482376000838201600081528351614e54818360208801613c5a565b0195945050505050565b600063ffffffff80831681851681830481118215151615614d8057614d80614444565b60008060008060008060c08789031215614e9a57600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600060408284031215614edd57600080fd5b6040516040810181811067ffffffffffffffff82111715614f0057614f00613eee565b6040528251614f0e81614062565b81526020928301519281019290925250919050565b600060a08284031215614f3557600080fd5b60405160a0810181811067ffffffffffffffff82111715614f5857614f58613eee565b806040525082518152602083015160208201526040830151614f7981614062565b60408201526060830151614f8c81614062565b60608201526080928301519281019290925250919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", +} + +var AutomationRegistryABI = AutomationRegistryMetaData.ABI + +var AutomationRegistryBin = AutomationRegistryMetaData.Bin + +func DeployAutomationRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, logicA common.Address) (common.Address, *types.Transaction, *AutomationRegistry, error) { + parsed, err := AutomationRegistryMetaData.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(AutomationRegistryBin), backend, logicA) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &AutomationRegistry{address: address, abi: *parsed, AutomationRegistryCaller: AutomationRegistryCaller{contract: contract}, AutomationRegistryTransactor: AutomationRegistryTransactor{contract: contract}, AutomationRegistryFilterer: AutomationRegistryFilterer{contract: contract}}, nil +} + +type AutomationRegistry struct { + address common.Address + abi abi.ABI + AutomationRegistryCaller + AutomationRegistryTransactor + AutomationRegistryFilterer +} + +type AutomationRegistryCaller struct { + contract *bind.BoundContract +} + +type AutomationRegistryTransactor struct { + contract *bind.BoundContract +} + +type AutomationRegistryFilterer struct { + contract *bind.BoundContract +} + +type AutomationRegistrySession struct { + Contract *AutomationRegistry + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type AutomationRegistryCallerSession struct { + Contract *AutomationRegistryCaller + CallOpts bind.CallOpts +} + +type AutomationRegistryTransactorSession struct { + Contract *AutomationRegistryTransactor + TransactOpts bind.TransactOpts +} + +type AutomationRegistryRaw struct { + Contract *AutomationRegistry +} + +type AutomationRegistryCallerRaw struct { + Contract *AutomationRegistryCaller +} + +type AutomationRegistryTransactorRaw struct { + Contract *AutomationRegistryTransactor +} + +func NewAutomationRegistry(address common.Address, backend bind.ContractBackend) (*AutomationRegistry, error) { + abi, err := abi.JSON(strings.NewReader(AutomationRegistryABI)) + if err != nil { + return nil, err + } + contract, err := bindAutomationRegistry(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AutomationRegistry{address: address, abi: abi, AutomationRegistryCaller: AutomationRegistryCaller{contract: contract}, AutomationRegistryTransactor: AutomationRegistryTransactor{contract: contract}, AutomationRegistryFilterer: AutomationRegistryFilterer{contract: contract}}, nil +} + +func NewAutomationRegistryCaller(address common.Address, caller bind.ContractCaller) (*AutomationRegistryCaller, error) { + contract, err := bindAutomationRegistry(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AutomationRegistryCaller{contract: contract}, nil +} + +func NewAutomationRegistryTransactor(address common.Address, transactor bind.ContractTransactor) (*AutomationRegistryTransactor, error) { + contract, err := bindAutomationRegistry(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AutomationRegistryTransactor{contract: contract}, nil +} + +func NewAutomationRegistryFilterer(address common.Address, filterer bind.ContractFilterer) (*AutomationRegistryFilterer, error) { + contract, err := bindAutomationRegistry(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AutomationRegistryFilterer{contract: contract}, nil +} + +func bindAutomationRegistry(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AutomationRegistryMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_AutomationRegistry *AutomationRegistryRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistry.Contract.AutomationRegistryCaller.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistry *AutomationRegistryRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistry.Contract.AutomationRegistryTransactor.contract.Transfer(opts) +} + +func (_AutomationRegistry *AutomationRegistryRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistry.Contract.AutomationRegistryTransactor.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistry *AutomationRegistryCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AutomationRegistry.Contract.contract.Call(opts, result, method, params...) +} + +func (_AutomationRegistry *AutomationRegistryTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistry.Contract.contract.Transfer(opts) +} + +func (_AutomationRegistry *AutomationRegistryTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AutomationRegistry.Contract.contract.Transact(opts, method, params...) +} + +func (_AutomationRegistry *AutomationRegistryCaller) FallbackTo(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistry.contract.Call(opts, &out, "fallbackTo") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistry *AutomationRegistrySession) FallbackTo() (common.Address, error) { + return _AutomationRegistry.Contract.FallbackTo(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCallerSession) FallbackTo() (common.Address, error) { + return _AutomationRegistry.Contract.FallbackTo(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCaller) LatestConfigDetails(opts *bind.CallOpts) (LatestConfigDetails, + + error) { + var out []interface{} + err := _AutomationRegistry.contract.Call(opts, &out, "latestConfigDetails") + + outstruct := new(LatestConfigDetails) + if err != nil { + return *outstruct, err + } + + outstruct.ConfigCount = *abi.ConvertType(out[0], new(uint32)).(*uint32) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.ConfigDigest = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte) + + return *outstruct, err + +} + +func (_AutomationRegistry *AutomationRegistrySession) LatestConfigDetails() (LatestConfigDetails, + + error) { + return _AutomationRegistry.Contract.LatestConfigDetails(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCallerSession) LatestConfigDetails() (LatestConfigDetails, + + error) { + return _AutomationRegistry.Contract.LatestConfigDetails(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCaller) LatestConfigDigestAndEpoch(opts *bind.CallOpts) (LatestConfigDigestAndEpoch, + + error) { + var out []interface{} + err := _AutomationRegistry.contract.Call(opts, &out, "latestConfigDigestAndEpoch") + + outstruct := new(LatestConfigDigestAndEpoch) + if err != nil { + return *outstruct, err + } + + outstruct.ScanLogs = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.ConfigDigest = *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + outstruct.Epoch = *abi.ConvertType(out[2], new(uint32)).(*uint32) + + return *outstruct, err + +} + +func (_AutomationRegistry *AutomationRegistrySession) LatestConfigDigestAndEpoch() (LatestConfigDigestAndEpoch, + + error) { + return _AutomationRegistry.Contract.LatestConfigDigestAndEpoch(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCallerSession) LatestConfigDigestAndEpoch() (LatestConfigDigestAndEpoch, + + error) { + return _AutomationRegistry.Contract.LatestConfigDigestAndEpoch(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AutomationRegistry.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_AutomationRegistry *AutomationRegistrySession) Owner() (common.Address, error) { + return _AutomationRegistry.Contract.Owner(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCallerSession) Owner() (common.Address, error) { + return _AutomationRegistry.Contract.Owner(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AutomationRegistry.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_AutomationRegistry *AutomationRegistrySession) TypeAndVersion() (string, error) { + return _AutomationRegistry.Contract.TypeAndVersion(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryCallerSession) TypeAndVersion() (string, error) { + return _AutomationRegistry.Contract.TypeAndVersion(&_AutomationRegistry.CallOpts) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "acceptOwnership") +} + +func (_AutomationRegistry *AutomationRegistrySession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistry.Contract.AcceptOwnership(&_AutomationRegistry.TransactOpts) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _AutomationRegistry.Contract.AcceptOwnership(&_AutomationRegistry.TransactOpts) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "onTokenTransfer", sender, amount, data) +} + +func (_AutomationRegistry *AutomationRegistrySession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.OnTokenTransfer(&_AutomationRegistry.TransactOpts, sender, amount, data) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.OnTokenTransfer(&_AutomationRegistry.TransactOpts, sender, amount, data) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) SetConfig(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "setConfig", signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig) +} + +func (_AutomationRegistry *AutomationRegistrySession) SetConfig(signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.SetConfig(&_AutomationRegistry.TransactOpts, signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) SetConfig(signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.SetConfig(&_AutomationRegistry.TransactOpts, signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) SetConfigTypeSafe(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "setConfigTypeSafe", signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) +} + +func (_AutomationRegistry *AutomationRegistrySession) SetConfigTypeSafe(signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.SetConfigTypeSafe(&_AutomationRegistry.TransactOpts, signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) SetConfigTypeSafe(signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.SetConfigTypeSafe(&_AutomationRegistry.TransactOpts, signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) SimulatePerformUpkeep(opts *bind.TransactOpts, id *big.Int, performData []byte) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "simulatePerformUpkeep", id, performData) +} + +func (_AutomationRegistry *AutomationRegistrySession) SimulatePerformUpkeep(id *big.Int, performData []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.SimulatePerformUpkeep(&_AutomationRegistry.TransactOpts, id, performData) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) SimulatePerformUpkeep(id *big.Int, performData []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.SimulatePerformUpkeep(&_AutomationRegistry.TransactOpts, id, performData) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "transferOwnership", to) +} + +func (_AutomationRegistry *AutomationRegistrySession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistry.Contract.TransferOwnership(&_AutomationRegistry.TransactOpts, to) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _AutomationRegistry.Contract.TransferOwnership(&_AutomationRegistry.TransactOpts, to) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _AutomationRegistry.contract.Transact(opts, "transmit", reportContext, rawReport, rs, ss, rawVs) +} + +func (_AutomationRegistry *AutomationRegistrySession) Transmit(reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.Transmit(&_AutomationRegistry.TransactOpts, reportContext, rawReport, rs, ss, rawVs) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) Transmit(reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.Transmit(&_AutomationRegistry.TransactOpts, reportContext, rawReport, rs, ss, rawVs) +} + +func (_AutomationRegistry *AutomationRegistryTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _AutomationRegistry.contract.RawTransact(opts, calldata) +} + +func (_AutomationRegistry *AutomationRegistrySession) Fallback(calldata []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.Fallback(&_AutomationRegistry.TransactOpts, calldata) +} + +func (_AutomationRegistry *AutomationRegistryTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _AutomationRegistry.Contract.Fallback(&_AutomationRegistry.TransactOpts, calldata) +} + +type AutomationRegistryAdminPrivilegeConfigSetIterator struct { + Event *AutomationRegistryAdminPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryAdminPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryAdminPrivilegeConfigSet) + 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(AutomationRegistryAdminPrivilegeConfigSet) + 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 *AutomationRegistryAdminPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryAdminPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryAdminPrivilegeConfigSet struct { + Admin common.Address + PrivilegeConfig []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*AutomationRegistryAdminPrivilegeConfigSetIterator, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return &AutomationRegistryAdminPrivilegeConfigSetIterator{contract: _AutomationRegistry.contract, event: "AdminPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) { + + var adminRule []interface{} + for _, adminItem := range admin { + adminRule = append(adminRule, adminItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "AdminPrivilegeConfigSet", adminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryAdminPrivilegeConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "AdminPrivilegeConfigSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseAdminPrivilegeConfigSet(log types.Log) (*AutomationRegistryAdminPrivilegeConfigSet, error) { + event := new(AutomationRegistryAdminPrivilegeConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "AdminPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryCancelledUpkeepReportIterator struct { + Event *AutomationRegistryCancelledUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryCancelledUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryCancelledUpkeepReport) + 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(AutomationRegistryCancelledUpkeepReport) + 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 *AutomationRegistryCancelledUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryCancelledUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryCancelledUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryCancelledUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryCancelledUpkeepReportIterator{contract: _AutomationRegistry.contract, event: "CancelledUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryCancelledUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "CancelledUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryCancelledUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "CancelledUpkeepReport", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseCancelledUpkeepReport(log types.Log) (*AutomationRegistryCancelledUpkeepReport, error) { + event := new(AutomationRegistryCancelledUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "CancelledUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryConfigSetIterator struct { + Event *AutomationRegistryConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryConfigSet) + 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(AutomationRegistryConfigSet) + 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 *AutomationRegistryConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryConfigSet struct { + PreviousConfigBlockNumber uint32 + ConfigDigest [32]byte + ConfigCount uint64 + Signers []common.Address + Transmitters []common.Address + F uint8 + OnchainConfig []byte + OffchainConfigVersion uint64 + OffchainConfig []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterConfigSet(opts *bind.FilterOpts) (*AutomationRegistryConfigSetIterator, error) { + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return &AutomationRegistryConfigSetIterator{contract: _AutomationRegistry.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryConfigSet) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "ConfigSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseConfigSet(log types.Log) (*AutomationRegistryConfigSet, error) { + event := new(AutomationRegistryConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryDedupKeyAddedIterator struct { + Event *AutomationRegistryDedupKeyAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryDedupKeyAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryDedupKeyAdded) + 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(AutomationRegistryDedupKeyAdded) + 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 *AutomationRegistryDedupKeyAddedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryDedupKeyAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryDedupKeyAdded struct { + DedupKey [32]byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*AutomationRegistryDedupKeyAddedIterator, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return &AutomationRegistryDedupKeyAddedIterator{contract: _AutomationRegistry.contract, event: "DedupKeyAdded", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryDedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) { + + var dedupKeyRule []interface{} + for _, dedupKeyItem := range dedupKey { + dedupKeyRule = append(dedupKeyRule, dedupKeyItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "DedupKeyAdded", dedupKeyRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryDedupKeyAdded) + if err := _AutomationRegistry.contract.UnpackLog(event, "DedupKeyAdded", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseDedupKeyAdded(log types.Log) (*AutomationRegistryDedupKeyAdded, error) { + event := new(AutomationRegistryDedupKeyAdded) + if err := _AutomationRegistry.contract.UnpackLog(event, "DedupKeyAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryFundsAddedIterator struct { + Event *AutomationRegistryFundsAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryFundsAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryFundsAdded) + 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(AutomationRegistryFundsAdded) + 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 *AutomationRegistryFundsAddedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryFundsAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryFundsAdded struct { + Id *big.Int + From common.Address + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*AutomationRegistryFundsAddedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return &AutomationRegistryFundsAddedIterator{contract: _AutomationRegistry.contract, event: "FundsAdded", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "FundsAdded", idRule, fromRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryFundsAdded) + if err := _AutomationRegistry.contract.UnpackLog(event, "FundsAdded", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseFundsAdded(log types.Log) (*AutomationRegistryFundsAdded, error) { + event := new(AutomationRegistryFundsAdded) + if err := _AutomationRegistry.contract.UnpackLog(event, "FundsAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryFundsWithdrawnIterator struct { + Event *AutomationRegistryFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryFundsWithdrawn) + 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(AutomationRegistryFundsWithdrawn) + 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 *AutomationRegistryFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryFundsWithdrawn struct { + Id *big.Int + Amount *big.Int + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryFundsWithdrawnIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryFundsWithdrawnIterator{contract: _AutomationRegistry.contract, event: "FundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryFundsWithdrawn, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "FundsWithdrawn", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryFundsWithdrawn) + if err := _AutomationRegistry.contract.UnpackLog(event, "FundsWithdrawn", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseFundsWithdrawn(log types.Log) (*AutomationRegistryFundsWithdrawn, error) { + event := new(AutomationRegistryFundsWithdrawn) + if err := _AutomationRegistry.contract.UnpackLog(event, "FundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryInsufficientFundsUpkeepReportIterator struct { + Event *AutomationRegistryInsufficientFundsUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryInsufficientFundsUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryInsufficientFundsUpkeepReport) + 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(AutomationRegistryInsufficientFundsUpkeepReport) + 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 *AutomationRegistryInsufficientFundsUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryInsufficientFundsUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryInsufficientFundsUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryInsufficientFundsUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryInsufficientFundsUpkeepReportIterator{contract: _AutomationRegistry.contract, event: "InsufficientFundsUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "InsufficientFundsUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryInsufficientFundsUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseInsufficientFundsUpkeepReport(log types.Log) (*AutomationRegistryInsufficientFundsUpkeepReport, error) { + event := new(AutomationRegistryInsufficientFundsUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "InsufficientFundsUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryOwnerFundsWithdrawnIterator struct { + Event *AutomationRegistryOwnerFundsWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryOwnerFundsWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryOwnerFundsWithdrawn) + 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(AutomationRegistryOwnerFundsWithdrawn) + 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 *AutomationRegistryOwnerFundsWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryOwnerFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryOwnerFundsWithdrawn struct { + Amount *big.Int + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*AutomationRegistryOwnerFundsWithdrawnIterator, error) { + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return &AutomationRegistryOwnerFundsWithdrawnIterator{contract: _AutomationRegistry.contract, event: "OwnerFundsWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryOwnerFundsWithdrawn) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "OwnerFundsWithdrawn") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryOwnerFundsWithdrawn) + if err := _AutomationRegistry.contract.UnpackLog(event, "OwnerFundsWithdrawn", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseOwnerFundsWithdrawn(log types.Log) (*AutomationRegistryOwnerFundsWithdrawn, error) { + event := new(AutomationRegistryOwnerFundsWithdrawn) + if err := _AutomationRegistry.contract.UnpackLog(event, "OwnerFundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryOwnershipTransferRequestedIterator struct { + Event *AutomationRegistryOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryOwnershipTransferRequested) + 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(AutomationRegistryOwnershipTransferRequested) + 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 *AutomationRegistryOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryOwnershipTransferRequestedIterator{contract: _AutomationRegistry.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryOwnershipTransferRequested) + if err := _AutomationRegistry.contract.UnpackLog(event, "OwnershipTransferRequested", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistryOwnershipTransferRequested, error) { + event := new(AutomationRegistryOwnershipTransferRequested) + if err := _AutomationRegistry.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryOwnershipTransferredIterator struct { + Event *AutomationRegistryOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryOwnershipTransferred) + 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(AutomationRegistryOwnershipTransferred) + 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 *AutomationRegistryOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryOwnershipTransferredIterator{contract: _AutomationRegistry.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryOwnershipTransferred) + if err := _AutomationRegistry.contract.UnpackLog(event, "OwnershipTransferred", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseOwnershipTransferred(log types.Log) (*AutomationRegistryOwnershipTransferred, error) { + event := new(AutomationRegistryOwnershipTransferred) + if err := _AutomationRegistry.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryPausedIterator struct { + Event *AutomationRegistryPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryPaused) + 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(AutomationRegistryPaused) + 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 *AutomationRegistryPausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryPaused struct { + Account common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterPaused(opts *bind.FilterOpts) (*AutomationRegistryPausedIterator, error) { + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "Paused") + if err != nil { + return nil, err + } + return &AutomationRegistryPausedIterator{contract: _AutomationRegistry.contract, event: "Paused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPaused) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "Paused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryPaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "Paused", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParsePaused(log types.Log) (*AutomationRegistryPaused, error) { + event := new(AutomationRegistryPaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "Paused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryPayeesUpdatedIterator struct { + Event *AutomationRegistryPayeesUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryPayeesUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryPayeesUpdated) + 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(AutomationRegistryPayeesUpdated) + 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 *AutomationRegistryPayeesUpdatedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryPayeesUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryPayeesUpdated struct { + Transmitters []common.Address + Payees []common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterPayeesUpdated(opts *bind.FilterOpts) (*AutomationRegistryPayeesUpdatedIterator, error) { + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return &AutomationRegistryPayeesUpdatedIterator{contract: _AutomationRegistry.contract, event: "PayeesUpdated", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPayeesUpdated) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "PayeesUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryPayeesUpdated) + if err := _AutomationRegistry.contract.UnpackLog(event, "PayeesUpdated", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParsePayeesUpdated(log types.Log) (*AutomationRegistryPayeesUpdated, error) { + event := new(AutomationRegistryPayeesUpdated) + if err := _AutomationRegistry.contract.UnpackLog(event, "PayeesUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryPayeeshipTransferRequestedIterator struct { + Event *AutomationRegistryPayeeshipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryPayeeshipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryPayeeshipTransferRequested) + 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(AutomationRegistryPayeeshipTransferRequested) + 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 *AutomationRegistryPayeeshipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryPayeeshipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryPayeeshipTransferRequested struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryPayeeshipTransferRequestedIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryPayeeshipTransferRequestedIterator{contract: _AutomationRegistry.contract, event: "PayeeshipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "PayeeshipTransferRequested", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryPayeeshipTransferRequested) + if err := _AutomationRegistry.contract.UnpackLog(event, "PayeeshipTransferRequested", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParsePayeeshipTransferRequested(log types.Log) (*AutomationRegistryPayeeshipTransferRequested, error) { + event := new(AutomationRegistryPayeeshipTransferRequested) + if err := _AutomationRegistry.contract.UnpackLog(event, "PayeeshipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryPayeeshipTransferredIterator struct { + Event *AutomationRegistryPayeeshipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryPayeeshipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryPayeeshipTransferred) + 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(AutomationRegistryPayeeshipTransferred) + 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 *AutomationRegistryPayeeshipTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryPayeeshipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryPayeeshipTransferred struct { + Transmitter common.Address + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryPayeeshipTransferredIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryPayeeshipTransferredIterator{contract: _AutomationRegistry.contract, event: "PayeeshipTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "PayeeshipTransferred", transmitterRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryPayeeshipTransferred) + if err := _AutomationRegistry.contract.UnpackLog(event, "PayeeshipTransferred", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParsePayeeshipTransferred(log types.Log) (*AutomationRegistryPayeeshipTransferred, error) { + event := new(AutomationRegistryPayeeshipTransferred) + if err := _AutomationRegistry.contract.UnpackLog(event, "PayeeshipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryPaymentWithdrawnIterator struct { + Event *AutomationRegistryPaymentWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryPaymentWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryPaymentWithdrawn) + 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(AutomationRegistryPaymentWithdrawn) + 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 *AutomationRegistryPaymentWithdrawnIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryPaymentWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryPaymentWithdrawn struct { + Transmitter common.Address + Amount *big.Int + To common.Address + Payee common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*AutomationRegistryPaymentWithdrawnIterator, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryPaymentWithdrawnIterator{contract: _AutomationRegistry.contract, event: "PaymentWithdrawn", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) { + + var transmitterRule []interface{} + for _, transmitterItem := range transmitter { + transmitterRule = append(transmitterRule, transmitterItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "PaymentWithdrawn", transmitterRule, amountRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryPaymentWithdrawn) + if err := _AutomationRegistry.contract.UnpackLog(event, "PaymentWithdrawn", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParsePaymentWithdrawn(log types.Log) (*AutomationRegistryPaymentWithdrawn, error) { + event := new(AutomationRegistryPaymentWithdrawn) + if err := _AutomationRegistry.contract.UnpackLog(event, "PaymentWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryReorgedUpkeepReportIterator struct { + Event *AutomationRegistryReorgedUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryReorgedUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryReorgedUpkeepReport) + 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(AutomationRegistryReorgedUpkeepReport) + 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 *AutomationRegistryReorgedUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryReorgedUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryReorgedUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryReorgedUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryReorgedUpkeepReportIterator{contract: _AutomationRegistry.contract, event: "ReorgedUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "ReorgedUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryReorgedUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "ReorgedUpkeepReport", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseReorgedUpkeepReport(log types.Log) (*AutomationRegistryReorgedUpkeepReport, error) { + event := new(AutomationRegistryReorgedUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "ReorgedUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryStaleUpkeepReportIterator struct { + Event *AutomationRegistryStaleUpkeepReport + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryStaleUpkeepReportIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryStaleUpkeepReport) + 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(AutomationRegistryStaleUpkeepReport) + 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 *AutomationRegistryStaleUpkeepReportIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryStaleUpkeepReportIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryStaleUpkeepReport struct { + Id *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryStaleUpkeepReportIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryStaleUpkeepReportIterator{contract: _AutomationRegistry.contract, event: "StaleUpkeepReport", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryStaleUpkeepReport, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "StaleUpkeepReport", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryStaleUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "StaleUpkeepReport", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseStaleUpkeepReport(log types.Log) (*AutomationRegistryStaleUpkeepReport, error) { + event := new(AutomationRegistryStaleUpkeepReport) + if err := _AutomationRegistry.contract.UnpackLog(event, "StaleUpkeepReport", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryTransmittedIterator struct { + Event *AutomationRegistryTransmitted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryTransmittedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryTransmitted) + 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(AutomationRegistryTransmitted) + 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 *AutomationRegistryTransmittedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryTransmittedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryTransmitted struct { + ConfigDigest [32]byte + Epoch uint32 + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterTransmitted(opts *bind.FilterOpts) (*AutomationRegistryTransmittedIterator, error) { + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "Transmitted") + if err != nil { + return nil, err + } + return &AutomationRegistryTransmittedIterator{contract: _AutomationRegistry.contract, event: "Transmitted", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchTransmitted(opts *bind.WatchOpts, sink chan<- *AutomationRegistryTransmitted) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "Transmitted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryTransmitted) + if err := _AutomationRegistry.contract.UnpackLog(event, "Transmitted", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseTransmitted(log types.Log) (*AutomationRegistryTransmitted, error) { + event := new(AutomationRegistryTransmitted) + if err := _AutomationRegistry.contract.UnpackLog(event, "Transmitted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUnpausedIterator struct { + Event *AutomationRegistryUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUnpaused) + 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(AutomationRegistryUnpaused) + 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 *AutomationRegistryUnpausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUnpaused struct { + Account common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUnpaused(opts *bind.FilterOpts) (*AutomationRegistryUnpausedIterator, error) { + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return &AutomationRegistryUnpausedIterator{contract: _AutomationRegistry.contract, event: "Unpaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUnpaused) (event.Subscription, error) { + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "Unpaused") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUnpaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "Unpaused", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUnpaused(log types.Log) (*AutomationRegistryUnpaused, error) { + event := new(AutomationRegistryUnpaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "Unpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepAdminTransferRequestedIterator struct { + Event *AutomationRegistryUpkeepAdminTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepAdminTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepAdminTransferRequested) + 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(AutomationRegistryUpkeepAdminTransferRequested) + 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 *AutomationRegistryUpkeepAdminTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepAdminTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepAdminTransferRequested struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryUpkeepAdminTransferRequestedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepAdminTransferRequestedIterator{contract: _AutomationRegistry.contract, event: "UpkeepAdminTransferRequested", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepAdminTransferRequested", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepAdminTransferRequested) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepAdminTransferRequested", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepAdminTransferRequested(log types.Log) (*AutomationRegistryUpkeepAdminTransferRequested, error) { + event := new(AutomationRegistryUpkeepAdminTransferRequested) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepAdminTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepAdminTransferredIterator struct { + Event *AutomationRegistryUpkeepAdminTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepAdminTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepAdminTransferred) + 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(AutomationRegistryUpkeepAdminTransferred) + 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 *AutomationRegistryUpkeepAdminTransferredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepAdminTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepAdminTransferred struct { + Id *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryUpkeepAdminTransferredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepAdminTransferredIterator{contract: _AutomationRegistry.contract, event: "UpkeepAdminTransferred", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepAdminTransferred", idRule, fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepAdminTransferred) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepAdminTransferred", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepAdminTransferred(log types.Log) (*AutomationRegistryUpkeepAdminTransferred, error) { + event := new(AutomationRegistryUpkeepAdminTransferred) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepAdminTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepCanceledIterator struct { + Event *AutomationRegistryUpkeepCanceled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepCanceledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepCanceled) + 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(AutomationRegistryUpkeepCanceled) + 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 *AutomationRegistryUpkeepCanceledIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepCanceled struct { + Id *big.Int + AtBlockHeight uint64 + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*AutomationRegistryUpkeepCanceledIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepCanceledIterator{contract: _AutomationRegistry.contract, event: "UpkeepCanceled", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var atBlockHeightRule []interface{} + for _, atBlockHeightItem := range atBlockHeight { + atBlockHeightRule = append(atBlockHeightRule, atBlockHeightItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepCanceled", idRule, atBlockHeightRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepCanceled) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepCanceled", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepCanceled(log types.Log) (*AutomationRegistryUpkeepCanceled, error) { + event := new(AutomationRegistryUpkeepCanceled) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepCheckDataSetIterator struct { + Event *AutomationRegistryUpkeepCheckDataSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepCheckDataSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepCheckDataSet) + 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(AutomationRegistryUpkeepCheckDataSet) + 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 *AutomationRegistryUpkeepCheckDataSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepCheckDataSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepCheckDataSet struct { + Id *big.Int + NewCheckData []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepCheckDataSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepCheckDataSetIterator{contract: _AutomationRegistry.contract, event: "UpkeepCheckDataSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepCheckDataSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepCheckDataSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepCheckDataSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepCheckDataSet(log types.Log) (*AutomationRegistryUpkeepCheckDataSet, error) { + event := new(AutomationRegistryUpkeepCheckDataSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepCheckDataSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepGasLimitSetIterator struct { + Event *AutomationRegistryUpkeepGasLimitSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepGasLimitSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepGasLimitSet) + 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(AutomationRegistryUpkeepGasLimitSet) + 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 *AutomationRegistryUpkeepGasLimitSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepGasLimitSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepGasLimitSet struct { + Id *big.Int + GasLimit *big.Int + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepGasLimitSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepGasLimitSetIterator{contract: _AutomationRegistry.contract, event: "UpkeepGasLimitSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepGasLimitSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepGasLimitSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepGasLimitSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepGasLimitSet(log types.Log) (*AutomationRegistryUpkeepGasLimitSet, error) { + event := new(AutomationRegistryUpkeepGasLimitSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepGasLimitSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepMigratedIterator struct { + Event *AutomationRegistryUpkeepMigrated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepMigratedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepMigrated) + 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(AutomationRegistryUpkeepMigrated) + 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 *AutomationRegistryUpkeepMigratedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepMigratedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepMigrated struct { + Id *big.Int + RemainingBalance *big.Int + Destination common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepMigratedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepMigratedIterator{contract: _AutomationRegistry.contract, event: "UpkeepMigrated", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepMigrated, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepMigrated", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepMigrated) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepMigrated", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepMigrated(log types.Log) (*AutomationRegistryUpkeepMigrated, error) { + event := new(AutomationRegistryUpkeepMigrated) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepMigrated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepOffchainConfigSetIterator struct { + Event *AutomationRegistryUpkeepOffchainConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepOffchainConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepOffchainConfigSet) + 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(AutomationRegistryUpkeepOffchainConfigSet) + 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 *AutomationRegistryUpkeepOffchainConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepOffchainConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepOffchainConfigSet struct { + Id *big.Int + OffchainConfig []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepOffchainConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepOffchainConfigSetIterator{contract: _AutomationRegistry.contract, event: "UpkeepOffchainConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepOffchainConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepOffchainConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepOffchainConfigSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepOffchainConfigSet(log types.Log) (*AutomationRegistryUpkeepOffchainConfigSet, error) { + event := new(AutomationRegistryUpkeepOffchainConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepOffchainConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepPausedIterator struct { + Event *AutomationRegistryUpkeepPaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepPausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepPaused) + 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(AutomationRegistryUpkeepPaused) + 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 *AutomationRegistryUpkeepPausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepPaused struct { + Id *big.Int + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepPausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepPausedIterator{contract: _AutomationRegistry.contract, event: "UpkeepPaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepPaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepPaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepPaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepPaused", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepPaused(log types.Log) (*AutomationRegistryUpkeepPaused, error) { + event := new(AutomationRegistryUpkeepPaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepPaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepPerformedIterator struct { + Event *AutomationRegistryUpkeepPerformed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepPerformedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepPerformed) + 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(AutomationRegistryUpkeepPerformed) + 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 *AutomationRegistryUpkeepPerformedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepPerformedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepPerformed struct { + Id *big.Int + Success bool + TotalPayment *big.Int + GasUsed *big.Int + GasOverhead *big.Int + Trigger []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*AutomationRegistryUpkeepPerformedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepPerformedIterator{contract: _AutomationRegistry.contract, event: "UpkeepPerformed", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var successRule []interface{} + for _, successItem := range success { + successRule = append(successRule, successItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepPerformed", idRule, successRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepPerformed) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepPerformed", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepPerformed(log types.Log) (*AutomationRegistryUpkeepPerformed, error) { + event := new(AutomationRegistryUpkeepPerformed) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepPerformed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepPrivilegeConfigSetIterator struct { + Event *AutomationRegistryUpkeepPrivilegeConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepPrivilegeConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepPrivilegeConfigSet) + 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(AutomationRegistryUpkeepPrivilegeConfigSet) + 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 *AutomationRegistryUpkeepPrivilegeConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepPrivilegeConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepPrivilegeConfigSet struct { + Id *big.Int + PrivilegeConfig []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepPrivilegeConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepPrivilegeConfigSetIterator{contract: _AutomationRegistry.contract, event: "UpkeepPrivilegeConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepPrivilegeConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepPrivilegeConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepPrivilegeConfigSet(log types.Log) (*AutomationRegistryUpkeepPrivilegeConfigSet, error) { + event := new(AutomationRegistryUpkeepPrivilegeConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepPrivilegeConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepReceivedIterator struct { + Event *AutomationRegistryUpkeepReceived + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepReceivedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepReceived) + 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(AutomationRegistryUpkeepReceived) + 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 *AutomationRegistryUpkeepReceivedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepReceivedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepReceived struct { + Id *big.Int + StartingBalance *big.Int + ImportedFrom common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepReceivedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepReceivedIterator{contract: _AutomationRegistry.contract, event: "UpkeepReceived", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepReceived, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepReceived", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepReceived) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepReceived", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepReceived(log types.Log) (*AutomationRegistryUpkeepReceived, error) { + event := new(AutomationRegistryUpkeepReceived) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepReceived", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepRegisteredIterator struct { + Event *AutomationRegistryUpkeepRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepRegistered) + 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(AutomationRegistryUpkeepRegistered) + 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 *AutomationRegistryUpkeepRegisteredIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepRegistered struct { + Id *big.Int + PerformGas uint32 + Admin common.Address + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepRegisteredIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepRegisteredIterator{contract: _AutomationRegistry.contract, event: "UpkeepRegistered", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepRegistered, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepRegistered", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepRegistered) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepRegistered", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepRegistered(log types.Log) (*AutomationRegistryUpkeepRegistered, error) { + event := new(AutomationRegistryUpkeepRegistered) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepTriggerConfigSetIterator struct { + Event *AutomationRegistryUpkeepTriggerConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepTriggerConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepTriggerConfigSet) + 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(AutomationRegistryUpkeepTriggerConfigSet) + 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 *AutomationRegistryUpkeepTriggerConfigSetIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepTriggerConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepTriggerConfigSet struct { + Id *big.Int + TriggerConfig []byte + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepTriggerConfigSetIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepTriggerConfigSetIterator{contract: _AutomationRegistry.contract, event: "UpkeepTriggerConfigSet", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepTriggerConfigSet", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepTriggerConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepTriggerConfigSet", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepTriggerConfigSet(log types.Log) (*AutomationRegistryUpkeepTriggerConfigSet, error) { + event := new(AutomationRegistryUpkeepTriggerConfigSet) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepTriggerConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type AutomationRegistryUpkeepUnpausedIterator struct { + Event *AutomationRegistryUpkeepUnpaused + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *AutomationRegistryUpkeepUnpausedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(AutomationRegistryUpkeepUnpaused) + 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(AutomationRegistryUpkeepUnpaused) + 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 *AutomationRegistryUpkeepUnpausedIterator) Error() error { + return it.fail +} + +func (it *AutomationRegistryUpkeepUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type AutomationRegistryUpkeepUnpaused struct { + Id *big.Int + Raw types.Log +} + +func (_AutomationRegistry *AutomationRegistryFilterer) FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepUnpausedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.FilterLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return &AutomationRegistryUpkeepUnpausedIterator{contract: _AutomationRegistry.contract, event: "UpkeepUnpaused", logs: logs, sub: sub}, nil +} + +func (_AutomationRegistry *AutomationRegistryFilterer) WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepUnpaused, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _AutomationRegistry.contract.WatchLogs(opts, "UpkeepUnpaused", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(AutomationRegistryUpkeepUnpaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepUnpaused", 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 (_AutomationRegistry *AutomationRegistryFilterer) ParseUpkeepUnpaused(log types.Log) (*AutomationRegistryUpkeepUnpaused, error) { + event := new(AutomationRegistryUpkeepUnpaused) + if err := _AutomationRegistry.contract.UnpackLog(event, "UpkeepUnpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LatestConfigDetails struct { + ConfigCount uint32 + BlockNumber uint32 + ConfigDigest [32]byte +} +type LatestConfigDigestAndEpoch struct { + ScanLogs bool + ConfigDigest [32]byte + Epoch uint32 +} + +func (_AutomationRegistry *AutomationRegistry) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _AutomationRegistry.abi.Events["AdminPrivilegeConfigSet"].ID: + return _AutomationRegistry.ParseAdminPrivilegeConfigSet(log) + case _AutomationRegistry.abi.Events["CancelledUpkeepReport"].ID: + return _AutomationRegistry.ParseCancelledUpkeepReport(log) + case _AutomationRegistry.abi.Events["ConfigSet"].ID: + return _AutomationRegistry.ParseConfigSet(log) + case _AutomationRegistry.abi.Events["DedupKeyAdded"].ID: + return _AutomationRegistry.ParseDedupKeyAdded(log) + case _AutomationRegistry.abi.Events["FundsAdded"].ID: + return _AutomationRegistry.ParseFundsAdded(log) + case _AutomationRegistry.abi.Events["FundsWithdrawn"].ID: + return _AutomationRegistry.ParseFundsWithdrawn(log) + case _AutomationRegistry.abi.Events["InsufficientFundsUpkeepReport"].ID: + return _AutomationRegistry.ParseInsufficientFundsUpkeepReport(log) + case _AutomationRegistry.abi.Events["OwnerFundsWithdrawn"].ID: + return _AutomationRegistry.ParseOwnerFundsWithdrawn(log) + case _AutomationRegistry.abi.Events["OwnershipTransferRequested"].ID: + return _AutomationRegistry.ParseOwnershipTransferRequested(log) + case _AutomationRegistry.abi.Events["OwnershipTransferred"].ID: + return _AutomationRegistry.ParseOwnershipTransferred(log) + case _AutomationRegistry.abi.Events["Paused"].ID: + return _AutomationRegistry.ParsePaused(log) + case _AutomationRegistry.abi.Events["PayeesUpdated"].ID: + return _AutomationRegistry.ParsePayeesUpdated(log) + case _AutomationRegistry.abi.Events["PayeeshipTransferRequested"].ID: + return _AutomationRegistry.ParsePayeeshipTransferRequested(log) + case _AutomationRegistry.abi.Events["PayeeshipTransferred"].ID: + return _AutomationRegistry.ParsePayeeshipTransferred(log) + case _AutomationRegistry.abi.Events["PaymentWithdrawn"].ID: + return _AutomationRegistry.ParsePaymentWithdrawn(log) + case _AutomationRegistry.abi.Events["ReorgedUpkeepReport"].ID: + return _AutomationRegistry.ParseReorgedUpkeepReport(log) + case _AutomationRegistry.abi.Events["StaleUpkeepReport"].ID: + return _AutomationRegistry.ParseStaleUpkeepReport(log) + case _AutomationRegistry.abi.Events["Transmitted"].ID: + return _AutomationRegistry.ParseTransmitted(log) + case _AutomationRegistry.abi.Events["Unpaused"].ID: + return _AutomationRegistry.ParseUnpaused(log) + case _AutomationRegistry.abi.Events["UpkeepAdminTransferRequested"].ID: + return _AutomationRegistry.ParseUpkeepAdminTransferRequested(log) + case _AutomationRegistry.abi.Events["UpkeepAdminTransferred"].ID: + return _AutomationRegistry.ParseUpkeepAdminTransferred(log) + case _AutomationRegistry.abi.Events["UpkeepCanceled"].ID: + return _AutomationRegistry.ParseUpkeepCanceled(log) + case _AutomationRegistry.abi.Events["UpkeepCheckDataSet"].ID: + return _AutomationRegistry.ParseUpkeepCheckDataSet(log) + case _AutomationRegistry.abi.Events["UpkeepGasLimitSet"].ID: + return _AutomationRegistry.ParseUpkeepGasLimitSet(log) + case _AutomationRegistry.abi.Events["UpkeepMigrated"].ID: + return _AutomationRegistry.ParseUpkeepMigrated(log) + case _AutomationRegistry.abi.Events["UpkeepOffchainConfigSet"].ID: + return _AutomationRegistry.ParseUpkeepOffchainConfigSet(log) + case _AutomationRegistry.abi.Events["UpkeepPaused"].ID: + return _AutomationRegistry.ParseUpkeepPaused(log) + case _AutomationRegistry.abi.Events["UpkeepPerformed"].ID: + return _AutomationRegistry.ParseUpkeepPerformed(log) + case _AutomationRegistry.abi.Events["UpkeepPrivilegeConfigSet"].ID: + return _AutomationRegistry.ParseUpkeepPrivilegeConfigSet(log) + case _AutomationRegistry.abi.Events["UpkeepReceived"].ID: + return _AutomationRegistry.ParseUpkeepReceived(log) + case _AutomationRegistry.abi.Events["UpkeepRegistered"].ID: + return _AutomationRegistry.ParseUpkeepRegistered(log) + case _AutomationRegistry.abi.Events["UpkeepTriggerConfigSet"].ID: + return _AutomationRegistry.ParseUpkeepTriggerConfigSet(log) + case _AutomationRegistry.abi.Events["UpkeepUnpaused"].ID: + return _AutomationRegistry.ParseUpkeepUnpaused(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (AutomationRegistryAdminPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d2") +} + +func (AutomationRegistryCancelledUpkeepReport) Topic() common.Hash { + return common.HexToHash("0xc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636") +} + +func (AutomationRegistryConfigSet) Topic() common.Hash { + return common.HexToHash("0x1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05") +} + +func (AutomationRegistryDedupKeyAdded) Topic() common.Hash { + return common.HexToHash("0xa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f2") +} + +func (AutomationRegistryFundsAdded) Topic() common.Hash { + return common.HexToHash("0xafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa734891506203") +} + +func (AutomationRegistryFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0xf3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318") +} + +func (AutomationRegistryInsufficientFundsUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e02") +} + +func (AutomationRegistryOwnerFundsWithdrawn) Topic() common.Hash { + return common.HexToHash("0x1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f1") +} + +func (AutomationRegistryOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (AutomationRegistryOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (AutomationRegistryPaused) Topic() common.Hash { + return common.HexToHash("0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258") +} + +func (AutomationRegistryPayeesUpdated) Topic() common.Hash { + return common.HexToHash("0xa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725") +} + +func (AutomationRegistryPayeeshipTransferRequested) Topic() common.Hash { + return common.HexToHash("0x84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367") +} + +func (AutomationRegistryPayeeshipTransferred) Topic() common.Hash { + return common.HexToHash("0x78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b3") +} + +func (AutomationRegistryPaymentWithdrawn) Topic() common.Hash { + return common.HexToHash("0x9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f40698") +} + +func (AutomationRegistryReorgedUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301") +} + +func (AutomationRegistryStaleUpkeepReport) Topic() common.Hash { + return common.HexToHash("0x405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8") +} + +func (AutomationRegistryTransmitted) Topic() common.Hash { + return common.HexToHash("0xb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62") +} + +func (AutomationRegistryUnpaused) Topic() common.Hash { + return common.HexToHash("0x5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa") +} + +func (AutomationRegistryUpkeepAdminTransferRequested) Topic() common.Hash { + return common.HexToHash("0xb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b35") +} + +func (AutomationRegistryUpkeepAdminTransferred) Topic() common.Hash { + return common.HexToHash("0x5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c") +} + +func (AutomationRegistryUpkeepCanceled) Topic() common.Hash { + return common.HexToHash("0x91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f791181") +} + +func (AutomationRegistryUpkeepCheckDataSet) Topic() common.Hash { + return common.HexToHash("0xcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d") +} + +func (AutomationRegistryUpkeepGasLimitSet) Topic() common.Hash { + return common.HexToHash("0xc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c") +} + +func (AutomationRegistryUpkeepMigrated) Topic() common.Hash { + return common.HexToHash("0xb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff") +} + +func (AutomationRegistryUpkeepOffchainConfigSet) Topic() common.Hash { + return common.HexToHash("0x3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf4850") +} + +func (AutomationRegistryUpkeepPaused) Topic() common.Hash { + return common.HexToHash("0x8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f") +} + +func (AutomationRegistryUpkeepPerformed) Topic() common.Hash { + return common.HexToHash("0xad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b") +} + +func (AutomationRegistryUpkeepPrivilegeConfigSet) Topic() common.Hash { + return common.HexToHash("0x2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae7769") +} + +func (AutomationRegistryUpkeepReceived) Topic() common.Hash { + return common.HexToHash("0x74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a71") +} + +func (AutomationRegistryUpkeepRegistered) Topic() common.Hash { + return common.HexToHash("0xbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d012") +} + +func (AutomationRegistryUpkeepTriggerConfigSet) Topic() common.Hash { + return common.HexToHash("0x2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664") +} + +func (AutomationRegistryUpkeepUnpaused) Topic() common.Hash { + return common.HexToHash("0x7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a47456") +} + +func (_AutomationRegistry *AutomationRegistry) Address() common.Address { + return _AutomationRegistry.address +} + +type AutomationRegistryInterface interface { + FallbackTo(opts *bind.CallOpts) (common.Address, error) + + LatestConfigDetails(opts *bind.CallOpts) (LatestConfigDetails, + + error) + + LatestConfigDigestAndEpoch(opts *bind.CallOpts) (LatestConfigDigestAndEpoch, + + error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfigBytes []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) + + SetConfigTypeSafe(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfig AutomationRegistryBase22OnchainConfig, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) + + SimulatePerformUpkeep(opts *bind.TransactOpts, id *big.Int, performData []byte) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, rawReport []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) + + Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) + + FilterAdminPrivilegeConfigSet(opts *bind.FilterOpts, admin []common.Address) (*AutomationRegistryAdminPrivilegeConfigSetIterator, error) + + WatchAdminPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryAdminPrivilegeConfigSet, admin []common.Address) (event.Subscription, error) + + ParseAdminPrivilegeConfigSet(log types.Log) (*AutomationRegistryAdminPrivilegeConfigSet, error) + + FilterCancelledUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryCancelledUpkeepReportIterator, error) + + WatchCancelledUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryCancelledUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseCancelledUpkeepReport(log types.Log) (*AutomationRegistryCancelledUpkeepReport, error) + + FilterConfigSet(opts *bind.FilterOpts) (*AutomationRegistryConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryConfigSet) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*AutomationRegistryConfigSet, error) + + FilterDedupKeyAdded(opts *bind.FilterOpts, dedupKey [][32]byte) (*AutomationRegistryDedupKeyAddedIterator, error) + + WatchDedupKeyAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryDedupKeyAdded, dedupKey [][32]byte) (event.Subscription, error) + + ParseDedupKeyAdded(log types.Log) (*AutomationRegistryDedupKeyAdded, error) + + FilterFundsAdded(opts *bind.FilterOpts, id []*big.Int, from []common.Address) (*AutomationRegistryFundsAddedIterator, error) + + WatchFundsAdded(opts *bind.WatchOpts, sink chan<- *AutomationRegistryFundsAdded, id []*big.Int, from []common.Address) (event.Subscription, error) + + ParseFundsAdded(log types.Log) (*AutomationRegistryFundsAdded, error) + + FilterFundsWithdrawn(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryFundsWithdrawnIterator, error) + + WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryFundsWithdrawn, id []*big.Int) (event.Subscription, error) + + ParseFundsWithdrawn(log types.Log) (*AutomationRegistryFundsWithdrawn, error) + + FilterInsufficientFundsUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryInsufficientFundsUpkeepReportIterator, error) + + WatchInsufficientFundsUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryInsufficientFundsUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseInsufficientFundsUpkeepReport(log types.Log) (*AutomationRegistryInsufficientFundsUpkeepReport, error) + + FilterOwnerFundsWithdrawn(opts *bind.FilterOpts) (*AutomationRegistryOwnerFundsWithdrawnIterator, error) + + WatchOwnerFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryOwnerFundsWithdrawn) (event.Subscription, error) + + ParseOwnerFundsWithdrawn(log types.Log) (*AutomationRegistryOwnerFundsWithdrawn, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*AutomationRegistryOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AutomationRegistryOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*AutomationRegistryOwnershipTransferred, error) + + FilterPaused(opts *bind.FilterOpts) (*AutomationRegistryPausedIterator, error) + + WatchPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPaused) (event.Subscription, error) + + ParsePaused(log types.Log) (*AutomationRegistryPaused, error) + + FilterPayeesUpdated(opts *bind.FilterOpts) (*AutomationRegistryPayeesUpdatedIterator, error) + + WatchPayeesUpdated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPayeesUpdated) (event.Subscription, error) + + ParsePayeesUpdated(log types.Log) (*AutomationRegistryPayeesUpdated, error) + + FilterPayeeshipTransferRequested(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryPayeeshipTransferRequestedIterator, error) + + WatchPayeeshipTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPayeeshipTransferRequested, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferRequested(log types.Log) (*AutomationRegistryPayeeshipTransferRequested, error) + + FilterPayeeshipTransferred(opts *bind.FilterOpts, transmitter []common.Address, from []common.Address, to []common.Address) (*AutomationRegistryPayeeshipTransferredIterator, error) + + WatchPayeeshipTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPayeeshipTransferred, transmitter []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) + + ParsePayeeshipTransferred(log types.Log) (*AutomationRegistryPayeeshipTransferred, error) + + FilterPaymentWithdrawn(opts *bind.FilterOpts, transmitter []common.Address, amount []*big.Int, to []common.Address) (*AutomationRegistryPaymentWithdrawnIterator, error) + + WatchPaymentWithdrawn(opts *bind.WatchOpts, sink chan<- *AutomationRegistryPaymentWithdrawn, transmitter []common.Address, amount []*big.Int, to []common.Address) (event.Subscription, error) + + ParsePaymentWithdrawn(log types.Log) (*AutomationRegistryPaymentWithdrawn, error) + + FilterReorgedUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryReorgedUpkeepReportIterator, error) + + WatchReorgedUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryReorgedUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseReorgedUpkeepReport(log types.Log) (*AutomationRegistryReorgedUpkeepReport, error) + + FilterStaleUpkeepReport(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryStaleUpkeepReportIterator, error) + + WatchStaleUpkeepReport(opts *bind.WatchOpts, sink chan<- *AutomationRegistryStaleUpkeepReport, id []*big.Int) (event.Subscription, error) + + ParseStaleUpkeepReport(log types.Log) (*AutomationRegistryStaleUpkeepReport, error) + + FilterTransmitted(opts *bind.FilterOpts) (*AutomationRegistryTransmittedIterator, error) + + WatchTransmitted(opts *bind.WatchOpts, sink chan<- *AutomationRegistryTransmitted) (event.Subscription, error) + + ParseTransmitted(log types.Log) (*AutomationRegistryTransmitted, error) + + FilterUnpaused(opts *bind.FilterOpts) (*AutomationRegistryUnpausedIterator, error) + + WatchUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUnpaused) (event.Subscription, error) + + ParseUnpaused(log types.Log) (*AutomationRegistryUnpaused, error) + + FilterUpkeepAdminTransferRequested(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryUpkeepAdminTransferRequestedIterator, error) + + WatchUpkeepAdminTransferRequested(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepAdminTransferRequested, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferRequested(log types.Log) (*AutomationRegistryUpkeepAdminTransferRequested, error) + + FilterUpkeepAdminTransferred(opts *bind.FilterOpts, id []*big.Int, from []common.Address, to []common.Address) (*AutomationRegistryUpkeepAdminTransferredIterator, error) + + WatchUpkeepAdminTransferred(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepAdminTransferred, id []*big.Int, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseUpkeepAdminTransferred(log types.Log) (*AutomationRegistryUpkeepAdminTransferred, error) + + FilterUpkeepCanceled(opts *bind.FilterOpts, id []*big.Int, atBlockHeight []uint64) (*AutomationRegistryUpkeepCanceledIterator, error) + + WatchUpkeepCanceled(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepCanceled, id []*big.Int, atBlockHeight []uint64) (event.Subscription, error) + + ParseUpkeepCanceled(log types.Log) (*AutomationRegistryUpkeepCanceled, error) + + FilterUpkeepCheckDataSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepCheckDataSetIterator, error) + + WatchUpkeepCheckDataSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepCheckDataSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepCheckDataSet(log types.Log) (*AutomationRegistryUpkeepCheckDataSet, error) + + FilterUpkeepGasLimitSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepGasLimitSetIterator, error) + + WatchUpkeepGasLimitSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepGasLimitSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepGasLimitSet(log types.Log) (*AutomationRegistryUpkeepGasLimitSet, error) + + FilterUpkeepMigrated(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepMigratedIterator, error) + + WatchUpkeepMigrated(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepMigrated, id []*big.Int) (event.Subscription, error) + + ParseUpkeepMigrated(log types.Log) (*AutomationRegistryUpkeepMigrated, error) + + FilterUpkeepOffchainConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepOffchainConfigSetIterator, error) + + WatchUpkeepOffchainConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepOffchainConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepOffchainConfigSet(log types.Log) (*AutomationRegistryUpkeepOffchainConfigSet, error) + + FilterUpkeepPaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepPausedIterator, error) + + WatchUpkeepPaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepPaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPaused(log types.Log) (*AutomationRegistryUpkeepPaused, error) + + FilterUpkeepPerformed(opts *bind.FilterOpts, id []*big.Int, success []bool) (*AutomationRegistryUpkeepPerformedIterator, error) + + WatchUpkeepPerformed(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepPerformed, id []*big.Int, success []bool) (event.Subscription, error) + + ParseUpkeepPerformed(log types.Log) (*AutomationRegistryUpkeepPerformed, error) + + FilterUpkeepPrivilegeConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepPrivilegeConfigSetIterator, error) + + WatchUpkeepPrivilegeConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepPrivilegeConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepPrivilegeConfigSet(log types.Log) (*AutomationRegistryUpkeepPrivilegeConfigSet, error) + + FilterUpkeepReceived(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepReceivedIterator, error) + + WatchUpkeepReceived(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepReceived, id []*big.Int) (event.Subscription, error) + + ParseUpkeepReceived(log types.Log) (*AutomationRegistryUpkeepReceived, error) + + FilterUpkeepRegistered(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepRegisteredIterator, error) + + WatchUpkeepRegistered(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepRegistered, id []*big.Int) (event.Subscription, error) + + ParseUpkeepRegistered(log types.Log) (*AutomationRegistryUpkeepRegistered, error) + + FilterUpkeepTriggerConfigSet(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepTriggerConfigSetIterator, error) + + WatchUpkeepTriggerConfigSet(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepTriggerConfigSet, id []*big.Int) (event.Subscription, error) + + ParseUpkeepTriggerConfigSet(log types.Log) (*AutomationRegistryUpkeepTriggerConfigSet, error) + + FilterUpkeepUnpaused(opts *bind.FilterOpts, id []*big.Int) (*AutomationRegistryUpkeepUnpausedIterator, error) + + WatchUpkeepUnpaused(opts *bind.WatchOpts, sink chan<- *AutomationRegistryUpkeepUnpaused, id []*big.Int) (event.Subscription, error) + + ParseUpkeepUnpaused(log types.Log) (*AutomationRegistryUpkeepUnpaused, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 394f4636667..e967964daa1 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -6,7 +6,9 @@ authorized_receiver: ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedR automation_consumer_benchmark: ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.abi ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.bin f52c76f1aaed4be541d82d97189d70f5aa027fc9838037dd7a7d21910c8c488e automation_forwarder_logic: ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.bin 15ae0c367297955fdab4b552dbb10e1f2be80a8fde0efec4a4d398693e9d72b5 automation_registrar_wrapper2_1: ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.bin eb06d853aab39d3196c593b03e555851cbe8386e0fe54a74c2479f62d14b3c42 +automation_registrar_wrapper2_2: ../../contracts/solc/v0.8.16/AutomationRegistrar2_2/AutomationRegistrar2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_2/AutomationRegistrar2_2.bin 1da3ede9459c097482de224a47025ef7d578c7a73bafdbe4d471989ca4ecb608 automation_utils_2_1: ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin 331bfa79685aee6ddf63b64c0747abee556c454cae3fb8175edff425b615d8aa +automation_utils_2_2: ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.abi ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.bin 4f8055e0d47c0fa88f545d0160b04666a0d7720da8fd1206405da0bcf410b426 batch_blockhash_store: ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.abi ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.bin 14356c48ef70f66ef74f22f644450dbf3b2a147c1b68deaa7e7d1eb8ffab15db batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.bin d0a54963260d8c1f1bbd984b758285e6027cfb5a7e42701bcb562ab123219332 batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin 7bb76ae241cf1b37b41920830b836cb99f1ad33efd7435ca2398ff6cd2fe5d48 @@ -22,6 +24,7 @@ flux_aggregator_wrapper: ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator gas_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin 4a5dcdac486d18fcd58e3488c15c1710ae76b977556a3f3191bd269a4bc75723 gas_wrapper_mock: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin a9b08f18da59125c6fc305855710241f3d35161b8b9f3e3f635a7b1d5c6da9c8 i_keeper_registry_master_wrapper_2_1: ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin 6501bb9bcf5048bab2737b00685c6984a24867e234ddf5b60a65904eee9a4ebc +i_keeper_registry_master_wrapper_2_2: ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.bin 69fe8b127b71739606600760fc8fb01b3af1e8dba786ba0d047d766416acf83b i_log_automation: ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin 296beccb6af655d6fc3a6e676b244831cce2da6688d3afc4f21f8738ae59e03e keeper_consumer_performance_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 keeper_consumer_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 2c6163b145082fbab74b7343577a9cec8fda8b0da9daccf2a82581b1f5a84b83 @@ -31,13 +34,16 @@ keeper_registrar_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/Keep keeper_registry_logic1_3: ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.bin 903f8b9c8e25425ca6d0b81b89e339d695a83630bfbfa24a6f3b38869676bc5a keeper_registry_logic2_0: ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.bin d69d2bc8e4844293dbc2d45abcddc50b84c88554ecccfa4fa77c0ca45ec80871 keeper_registry_logic_a_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.bin 77481ab75c9aa86a62a7b2a708599b5ea1a6346ed1c0def6d4826e7ae523f1ee +keeper_registry_logic_a_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.bin 202d0d7829daa0e6cb7b2cf55bacf5f0b0ea7d17ffe3d8241e5bf96a37f1b416 keeper_registry_logic_b_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.bin 467d10741a04601b136553a2b1c6ab37f2a65d809366faf03180a22ff26be215 +keeper_registry_logic_b_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin b1cca74805530042f1255b4cf50115c47e4186603d3f9d276bbe3773ad1d8285 keeper_registry_wrapper1_1: ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.abi ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.bin 6ce079f2738f015f7374673a2816e8e9787143d00b780ea7652c8aa9ad9e1e20 keeper_registry_wrapper1_1_mock: ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.abi ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.bin 98ddb3680e86359de3b5d17e648253ba29a84703f087a1b52237824003a8c6df keeper_registry_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.bin a40ff877dd7c280f984cbbb2b428e160662b0c295e881d5f778f941c0088ca22 keeper_registry_wrapper1_3: ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.bin d4dc760b767ae274ee25c4a604ea371e1fa603a7b6421b69efb2088ad9e8abb3 keeper_registry_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.bin c32dea7d5ef66b7c58ddc84ddf69aa44df1b3ae8601fbc271c95be4ff5853056 keeper_registry_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.bin 604e4a0cd980c713929b523b999462a3aa0ed06f96ff563a4c8566cf59c8445b +keeper_registry_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.bin 9242df418f129e08d4e2eb0ee02a67781d3261f9c303035d87864765eed35b9b keepers_vrf_consumer: ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin fa75572e689c9e84705c63e8dbe1b7b8aa1a8fe82d66356c4873d024bb9166e8 log_emitter: ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin 4b129ab93432c95ff9143f0631323e189887668889e0b36ccccf18a571e41ccf log_triggered_streams_lookup_wrapper: ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin f8da43a927c1a66238a9f4fd5d5dd7e280e361daa0444da1f7f79498ace901e1 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index d4bbbcab11c..b854d69970a 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -55,8 +55,14 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.bin KeeperRegistryLogicA keeper_registry_logic_a_wrapper_2_1 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.bin KeeperRegistryLogicB keeper_registry_logic_b_wrapper_2_1 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin IKeeperRegistryMaster i_keeper_registry_master_wrapper_2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin ILogAutomation i_log_automation //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin AutomationUtils automation_utils_2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationRegistrar2_2/AutomationRegistrar2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_2/AutomationRegistrar2_2.bin AutomationRegistrar automation_registrar_wrapper2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.bin AutomationRegistry keeper_registry_wrapper_2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.bin AutomationRegistryLogicA keeper_registry_logic_a_wrapper_2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin AutomationRegistryLogicB keeper_registry_logic_b_wrapper_2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.bin IAutomationRegistryMaster i_keeper_registry_master_wrapper_2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.abi ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.bin AutomationUtils automation_utils_2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin ILogAutomation i_log_automation //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.bin AutomationForwarderLogic automation_forwarder_logic //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin LogUpkeepCounter log_upkeep_counter_wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin SimpleLogUpkeepCounter simple_log_upkeep_counter_wrapper From 1d7e01b6b758cf97e9d46d1f1818728e4c90cecd Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 24 Jan 2024 13:15:39 -0500 Subject: [PATCH 205/234] Optimizes OCR2 Test Runtimes (#11875) * Parallel * Re-enable tests --- .github/workflows/integration-tests.yml | 10 ++++++---- integration-tests/smoke/ocr2_test.go | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 3ea98ddd8ce..376d49a8e18 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -418,6 +418,10 @@ jobs: fail-fast: false matrix: product: + - name: runlog + nodes: 2 + os: ubuntu-latest + pyroscope_env: "ci-smoke-runlog-evm-simulated" - name: cron nodes: 2 os: ubuntu-latest @@ -429,13 +433,11 @@ jobs: - name: ocr nodes: 2 os: ubuntu-latest - run: -run TestOCRJobReplacement file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr2 nodes: 6 os: ubuntu-latest - run: -run TestOCRv2JobReplacement file: ocr2 pyroscope_env: ci-smoke-ocr2-evm-simulated - name: ocr2 @@ -456,11 +458,11 @@ jobs: os: ubuntu-latest pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr - nodes: 1 + nodes: 2 os: ubuntu-latest pyroscope_env: ci-smoke-forwarder-ocr-evm-simulated - name: forwarders_ocr2 - nodes: 1 + nodes: 2 os: ubuntu-latest pyroscope_env: ci-smoke-forwarder-ocr-evm-simulated runs-on: ${{ matrix.product.os }} diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 0898dd69985..89dcfc187df 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -102,7 +102,7 @@ func TestOCRv2Basic(t *testing.T) { require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error starting new OCR2 round") + require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), @@ -189,7 +189,7 @@ func TestOCRv2Request(t *testing.T) { require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error starting new OCR2 round") + require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), @@ -211,7 +211,9 @@ func TestOCRv2Request(t *testing.T) { } } +// TestOCRv2JobReplacement tests that a func TestOCRv2JobReplacement(t *testing.T) { + t.Parallel() l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.OCR2) @@ -270,7 +272,7 @@ func TestOCRv2JobReplacement(t *testing.T) { require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error starting new OCR2 round") + require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), @@ -300,7 +302,7 @@ func TestOCRv2JobReplacement(t *testing.T) { require.NoError(t, err, "Error creating OCRv2 jobs") err = actions.WatchNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) - require.NoError(t, err, "Error starting new OCR2 round") + require.NoError(t, err, "Error watching for new OCR2 round") roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(15), roundData.Answer.Int64(), From d37c085a8cf2bbed2c073f63cd852c5f50d73fa3 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 24 Jan 2024 14:26:16 -0500 Subject: [PATCH 206/234] [TT-756] Marks Failing Remote Runner Tests as Failed (#11840) * TT-756 * Better failing for soak tests --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/scripts/entrypoint | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 69613eccb7a..6106cc0f9e8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 - github.com/smartcontractkit/chainlink-testing-framework v1.23.0 + github.com/smartcontractkit/chainlink-testing-framework v1.23.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index beadcb56af0..affdb688039 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1514,8 +1514,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8b github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= -github.com/smartcontractkit/chainlink-testing-framework v1.23.0 h1:x9xDkPCgukjU0GStjIb2x7qlLytycYujWKVLcG1YQow= -github.com/smartcontractkit/chainlink-testing-framework v1.23.0/go.mod h1:StIOdxvwd8AMO6xuBtmD6FQfJXktEn/mJJEr7728BTc= +github.com/smartcontractkit/chainlink-testing-framework v1.23.1 h1:3Ev/twUcnhoe/l3ZreSoR6fx/GKKlIHxdCT3449U2hI= +github.com/smartcontractkit/chainlink-testing-framework v1.23.1/go.mod h1:StIOdxvwd8AMO6xuBtmD6FQfJXktEn/mJJEr7728BTc= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/scripts/entrypoint b/integration-tests/scripts/entrypoint index d2297abaa98..8797765ab2c 100755 --- a/integration-tests/scripts/entrypoint +++ b/integration-tests/scripts/entrypoint @@ -26,6 +26,9 @@ echo "Test exit code: ${exit_code}" if [ $exit_code -eq 3 ]; then echo "Test was interrupted, exiting with 1 exit code to trigger K8s to restart" exit 1 # Exiting with non-zero status to trigger pod restart +else + echo "Test either panicked or had some sort of failure. We're exiting with a non-zero exit code so that K8s doesn't restart the pod." + echo "TEST_FAILED" fi # Sleep for the amount of time provided by the POST_RUN_SLEEP env var From d627fe96dfdc91b214ea94850fa63ffa3746b17f Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Wed, 24 Jan 2024 15:20:47 -0500 Subject: [PATCH 207/234] update common to remove data race in test (#11880) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c533d486f60..32dfd6a5818 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -19,7 +19,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index b956198b8f2..86c6bc84e53 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1169,8 +1169,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 h1:uSLn+JbClhldYn7zm0GEALGvi6KAfrY5Kp46IX9sy0E= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa h1:9g7e1C3295ALDK8Gs42fIKSSJfI+H1RoBmivGWTvIZo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/go.mod b/go.mod index 0eed0de9a0f..cbcad20919f 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 diff --git a/go.sum b/go.sum index 46a9a5343e5..c8f6620af85 100644 --- a/go.sum +++ b/go.sum @@ -1164,8 +1164,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 h1:uSLn+JbClhldYn7zm0GEALGvi6KAfrY5Kp46IX9sy0E= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa h1:9g7e1C3295ALDK8Gs42fIKSSJfI+H1RoBmivGWTvIZo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 6106cc0f9e8..cbcb47e38ec 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa github.com/smartcontractkit/chainlink-testing-framework v1.23.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index affdb688039..8fddde84880 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1502,8 +1502,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3 h1:uSLn+JbClhldYn7zm0GEALGvi6KAfrY5Kp46IX9sy0E= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240123141952-a879db9d23b3/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa h1:9g7e1C3295ALDK8Gs42fIKSSJfI+H1RoBmivGWTvIZo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= From cb785bb3ee7679b00382f5c8878ddd12fae00bab Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Wed, 24 Jan 2024 16:08:36 -0500 Subject: [PATCH 208/234] fix some warnings in automation contracts (#11867) --- .../src/v0.8/automation/dev/MercuryRegistry.sol | 8 ++++---- .../dev/MercuryRegistryBatchUpkeep.sol | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/src/v0.8/automation/dev/MercuryRegistry.sol b/contracts/src/v0.8/automation/dev/MercuryRegistry.sol index 9c14ce69f48..6a5dafc196d 100644 --- a/contracts/src/v0.8/automation/dev/MercuryRegistry.sol +++ b/contracts/src/v0.8/automation/dev/MercuryRegistry.sol @@ -1,9 +1,9 @@ pragma solidity 0.8.6; -import "../../shared/access/ConfirmedOwner.sol"; -import "../interfaces/AutomationCompatibleInterface.sol"; -import "../interfaces/StreamsLookupCompatibleInterface.sol"; -import "../../ChainSpecificUtil.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; +import {StreamsLookupCompatibleInterface} from "../interfaces/StreamsLookupCompatibleInterface.sol"; +import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol"; /*--------------------------------------------------------------------------------------------------------------------+ | Mercury + Automation | diff --git a/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol b/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol index 416b68f3a79..8fa32c8a68e 100644 --- a/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol +++ b/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol @@ -1,20 +1,20 @@ pragma solidity 0.8.6; -import "../../shared/access/ConfirmedOwner.sol"; -import "../interfaces/AutomationCompatibleInterface.sol"; -import "../interfaces/StreamsLookupCompatibleInterface.sol"; -import "./MercuryRegistry.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; +import {StreamsLookupCompatibleInterface} from "../interfaces/StreamsLookupCompatibleInterface.sol"; +import {MercuryRegistry} from "./MercuryRegistry.sol"; contract MercuryRegistryBatchUpkeep is ConfirmedOwner, AutomationCompatibleInterface, StreamsLookupCompatibleInterface { error BatchSizeTooLarge(uint256 batchsize, uint256 maxBatchSize); // Use a reasonable maximum batch size. Every Mercury report is ~750 bytes, too many reports // passed into a single batch could exceed the calldata or transaction size limit for some blockchains. - uint256 constant MAX_BATCH_SIZE = 50; + uint256 public constant MAX_BATCH_SIZE = 50; - MercuryRegistry immutable i_registry; // master registry, where feed data is stored + MercuryRegistry public immutable i_registry; // master registry, where feed data is stored - uint256 s_batchStart; // starting index of upkeep batch on the MercuryRegistry's s_feeds array, inclusive - uint256 s_batchEnd; // ending index of upkeep batch on the MercuryRegistry's s_feeds array, exclusive + uint256 public s_batchStart; // starting index of upkeep batch on the MercuryRegistry's s_feeds array, inclusive + uint256 public s_batchEnd; // ending index of upkeep batch on the MercuryRegistry's s_feeds array, exclusive constructor(address mercuryRegistry, uint256 batchStart, uint256 batchEnd) ConfirmedOwner(msg.sender) { i_registry = MercuryRegistry(mercuryRegistry); From 78e96d1063f22dd1fc33d979eb9d17be92c336bd Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 24 Jan 2024 22:36:00 +0100 Subject: [PATCH 209/234] run compatibility tests also in nethermind (#11816) * run compatibility tests also in nethermind * use latest ctf * check if a stronger machine fixes issues with erigon * run max 2 tests in parallel on free runner * run only ocrv2 plugins test * Update .github/workflows/client-compatibility-tests.yml Co-authored-by: Adam Hamrick --------- Co-authored-by: Adam Hamrick --- .../workflows/client-compatibility-tests.yml | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index 0334d32cae8..a96ace1b5a2 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -37,7 +37,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Chainlink Image uses: ./.github/actions/build-chainlink-image - with: + with: tag_suffix: "" dockerfile: core/chainlink.Dockerfile git_commit_sha: ${{ github.sha }} @@ -45,7 +45,7 @@ jobs: GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - + build-tests: environment: integration permissions: @@ -102,51 +102,49 @@ jobs: file: ocr client: geth timeout: 30m - pyroscope_env: ci-smoke-ocr-geth-simulated - # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE - # - name: ocr-nethermind - # test: TestOCRBasic - # file: ocr - # client: nethermind - # timeout: 30m - # pyroscope_env: ci-smoke-ocr-nethermind-simulated + pyroscope_env: ci-smoke-ocr-geth-simulated + - name: ocr-nethermind + test: TestOCRBasic + file: ocr + client: nethermind + timeout: 30m + pyroscope_env: ci-smoke-ocr-nethermind-simulated - name: ocr-besu test: TestOCRBasic file: ocr client: besu timeout: 30m - pyroscope_env: ci-smoke-ocr-besu-simulated + pyroscope_env: ci-smoke-ocr-besu-simulated - name: ocr-erigon test: TestOCRBasic file: ocr client: erigon timeout: 30m - pyroscope_env: ci-smoke-ocr-erigon-simulated + pyroscope_env: ci-smoke-ocr-erigon-simulated - name: ocr2-geth - test: TestOCRv2Basic + test: "^TestOCRv2Basic/plugins$" file: ocr2 client: geth timeout: 30m - pyroscope_env: ci-smoke-ocr2-geth-simulated - # Uncomment, when https://smartcontract-it.atlassian.net/browse/TT-753 is DONE - # - name: ocr2-nethermind - # test: TestOCRv2Basic - # file: ocr2 - # client: nethermind - # timeout: 30m - # pyroscope_env: ci-smoke-nethermind-evm-simulated + pyroscope_env: ci-smoke-ocr2-geth-simulated + - name: ocr2-nethermind + test: "^TestOCRv2Basic/plugins$" + file: ocr2 + client: nethermind + timeout: 30m + pyroscope_env: ci-smoke-nethermind-evm-simulated - name: ocr2-besu - test: TestOCRv2Basic + test: "^TestOCRv2Basic/plugins$" file: ocr2 client: besu - timeout: 30m - pyroscope_env: ci-smoke-ocr2-besu-simulated + timeout: 30m + pyroscope_env: ci-smoke-ocr2-besu-simulated - name: ocr2-erigon - test: TestOCRv2Basic + test: "^TestOCRv2Basic/plugins$" file: ocr2 client: erigon timeout: 60m - pyroscope_env: ci-smoke-ocr2-erigon-simulated + pyroscope_env: ci-smoke-ocr2-erigon-simulated runs-on: ubuntu-latest name: Client Compatibility Test ${{ matrix.name }} steps: @@ -175,8 +173,8 @@ jobs: toml_array_format="${toml_array_format%,}]" echo "$toml_array_format" } - - selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") if [ -n "$ETH2_EL_CLIENT" ]; then execution_layer="$ETH2_EL_CLIENT" @@ -188,12 +186,12 @@ jobs: pyroscope_enabled=true else pyroscope_enabled=false - fi + fi cat << EOF > config.toml [Network] selected_networks=$selected_networks - + [ChainlinkImage] image="$CHAINLINK_IMAGE" version="$CHAINLINK_VERSION" @@ -218,7 +216,7 @@ jobs: slots_per_epoch=2 addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] EOF - + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) echo ::add-mask::$BASE64_CONFIG_OVERRIDE echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV @@ -240,7 +238,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - + start-slack-thread: name: Start Slack Thread if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} From d4c518d57d006d1aeeec18bdd543cbaed961dc5f Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:00:03 -0800 Subject: [PATCH 210/234] release/2.8.0 -> develop (#11884) * Revert "make streams lookup modular (#11368)" (#11482) This reverts commit fa0f16ad0acf417db1186728560278a049357914. * core/services/relay/evm: start RequestRoundTracker; report full health (#11643) * core/services/relay/evm: start RequestRoundTracker; report full health * Tests round requests and implicit changes separately * Add test to CI * Fixes other OCR2 checks --------- Co-authored-by: Adam Hamrick (cherry picked from commit 7236361119339369127a488a7c238745e4c44099) * Document metrics renaming * Hotfix attempt for Canary : v2.8.0-rc0 upgrade fails on Polygon. (#11828) (#11839) * Remove start lock + add logging * Add loggs * Update tracker.go * Update tracker.go * Update common/txmgr/txmgr.go Co-authored-by: Jordan Krage * Update logging --------- Co-authored-by: Jordan Krage (cherry picked from commit 6133df8a2a8b527155a8a822d2924d5ca4bfd122) * Temporarily disable tracker (#11857) * Disable tracker * Remove all tracker calls (cherry picked from commit 5057899e96a1b914ca4b785eacf898827ab742fe) * Update CHANGELOG.md * Update docs/CHANGELOG.md Co-authored-by: Jordan Krage * Finalize date on changelog for 2.8.0 (#11881) --------- Co-authored-by: Lei Co-authored-by: Jordan Krage Co-authored-by: Dmytro Haidashenko Co-authored-by: Dylan Tinianov Co-authored-by: Dylan Tinianov --- docs/CHANGELOG.md | 23 +++++++++++++++++------ integration-tests/smoke/ocr2_test.go | 1 - 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 113ac50e701..5941ae8a747 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -36,11 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ServerPubKey = '...' ``` -## 2.8.0 - UNRELEASED + + +## 2.8.0 - 2024-01-24 ### Added -- Added a tracker component to the txmgr for tracking and gracefully handling abandoned transactions. Abandoned transactions occur when a fromAddress is removed from the keystore by a node operator. The tracker gives abandoned transactions a chance to be finalized on chain, or marks them as fatal_error if they are not finalized within a specified time to live (default 6hrs). - Added distributed tracing in the OpenTelemetry trace format to the node, currently focused at the LOOPP Plugin development effort. This includes a new set of `Tracing` TOML configurations. The default for collecting traces is off - you must explicitly enable traces and setup a valid OpenTelemetry collector. Refer to `.github/tracing/README.md` for more details. - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. - New prom metrics for mercury transmit queue: @@ -90,6 +91,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `PromReporter` no longer directly reads txm related status from the db, and instead uses the txStore API. - `L2Suggested` mode is now called `SuggestedPrice` - Console logs will now escape (non-whitespace) control characters +- Following EVM Pool metrics were renamed: + - `evm_pool_rpc_node_states` → `multi_node_states` + - `evm_pool_rpc_node_num_transitions_to_alive` → `pool_rpc_node_num_transitions_to_alive` + - `evm_pool_rpc_node_num_transitions_to_in_sync` → `pool_rpc_node_num_transitions_to_in_sync` + - `evm_pool_rpc_node_num_transitions_to_out_of_sync` → `pool_rpc_node_num_transitions_to_out_of_sync` + - `evm_pool_rpc_node_num_transitions_to_unreachable` → `pool_rpc_node_num_transitions_to_unreachable` + - `evm_pool_rpc_node_num_transitions_to_invalid_chain_id` → `pool_rpc_node_num_transitions_to_invalid_chain_id` + - `evm_pool_rpc_node_num_transitions_to_unusable` → `pool_rpc_node_num_transitions_to_unusable` + - `evm_pool_rpc_node_highest_seen_block` → `pool_rpc_node_highest_seen_block` + - `evm_pool_rpc_node_num_seen_blocks` → `pool_rpc_node_num_seen_blocks` + - `evm_pool_rpc_node_polls_total` → `pool_rpc_node_polls_total` + - `evm_pool_rpc_node_polls_failed` → `pool_rpc_node_polls_failed` + - `evm_pool_rpc_node_polls_success` → `pool_rpc_node_polls_success` ### Removed @@ -105,14 +119,11 @@ Starting in `v2.9.0`: - `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` will no longer be allowed. Any TOML configuration that sets this fields will prevent the node from booting. These fields will be replaced by `[[TelemetryIngress.Endpoints]]` - `P2P.V1` will no longer be supported and must not be set in TOML configuration in order to boot. Use `P2P.V2` instead. If you are using both, `V1` can simply be removed. -... - - ## 2.7.2 - 2023-12-14 ### Fixed -- Fixed a bug that caused nodes without OCR or OCR2 enabled to fail config validation if `P2P.V2` was not explicitly disabled. With this fix, NOPs will not have to make changes to their config. +- Fixed a bug that caused nodes without OCR or OCR2 enabled to fail config validation if `P2P.V2` was not explicitly disabled. With this fix, NOPs will not have to make changes to their config. ## 2.7.1 - 2023-11-21 diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 89dcfc187df..6d3cf796cea 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -211,7 +211,6 @@ func TestOCRv2Request(t *testing.T) { } } -// TestOCRv2JobReplacement tests that a func TestOCRv2JobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) From 70b94e94765ad14ffe9826b89a30ec3156100808 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Wed, 24 Jan 2024 19:33:19 -0500 Subject: [PATCH 211/234] add reorgProtectionEnabled feature flag in registry 2.2 (#11862) * add skipReorgProtection feature flag in registry 2.2 * update tests * fix lint error * fix tests and add docs * remove unused param * prettier * update * prettier * put the boolean in hot vars * run prettier * rename * gen wrappers * add foundry test for 2.2 * fix tests * remove only * remove unnecessary change --- .../v2_2/IAutomationRegistryMaster.sol | 3 +- .../dev/test/AutomationRegistry2_2.t.sol | 189 ++++++++++++++++++ .../v0.8/automation/dev/test/BaseTest.t.sol | 13 ++ .../dev/v2_2/AutomationRegistry2_2.sol | 6 +- .../dev/v2_2/AutomationRegistryBase2_2.sol | 44 ++-- .../dev/v2_2/AutomationRegistryLogicB2_2.sol | 3 +- .../automation/AutomationRegistry2_2.test.ts | 7 + .../automation_utils_2_2.go | 5 +- .../i_keeper_registry_master_wrapper_2_2.go | 3 +- .../keeper_registry_logic_a_wrapper_2_2.go | 2 +- .../keeper_registry_logic_b_wrapper_2_2.go | 5 +- .../keeper_registry_wrapper_2_2.go | 5 +- ...rapper-dependency-versions-do-not-edit.txt | 10 +- 13 files changed, 261 insertions(+), 34 deletions(-) create mode 100644 contracts/src/v0.8/automation/dev/test/AutomationRegistry2_2.t.sol create mode 100644 contracts/src/v0.8/automation/dev/test/BaseTest.t.sol diff --git a/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol b/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol index a72177bef2f..e67622a3779 100644 --- a/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol +++ b/contracts/src/v0.8/automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol @@ -281,6 +281,7 @@ interface AutomationRegistryBase2_2 { address transcoder; address[] registrars; address upkeepPrivilegeManager; + bool reorgProtectionEnabled; } struct State { @@ -312,5 +313,5 @@ interface AutomationRegistryBase2_2 { // THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON: /* -[{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_2","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MaxCheckDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MaxPerformDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"PaymentGreaterThanAllLINK","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"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":"address[]","name":"signers","type":"address[]"},{"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":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"OwnerFundsWithdrawn","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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","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":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct AutomationRegistryBase2_2.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_2","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum AutomationRegistryBase2_2.Mode","name":"mode","type":"uint8"},{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkNativeFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkNativeFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMode","outputs":[{"internalType":"enum AutomationRegistryBase2_2.Mode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum AutomationRegistryBase2_2.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerPerformByteGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct AutomationRegistryBase2_2.State","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct AutomationRegistryBase2_2.OnchainConfig","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct AutomationRegistryBase2_2.UpkeepInfo","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum AutomationRegistryBase2_2.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepTranscoderVersion","outputs":[{"internalType":"enum UpkeepFormat","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwnerFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] +[{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_2","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MaxCheckDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MaxPerformDataSizeCanOnlyIncrease","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"PaymentGreaterThanAllLINK","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"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":"address[]","name":"signers","type":"address[]"},{"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":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"OwnerFundsWithdrawn","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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","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":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"}],"internalType":"struct AutomationRegistryBase2_2.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_2","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkNative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_2.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum AutomationRegistryBase2_2.Mode","name":"mode","type":"uint8"},{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkNativeFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkNativeFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMode","outputs":[{"internalType":"enum AutomationRegistryBase2_2.Mode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum AutomationRegistryBase2_2.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerPerformByteGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct AutomationRegistryBase2_2.State","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"}],"internalType":"struct AutomationRegistryBase2_2.OnchainConfig","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum AutomationRegistryBase2_2.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct AutomationRegistryBase2_2.UpkeepInfo","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum AutomationRegistryBase2_2.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepTranscoderVersion","outputs":[{"internalType":"enum UpkeepFormat","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwnerFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] */ diff --git a/contracts/src/v0.8/automation/dev/test/AutomationRegistry2_2.t.sol b/contracts/src/v0.8/automation/dev/test/AutomationRegistry2_2.t.sol new file mode 100644 index 00000000000..e6087ce6acd --- /dev/null +++ b/contracts/src/v0.8/automation/dev/test/AutomationRegistry2_2.t.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.16; + +import {AutomationForwarderLogic} from "../../AutomationForwarderLogic.sol"; +import {BaseTest} from "./BaseTest.t.sol"; +import {AutomationRegistry2_2} from "../v2_2/AutomationRegistry2_2.sol"; +import {AutomationRegistryBase2_2} from "../v2_2/AutomationRegistryBase2_2.sol"; +import {AutomationRegistryLogicA2_2} from "../v2_2/AutomationRegistryLogicA2_2.sol"; +import {AutomationRegistryLogicB2_2} from "../v2_2/AutomationRegistryLogicB2_2.sol"; +import {IAutomationRegistryMaster} from "../interfaces/v2_2/IAutomationRegistryMaster.sol"; + +contract AutomationRegistry2_2_SetUp is BaseTest { + address internal constant LINK_ETH_FEED = 0x1111111111111111111111111111111111111110; + address internal constant FAST_GAS_FEED = 0x1111111111111111111111111111111111111112; + address internal constant LINK_TOKEN = 0x1111111111111111111111111111111111111113; + + // Signer private keys used for these test + uint256 internal constant PRIVATE0 = 0x7b2e97fe057e6de99d6872a2ef2abf52c9b4469bc848c2465ac3fcd8d336e81d; + uint256 internal constant PRIVATE1 = 0xab56160806b05ef1796789248e1d7f34a6465c5280899159d645218cd216cee6; + uint256 internal constant PRIVATE2 = 0x6ec7caa8406a49b76736602810e0a2871959fbbb675e23a8590839e4717f1f7f; + uint256 internal constant PRIVATE3 = 0x80f14b11da94ae7f29d9a7713ea13dc838e31960a5c0f2baf45ed458947b730a; + + uint64 internal constant OFFCHAIN_CONFIG_VERSION = 30; // 2 for OCR2 + uint8 internal constant F = 1; + + address[] internal s_valid_signers; + address[] internal s_valid_transmitters; + address[] internal s_registrars; + + function setUp() public override { + 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 + + s_registrars = new address[](1); + s_registrars[0] = 0x3a0eDE26aa188BFE00b9A0C9A431A1a0CA5f7966; + } + + function deployRegistry2_2(AutomationRegistryBase2_2.Mode mode) public returns (IAutomationRegistryMaster) { + AutomationForwarderLogic forwarderLogic = new AutomationForwarderLogic(); + AutomationRegistryLogicB2_2 logicB2_2 = new AutomationRegistryLogicB2_2( + mode, + LINK_TOKEN, + LINK_ETH_FEED, + FAST_GAS_FEED, + address(forwarderLogic) + ); + AutomationRegistryLogicA2_2 logicA2_2 = new AutomationRegistryLogicA2_2(logicB2_2); + IAutomationRegistryMaster registry2_2 = IAutomationRegistryMaster( + address(new AutomationRegistry2_2(AutomationRegistryLogicB2_2(address(logicA2_2)))) + ); + return registry2_2; + } +} + +contract AutomationRegistry2_2_LatestConfigDetails is AutomationRegistry2_2_SetUp { + function testGet() public { + IAutomationRegistryMaster registry = IAutomationRegistryMaster( + address(deployRegistry2_2(AutomationRegistryBase2_2.Mode(0))) + ); + (uint32 configCount, uint32 blockNumber, bytes32 configDigest) = registry.latestConfigDetails(); + assertEq(configCount, 0); + assertEq(blockNumber, 0); + assertEq(configDigest, ""); + } +} + +contract AutomationRegistry2_2_SetConfig is AutomationRegistry2_2_SetUp { + event ConfigSet( + uint32 previousConfigBlockNumber, + bytes32 configDigest, + uint64 configCount, + address[] signers, + address[] transmitters, + uint8 f, + bytes onchainConfig, + uint64 offchainConfigVersion, + bytes offchainConfig + ); + + function testSetConfigSuccess() public { + IAutomationRegistryMaster registry = IAutomationRegistryMaster( + address(deployRegistry2_2(AutomationRegistryBase2_2.Mode(0))) + ); + (uint32 configCount, , ) = registry.latestConfigDetails(); + assertEq(configCount, 0); + + AutomationRegistryBase2_2.OnchainConfig memory cfg = AutomationRegistryBase2_2.OnchainConfig({ + paymentPremiumPPB: 10_000, + flatFeeMicroLink: 40_000, + checkGasLimit: 5_000_000, + stalenessSeconds: 90_000, + gasCeilingMultiplier: 0, + minUpkeepSpend: 0, + maxPerformGas: 10_000_000, + maxCheckDataSize: 5_000, + maxPerformDataSize: 5_000, + maxRevertDataSize: 5_000, + fallbackGasPrice: 20_000_000_000, + fallbackLinkPrice: 200_000_000_000, + transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c, + registrars: s_registrars, + upkeepPrivilegeManager: 0xD9c855F08A7e460691F41bBDDe6eC310bc0593D8, + reorgProtectionEnabled: true + }); + bytes memory onchainConfigBytes = abi.encode(cfg); + + uint256 a = 1234; + address b = address(0); + bytes memory offchainConfigBytes = abi.encode(a, b); + bytes32 configDigest = _configDigestFromConfigData( + block.chainid, + address(registry), + ++configCount, + s_valid_signers, + s_valid_transmitters, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + vm.expectEmit(); + emit ConfigSet( + 0, + configDigest, + configCount, + s_valid_signers, + s_valid_transmitters, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + registry.setConfig( + s_valid_signers, + s_valid_transmitters, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + (, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState(); + + assertEq(signers, s_valid_signers); + assertEq(transmitters, s_valid_transmitters); + assertEq(f, F); + } + + 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)); + } +} diff --git a/contracts/src/v0.8/automation/dev/test/BaseTest.t.sol b/contracts/src/v0.8/automation/dev/test/BaseTest.t.sol new file mode 100644 index 00000000000..3d28c4c35b2 --- /dev/null +++ b/contracts/src/v0.8/automation/dev/test/BaseTest.t.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import "forge-std/Test.sol"; + +contract BaseTest is Test { + address internal OWNER = 0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e; + + function setUp() public virtual { + vm.startPrank(OWNER); + deal(OWNER, 1e20); + } +} diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol index e21387936e0..e3b429518cf 100644 --- a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistry2_2.sol @@ -101,7 +101,8 @@ contract AutomationRegistry2_2 is AutomationRegistryBase2_2, OCR2Abstract, Chain (upkeepTransmitInfo[i].earlyChecksPassed, upkeepTransmitInfo[i].dedupID) = _prePerformChecks( report.upkeepIds[i], report.triggers[i], - upkeepTransmitInfo[i] + upkeepTransmitInfo[i], + hotVars ); if (upkeepTransmitInfo[i].earlyChecksPassed) { @@ -308,7 +309,8 @@ contract AutomationRegistry2_2 is AutomationRegistryBase2_2, OCR2Abstract, Chain paused: s_hotVars.paused, reentrancyGuard: s_hotVars.reentrancyGuard, totalPremium: totalPremium, - latestEpoch: 0 // DON restarts epoch + latestEpoch: 0, // DON restarts epoch + reorgProtectionEnabled: onchainConfig.reorgProtectionEnabled }); s_storage = Storage({ diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol index e4b6e61d6f7..43fd3eb18e2 100644 --- a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryBase2_2.sol @@ -208,6 +208,7 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi * @member transcoder address of the transcoder contract * @member registrars addresses of the registrar contracts * @member upkeepPrivilegeManager address which can set privilege for upkeeps + * @member reorgProtectionEnabled if this registry will enable re-org protection checks */ struct OnchainConfig { uint32 paymentPremiumPPB; @@ -225,6 +226,7 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi address transcoder; address[] registrars; address upkeepPrivilegeManager; + bool reorgProtectionEnabled; } /** @@ -307,16 +309,16 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi /// @dev Config + State storage struct which is on hot transmit path struct HotVars { - uint8 f; // maximum number of faulty oracles - uint32 paymentPremiumPPB; // premium percentage charged to user over tx cost - uint32 flatFeeMicroLink; // flat fee charged to user for every perform - uint24 stalenessSeconds; // Staleness tolerance for feeds - uint16 gasCeilingMultiplier; // multiplier on top of fast gas feed for upper bound - bool paused; // pause switch for all upkeeps in the registry - bool reentrancyGuard; // guard against reentrancy - uint96 totalPremium; // total historical payment to oracles for premium - uint32 latestEpoch; // latest epoch for which a report was transmitted - // 1 EVM word full + uint96 totalPremium; // ─────────╮ total historical payment to oracles for premium + uint32 paymentPremiumPPB; // │ premium percentage charged to user over tx cost + uint32 flatFeeMicroLink; // │ flat fee charged to user for every perform + uint32 latestEpoch; // │ latest epoch for which a report was transmitted + uint24 stalenessSeconds; // │ Staleness tolerance for feeds + uint16 gasCeilingMultiplier; // │ multiplier on top of fast gas feed for upper bound + uint8 f; // │ maximum number of faulty oracles + bool paused; // │ pause switch for all upkeeps in the registry + bool reentrancyGuard; // ────────╯ guard against reentrancy + bool reorgProtectionEnabled; // if this registry should enable re-org protection mechanism } /// @dev Config + State storage struct which is not on hot transmit path @@ -733,14 +735,15 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi function _prePerformChecks( uint256 upkeepId, bytes memory rawTrigger, - UpkeepTransmitInfo memory transmitInfo + UpkeepTransmitInfo memory transmitInfo, + HotVars memory hotVars ) internal returns (bool, bytes32) { bytes32 dedupID; if (transmitInfo.triggerType == Trigger.CONDITION) { - if (!_validateConditionalTrigger(upkeepId, rawTrigger, transmitInfo)) return (false, dedupID); + if (!_validateConditionalTrigger(upkeepId, rawTrigger, transmitInfo, hotVars)) return (false, dedupID); } else if (transmitInfo.triggerType == Trigger.LOG) { bool valid; - (valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger); + (valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger, hotVars); if (!valid) return (false, dedupID); } else { revert InvalidTriggerType(); @@ -765,7 +768,8 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi function _validateConditionalTrigger( uint256 upkeepId, bytes memory rawTrigger, - UpkeepTransmitInfo memory transmitInfo + UpkeepTransmitInfo memory transmitInfo, + HotVars memory hotVars ) internal returns (bool) { ConditionalTrigger memory trigger = abi.decode(rawTrigger, (ConditionalTrigger)); if (trigger.blockNum < transmitInfo.upkeep.lastPerformedBlockNumber) { @@ -774,7 +778,8 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi return false; } if ( - (trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash) || + (hotVars.reorgProtectionEnabled && + (trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash)) || trigger.blockNum >= _blockNum() ) { // There are two cases of reorged report @@ -789,11 +794,16 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi return true; } - function _validateLogTrigger(uint256 upkeepId, bytes memory rawTrigger) internal returns (bool, bytes32) { + function _validateLogTrigger( + uint256 upkeepId, + bytes memory rawTrigger, + HotVars memory hotVars + ) internal returns (bool, bytes32) { LogTrigger memory trigger = abi.decode(rawTrigger, (LogTrigger)); bytes32 dedupID = keccak256(abi.encodePacked(upkeepId, trigger.logBlockHash, trigger.txHash, trigger.logIndex)); if ( - (trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash) || + (hotVars.reorgProtectionEnabled && + (trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash)) || trigger.blockNum >= _blockNum() ) { // Reorg protection is same as conditional trigger upkeeps diff --git a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol index 0724fbd53f0..04fec3d5f2b 100644 --- a/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol +++ b/contracts/src/v0.8/automation/dev/v2_2/AutomationRegistryLogicB2_2.sol @@ -437,7 +437,8 @@ contract AutomationRegistryLogicB2_2 is AutomationRegistryBase2_2 { fallbackLinkPrice: s_fallbackLinkPrice, transcoder: s_storage.transcoder, registrars: s_registrars.values(), - upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager + upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager, + reorgProtectionEnabled: s_hotVars.reorgProtectionEnabled }); return (state, config, s_signersList, s_transmittersList, s_hotVars.f); diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts index bb460aa60c9..740e4ac1c27 100644 --- a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts +++ b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts @@ -628,6 +628,7 @@ describe('AutomationRegistry2_2', () => { transcoder: transcoder.address, registrars: [], upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, }), offchainVersion, offchainBytes, @@ -880,6 +881,7 @@ describe('AutomationRegistry2_2', () => { transcoder: transcoder.address, registrars: [], upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, } baseConfig = [ @@ -3544,6 +3546,7 @@ describe('AutomationRegistry2_2', () => { transcoder: newTranscoder, registrars: newRegistrars, upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, } it('reverts when called by anyone but the proposed owner', async () => { @@ -4565,6 +4568,7 @@ describe('AutomationRegistry2_2', () => { transcoder: transcoder.address, registrars: [], upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, }, offchainVersion, offchainBytes, @@ -5209,6 +5213,7 @@ describe('AutomationRegistry2_2', () => { transcoder: transcoder.address, registrars: [], upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, }, offchainVersion, offchainBytes, @@ -5262,6 +5267,7 @@ describe('AutomationRegistry2_2', () => { transcoder: transcoder.address, registrars: [], upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, }, offchainVersion, offchainBytes, @@ -5310,6 +5316,7 @@ describe('AutomationRegistry2_2', () => { transcoder: transcoder.address, registrars: [], upkeepPrivilegeManager: upkeepManager, + reorgProtectionEnabled: true, }, offchainVersion, offchainBytes, diff --git a/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go b/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go index 5eb64110247..1239d6baf7a 100644 --- a/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go +++ b/core/gethwrappers/generated/automation_utils_2_2/automation_utils_2_2.go @@ -57,6 +57,7 @@ type AutomationRegistryBase22OnchainConfig struct { Transcoder common.Address Registrars []common.Address UpkeepPrivilegeManager common.Address + ReorgProtectionEnabled bool } type AutomationRegistryBase22Report struct { @@ -89,8 +90,8 @@ type LogTriggerConfig struct { } var AutomationUtilsMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNum\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"internalType\":\"structAutomationRegistryBase2_2.ConditionalTrigger\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_conditionalTrigger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_log\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"logBlockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"logIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNum\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"internalType\":\"structAutomationRegistryBase2_2.LogTrigger\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_logTrigger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"filterSelector\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"topic0\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic1\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic2\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic3\",\"type\":\"bytes32\"}],\"internalType\":\"structLogTriggerConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_logTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_onChainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"upkeepIds\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"gasLimits\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"triggers\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes[]\",\"name\":\"performDatas\",\"type\":\"bytes[]\"}],\"internalType\":\"structAutomationRegistryBase2_2.Report\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b506108ca806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c8063776f306111610050578063776f3061146100a6578063e65d6546146100b4578063e9720a49146100c257600080fd5b806321f373d7146100775780632ff92a811461008a5780634b6df29414610098575b600080fd5b6100886100853660046101e8565b50565b005b610088610085366004610363565b6100886100853660046104bd565b610088610085366004610514565b6100886100853660046106fb565b6100886100853660046107e8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101e0810167ffffffffffffffff81118282101715610123576101236100d0565b60405290565b60405160c0810167ffffffffffffffff81118282101715610123576101236100d0565b604051610100810167ffffffffffffffff81118282101715610123576101236100d0565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156101b7576101b76100d0565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101e357600080fd5b919050565b600060c082840312156101fa57600080fd5b60405160c0810181811067ffffffffffffffff8211171561021d5761021d6100d0565b604052610229836101bf565b8152602083013560ff8116811461023f57600080fd5b8060208301525060408301356040820152606083013560608201526080830135608082015260a083013560a08201528091505092915050565b803563ffffffff811681146101e357600080fd5b803562ffffff811681146101e357600080fd5b803561ffff811681146101e357600080fd5b80356bffffffffffffffffffffffff811681146101e357600080fd5b600067ffffffffffffffff8211156102e7576102e76100d0565b5060051b60200190565b600082601f83011261030257600080fd5b81356020610317610312836102cd565b610170565b82815260059290921b8401810191818101908684111561033657600080fd5b8286015b848110156103585761034b816101bf565b835291830191830161033a565b509695505050505050565b60006020828403121561037557600080fd5b813567ffffffffffffffff8082111561038d57600080fd5b908301906101e082860312156103a257600080fd5b6103aa6100ff565b6103b383610278565b81526103c160208401610278565b60208201526103d260408401610278565b60408201526103e36060840161028c565b60608201526103f46080840161029f565b608082015261040560a084016102b1565b60a082015261041660c08401610278565b60c082015261042760e08401610278565b60e082015261010061043a818501610278565b9082015261012061044c848201610278565b90820152610140838101359082015261016080840135908201526101806104748185016101bf565b908201526101a0838101358381111561048c57600080fd5b610498888287016102f1565b8284015250506101c091506104ae8284016101bf565b91810191909152949350505050565b6000604082840312156104cf57600080fd5b6040516040810181811067ffffffffffffffff821117156104f2576104f26100d0565b6040526104fe83610278565b8152602083013560208201528091505092915050565b600060a0828403121561052657600080fd5b60405160a0810181811067ffffffffffffffff82111715610549576105496100d0565b8060405250823581526020830135602082015261056860408401610278565b604082015261057960608401610278565b6060820152608083013560808201528091505092915050565b600082601f8301126105a357600080fd5b813560206105b3610312836102cd565b82815260059290921b840181019181810190868411156105d257600080fd5b8286015b8481101561035857803583529183019183016105d6565b600082601f8301126105fe57600080fd5b813567ffffffffffffffff811115610618576106186100d0565b61064960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610170565b81815284602083860101111561065e57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261068c57600080fd5b8135602061069c610312836102cd565b82815260059290921b840181019181810190868411156106bb57600080fd5b8286015b8481101561035857803567ffffffffffffffff8111156106df5760008081fd5b6106ed8986838b01016105ed565b8452509183019183016106bf565b60006020828403121561070d57600080fd5b813567ffffffffffffffff8082111561072557600080fd5b9083019060c0828603121561073957600080fd5b610741610129565b823581526020830135602082015260408301358281111561076157600080fd5b61076d87828601610592565b60408301525060608301358281111561078557600080fd5b61079187828601610592565b6060830152506080830135828111156107a957600080fd5b6107b58782860161067b565b60808301525060a0830135828111156107cd57600080fd5b6107d98782860161067b565b60a08301525095945050505050565b6000602082840312156107fa57600080fd5b813567ffffffffffffffff8082111561081257600080fd5b90830190610100828603121561082757600080fd5b61082f61014c565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261086760a084016101bf565b60a082015260c08301358281111561087e57600080fd5b61088a87828601610592565b60c08301525060e0830135828111156108a257600080fd5b6108ae878286016105ed565b60e0830152509594505050505056fea164736f6c6343000810000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNum\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"internalType\":\"structAutomationRegistryBase2_2.ConditionalTrigger\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_conditionalTrigger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_log\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"logBlockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"logIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNum\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"internalType\":\"structAutomationRegistryBase2_2.LogTrigger\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_logTrigger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"filterSelector\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"topic0\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic1\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic2\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"topic3\",\"type\":\"bytes32\"}],\"internalType\":\"structLogTriggerConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_logTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_onChainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"upkeepIds\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"gasLimits\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"triggers\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes[]\",\"name\":\"performDatas\",\"type\":\"bytes[]\"}],\"internalType\":\"structAutomationRegistryBase2_2.Report\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"_report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b506108ee806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c8063e32442c911610050578063e32442c9146100a6578063e65d6546146100b4578063e9720a49146100c257600080fd5b806321f373d7146100775780634b6df2941461008a578063776f306114610098575b600080fd5b6100886100853660046101e8565b50565b005b61008861008536600461028c565b6100886100853660046102e3565b610088610085366004610448565b61008861008536600461071f565b61008861008536600461080c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610200810167ffffffffffffffff81118282101715610123576101236100d0565b60405290565b60405160c0810167ffffffffffffffff81118282101715610123576101236100d0565b604051610100810167ffffffffffffffff81118282101715610123576101236100d0565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156101b7576101b76100d0565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101e357600080fd5b919050565b600060c082840312156101fa57600080fd5b60405160c0810181811067ffffffffffffffff8211171561021d5761021d6100d0565b604052610229836101bf565b8152602083013560ff8116811461023f57600080fd5b8060208301525060408301356040820152606083013560608201526080830135608082015260a083013560a08201528091505092915050565b803563ffffffff811681146101e357600080fd5b60006040828403121561029e57600080fd5b6040516040810181811067ffffffffffffffff821117156102c1576102c16100d0565b6040526102cd83610278565b8152602083013560208201528091505092915050565b600060a082840312156102f557600080fd5b60405160a0810181811067ffffffffffffffff82111715610318576103186100d0565b8060405250823581526020830135602082015261033760408401610278565b604082015261034860608401610278565b6060820152608083013560808201528091505092915050565b803562ffffff811681146101e357600080fd5b803561ffff811681146101e357600080fd5b80356bffffffffffffffffffffffff811681146101e357600080fd5b600067ffffffffffffffff8211156103bc576103bc6100d0565b5060051b60200190565b600082601f8301126103d757600080fd5b813560206103ec6103e7836103a2565b610170565b82815260059290921b8401810191818101908684111561040b57600080fd5b8286015b8481101561042d57610420816101bf565b835291830191830161040f565b509695505050505050565b803580151581146101e357600080fd5b60006020828403121561045a57600080fd5b813567ffffffffffffffff8082111561047257600080fd5b90830190610200828603121561048757600080fd5b61048f6100ff565b61049883610278565b81526104a660208401610278565b60208201526104b760408401610278565b60408201526104c860608401610361565b60608201526104d960808401610374565b60808201526104ea60a08401610386565b60a08201526104fb60c08401610278565b60c082015261050c60e08401610278565b60e082015261010061051f818501610278565b90820152610120610531848201610278565b90820152610140838101359082015261016080840135908201526101806105598185016101bf565b908201526101a0838101358381111561057157600080fd5b61057d888287016103c6565b8284015250506101c091506105938284016101bf565b828201526101e091506105a7828401610438565b91810191909152949350505050565b600082601f8301126105c757600080fd5b813560206105d76103e7836103a2565b82815260059290921b840181019181810190868411156105f657600080fd5b8286015b8481101561042d57803583529183019183016105fa565b600082601f83011261062257600080fd5b813567ffffffffffffffff81111561063c5761063c6100d0565b61066d60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610170565b81815284602083860101111561068257600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f8301126106b057600080fd5b813560206106c06103e7836103a2565b82815260059290921b840181019181810190868411156106df57600080fd5b8286015b8481101561042d57803567ffffffffffffffff8111156107035760008081fd5b6107118986838b0101610611565b8452509183019183016106e3565b60006020828403121561073157600080fd5b813567ffffffffffffffff8082111561074957600080fd5b9083019060c0828603121561075d57600080fd5b610765610129565b823581526020830135602082015260408301358281111561078557600080fd5b610791878286016105b6565b6040830152506060830135828111156107a957600080fd5b6107b5878286016105b6565b6060830152506080830135828111156107cd57600080fd5b6107d98782860161069f565b60808301525060a0830135828111156107f157600080fd5b6107fd8782860161069f565b60a08301525095945050505050565b60006020828403121561081e57600080fd5b813567ffffffffffffffff8082111561083657600080fd5b90830190610100828603121561084b57600080fd5b61085361014c565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261088b60a084016101bf565b60a082015260c0830135828111156108a257600080fd5b6108ae878286016105b6565b60c08301525060e0830135828111156108c657600080fd5b6108d287828601610611565b60e0830152509594505050505056fea164736f6c6343000810000a", } var AutomationUtilsABI = AutomationUtilsMetaData.ABI diff --git a/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go b/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go index 964e54f1893..fe514342216 100644 --- a/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go +++ b/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_2/i_keeper_registry_master_wrapper_2_2.go @@ -46,6 +46,7 @@ type AutomationRegistryBase22OnchainConfig struct { Transcoder common.Address Registrars []common.Address UpkeepPrivilegeManager common.Address + ReorgProtectionEnabled bool } type AutomationRegistryBase22State struct { @@ -75,7 +76,7 @@ type AutomationRegistryBase22UpkeepInfo struct { } var IAutomationRegistryMasterMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"addFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancelUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"values\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"checkCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"executeCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkNativeFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMode\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.State\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structAutomationRegistryBase2_2.UpkeepInfo\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"migrateUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedUpkeeps\",\"type\":\"bytes\"}],\"name\":\"receiveUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"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\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepTranscoderVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawOwnerFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"addFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancelUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"values\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"checkCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"executeCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkNativeFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMode\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.State\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structAutomationRegistryBase2_2.UpkeepInfo\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"migrateUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedUpkeeps\",\"type\":\"bytes\"}],\"name\":\"receiveUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"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\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepTranscoderVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawOwnerFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } var IAutomationRegistryMasterABI = IAutomationRegistryMasterMetaData.ABI diff --git a/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go b/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go index 31aa106358c..064fc2eeca9 100644 --- a/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go +++ b/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_2/keeper_registry_logic_a_wrapper_2_2.go @@ -32,7 +32,7 @@ var ( var AutomationRegistryLogicAMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicB2_2\",\"name\":\"logicB\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"addFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancelUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"values\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"checkCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkNative\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"executeCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_2.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"migrateUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedUpkeeps\",\"type\":\"bytes\"}],\"name\":\"receiveUpkeeps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"registerUpkeep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101406040523480156200001257600080fd5b50604051620061d1380380620061d18339810160408190526200003591620003df565b80816001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b919062000406565b826001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003df565b836001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003df565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003df565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003df565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b9816200031b565b505050846002811115620002d157620002d162000429565b60e0816002811115620002e857620002e862000429565b9052506001600160a01b0393841660805291831660a052821660c0528116610100529190911661012052506200043f9050565b336001600160a01b03821603620003755760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003dc57600080fd5b50565b600060208284031215620003f257600080fd5b8151620003ff81620003c6565b9392505050565b6000602082840312156200041957600080fd5b815160038110620003ff57600080fd5b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e0516101005161012051615d18620004b96000396000818161010e01526101a90152600081816103e10152611fa10152600081816135370152818161376d015281816139b50152613b5d015260006130e1015260006131c5015260008181611de301526123af0152615d186000f3fe60806040523480156200001157600080fd5b50600436106200010c5760003560e01c806385c1b0ba11620000a5578063c8048022116200006f578063c804802214620002b7578063ce7dc5b414620002ce578063f2fde38b14620002e5578063f7d334ba14620002fc576200010c565b806385c1b0ba14620002535780638da5cb5b146200026a5780638e86139b1462000289578063948108f714620002a0576200010c565b80634ee88d3511620000e75780634ee88d3514620001ef5780636ded9eae146200020657806371791aa0146200021d57806379ba50971462000249576200010c565b806328f32f38146200015457806329c5efad146200017e578063349e8cca14620001a7575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156200014d573d6000f35b3d6000fd5b005b6200016b62000165366004620041ea565b62000313565b6040519081526020015b60405180910390f35b620001956200018f366004620042d0565b6200068c565b604051620001759493929190620043f8565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200162000175565b620001526200020036600462004435565b62000930565b6200016b6200021736600462004485565b62000998565b620002346200022e366004620042d0565b620009fe565b60405162000175979695949392919062004538565b62000152620010f0565b62000152620002643660046200458a565b620011f3565b60005473ffffffffffffffffffffffffffffffffffffffff16620001c9565b620001526200029a36600462004617565b62001e64565b62000152620002b13660046200467a565b620021ec565b62000152620002c8366004620046a9565b6200247f565b62000195620002df3660046200477f565b62002846565b62000152620002f6366004620047f6565b62002916565b620002346200030d366004620046a9565b6200292e565b6000805473ffffffffffffffffffffffffffffffffffffffff163314801590620003475750620003456009336200296c565b155b156200037f576040517fd48b678b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff89163b620003ce576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b620003d986620029a0565b9050600089307f00000000000000000000000000000000000000000000000000000000000000006040516200040e9062003f7b565b73ffffffffffffffffffffffffffffffffffffffff938416815291831660208301529091166040820152606001604051809103906000f08015801562000458573d6000803e3d6000fd5b5090506200051f826040518060e001604052806000151581526020018c63ffffffff16815260200163ffffffff801681526020018473ffffffffffffffffffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff168152602001600063ffffffff168152508a89898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b92508a915062002b449050565b6014805474010000000000000000000000000000000000000000900463ffffffff1690806200054e8362004845565b91906101000a81548163ffffffff021916908363ffffffff16021790555050817fbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d0128a8a604051620005c792919063ffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a2817fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d878760405162000603929190620048b4565b60405180910390a2817f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664856040516200063d9190620048ca565b60405180910390a2817f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf485084604051620006779190620048ca565b60405180910390a25098975050505050505050565b600060606000806200069d62002f1f565b600086815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff90811694830194909452650100000000008104841694820194909452690100000000000000000090930473ffffffffffffffffffffffffffffffffffffffff166060840152600101546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a0840152780100000000000000000000000000000000000000000000000090041660c08201525a9150600080826060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620007b7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007dd9190620048ec565b73ffffffffffffffffffffffffffffffffffffffff166013600101600c9054906101000a900463ffffffff1663ffffffff16896040516200081f91906200490c565b60006040518083038160008787f1925050503d80600081146200085f576040519150601f19603f3d011682016040523d82523d6000602084013e62000864565b606091505b50915091505a6200087690856200492a565b935081620008a157600060405180602001604052806000815250600796509650965050505062000927565b80806020019051810190620008b791906200499b565b909750955086620008e557600060405180602001604052806000815250600496509650965050505062000927565b601554865164010000000090910463ffffffff1610156200092357600060405180602001604052806000815250600596509650965050505062000927565b5050505b92959194509250565b6200093b8362002f5a565b6000838152601a602052604090206200095682848362004a90565b50827f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d566483836040516200098b929190620048b4565b60405180910390a2505050565b6000620009f288888860008989604051806020016040528060008152508a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506200031392505050565b98975050505050505050565b60006060600080600080600062000a1462002f1f565b600062000a218a62003010565b905060006012604051806101200160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160059054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160099054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600c9054906101000a900461ffff1661ffff1661ffff16815260200160008201600e9054906101000a900460ff1615151515815260200160008201600f9054906101000a900460ff161515151581526020016000820160109054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160008201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090506000600460008d81526020019081526020016000206040518060e00160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160059054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160099054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160018201600c9054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff1681526020016001820160189054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090508160a001511562000d45576000604051806020016040528060008152506009600084602001516000808263ffffffff1692509950995099509950995099509950505050620010e4565b604081015163ffffffff9081161462000d96576000604051806020016040528060008152506001600084602001516000808263ffffffff1692509950995099509950995099509950505050620010e4565b80511562000ddc576000604051806020016040528060008152506002600084602001516000808263ffffffff1692509950995099509950995099509950505050620010e4565b62000de782620030be565b602083015160155492975090955060009162000e19918591879190640100000000900463ffffffff168a8a87620032b0565b9050806bffffffffffffffffffffffff168260a001516bffffffffffffffffffffffff16101562000e83576000604051806020016040528060008152506006600085602001516000808263ffffffff1692509a509a509a509a509a509a509a5050505050620010e4565b600062000e928e868f62003301565b90505a9850600080846060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000eea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000f109190620048ec565b73ffffffffffffffffffffffffffffffffffffffff166013600101600c9054906101000a900463ffffffff1663ffffffff168460405162000f5291906200490c565b60006040518083038160008787f1925050503d806000811462000f92576040519150601f19603f3d011682016040523d82523d6000602084013e62000f97565b606091505b50915091505a62000fa9908c6200492a565b9a5081620010295760155481516801000000000000000090910463ffffffff1610156200100657505060408051602080820190925260008082529490910151939c509a50600899505063ffffffff9091169550620010e492505050565b602090940151939b5060039a505063ffffffff9092169650620010e49350505050565b808060200190518101906200103f91906200499b565b909e509c508d6200108057505060408051602080820190925260008082529490910151939c509a50600499505063ffffffff9091169550620010e492505050565b6015548d5164010000000090910463ffffffff161015620010d157505060408051602080820190925260008082529490910151939c509a50600599505063ffffffff9091169550620010e492505050565b505050506020015163ffffffff16945050505b92959891949750929550565b60015473ffffffffffffffffffffffffffffffffffffffff16331462001177576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600173ffffffffffffffffffffffffffffffffffffffff821660009081526019602052604090205460ff1660038111156200123257620012326200438d565b141580156200127e5750600373ffffffffffffffffffffffffffffffffffffffff821660009081526019602052604090205460ff1660038111156200127b576200127b6200438d565b14155b15620012b6576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6013546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1662001316576040517fd12d7d8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082900362001352576040517f2c2fc94100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290526000808567ffffffffffffffff811115620013a957620013a962004071565b604051908082528060200260200182016040528015620013d3578160200160208202803683370190505b50905060008667ffffffffffffffff811115620013f457620013f462004071565b6040519080825280602002602001820160405280156200147b57816020015b6040805160e08101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181620014135790505b50905060008767ffffffffffffffff8111156200149c576200149c62004071565b604051908082528060200260200182016040528015620014d157816020015b6060815260200190600190039081620014bb5790505b50905060008867ffffffffffffffff811115620014f257620014f262004071565b6040519080825280602002602001820160405280156200152757816020015b6060815260200190600190039081620015115790505b50905060008967ffffffffffffffff81111562001548576200154862004071565b6040519080825280602002602001820160405280156200157d57816020015b6060815260200190600190039081620015675790505b50905060005b8a81101562001b61578b8b82818110620015a157620015a162004bb8565b60209081029290920135600081815260048452604090819020815160e081018352815460ff811615158252610100810463ffffffff90811697830197909752650100000000008104871693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490931660c08401529a509098506200168090508962002f5a565b60608801516040517f1a5da6c800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c8116600483015290911690631a5da6c890602401600060405180830381600087803b158015620016f057600080fd5b505af115801562001705573d6000803e3d6000fd5b50505050878582815181106200171f576200171f62004bb8565b6020026020010181905250600560008a815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1686828151811062001773576200177362004bb8565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910182015260008a81526007909152604090208054620017b290620049e8565b80601f0160208091040260200160405190810160405280929190818152602001828054620017e090620049e8565b8015620018315780601f10620018055761010080835404028352916020019162001831565b820191906000526020600020905b8154815290600101906020018083116200181357829003601f168201915b50505050508482815181106200184b576200184b62004bb8565b6020026020010181905250601a60008a815260200190815260200160002080546200187690620049e8565b80601f0160208091040260200160405190810160405280929190818152602001828054620018a490620049e8565b8015620018f55780601f10620018c957610100808354040283529160200191620018f5565b820191906000526020600020905b815481529060010190602001808311620018d757829003601f168201915b50505050508382815181106200190f576200190f62004bb8565b6020026020010181905250601b60008a815260200190815260200160002080546200193a90620049e8565b80601f01602080910402602001604051908101604052809291908181526020018280546200196890620049e8565b8015620019b95780601f106200198d57610100808354040283529160200191620019b9565b820191906000526020600020905b8154815290600101906020018083116200199b57829003601f168201915b5050505050828281518110620019d357620019d362004bb8565b60200260200101819052508760a001516bffffffffffffffffffffffff1687620019fe919062004be7565b60008a815260046020908152604080832080547fffffff000000000000000000000000000000000000000000000000000000000016815560010180547fffffffff000000000000000000000000000000000000000000000000000000001690556007909152812091985062001a74919062003f89565b6000898152601a6020526040812062001a8d9162003f89565b6000898152601b6020526040812062001aa69162003f89565b600089815260066020526040902080547fffffffffffffffffffffffff000000000000000000000000000000000000000016905562001ae760028a62003523565b5060a0880151604080516bffffffffffffffffffffffff909216825273ffffffffffffffffffffffffffffffffffffffff8c1660208301528a917fb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff910160405180910390a28062001b588162004bfd565b91505062001583565b508560185462001b7291906200492a565b60185560008b8b868167ffffffffffffffff81111562001b965762001b9662004071565b60405190808252806020026020018201604052801562001bc0578160200160208202803683370190505b508988888860405160200162001bde98979695949392919062004da3565b60405160208183030381529060405290508973ffffffffffffffffffffffffffffffffffffffff16638e86139b6013600001600c9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c71249ab60038e73ffffffffffffffffffffffffffffffffffffffff1663aab9edd66040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001c9a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cc0919062004e73565b866040518463ffffffff1660e01b815260040162001ce19392919062004e98565b600060405180830381865afa15801562001cff573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405262001d47919081019062004ebf565b6040518263ffffffff1660e01b815260040162001d659190620048ca565b600060405180830381600087803b15801562001d8057600080fd5b505af115801562001d95573d6000803e3d6000fd5b50506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018b90527f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb91506044016020604051808303816000875af115801562001e2f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001e55919062004ef8565b50505050505050505050505050565b60023360009081526019602052604090205460ff16600381111562001e8d5762001e8d6200438d565b1415801562001ec3575060033360009081526019602052604090205460ff16600381111562001ec05762001ec06200438d565b14155b1562001efb576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080808080808062001f11888a018a620050f0565b965096509650965096509650965060005b8751811015620021e057600073ffffffffffffffffffffffffffffffffffffffff1687828151811062001f595762001f5962004bb8565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff16036200206d5785818151811062001f965762001f9662004bb8565b6020026020010151307f000000000000000000000000000000000000000000000000000000000000000060405162001fce9062003f7b565b73ffffffffffffffffffffffffffffffffffffffff938416815291831660208301529091166040820152606001604051809103906000f08015801562002018573d6000803e3d6000fd5b508782815181106200202e576200202e62004bb8565b60200260200101516060019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b6200212588828151811062002086576200208662004bb8565b6020026020010151888381518110620020a357620020a362004bb8565b6020026020010151878481518110620020c057620020c062004bb8565b6020026020010151878581518110620020dd57620020dd62004bb8565b6020026020010151878681518110620020fa57620020fa62004bb8565b602002602001015187878151811062002117576200211762004bb8565b602002602001015162002b44565b8781815181106200213a576200213a62004bb8565b60200260200101517f74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a7188838151811062002178576200217862004bb8565b602002602001015160a0015133604051620021c39291906bffffffffffffffffffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a280620021d78162004bfd565b91505062001f22565b50505050505050505050565b600082815260046020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452650100000000008104841694820185905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004821660c08201529114620022ea576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160a00151620022fc919062005221565b600084815260046020526040902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff166c010000000000000000000000006bffffffffffffffffffffffff93841602179055601854620023649184169062004be7565b6018556040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526bffffffffffffffffffffffff831660448201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af11580156200240e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002434919062004ef8565b506040516bffffffffffffffffffffffff83168152339084907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a3505050565b6000818152600460209081526040808320815160e081018352815460ff81161515825263ffffffff6101008204811695830195909552650100000000008104851693820184905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004831660c082015292911415906200256860005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16149050818015620025c35750808015620025c15750620025b462003531565b836040015163ffffffff16115b155b15620025fb576040517ffbc0357800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156200262e575060008481526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b1562002666576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200267262003531565b9050816200268a576200268760328262004be7565b90505b6000858152600460205260409020805463ffffffff80841665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff90921691909117909155620026e69060029087906200352316565b5060135460808501516bffffffffffffffffffffffff91821691600091168211156200274f5760808601516200271d908362005249565b90508560a001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff1611156200274f575060a08501515b808660a0015162002761919062005249565b600088815260046020526040902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff166c010000000000000000000000006bffffffffffffffffffffffff93841602179055601454620027c99183911662005221565b601480547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff9290921691909117905560405167ffffffffffffffff84169088907f91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f79118190600090a350505050505050565b600060606000806200285762002f1f565b6000634b56a42e60e01b888888604051602401620028789392919062005271565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506200290389826200068c565b929c919b50995090975095505050505050565b62002920620035ed565b6200292b8162003670565b50565b600060606000806000806000620029558860405180602001604052806000815250620009fe565b959e949d50929b5090995097509550909350915050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415155b90505b92915050565b6000806000620029c76001620029b562003531565b620029c191906200492a565b62003767565b601454604080516020810193909352309083015274010000000000000000000000000000000000000000900463ffffffff166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083015201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060045b600f81101562002ad3578282828151811062002a8f5762002a8f62004bb8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508062002aca8162004bfd565b91505062002a6f565b5083600181111562002ae95762002ae96200438d565b60f81b81600f8151811062002b025762002b0262004bb8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535062002b3c81620052a5565b949350505050565b6012546e010000000000000000000000000000900460ff161562002b94576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601554835163ffffffff909116101562002bda576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108fc856020015163ffffffff16108062002c185750601454602086015163ffffffff70010000000000000000000000000000000090920482169116115b1562002c50576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600460205260409020546901000000000000000000900473ffffffffffffffffffffffffffffffffffffffff161562002cba576040517f6e3b930b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600460209081526040808320885181548a8501518b85015160608d01517fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009093169315157fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff169390931761010063ffffffff92831602177fffffff000000000000000000000000000000000000000000000000ffffffffff1665010000000000938216939093027fffffff0000000000000000000000000000000000000000ffffffffffffffffff1692909217690100000000000000000073ffffffffffffffffffffffffffffffffffffffff9283160217835560808b01516001909301805460a08d015160c08e01516bffffffffffffffffffffffff9687167fffffffffffffffff000000000000000000000000000000000000000000000000909316929092176c010000000000000000000000009690911695909502949094177fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000009490931693909302919091179091556005835281842080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169189169190911790556007909152902062002ead8482620052e8565b508460a001516bffffffffffffffffffffffff1660185462002ed0919062004be7565b6018556000868152601a6020526040902062002eed8382620052e8565b506000868152601b6020526040902062002f088282620052e8565b5062002f16600287620038cf565b50505050505050565b321562002f58576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff16331462002fb8576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090205465010000000000900463ffffffff908116146200292b576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818160045b600f811015620030a5577fff00000000000000000000000000000000000000000000000000000000000000821683826020811062003059576200305962004bb8565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146200309057506000949350505050565b806200309c8162004bfd565b91505062003017565b5081600f1a600181111562002b3c5762002b3c6200438d565b6000806000836060015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156200314b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200317191906200542a565b50945090925050506000811315806200318957508142105b80620031ae5750828015620031ae5750620031a582426200492a565b8463ffffffff16105b15620031bf576016549550620031c3565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156200322f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200325591906200542a565b50945090925050506000811315806200326d57508142105b806200329257508280156200329257506200328982426200492a565b8463ffffffff16105b15620032a3576017549450620032a7565b8094505b50505050915091565b600080620032c488878b60000151620038dd565b9050600080620032e18b8a63ffffffff16858a8a60018b6200397c565b9092509050620032f2818362005221565b9b9a5050505050505050505050565b606060008360018111156200331a576200331a6200438d565b03620033e7576000848152600760205260409081902090517f6e04ff0d0000000000000000000000000000000000000000000000000000000091620033629160240162005522565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506200351c565b6001836001811115620033fe57620033fe6200438d565b03620034ea576000828060200190518101906200341c919062005599565b6000868152600760205260409081902090519192507f40691db4000000000000000000000000000000000000000000000000000000009162003463918491602401620056ad565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291506200351c9050565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b600062002997838362003e1e565b600060017f000000000000000000000000000000000000000000000000000000000000000060028111156200356a576200356a6200438d565b03620035e857606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620035bd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620035e3919062005775565b905090565b504390565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016200116e565b3373ffffffffffffffffffffffffffffffffffffffff821603620036f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200116e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115620037a057620037a06200438d565b03620038c5576000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620037f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200381b919062005775565b905080831015806200383957506101006200383784836200492a565b115b15620038485750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa1580156200389f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200351c919062005775565b504090565b919050565b600062002997838362003f29565b60008080856001811115620038f657620038f66200438d565b0362003907575062015f906200392a565b60018560018111156200391e576200391e6200438d565b03620034ea57506201adb05b6200393d63ffffffff851660146200578f565b6200394a846001620057cf565b6200395b9060ff16611d4c6200578f565b62003967908362004be7565b62003973919062004be7565b95945050505050565b6000806000896080015161ffff16876200399791906200578f565b9050838015620039a65750803a105b15620039af57503a5b600060027f00000000000000000000000000000000000000000000000000000000000000006002811115620039e857620039e86200438d565b0362003b5957604080516000815260208101909152851562003a4c5760003660405180608001604052806048815260200162005cc46048913960405160200162003a3593929190620057eb565b604051602081830303815290604052905062003aba565b60155462003a6a90640100000000900463ffffffff16600462005814565b63ffffffff1667ffffffffffffffff81111562003a8b5762003a8b62004071565b6040519080825280601f01601f19166020018201604052801562003ab6576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e9062003b0c908490600401620048ca565b602060405180830381865afa15801562003b2a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003b50919062005775565b91505062003cc3565b60017f0000000000000000000000000000000000000000000000000000000000000000600281111562003b905762003b906200438d565b0362003cc357841562003c1857606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562003bea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003c10919062005775565b905062003cc3565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa15801562003c67573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003c8d919062005843565b505060155492945062003cb293505050640100000000900463ffffffff16826200578f565b62003cbf9060106200578f565b9150505b8462003ce257808b6080015161ffff1662003cdf91906200578f565b90505b62003cf261ffff8716826200588e565b90506000878262003d048c8e62004be7565b62003d1090866200578f565b62003d1c919062004be7565b62003d3090670de0b6b3a76400006200578f565b62003d3c91906200588e565b905060008c6040015163ffffffff1664e8d4a5100062003d5d91906200578f565b898e6020015163ffffffff16858f8862003d7891906200578f565b62003d84919062004be7565b62003d9490633b9aca006200578f565b62003da091906200578f565b62003dac91906200588e565b62003db8919062004be7565b90506b033b2e3c9fd0803ce800000062003dd3828462004be7565b111562003e0c576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b6000818152600183016020526040812054801562003f1757600062003e456001836200492a565b855490915060009062003e5b906001906200492a565b905081811462003ec757600086600001828154811062003e7f5762003e7f62004bb8565b906000526020600020015490508087600001848154811062003ea55762003ea562004bb8565b6000918252602080832090910192909255918252600188019052604090208390555b855486908062003edb5762003edb620058ca565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506200299a565b60009150506200299a565b5092915050565b600081815260018301602052604081205462003f72575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200299a565b5060006200299a565b6103ca80620058fa83390190565b50805462003f9790620049e8565b6000825580601f1062003fa8575050565b601f0160209004906000526020600020908101906200292b91905b8082111562003fd9576000815560010162003fc3565b5090565b73ffffffffffffffffffffffffffffffffffffffff811681146200292b57600080fd5b803563ffffffff81168114620038ca57600080fd5b803560028110620038ca57600080fd5b60008083601f8401126200403857600080fd5b50813567ffffffffffffffff8111156200405157600080fd5b6020830191508360208285010111156200406a57600080fd5b9250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715620040c657620040c662004071565b60405290565b604051610100810167ffffffffffffffff81118282101715620040c657620040c662004071565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156200413d576200413d62004071565b604052919050565b600067ffffffffffffffff82111562004162576200416262004071565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112620041a057600080fd5b8135620041b7620041b18262004145565b620040f3565b818152846020838601011115620041cd57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060008060e0898b0312156200420757600080fd5b8835620042148162003fdd565b97506200422460208a0162004000565b96506040890135620042368162003fdd565b95506200424660608a0162004015565b9450608089013567ffffffffffffffff808211156200426457600080fd5b620042728c838d0162004025565b909650945060a08b01359150808211156200428c57600080fd5b6200429a8c838d016200418e565b935060c08b0135915080821115620042b157600080fd5b50620042c08b828c016200418e565b9150509295985092959890939650565b60008060408385031215620042e457600080fd5b82359150602083013567ffffffffffffffff8111156200430357600080fd5b62004311858286016200418e565b9150509250929050565b60005b83811015620043385781810151838201526020016200431e565b50506000910152565b600081518084526200435b8160208601602086016200431b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600a8110620043f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b841515815260806020820152600062004415608083018662004341565b9050620044266040830185620043bc565b82606083015295945050505050565b6000806000604084860312156200444b57600080fd5b83359250602084013567ffffffffffffffff8111156200446a57600080fd5b620044788682870162004025565b9497909650939450505050565b600080600080600080600060a0888a031215620044a157600080fd5b8735620044ae8162003fdd565b9650620044be6020890162004000565b95506040880135620044d08162003fdd565b9450606088013567ffffffffffffffff80821115620044ee57600080fd5b620044fc8b838c0162004025565b909650945060808a01359150808211156200451657600080fd5b50620045258a828b0162004025565b989b979a50959850939692959293505050565b871515815260e0602082015260006200455560e083018962004341565b9050620045666040830188620043bc565b8560608301528460808301528360a08301528260c083015298975050505050505050565b600080600060408486031215620045a057600080fd5b833567ffffffffffffffff80821115620045b957600080fd5b818601915086601f830112620045ce57600080fd5b813581811115620045de57600080fd5b8760208260051b8501011115620045f457600080fd5b602092830195509350508401356200460c8162003fdd565b809150509250925092565b600080602083850312156200462b57600080fd5b823567ffffffffffffffff8111156200464357600080fd5b620046518582860162004025565b90969095509350505050565b80356bffffffffffffffffffffffff81168114620038ca57600080fd5b600080604083850312156200468e57600080fd5b82359150620046a0602084016200465d565b90509250929050565b600060208284031215620046bc57600080fd5b5035919050565b600067ffffffffffffffff821115620046e057620046e062004071565b5060051b60200190565b600082601f830112620046fc57600080fd5b813560206200470f620041b183620046c3565b82815260059290921b840181019181810190868411156200472f57600080fd5b8286015b848110156200477457803567ffffffffffffffff811115620047555760008081fd5b620047658986838b01016200418e565b84525091830191830162004733565b509695505050505050565b600080600080606085870312156200479657600080fd5b84359350602085013567ffffffffffffffff80821115620047b657600080fd5b620047c488838901620046ea565b94506040870135915080821115620047db57600080fd5b50620047ea8782880162004025565b95989497509550505050565b6000602082840312156200480957600080fd5b81356200351c8162003fdd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff80831681810362004861576200486162004816565b6001019392505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60208152600062002b3c6020830184866200486b565b60208152600062002997602083018462004341565b8051620038ca8162003fdd565b600060208284031215620048ff57600080fd5b81516200351c8162003fdd565b60008251620049208184602087016200431b565b9190910192915050565b818103818111156200299a576200299a62004816565b80151581146200292b57600080fd5b600082601f8301126200496157600080fd5b815162004972620041b18262004145565b8181528460208386010111156200498857600080fd5b62002b3c8260208301602087016200431b565b60008060408385031215620049af57600080fd5b8251620049bc8162004940565b602084015190925067ffffffffffffffff811115620049da57600080fd5b62004311858286016200494f565b600181811c90821680620049fd57607f821691505b60208210810362004a37577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111562004a8b57600081815260208120601f850160051c8101602086101562004a665750805b601f850160051c820191505b8181101562004a875782815560010162004a72565b5050505b505050565b67ffffffffffffffff83111562004aab5762004aab62004071565b62004ac38362004abc8354620049e8565b8362004a3d565b6000601f84116001811462004b18576000851562004ae15750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835562004bb1565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101562004b69578685013582556020948501946001909201910162004b47565b508682101562004ba5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808201808211156200299a576200299a62004816565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362004c315762004c3162004816565b5060010190565b600081518084526020808501945080840160005b8381101562004cf75781518051151588528381015163ffffffff908116858a01526040808301519091169089015260608082015173ffffffffffffffffffffffffffffffffffffffff16908901526080808201516bffffffffffffffffffffffff169089015260a08082015162004cd2828b01826bffffffffffffffffffffffff169052565b505060c09081015163ffffffff169088015260e0909601959082019060010162004c4c565b509495945050505050565b600081518084526020808501945080840160005b8381101562004cf757815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010162004d16565b600081518084526020808501808196508360051b8101915082860160005b8581101562004d9657828403895262004d8384835162004341565b9885019893509084019060010162004d68565b5091979650505050505050565b60e081528760e082015260006101007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a111562004de057600080fd5b8960051b808c8386013783018381038201602085015262004e048282018b62004c38565b915050828103604084015262004e1b818962004d02565b9050828103606084015262004e31818862004d02565b9050828103608084015262004e47818762004d4a565b905082810360a084015262004e5d818662004d4a565b905082810360c0840152620032f2818562004d4a565b60006020828403121562004e8657600080fd5b815160ff811681146200351c57600080fd5b60ff8416815260ff8316602082015260606040820152600062003973606083018462004341565b60006020828403121562004ed257600080fd5b815167ffffffffffffffff81111562004eea57600080fd5b62002b3c848285016200494f565b60006020828403121562004f0b57600080fd5b81516200351c8162004940565b600082601f83011262004f2a57600080fd5b8135602062004f3d620041b183620046c3565b82815260059290921b8401810191818101908684111562004f5d57600080fd5b8286015b8481101562004774578035835291830191830162004f61565b600082601f83011262004f8c57600080fd5b8135602062004f9f620041b183620046c3565b82815260e0928302850182019282820191908785111562004fbf57600080fd5b8387015b85811015620050765781818a03121562004fdd5760008081fd5b62004fe7620040a0565b813562004ff48162004940565b81526200500382870162004000565b8682015260406200501681840162004000565b908201526060828101356200502b8162003fdd565b9082015260806200503e8382016200465d565b9082015260a0620050518382016200465d565b9082015260c06200506483820162004000565b90820152845292840192810162004fc3565b5090979650505050505050565b600082601f8301126200509557600080fd5b81356020620050a8620041b183620046c3565b82815260059290921b84018101918181019086841115620050c857600080fd5b8286015b8481101562004774578035620050e28162003fdd565b8352918301918301620050cc565b600080600080600080600060e0888a0312156200510c57600080fd5b873567ffffffffffffffff808211156200512557600080fd5b620051338b838c0162004f18565b985060208a01359150808211156200514a57600080fd5b620051588b838c0162004f7a565b975060408a01359150808211156200516f57600080fd5b6200517d8b838c0162005083565b965060608a01359150808211156200519457600080fd5b620051a28b838c0162005083565b955060808a0135915080821115620051b957600080fd5b620051c78b838c01620046ea565b945060a08a0135915080821115620051de57600080fd5b620051ec8b838c01620046ea565b935060c08a01359150808211156200520357600080fd5b50620052128a828b01620046ea565b91505092959891949750929550565b6bffffffffffffffffffffffff81811683821601908082111562003f225762003f2262004816565b6bffffffffffffffffffffffff82811682821603908082111562003f225762003f2262004816565b60408152600062005286604083018662004d4a565b82810360208401526200529b8185876200486b565b9695505050505050565b8051602080830151919081101562004a37577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b16919050565b815167ffffffffffffffff81111562005305576200530562004071565b6200531d81620053168454620049e8565b8462004a3d565b602080601f8311600181146200537357600084156200533c5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855562004a87565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015620053c257888601518255948401946001909101908401620053a1565b5085821015620053ff57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b805169ffffffffffffffffffff81168114620038ca57600080fd5b600080600080600060a086880312156200544357600080fd5b6200544e866200540f565b945060208601519350604086015192506060860151915062005473608087016200540f565b90509295509295909350565b600081546200548e81620049e8565b808552602060018381168015620054ae5760018114620054e75762005517565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b890101955062005517565b866000528260002060005b858110156200550f5781548a8201860152908301908401620054f2565b890184019650505b505050505092915050565b6020815260006200299760208301846200547f565b600082601f8301126200554957600080fd5b815160206200555c620041b183620046c3565b82815260059290921b840181019181810190868411156200557c57600080fd5b8286015b8481101562004774578051835291830191830162005580565b600060208284031215620055ac57600080fd5b815167ffffffffffffffff80821115620055c557600080fd5b908301906101008286031215620055db57600080fd5b620055e5620040cc565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201526200561f60a08401620048df565b60a082015260c0830151828111156200563757600080fd5b620056458782860162005537565b60c08301525060e0830151828111156200565e57600080fd5b6200566c878286016200494f565b60e08301525095945050505050565b600081518084526020808501945080840160005b8381101562004cf7578151875295820195908201906001016200568f565b60408152825160408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015273ffffffffffffffffffffffffffffffffffffffff60a08401511660e0820152600060c0840151610100808185015250620057206101408401826200567b565b905060e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0848303016101208501526200575e828262004341565b91505082810360208401526200397381856200547f565b6000602082840312156200578857600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615620057ca57620057ca62004816565b500290565b60ff81811683821601908111156200299a576200299a62004816565b8284823760008382016000815283516200580a8183602088016200431b565b0195945050505050565b600063ffffffff808316818516818304811182151516156200583a576200583a62004816565b02949350505050565b60008060008060008060c087890312156200585d57600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600082620058c5577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe60c060405234801561001057600080fd5b506040516103ca3803806103ca83398101604081905261002f91610076565b600080546001600160a01b0319166001600160a01b039384161790559181166080521660a0526100b9565b80516001600160a01b038116811461007157600080fd5b919050565b60008060006060848603121561008b57600080fd5b6100948461005a565b92506100a26020850161005a565b91506100b06040850161005a565b90509250925092565b60805160a0516102e76100e36000396000603801526000818160c4015261011701526102e76000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806379188d161461007b578063f00e6a2a146100aa575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610076573d6000f35b3d6000fd5b61008e6100893660046101c1565b6100ee565b6040805192151583526020830191909152015b60405180910390f35b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526020016100a1565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331461011557600080fd5b7f00000000000000000000000000000000000000000000000000000000000000005a91505a61138881101561014957600080fd5b61138881039050856040820482031161016157600080fd5b50803b61016d57600080fd5b6000808551602087016000858af192505a610188908361029a565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080604083850312156101d457600080fd5b82359150602083013567ffffffffffffffff808211156101f357600080fd5b818501915085601f83011261020757600080fd5b81358181111561021957610219610192565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561025f5761025f610192565b8160405282815288602084870101111561027857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b818103818111156102d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000810000a307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", + Bin: "0x6101406040523480156200001257600080fd5b50604051620061fd380380620061fd8339810160408190526200003591620003df565b80816001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b919062000406565b826001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003df565b836001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003df565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003df565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003df565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b9816200031b565b505050846002811115620002d157620002d162000429565b60e0816002811115620002e857620002e862000429565b9052506001600160a01b0393841660805291831660a052821660c0528116610100529190911661012052506200043f9050565b336001600160a01b03821603620003755760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003dc57600080fd5b50565b600060208284031215620003f257600080fd5b8151620003ff81620003c6565b9392505050565b6000602082840312156200041957600080fd5b815160038110620003ff57600080fd5b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e0516101005161012051615d44620004b96000396000818161010e01526101a90152600081816103e10152611fbd01526000818161356301528181613799015281816139e10152613b890152600061310d015260006131f1015260008181611dff01526123cb0152615d446000f3fe60806040523480156200001157600080fd5b50600436106200010c5760003560e01c806385c1b0ba11620000a5578063c8048022116200006f578063c804802214620002b7578063ce7dc5b414620002ce578063f2fde38b14620002e5578063f7d334ba14620002fc576200010c565b806385c1b0ba14620002535780638da5cb5b146200026a5780638e86139b1462000289578063948108f714620002a0576200010c565b80634ee88d3511620000e75780634ee88d3514620001ef5780636ded9eae146200020657806371791aa0146200021d57806379ba50971462000249576200010c565b806328f32f38146200015457806329c5efad146200017e578063349e8cca14620001a7575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156200014d573d6000f35b3d6000fd5b005b6200016b6200016536600462004216565b62000313565b6040519081526020015b60405180910390f35b620001956200018f366004620042fc565b6200068d565b60405162000175949392919062004424565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200162000175565b620001526200020036600462004461565b62000931565b6200016b62000217366004620044b1565b62000999565b620002346200022e366004620042fc565b620009ff565b60405162000175979695949392919062004564565b620001526200110c565b6200015262000264366004620045b6565b6200120f565b60005473ffffffffffffffffffffffffffffffffffffffff16620001c9565b620001526200029a36600462004643565b62001e80565b62000152620002b1366004620046a6565b62002208565b62000152620002c8366004620046d5565b6200249b565b62000195620002df366004620047ab565b62002862565b62000152620002f636600462004822565b62002932565b620002346200030d366004620046d5565b6200294a565b6000805473ffffffffffffffffffffffffffffffffffffffff1633148015906200034757506200034560093362002988565b155b156200037f576040517fd48b678b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff89163b620003ce576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b620003d986620029bc565b9050600089307f00000000000000000000000000000000000000000000000000000000000000006040516200040e9062003fa7565b73ffffffffffffffffffffffffffffffffffffffff938416815291831660208301529091166040820152606001604051809103906000f08015801562000458573d6000803e3d6000fd5b5090506200051f826040518060e001604052806000151581526020018c63ffffffff16815260200163ffffffff801681526020018473ffffffffffffffffffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff168152602001600063ffffffff168152508a89898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b92508a915062002b609050565b6015805474010000000000000000000000000000000000000000900463ffffffff169060146200054f8362004871565b91906101000a81548163ffffffff021916908363ffffffff16021790555050817fbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d0128a8a604051620005c892919063ffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a2817fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d878760405162000604929190620048e0565b60405180910390a2817f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664856040516200063e9190620048f6565b60405180910390a2817f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf485084604051620006789190620048f6565b60405180910390a25098975050505050505050565b600060606000806200069e62002f4b565b600086815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff90811694830194909452650100000000008104841694820194909452690100000000000000000090930473ffffffffffffffffffffffffffffffffffffffff166060840152600101546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a0840152780100000000000000000000000000000000000000000000000090041660c08201525a9150600080826060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620007b8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007de919062004918565b73ffffffffffffffffffffffffffffffffffffffff166014600101600c9054906101000a900463ffffffff1663ffffffff168960405162000820919062004938565b60006040518083038160008787f1925050503d806000811462000860576040519150601f19603f3d011682016040523d82523d6000602084013e62000865565b606091505b50915091505a62000877908562004956565b935081620008a257600060405180602001604052806000815250600796509650965050505062000928565b80806020019051810190620008b89190620049c7565b909750955086620008e657600060405180602001604052806000815250600496509650965050505062000928565b601654865164010000000090910463ffffffff1610156200092457600060405180602001604052806000815250600596509650965050505062000928565b5050505b92959194509250565b6200093c8362002f86565b6000838152601b602052604090206200095782848362004abc565b50827f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d566483836040516200098c929190620048e0565b60405180910390a2505050565b6000620009f388888860008989604051806020016040528060008152508a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506200031392505050565b98975050505050505050565b60006060600080600080600062000a1562002f4b565b600062000a228a6200303c565b905060006012604051806101400160405290816000820160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160189054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201601b9054906101000a900461ffff1661ffff1661ffff16815260200160008201601d9054906101000a900460ff1660ff1660ff16815260200160008201601e9054906101000a900460ff1615151515815260200160008201601f9054906101000a900460ff161515151581526020016001820160009054906101000a900460ff16151515158152505090506000600460008d81526020019081526020016000206040518060e00160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160059054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160099054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160018201600c9054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff1681526020016001820160189054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090508160e001511562000d61576000604051806020016040528060008152506009600084602001516000808263ffffffff169250995099509950995099509950995050505062001100565b604081015163ffffffff9081161462000db2576000604051806020016040528060008152506001600084602001516000808263ffffffff169250995099509950995099509950995050505062001100565b80511562000df8576000604051806020016040528060008152506002600084602001516000808263ffffffff169250995099509950995099509950995050505062001100565b62000e0382620030ea565b602083015160165492975090955060009162000e35918591879190640100000000900463ffffffff168a8a87620032dc565b9050806bffffffffffffffffffffffff168260a001516bffffffffffffffffffffffff16101562000e9f576000604051806020016040528060008152506006600085602001516000808263ffffffff1692509a509a509a509a509a509a509a505050505062001100565b600062000eae8e868f6200332d565b90505a9850600080846060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000f06573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000f2c919062004918565b73ffffffffffffffffffffffffffffffffffffffff166014600101600c9054906101000a900463ffffffff1663ffffffff168460405162000f6e919062004938565b60006040518083038160008787f1925050503d806000811462000fae576040519150601f19603f3d011682016040523d82523d6000602084013e62000fb3565b606091505b50915091505a62000fc5908c62004956565b9a5081620010455760165481516801000000000000000090910463ffffffff1610156200102257505060408051602080820190925260008082529490910151939c509a50600899505063ffffffff90911695506200110092505050565b602090940151939b5060039a505063ffffffff9092169650620011009350505050565b808060200190518101906200105b9190620049c7565b909e509c508d6200109c57505060408051602080820190925260008082529490910151939c509a50600499505063ffffffff90911695506200110092505050565b6016548d5164010000000090910463ffffffff161015620010ed57505060408051602080820190925260008082529490910151939c509a50600599505063ffffffff90911695506200110092505050565b505050506020015163ffffffff16945050505b92959891949750929550565b60015473ffffffffffffffffffffffffffffffffffffffff16331462001193576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600173ffffffffffffffffffffffffffffffffffffffff82166000908152601a602052604090205460ff1660038111156200124e576200124e620043b9565b141580156200129a5750600373ffffffffffffffffffffffffffffffffffffffff82166000908152601a602052604090205460ff166003811115620012975762001297620043b9565b14155b15620012d2576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1662001332576040517fd12d7d8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008290036200136e576040517f2c2fc94100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290526000808567ffffffffffffffff811115620013c557620013c56200409d565b604051908082528060200260200182016040528015620013ef578160200160208202803683370190505b50905060008667ffffffffffffffff8111156200141057620014106200409d565b6040519080825280602002602001820160405280156200149757816020015b6040805160e08101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816200142f5790505b50905060008767ffffffffffffffff811115620014b857620014b86200409d565b604051908082528060200260200182016040528015620014ed57816020015b6060815260200190600190039081620014d75790505b50905060008867ffffffffffffffff8111156200150e576200150e6200409d565b6040519080825280602002602001820160405280156200154357816020015b60608152602001906001900390816200152d5790505b50905060008967ffffffffffffffff8111156200156457620015646200409d565b6040519080825280602002602001820160405280156200159957816020015b6060815260200190600190039081620015835790505b50905060005b8a81101562001b7d578b8b82818110620015bd57620015bd62004be4565b60209081029290920135600081815260048452604090819020815160e081018352815460ff811615158252610100810463ffffffff90811697830197909752650100000000008104871693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490931660c08401529a509098506200169c90508962002f86565b60608801516040517f1a5da6c800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c8116600483015290911690631a5da6c890602401600060405180830381600087803b1580156200170c57600080fd5b505af115801562001721573d6000803e3d6000fd5b50505050878582815181106200173b576200173b62004be4565b6020026020010181905250600560008a815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168682815181106200178f576200178f62004be4565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910182015260008a81526007909152604090208054620017ce9062004a14565b80601f0160208091040260200160405190810160405280929190818152602001828054620017fc9062004a14565b80156200184d5780601f1062001821576101008083540402835291602001916200184d565b820191906000526020600020905b8154815290600101906020018083116200182f57829003601f168201915b505050505084828151811062001867576200186762004be4565b6020026020010181905250601b60008a81526020019081526020016000208054620018929062004a14565b80601f0160208091040260200160405190810160405280929190818152602001828054620018c09062004a14565b8015620019115780601f10620018e55761010080835404028352916020019162001911565b820191906000526020600020905b815481529060010190602001808311620018f357829003601f168201915b50505050508382815181106200192b576200192b62004be4565b6020026020010181905250601c60008a81526020019081526020016000208054620019569062004a14565b80601f0160208091040260200160405190810160405280929190818152602001828054620019849062004a14565b8015620019d55780601f10620019a957610100808354040283529160200191620019d5565b820191906000526020600020905b815481529060010190602001808311620019b757829003601f168201915b5050505050828281518110620019ef57620019ef62004be4565b60200260200101819052508760a001516bffffffffffffffffffffffff168762001a1a919062004c13565b60008a815260046020908152604080832080547fffffff000000000000000000000000000000000000000000000000000000000016815560010180547fffffffff000000000000000000000000000000000000000000000000000000001690556007909152812091985062001a90919062003fb5565b6000898152601b6020526040812062001aa99162003fb5565b6000898152601c6020526040812062001ac29162003fb5565b600089815260066020526040902080547fffffffffffffffffffffffff000000000000000000000000000000000000000016905562001b0360028a6200354f565b5060a0880151604080516bffffffffffffffffffffffff909216825273ffffffffffffffffffffffffffffffffffffffff8c1660208301528a917fb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff910160405180910390a28062001b748162004c29565b9150506200159f565b508560195462001b8e919062004956565b60195560008b8b868167ffffffffffffffff81111562001bb25762001bb26200409d565b60405190808252806020026020018201604052801562001bdc578160200160208202803683370190505b508988888860405160200162001bfa98979695949392919062004dcf565b60405160208183030381529060405290508973ffffffffffffffffffffffffffffffffffffffff16638e86139b6014600001600c9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c71249ab60038e73ffffffffffffffffffffffffffffffffffffffff1663aab9edd66040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001cb6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cdc919062004e9f565b866040518463ffffffff1660e01b815260040162001cfd9392919062004ec4565b600060405180830381865afa15801562001d1b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405262001d63919081019062004eeb565b6040518263ffffffff1660e01b815260040162001d819190620048f6565b600060405180830381600087803b15801562001d9c57600080fd5b505af115801562001db1573d6000803e3d6000fd5b50506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018b90527f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb91506044016020604051808303816000875af115801562001e4b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001e71919062004f24565b50505050505050505050505050565b6002336000908152601a602052604090205460ff16600381111562001ea95762001ea9620043b9565b1415801562001edf57506003336000908152601a602052604090205460ff16600381111562001edc5762001edc620043b9565b14155b1562001f17576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080808080808062001f2d888a018a6200511c565b965096509650965096509650965060005b8751811015620021fc57600073ffffffffffffffffffffffffffffffffffffffff1687828151811062001f755762001f7562004be4565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1603620020895785818151811062001fb25762001fb262004be4565b6020026020010151307f000000000000000000000000000000000000000000000000000000000000000060405162001fea9062003fa7565b73ffffffffffffffffffffffffffffffffffffffff938416815291831660208301529091166040820152606001604051809103906000f08015801562002034573d6000803e3d6000fd5b508782815181106200204a576200204a62004be4565b60200260200101516060019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b62002141888281518110620020a257620020a262004be4565b6020026020010151888381518110620020bf57620020bf62004be4565b6020026020010151878481518110620020dc57620020dc62004be4565b6020026020010151878581518110620020f957620020f962004be4565b602002602001015187868151811062002116576200211662004be4565b602002602001015187878151811062002133576200213362004be4565b602002602001015162002b60565b87818151811062002156576200215662004be4565b60200260200101517f74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a7188838151811062002194576200219462004be4565b602002602001015160a0015133604051620021df9291906bffffffffffffffffffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a280620021f38162004c29565b91505062001f3e565b50505050505050505050565b600082815260046020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452650100000000008104841694820185905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004821660c0820152911462002306576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160a001516200231891906200524d565b600084815260046020526040902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff166c010000000000000000000000006bffffffffffffffffffffffff93841602179055601954620023809184169062004c13565b6019556040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526bffffffffffffffffffffffff831660448201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af11580156200242a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002450919062004f24565b506040516bffffffffffffffffffffffff83168152339084907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a3505050565b6000818152600460209081526040808320815160e081018352815460ff81161515825263ffffffff6101008204811695830195909552650100000000008104851693820184905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004831660c082015292911415906200258460005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16149050818015620025df5750808015620025dd5750620025d06200355d565b836040015163ffffffff16115b155b1562002617576040517ffbc0357800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156200264a575060008481526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b1562002682576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200268e6200355d565b905081620026a657620026a360328262004c13565b90505b6000858152600460205260409020805463ffffffff80841665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff90921691909117909155620027029060029087906200354f16565b5060145460808501516bffffffffffffffffffffffff91821691600091168211156200276b57608086015162002739908362005275565b90508560a001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff1611156200276b575060a08501515b808660a001516200277d919062005275565b600088815260046020526040902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff166c010000000000000000000000006bffffffffffffffffffffffff93841602179055601554620027e5918391166200524d565b601580547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff9290921691909117905560405167ffffffffffffffff84169088907f91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f79118190600090a350505050505050565b600060606000806200287362002f4b565b6000634b56a42e60e01b88888860405160240162002894939291906200529d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506200291f89826200068d565b929c919b50995090975095505050505050565b6200293c62003619565b62002947816200369c565b50565b600060606000806000806000620029718860405180602001604052806000815250620009ff565b959e949d50929b5090995097509550909350915050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415155b90505b92915050565b6000806000620029e36001620029d16200355d565b620029dd919062004956565b62003793565b601554604080516020810193909352309083015274010000000000000000000000000000000000000000900463ffffffff166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083015201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905060045b600f81101562002aef578282828151811062002aab5762002aab62004be4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508062002ae68162004c29565b91505062002a8b565b5083600181111562002b055762002b05620043b9565b60f81b81600f8151811062002b1e5762002b1e62004be4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535062002b5881620052d1565b949350505050565b6012547e01000000000000000000000000000000000000000000000000000000000000900460ff161562002bc0576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601654835163ffffffff909116101562002c06576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108fc856020015163ffffffff16108062002c445750601554602086015163ffffffff70010000000000000000000000000000000090920482169116115b1562002c7c576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600460205260409020546901000000000000000000900473ffffffffffffffffffffffffffffffffffffffff161562002ce6576040517f6e3b930b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600460209081526040808320885181548a8501518b85015160608d01517fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009093169315157fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff169390931761010063ffffffff92831602177fffffff000000000000000000000000000000000000000000000000ffffffffff1665010000000000938216939093027fffffff0000000000000000000000000000000000000000ffffffffffffffffff1692909217690100000000000000000073ffffffffffffffffffffffffffffffffffffffff9283160217835560808b01516001909301805460a08d015160c08e01516bffffffffffffffffffffffff9687167fffffffffffffffff000000000000000000000000000000000000000000000000909316929092176c010000000000000000000000009690911695909502949094177fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000009490931693909302919091179091556005835281842080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169189169190911790556007909152902062002ed9848262005314565b508460a001516bffffffffffffffffffffffff1660195462002efc919062004c13565b6019556000868152601b6020526040902062002f19838262005314565b506000868152601c6020526040902062002f34828262005314565b5062002f42600287620038fb565b50505050505050565b321562002f84576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff16331462002fe4576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090205465010000000000900463ffffffff9081161462002947576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818160045b600f811015620030d1577fff00000000000000000000000000000000000000000000000000000000000000821683826020811062003085576200308562004be4565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614620030bc57506000949350505050565b80620030c88162004c29565b91505062003043565b5081600f1a600181111562002b585762002b58620043b9565b6000806000836080015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801562003177573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200319d919062005456565b5094509092505050600081131580620031b557508142105b80620031da5750828015620031da5750620031d1824262004956565b8463ffffffff16105b15620031eb576017549550620031ef565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156200325b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003281919062005456565b50945090925050506000811315806200329957508142105b80620032be5750828015620032be5750620032b5824262004956565b8463ffffffff16105b15620032cf576018549450620032d3565b8094505b50505050915091565b600080620032f088878b60c0015162003909565b90506000806200330d8b8a63ffffffff16858a8a60018b620039a8565b90925090506200331e81836200524d565b9b9a5050505050505050505050565b60606000836001811115620033465762003346620043b9565b0362003413576000848152600760205260409081902090517f6e04ff0d00000000000000000000000000000000000000000000000000000000916200338e916024016200554e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152905062003548565b60018360018111156200342a576200342a620043b9565b036200351657600082806020019051810190620034489190620055c5565b6000868152600760205260409081902090519192507f40691db400000000000000000000000000000000000000000000000000000000916200348f918491602401620056d9565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150620035489050565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b6000620029b3838362003e4a565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115620035965762003596620043b9565b036200361457606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620035e9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200360f9190620057a1565b905090565b504390565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002f84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016200118a565b3373ffffffffffffffffffffffffffffffffffffffff8216036200371d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200118a565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115620037cc57620037cc620043b9565b03620038f1576000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562003821573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620038479190620057a1565b9050808310158062003865575061010062003863848362004956565b115b15620038745750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa158015620038cb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620035489190620057a1565b504090565b919050565b6000620029b3838362003f55565b60008080856001811115620039225762003922620043b9565b0362003933575062015f9062003956565b60018560018111156200394a576200394a620043b9565b036200351657506201adb05b6200396963ffffffff85166014620057bb565b62003976846001620057fb565b620039879060ff16611d4c620057bb565b62003993908362004c13565b6200399f919062004c13565b95945050505050565b60008060008960a0015161ffff1687620039c39190620057bb565b9050838015620039d25750803a105b15620039db57503a5b600060027f0000000000000000000000000000000000000000000000000000000000000000600281111562003a145762003a14620043b9565b0362003b8557604080516000815260208101909152851562003a785760003660405180608001604052806048815260200162005cf06048913960405160200162003a619392919062005817565b604051602081830303815290604052905062003ae6565b60165462003a9690640100000000900463ffffffff16600462005840565b63ffffffff1667ffffffffffffffff81111562003ab75762003ab76200409d565b6040519080825280601f01601f19166020018201604052801562003ae2576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e9062003b38908490600401620048f6565b602060405180830381865afa15801562003b56573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003b7c9190620057a1565b91505062003cef565b60017f0000000000000000000000000000000000000000000000000000000000000000600281111562003bbc5762003bbc620043b9565b0362003cef57841562003c4457606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562003c16573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003c3c9190620057a1565b905062003cef565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa15801562003c93573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003cb991906200586f565b505060165492945062003cde93505050640100000000900463ffffffff1682620057bb565b62003ceb906010620057bb565b9150505b8462003d0e57808b60a0015161ffff1662003d0b9190620057bb565b90505b62003d1e61ffff871682620058ba565b90506000878262003d308c8e62004c13565b62003d3c9086620057bb565b62003d48919062004c13565b62003d5c90670de0b6b3a7640000620057bb565b62003d689190620058ba565b905060008c6040015163ffffffff1664e8d4a5100062003d899190620057bb565b898e6020015163ffffffff16858f8862003da49190620057bb565b62003db0919062004c13565b62003dc090633b9aca00620057bb565b62003dcc9190620057bb565b62003dd89190620058ba565b62003de4919062004c13565b90506b033b2e3c9fd0803ce800000062003dff828462004c13565b111562003e38576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b6000818152600183016020526040812054801562003f4357600062003e7160018362004956565b855490915060009062003e879060019062004956565b905081811462003ef357600086600001828154811062003eab5762003eab62004be4565b906000526020600020015490508087600001848154811062003ed15762003ed162004be4565b6000918252602080832090910192909255918252600188019052604090208390555b855486908062003f075762003f07620058f6565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050620029b6565b6000915050620029b6565b5092915050565b600081815260018301602052604081205462003f9e57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620029b6565b506000620029b6565b6103ca806200592683390190565b50805462003fc39062004a14565b6000825580601f1062003fd4575050565b601f0160209004906000526020600020908101906200294791905b8082111562004005576000815560010162003fef565b5090565b73ffffffffffffffffffffffffffffffffffffffff811681146200294757600080fd5b803563ffffffff81168114620038f657600080fd5b803560028110620038f657600080fd5b60008083601f8401126200406457600080fd5b50813567ffffffffffffffff8111156200407d57600080fd5b6020830191508360208285010111156200409657600080fd5b9250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715620040f257620040f26200409d565b60405290565b604051610100810167ffffffffffffffff81118282101715620040f257620040f26200409d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156200416957620041696200409d565b604052919050565b600067ffffffffffffffff8211156200418e576200418e6200409d565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112620041cc57600080fd5b8135620041e3620041dd8262004171565b6200411f565b818152846020838601011115620041f957600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060008060e0898b0312156200423357600080fd5b8835620042408162004009565b97506200425060208a016200402c565b96506040890135620042628162004009565b95506200427260608a0162004041565b9450608089013567ffffffffffffffff808211156200429057600080fd5b6200429e8c838d0162004051565b909650945060a08b0135915080821115620042b857600080fd5b620042c68c838d01620041ba565b935060c08b0135915080821115620042dd57600080fd5b50620042ec8b828c01620041ba565b9150509295985092959890939650565b600080604083850312156200431057600080fd5b82359150602083013567ffffffffffffffff8111156200432f57600080fd5b6200433d85828601620041ba565b9150509250929050565b60005b83811015620043645781810151838201526020016200434a565b50506000910152565b600081518084526200438781602086016020860162004347565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600a811062004420577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b84151581526080602082015260006200444160808301866200436d565b9050620044526040830185620043e8565b82606083015295945050505050565b6000806000604084860312156200447757600080fd5b83359250602084013567ffffffffffffffff8111156200449657600080fd5b620044a48682870162004051565b9497909650939450505050565b600080600080600080600060a0888a031215620044cd57600080fd5b8735620044da8162004009565b9650620044ea602089016200402c565b95506040880135620044fc8162004009565b9450606088013567ffffffffffffffff808211156200451a57600080fd5b620045288b838c0162004051565b909650945060808a01359150808211156200454257600080fd5b50620045518a828b0162004051565b989b979a50959850939692959293505050565b871515815260e0602082015260006200458160e08301896200436d565b9050620045926040830188620043e8565b8560608301528460808301528360a08301528260c083015298975050505050505050565b600080600060408486031215620045cc57600080fd5b833567ffffffffffffffff80821115620045e557600080fd5b818601915086601f830112620045fa57600080fd5b8135818111156200460a57600080fd5b8760208260051b85010111156200462057600080fd5b60209283019550935050840135620046388162004009565b809150509250925092565b600080602083850312156200465757600080fd5b823567ffffffffffffffff8111156200466f57600080fd5b6200467d8582860162004051565b90969095509350505050565b80356bffffffffffffffffffffffff81168114620038f657600080fd5b60008060408385031215620046ba57600080fd5b82359150620046cc6020840162004689565b90509250929050565b600060208284031215620046e857600080fd5b5035919050565b600067ffffffffffffffff8211156200470c576200470c6200409d565b5060051b60200190565b600082601f8301126200472857600080fd5b813560206200473b620041dd83620046ef565b82815260059290921b840181019181810190868411156200475b57600080fd5b8286015b84811015620047a057803567ffffffffffffffff811115620047815760008081fd5b620047918986838b0101620041ba565b8452509183019183016200475f565b509695505050505050565b60008060008060608587031215620047c257600080fd5b84359350602085013567ffffffffffffffff80821115620047e257600080fd5b620047f08883890162004716565b945060408701359150808211156200480757600080fd5b50620048168782880162004051565b95989497509550505050565b6000602082840312156200483557600080fd5b8135620035488162004009565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff8083168181036200488d576200488d62004842565b6001019392505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60208152600062002b5860208301848662004897565b602081526000620029b360208301846200436d565b8051620038f68162004009565b6000602082840312156200492b57600080fd5b8151620035488162004009565b600082516200494c81846020870162004347565b9190910192915050565b81810381811115620029b657620029b662004842565b80151581146200294757600080fd5b600082601f8301126200498d57600080fd5b81516200499e620041dd8262004171565b818152846020838601011115620049b457600080fd5b62002b5882602083016020870162004347565b60008060408385031215620049db57600080fd5b8251620049e8816200496c565b602084015190925067ffffffffffffffff81111562004a0657600080fd5b6200433d858286016200497b565b600181811c9082168062004a2957607f821691505b60208210810362004a63577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111562004ab757600081815260208120601f850160051c8101602086101562004a925750805b601f850160051c820191505b8181101562004ab35782815560010162004a9e565b5050505b505050565b67ffffffffffffffff83111562004ad75762004ad76200409d565b62004aef8362004ae8835462004a14565b8362004a69565b6000601f84116001811462004b44576000851562004b0d5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835562004bdd565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101562004b95578685013582556020948501946001909201910162004b73565b508682101562004bd1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115620029b657620029b662004842565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362004c5d5762004c5d62004842565b5060010190565b600081518084526020808501945080840160005b8381101562004d235781518051151588528381015163ffffffff908116858a01526040808301519091169089015260608082015173ffffffffffffffffffffffffffffffffffffffff16908901526080808201516bffffffffffffffffffffffff169089015260a08082015162004cfe828b01826bffffffffffffffffffffffff169052565b505060c09081015163ffffffff169088015260e0909601959082019060010162004c78565b509495945050505050565b600081518084526020808501945080840160005b8381101562004d2357815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010162004d42565b600081518084526020808501808196508360051b8101915082860160005b8581101562004dc257828403895262004daf8483516200436d565b9885019893509084019060010162004d94565b5091979650505050505050565b60e081528760e082015260006101007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a111562004e0c57600080fd5b8960051b808c8386013783018381038201602085015262004e308282018b62004c64565b915050828103604084015262004e47818962004d2e565b9050828103606084015262004e5d818862004d2e565b9050828103608084015262004e73818762004d76565b905082810360a084015262004e89818662004d76565b905082810360c08401526200331e818562004d76565b60006020828403121562004eb257600080fd5b815160ff811681146200354857600080fd5b60ff8416815260ff831660208201526060604082015260006200399f60608301846200436d565b60006020828403121562004efe57600080fd5b815167ffffffffffffffff81111562004f1657600080fd5b62002b58848285016200497b565b60006020828403121562004f3757600080fd5b815162003548816200496c565b600082601f83011262004f5657600080fd5b8135602062004f69620041dd83620046ef565b82815260059290921b8401810191818101908684111562004f8957600080fd5b8286015b84811015620047a0578035835291830191830162004f8d565b600082601f83011262004fb857600080fd5b8135602062004fcb620041dd83620046ef565b82815260e0928302850182019282820191908785111562004feb57600080fd5b8387015b85811015620050a25781818a031215620050095760008081fd5b62005013620040cc565b813562005020816200496c565b81526200502f8287016200402c565b868201526040620050428184016200402c565b90820152606082810135620050578162004009565b9082015260806200506a83820162004689565b9082015260a06200507d83820162004689565b9082015260c0620050908382016200402c565b90820152845292840192810162004fef565b5090979650505050505050565b600082601f830112620050c157600080fd5b81356020620050d4620041dd83620046ef565b82815260059290921b84018101918181019086841115620050f457600080fd5b8286015b84811015620047a05780356200510e8162004009565b8352918301918301620050f8565b600080600080600080600060e0888a0312156200513857600080fd5b873567ffffffffffffffff808211156200515157600080fd5b6200515f8b838c0162004f44565b985060208a01359150808211156200517657600080fd5b620051848b838c0162004fa6565b975060408a01359150808211156200519b57600080fd5b620051a98b838c01620050af565b965060608a0135915080821115620051c057600080fd5b620051ce8b838c01620050af565b955060808a0135915080821115620051e557600080fd5b620051f38b838c0162004716565b945060a08a01359150808211156200520a57600080fd5b620052188b838c0162004716565b935060c08a01359150808211156200522f57600080fd5b506200523e8a828b0162004716565b91505092959891949750929550565b6bffffffffffffffffffffffff81811683821601908082111562003f4e5762003f4e62004842565b6bffffffffffffffffffffffff82811682821603908082111562003f4e5762003f4e62004842565b604081526000620052b2604083018662004d76565b8281036020840152620052c781858762004897565b9695505050505050565b8051602080830151919081101562004a63577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b16919050565b815167ffffffffffffffff8111156200533157620053316200409d565b620053498162005342845462004a14565b8462004a69565b602080601f8311600181146200539f5760008415620053685750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855562004ab3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015620053ee57888601518255948401946001909101908401620053cd565b50858210156200542b57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b805169ffffffffffffffffffff81168114620038f657600080fd5b600080600080600060a086880312156200546f57600080fd5b6200547a866200543b565b94506020860151935060408601519250606086015191506200549f608087016200543b565b90509295509295909350565b60008154620054ba8162004a14565b808552602060018381168015620054da5760018114620055135762005543565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b890101955062005543565b866000528260002060005b858110156200553b5781548a82018601529083019084016200551e565b890184019650505b505050505092915050565b602081526000620029b36020830184620054ab565b600082601f8301126200557557600080fd5b8151602062005588620041dd83620046ef565b82815260059290921b84018101918181019086841115620055a857600080fd5b8286015b84811015620047a05780518352918301918301620055ac565b600060208284031215620055d857600080fd5b815167ffffffffffffffff80821115620055f157600080fd5b9083019061010082860312156200560757600080fd5b62005611620040f8565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201526200564b60a084016200490b565b60a082015260c0830151828111156200566357600080fd5b620056718782860162005563565b60c08301525060e0830151828111156200568a57600080fd5b62005698878286016200497b565b60e08301525095945050505050565b600081518084526020808501945080840160005b8381101562004d2357815187529582019590820190600101620056bb565b60408152825160408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015273ffffffffffffffffffffffffffffffffffffffff60a08401511660e0820152600060c08401516101008081850152506200574c610140840182620056a7565b905060e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0848303016101208501526200578a82826200436d565b91505082810360208401526200399f8185620054ab565b600060208284031215620057b457600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615620057f657620057f662004842565b500290565b60ff8181168382160190811115620029b657620029b662004842565b8284823760008382016000815283516200583681836020880162004347565b0195945050505050565b600063ffffffff8083168185168183048111821515161562005866576200586662004842565b02949350505050565b60008060008060008060c087890312156200588957600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600082620058f1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe60c060405234801561001057600080fd5b506040516103ca3803806103ca83398101604081905261002f91610076565b600080546001600160a01b0319166001600160a01b039384161790559181166080521660a0526100b9565b80516001600160a01b038116811461007157600080fd5b919050565b60008060006060848603121561008b57600080fd5b6100948461005a565b92506100a26020850161005a565b91506100b06040850161005a565b90509250925092565b60805160a0516102e76100e36000396000603801526000818160c4015261011701526102e76000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806379188d161461007b578063f00e6a2a146100aa575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610076573d6000f35b3d6000fd5b61008e6100893660046101c1565b6100ee565b6040805192151583526020830191909152015b60405180910390f35b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526020016100a1565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331461011557600080fd5b7f00000000000000000000000000000000000000000000000000000000000000005a91505a61138881101561014957600080fd5b61138881039050856040820482031161016157600080fd5b50803b61016d57600080fd5b6000808551602087016000858af192505a610188908361029a565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080604083850312156101d457600080fd5b82359150602083013567ffffffffffffffff808211156101f357600080fd5b818501915085601f83011261020757600080fd5b81358181111561021957610219610192565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561025f5761025f610192565b8160405282815288602084870101111561027857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b818103818111156102d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000810000a307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", } var AutomationRegistryLogicAABI = AutomationRegistryLogicAMetaData.ABI diff --git a/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go b/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go index 1805d535680..b12b24c204a 100644 --- a/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go +++ b/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_2/keeper_registry_logic_b_wrapper_2_2.go @@ -46,6 +46,7 @@ type AutomationRegistryBase22OnchainConfig struct { Transcoder common.Address Registrars []common.Address UpkeepPrivilegeManager common.Address + ReorgProtectionEnabled bool } type AutomationRegistryBase22State struct { @@ -75,8 +76,8 @@ type AutomationRegistryBase22UpkeepInfo struct { } var AutomationRegistryLogicBMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Mode\",\"name\":\"mode\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"fastGasFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"automationForwarderLogic\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"contractIAutomationForwarder\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkNativeFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMode\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Mode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.MigrationPermission\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.State\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structAutomationRegistryBase2_2.UpkeepInfo\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_2.MigrationPermission\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepTranscoderVersion\",\"outputs\":[{\"internalType\":\"enumUpkeepFormat\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawOwnerFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101206040523480156200001257600080fd5b5060405162004fbc38038062004fbc8339810160408190526200003591620001e9565b84848484843380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000121565b505050846002811115620000dc57620000dc6200025e565b60e0816002811115620000f357620000f36200025e565b9052506001600160a01b0393841660805291831660a052821660c05216610100525062000274945050505050565b336001600160a01b038216036200017b5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001e457600080fd5b919050565b600080600080600060a086880312156200020257600080fd5b8551600381106200021257600080fd5b94506200022260208701620001cc565b93506200023260408701620001cc565b92506200024260608701620001cc565b91506200025260808701620001cc565b90509295509295909350565b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e05161010051614cbd620002ff600039600061058701526000818161052501528181613352015281816138d50152613a680152600081816105f4015261314601526000818161071c01526132200152600081816107aa01528181611bab01528181611e81015281816122ee0152818161280101526128850152614cbd6000f3fe608060405234801561001057600080fd5b50600436106103365760003560e01c806379ba5097116101b2578063b121e147116100f9578063ca30e603116100a2578063eb5dcd6c1161007c578063eb5dcd6c146107f4578063ed56b3e114610807578063f2fde38b1461087a578063faa3e9961461088d57600080fd5b8063ca30e603146107a8578063cd7f71b5146107ce578063d7632648146107e157600080fd5b8063b657bc9c116100d3578063b657bc9c1461076d578063b79550be14610780578063c7c3a19a1461078857600080fd5b8063b121e14714610740578063b148ab6b14610753578063b6511a2a1461076657600080fd5b80638dcf0fe71161015b578063aab9edd611610135578063aab9edd614610703578063abc76ae014610712578063b10b673c1461071a57600080fd5b80638dcf0fe7146106ca578063a710b221146106dd578063a72aa27e146106f057600080fd5b80638456cb591161018c5780638456cb59146106915780638765ecbe146106995780638da5cb5b146106ac57600080fd5b806379ba50971461063e57806379ea9943146106465780637d9b97e01461068957600080fd5b8063421d183b116102815780635165f2f51161022a5780636209e1e9116102045780636209e1e9146105df5780636709d0e5146105f2578063671d36ed14610618578063744bfe611461062b57600080fd5b80635165f2f5146105725780635425d8ac146105855780635b6aa71c146105cc57600080fd5b80634b4fd03b1161025b5780634b4fd03b146105235780634ca16c52146105495780635147cd591461055257600080fd5b8063421d183b1461047a57806344cb70b8146104e057806348013d7b1461051357600080fd5b80631a2af011116102e3578063232c1cc5116102bd578063232c1cc5146104585780633b9cce591461045f5780633f4ba83a1461047257600080fd5b80631a2af011146103d45780631e010439146103e7578063207b65161461044557600080fd5b80631865c57d116103145780631865c57d14610388578063187256e8146103a157806319d97a94146103b457600080fd5b8063050ee65d1461033b57806306e3b632146103535780630b7d33e614610373575b600080fd5b6201adb05b6040519081526020015b60405180910390f35b610366610361366004613df3565b6108d3565b60405161034a9190613e15565b610386610381366004613ea2565b6109f0565b005b610390610aaa565b60405161034a9594939291906140a5565b6103866103af3660046141dc565b610ec3565b6103c76103c2366004614219565b610f34565b60405161034a91906142a0565b6103866103e23660046142b3565b610fd6565b6104286103f5366004614219565b6000908152600460205260409020600101546c0100000000000000000000000090046bffffffffffffffffffffffff1690565b6040516bffffffffffffffffffffffff909116815260200161034a565b6103c7610453366004614219565b6110dc565b6014610340565b61038661046d3660046142d8565b6110f9565b61038661134f565b61048d61048836600461434d565b6113b5565b60408051951515865260ff90941660208601526bffffffffffffffffffffffff9283169385019390935216606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00161034a565b6105036104ee366004614219565b60009081526008602052604090205460ff1690565b604051901515815260200161034a565b60005b60405161034a91906143a9565b7f0000000000000000000000000000000000000000000000000000000000000000610516565b62015f90610340565b610565610560366004614219565b6114e8565b60405161034a91906143bc565b610386610580366004614219565b6114f3565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161034a565b6104286105da3660046143e9565b61166a565b6103c76105ed36600461434d565b61179c565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b610386610626366004614422565b6117d0565b6103866106393660046142b3565b6118aa565b610386611ca7565b6105a7610654366004614219565b6000908152600460205260409020546901000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b610386611da9565b610386611f04565b6103866106a7366004614219565b611f75565b60005473ffffffffffffffffffffffffffffffffffffffff166105a7565b6103866106d8366004613ea2565b6120ef565b6103866106eb36600461445e565b612144565b6103866106fe36600461448c565b6123c0565b6040516003815260200161034a565b611d4c610340565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b61038661074e36600461434d565b6124b5565b610386610761366004614219565b6125ad565b6032610340565b61042861077b366004614219565b61279b565b6103866127c8565b61079b610796366004614219565b612924565b60405161034a91906144af565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b6103866107dc366004613ea2565b612cf7565b6104286107ef366004614219565b612d8e565b61038661080236600461445e565b612d99565b61086161081536600461434d565b73ffffffffffffffffffffffffffffffffffffffff166000908152600c602090815260409182902082518084019093525460ff8082161515808552610100909204169290910182905291565b60408051921515835260ff90911660208301520161034a565b61038661088836600461434d565b612ef7565b6108c661089b36600461434d565b73ffffffffffffffffffffffffffffffffffffffff1660009081526019602052604090205460ff1690565b60405161034a91906145e6565b606060006108e16002612f0b565b905080841061091c576040517f1390f2a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109288486614629565b905081811180610936575083155b6109405780610942565b815b90506000610950868361463c565b67ffffffffffffffff8111156109685761096861464f565b604051908082528060200260200182016040528015610991578160200160208202803683370190505b50905060005b81518110156109e4576109b56109ad8883614629565b600290612f15565b8282815181106109c7576109c761467e565b6020908102919091010152806109dc816146ad565b915050610997565b50925050505b92915050565b6015546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314610a51576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152601c60205260409020610a6a828483614787565b50827f2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae77698383604051610a9d9291906148a2565b60405180910390a2505050565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152604080516101e08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a08201526101c0810191909152604080516101408101825260145463ffffffff7401000000000000000000000000000000000000000082041682526bffffffffffffffffffffffff908116602083015260185492820192909252601254700100000000000000000000000000000000900490911660608281019190915290819060009060808101610bf76002612f0b565b81526014547801000000000000000000000000000000000000000000000000810463ffffffff9081166020808501919091527c0100000000000000000000000000000000000000000000000000000000808404831660408087019190915260115460608088019190915260125492830485166080808901919091526e010000000000000000000000000000840460ff16151560a09889015282516101e081018452610100808604881682526501000000000086048816968201969096526c010000000000000000000000008089048816948201949094526901000000000000000000850462ffffff16928101929092529282900461ffff16928101929092526013546bffffffffffffffffffffffff811696830196909652700100000000000000000000000000000000909404831660c082015260155480841660e0830152640100000000810484169282019290925268010000000000000000909104909116610120820152601654610140820152601754610160820152910473ffffffffffffffffffffffffffffffffffffffff166101808201529095506101a08101610d9f6009612f28565b81526015546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16602091820152601254600d80546040805182860281018601909152818152949850899489949293600e9360ff909116928591830182828015610e4257602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e17575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015610eab57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e80575b50505050509150945094509450945094509091929394565b610ecb612f35565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260196020526040902080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115610f2b57610f2b61436a565b02179055505050565b6000818152601c60205260409020805460609190610f51906146e5565b80601f0160208091040260200160405190810160405280929190818152602001828054610f7d906146e5565b8015610fca5780601f10610f9f57610100808354040283529160200191610fca565b820191906000526020600020905b815481529060010190602001808311610fad57829003601f168201915b50505050509050919050565b610fdf82612fb8565b3373ffffffffffffffffffffffffffffffffffffffff82160361102e576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116146110d85760008281526006602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915590519091339185917fb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b3591a45b5050565b6000818152601a60205260409020805460609190610f51906146e5565b611101612f35565b600e54811461113c576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b600e5481101561130e576000600e828154811061115e5761115e61467e565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff908116808452600f909252604083205491935016908585858181106111a8576111a861467e565b90506020020160208101906111bd919061434d565b905073ffffffffffffffffffffffffffffffffffffffff81161580611250575073ffffffffffffffffffffffffffffffffffffffff82161580159061122e57508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b8015611250575073ffffffffffffffffffffffffffffffffffffffff81811614155b15611287576040517fb387a23800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff818116146112f85773ffffffffffffffffffffffffffffffffffffffff8381166000908152600f6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169183169190911790555b5050508080611306906146ad565b91505061113f565b507fa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725600e8383604051611343939291906148ef565b60405180910390a15050565b611357612f35565b601280547fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff1690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152829182918291829190829061148f57606082015160125460009161147b9170010000000000000000000000000000000090046bffffffffffffffffffffffff166149a1565b600e5490915061148b90826149f5565b9150505b8151602083015160408401516114a6908490614a20565b6060949094015173ffffffffffffffffffffffffffffffffffffffff9a8b166000908152600f6020526040902054929b919a9499509750921694509092505050565b60006109ea8261306c565b6114fc81612fb8565b600081815260046020908152604091829020825160e081018452815460ff8116151580835263ffffffff610100830481169584019590955265010000000000820485169583019590955273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c0820152906115fb576040517f1b88a78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905561163a600283613117565b5060405182907f7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a4745690600090a25050565b604080516101208101825260125460ff808216835263ffffffff6101008084048216602086015265010000000000840482169585019590955262ffffff6901000000000000000000840416606085015261ffff6c0100000000000000000000000084041660808501526e01000000000000000000000000000083048216151560a08501526f010000000000000000000000000000008304909116151560c08401526bffffffffffffffffffffffff70010000000000000000000000000000000083041660e08401527c01000000000000000000000000000000000000000000000000000000009091041691810191909152600090818061176983613123565b91509150611792838787601360020160049054906101000a900463ffffffff1686866000613301565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152601d60205260409020805460609190610f51906146e5565b6015546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314611831576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152601d60205260409020611861828483614787565b508273ffffffffffffffffffffffffffffffffffffffff167f7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d28383604051610a9d9291906148a2565b6012546f01000000000000000000000000000000900460ff16156118fa576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff166f0100000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff8116611981576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600460209081526040808320815160e081018352815460ff81161515825263ffffffff610100820481168387015265010000000000820481168386015273ffffffffffffffffffffffffffffffffffffffff6901000000000000000000909204821660608401526001909301546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a08401527801000000000000000000000000000000000000000000000000900490921660c082015286855260059093529220549091163314611a88576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a9061334c565b816040015163ffffffff161115611ad3576040517fff84e5dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600460205260409020600101546018546c010000000000000000000000009091046bffffffffffffffffffffffff1690611b1390829061463c565b60185560008481526004602081905260409182902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff16905590517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116928201929092526bffffffffffffffffffffffff831660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015611bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1a9190614a45565b50604080516bffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff8516602082015285917ff3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318910160405180910390a25050601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff1690555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d2d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611db1612f35565b6014546018546bffffffffffffffffffffffff90911690611dd390829061463c565b601855601480547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690556040516bffffffffffffffffffffffff821681527f1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f19060200160405180910390a16040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526bffffffffffffffffffffffff821660248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044015b6020604051808303816000875af1158015611ee0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d89190614a45565b611f0c612f35565b601280547fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff166e0100000000000000000000000000001790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016113ab565b611f7e81612fb8565b600081815260046020908152604091829020825160e081018452815460ff8116158015835263ffffffff610100830481169584019590955265010000000000820485169583019590955273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c08201529061207d576040517f514b6c2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556120bf600283613401565b5060405182907f8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f90600090a25050565b6120f883612fb8565b6000838152601b60205260409020612111828483614787565b50827f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf48508383604051610a9d9291906148a2565b73ffffffffffffffffffffffffffffffffffffffff8116612191576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600f60205260409020541633146121f1576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e5460009161222891859170010000000000000000000000000000000090046bffffffffffffffffffffffff169061340d565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600b6020526040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601854909150612292906bffffffffffffffffffffffff83169061463c565b6018556040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526bffffffffffffffffffffffff831660248301527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612337573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061235b9190614a45565b5060405133815273ffffffffffffffffffffffffffffffffffffffff808416916bffffffffffffffffffffffff8416918616907f9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f406989060200160405180910390a4505050565b6108fc8163ffffffff1610806123f5575060145463ffffffff7001000000000000000000000000000000009091048116908216115b1561242c576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61243582612fb8565b60008281526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff861690810291909117909155915191825283917fc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c910160405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff818116600090815260106020526040902054163314612515576040517f6752e7aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600f602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556010909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b600081815260046020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452650100000000008104841694820185905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004821660c082015291146126aa576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314612707576040517f6352a85300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602090815260408083208054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821790935560069094528285208054909216909155905173ffffffffffffffffffffffffffffffffffffffff90911692839186917f5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c91a4505050565b60006109ea6127a98361306c565b600084815260046020526040902054610100900463ffffffff1661166a565b6127d0612f35565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa15801561285d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128819190614a67565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33601854846128ce919061463c565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401611ec1565b604080516101408101825260008082526020820181905260609282018390528282018190526080820181905260a0820181905260c0820181905260e082018190526101008201526101208101919091526000828152600460209081526040808320815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606083018190526001909101546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a08401527801000000000000000000000000000000000000000000000000900490921660c0820152919015612abc57816060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab79190614a80565b612abf565b60005b90506040518061014001604052808273ffffffffffffffffffffffffffffffffffffffff168152602001836020015163ffffffff168152602001600760008781526020019081526020016000208054612b17906146e5565b80601f0160208091040260200160405190810160405280929190818152602001828054612b43906146e5565b8015612b905780601f10612b6557610100808354040283529160200191612b90565b820191906000526020600020905b815481529060010190602001808311612b7357829003601f168201915b505050505081526020018360a001516bffffffffffffffffffffffff1681526020016005600087815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001836040015163ffffffff1667ffffffffffffffff1681526020018360c0015163ffffffff16815260200183608001516bffffffffffffffffffffffff168152602001836000015115158152602001601b60008781526020019081526020016000208054612c6d906146e5565b80601f0160208091040260200160405190810160405280929190818152602001828054612c99906146e5565b8015612ce65780601f10612cbb57610100808354040283529160200191612ce6565b820191906000526020600020905b815481529060010190602001808311612cc957829003601f168201915b505050505081525092505050919050565b612d0083612fb8565b60155463ffffffff16811115612d42576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600760205260409020612d5b828483614787565b50827fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d8383604051610a9d9291906148a2565b60006109ea8261279b565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600f6020526040902054163314612df9576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821603612e48576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601060205260409020548116908216146110d85773ffffffffffffffffffffffffffffffffffffffff82811660008181526010602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055513392917f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836791a45050565b612eff612f35565b612f0881613615565b50565b60006109ea825490565b6000612f21838361370a565b9392505050565b60606000612f2183613734565b60005473ffffffffffffffffffffffffffffffffffffffff163314612fb6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401611d24565b565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314613015576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090205465010000000000900463ffffffff90811614612f08576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818160045b600f8110156130f9577fff0000000000000000000000000000000000000000000000000000000000000082168382602081106130b1576130b161467e565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146130e757506000949350505050565b806130f1816146ad565b915050613073565b5081600f1a600181111561310f5761310f61436a565b949350505050565b6000612f21838361378f565b6000806000836060015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156131af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d39190614ab7565b50945090925050506000811315806131ea57508142105b8061320b575082801561320b5750613202824261463c565b8463ffffffff16105b1561321a57601654955061321e565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613289573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ad9190614ab7565b50945090925050506000811315806132c457508142105b806132e557508280156132e557506132dc824261463c565b8463ffffffff16105b156132f45760175494506132f8565b8094505b50505050915091565b60008061331388878b600001516137de565b905060008061332e8b8a63ffffffff16858a8a60018b6138a0565b909250905061333d8183614a20565b9b9a5050505050505050505050565b600060017f000000000000000000000000000000000000000000000000000000000000000060028111156133825761338261436a565b036133fc57606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f79190614a67565b905090565b504390565b6000612f218383613cf9565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152906136095760008160600151856134a591906149a1565b905060006134b385836149f5565b905080836040018181516134c79190614a20565b6bffffffffffffffffffffffff169052506134e28582614b07565b836060018181516134f39190614a20565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613694576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401611d24565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008260000182815481106137215761372161467e565b9060005260206000200154905092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015610fca57602002820191906000526020600020905b8154815260200190600101908083116137705750505050509050919050565b60008181526001830160205260408120546137d6575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109ea565b5060006109ea565b600080808560018111156137f4576137f461436a565b03613803575062015f90613858565b60018560018111156138175761381761436a565b0361382657506201adb0613858565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61386963ffffffff85166014614b3b565b613874846001614b78565b6138839060ff16611d4c614b3b565b61388d9083614629565b6138979190614629565b95945050505050565b6000806000896080015161ffff16876138b99190614b3b565b90508380156138c75750803a105b156138cf57503a5b600060027f000000000000000000000000000000000000000000000000000000000000000060028111156139055761390561436a565b03613a6457604080516000815260208101909152851561396357600036604051806080016040528060488152602001614c696048913960405160200161394d93929190614b91565b60405160208183030381529060405290506139cb565b60155461397f90640100000000900463ffffffff166004614bb8565b63ffffffff1667ffffffffffffffff81111561399d5761399d61464f565b6040519080825280601f01601f1916602001820160405280156139c7576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e90613a1b9084906004016142a0565b602060405180830381865afa158015613a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5c9190614a67565b915050613bbe565b60017f00000000000000000000000000000000000000000000000000000000000000006002811115613a9857613a9861436a565b03613bbe578415613b1a57606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613aef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b139190614a67565b9050613bbe565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa158015613b68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8c9190614bdb565b5050601554929450613baf93505050640100000000900463ffffffff1682614b3b565b613bba906010614b3b565b9150505b84613bda57808b6080015161ffff16613bd79190614b3b565b90505b613be861ffff871682614c25565b905060008782613bf88c8e614629565b613c029086614b3b565b613c0c9190614629565b613c1e90670de0b6b3a7640000614b3b565b613c289190614c25565b905060008c6040015163ffffffff1664e8d4a51000613c479190614b3b565b898e6020015163ffffffff16858f88613c609190614b3b565b613c6a9190614629565b613c7890633b9aca00614b3b565b613c829190614b3b565b613c8c9190614c25565b613c969190614629565b90506b033b2e3c9fd0803ce8000000613caf8284614629565b1115613ce7576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b60008181526001830160205260408120548015613de2576000613d1d60018361463c565b8554909150600090613d319060019061463c565b9050818114613d96576000866000018281548110613d5157613d5161467e565b9060005260206000200154905080876000018481548110613d7457613d7461467e565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613da757613da7614c39565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109ea565b60009150506109ea565b5092915050565b60008060408385031215613e0657600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015613e4d57835183529284019291840191600101613e31565b50909695505050505050565b60008083601f840112613e6b57600080fd5b50813567ffffffffffffffff811115613e8357600080fd5b602083019150836020828501011115613e9b57600080fd5b9250929050565b600080600060408486031215613eb757600080fd5b83359250602084013567ffffffffffffffff811115613ed557600080fd5b613ee186828701613e59565b9497909650939450505050565b600081518084526020808501945080840160005b83811015613f3457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613f02565b509495945050505050565b805163ffffffff16825260006101e06020830151613f65602086018263ffffffff169052565b506040830151613f7d604086018263ffffffff169052565b506060830151613f94606086018262ffffff169052565b506080830151613faa608086018261ffff169052565b5060a0830151613fca60a08601826bffffffffffffffffffffffff169052565b5060c0830151613fe260c086018263ffffffff169052565b5060e0830151613ffa60e086018263ffffffff169052565b506101008381015163ffffffff908116918601919091526101208085015190911690850152610140808401519085015261016080840151908501526101808084015173ffffffffffffffffffffffffffffffffffffffff16908501526101a08084015181860183905261406f83870182613eee565b925050506101c08084015161409b8287018273ffffffffffffffffffffffffffffffffffffffff169052565b5090949350505050565b855163ffffffff16815260006101c060208801516140d360208501826bffffffffffffffffffffffff169052565b506040880151604084015260608801516140fd60608501826bffffffffffffffffffffffff169052565b506080880151608084015260a088015161411f60a085018263ffffffff169052565b5060c088015161413760c085018263ffffffff169052565b5060e088015160e08401526101008089015161415a8286018263ffffffff169052565b505061012088810151151590840152610140830181905261417d81840188613f3f565b90508281036101608401526141928187613eee565b90508281036101808401526141a78186613eee565b9150506117926101a083018460ff169052565b73ffffffffffffffffffffffffffffffffffffffff81168114612f0857600080fd5b600080604083850312156141ef57600080fd5b82356141fa816141ba565b915060208301356004811061420e57600080fd5b809150509250929050565b60006020828403121561422b57600080fd5b5035919050565b60005b8381101561424d578181015183820152602001614235565b50506000910152565b6000815180845261426e816020860160208601614232565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612f216020830184614256565b600080604083850312156142c657600080fd5b82359150602083013561420e816141ba565b600080602083850312156142eb57600080fd5b823567ffffffffffffffff8082111561430357600080fd5b818501915085601f83011261431757600080fd5b81358181111561432657600080fd5b8660208260051b850101111561433b57600080fd5b60209290920196919550909350505050565b60006020828403121561435f57600080fd5b8135612f21816141ba565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110612f0857612f0861436a565b602081016143b683614399565b91905290565b60208101600283106143b6576143b661436a565b803563ffffffff811681146143e457600080fd5b919050565b600080604083850312156143fc57600080fd5b82356002811061440b57600080fd5b9150614419602084016143d0565b90509250929050565b60008060006040848603121561443757600080fd5b8335614442816141ba565b9250602084013567ffffffffffffffff811115613ed557600080fd5b6000806040838503121561447157600080fd5b823561447c816141ba565b9150602083013561420e816141ba565b6000806040838503121561449f57600080fd5b82359150614419602084016143d0565b602081526144d660208201835173ffffffffffffffffffffffffffffffffffffffff169052565b600060208301516144ef604084018263ffffffff169052565b50604083015161014080606085015261450c610160850183614256565b9150606085015161452d60808601826bffffffffffffffffffffffff169052565b50608085015173ffffffffffffffffffffffffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015163ffffffff811660e08601525060e0850151610100614599818701836bffffffffffffffffffffffff169052565b86015190506101206145ae8682018315159052565b8601518584037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018387015290506117928382614256565b60208101600483106143b6576143b661436a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156109ea576109ea6145fa565b818103818111156109ea576109ea6145fa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036146de576146de6145fa565b5060010190565b600181811c908216806146f957607f821691505b602082108103614732577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111561478257600081815260208120601f850160051c8101602086101561475f5750805b601f850160051c820191505b8181101561477e5782815560010161476b565b5050505b505050565b67ffffffffffffffff83111561479f5761479f61464f565b6147b3836147ad83546146e5565b83614738565b6000601f84116001811461480557600085156147cf5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561489b565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156148545786850135825560209485019460019092019101614834565b508682101561488f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b6000604082016040835280865480835260608501915087600052602092508260002060005b8281101561494657815473ffffffffffffffffffffffffffffffffffffffff1684529284019260019182019101614914565b505050838103828501528481528590820160005b8681101561499557823561496d816141ba565b73ffffffffffffffffffffffffffffffffffffffff168252918301919083019060010161495a565b50979650505050505050565b6bffffffffffffffffffffffff828116828216039080821115613dec57613dec6145fa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff80841680614a1457614a146149c6565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115613dec57613dec6145fa565b600060208284031215614a5757600080fd5b81518015158114612f2157600080fd5b600060208284031215614a7957600080fd5b5051919050565b600060208284031215614a9257600080fd5b8151612f21816141ba565b805169ffffffffffffffffffff811681146143e457600080fd5b600080600080600060a08688031215614acf57600080fd5b614ad886614a9d565b9450602086015193506040860151925060608601519150614afb60808701614a9d565b90509295509295909350565b60006bffffffffffffffffffffffff80831681851681830481118215151615614b3257614b326145fa565b02949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615614b7357614b736145fa565b500290565b60ff81811683821601908111156109ea576109ea6145fa565b828482376000838201600081528351614bae818360208801614232565b0195945050505050565b600063ffffffff80831681851681830481118215151615614b3257614b326145fa565b60008060008060008060c08789031215614bf457600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600082614c3457614c346149c6565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", + ABI: "[{\"inputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Mode\",\"name\":\"mode\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"fastGasFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"automationForwarderLogic\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"contractIAutomationForwarder\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkNativeFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMode\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Mode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.MigrationPermission\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.State\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_2.Trigger\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structAutomationRegistryBase2_2.UpkeepInfo\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_2.MigrationPermission\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepTranscoderVersion\",\"outputs\":[{\"internalType\":\"enumUpkeepFormat\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawOwnerFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101206040523480156200001257600080fd5b5060405162005081380380620050818339810160408190526200003591620001e9565b84848484843380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000121565b505050846002811115620000dc57620000dc6200025e565b60e0816002811115620000f357620000f36200025e565b9052506001600160a01b0393841660805291831660a052821660c05216610100525062000274945050505050565b336001600160a01b038216036200017b5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001e457600080fd5b919050565b600080600080600060a086880312156200020257600080fd5b8551600381106200021257600080fd5b94506200022260208701620001cc565b93506200023260408701620001cc565b92506200024260608701620001cc565b91506200025260808701620001cc565b90509295509295909350565b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e05161010051614d82620002ff6000396000610587015260008181610525015281816134090152818161398c0152613b1f0152600081816105f401526131fd01526000818161071c01526132d70152600081816107aa01528181611c6701528181611f3c015281816123a5015281816128b8015261293c0152614d826000f3fe608060405234801561001057600080fd5b50600436106103365760003560e01c806379ba5097116101b2578063b121e147116100f9578063ca30e603116100a2578063eb5dcd6c1161007c578063eb5dcd6c146107f4578063ed56b3e114610807578063f2fde38b1461087a578063faa3e9961461088d57600080fd5b8063ca30e603146107a8578063cd7f71b5146107ce578063d7632648146107e157600080fd5b8063b657bc9c116100d3578063b657bc9c1461076d578063b79550be14610780578063c7c3a19a1461078857600080fd5b8063b121e14714610740578063b148ab6b14610753578063b6511a2a1461076657600080fd5b80638dcf0fe71161015b578063aab9edd611610135578063aab9edd614610703578063abc76ae014610712578063b10b673c1461071a57600080fd5b80638dcf0fe7146106ca578063a710b221146106dd578063a72aa27e146106f057600080fd5b80638456cb591161018c5780638456cb59146106915780638765ecbe146106995780638da5cb5b146106ac57600080fd5b806379ba50971461063e57806379ea9943146106465780637d9b97e01461068957600080fd5b8063421d183b116102815780635165f2f51161022a5780636209e1e9116102045780636209e1e9146105df5780636709d0e5146105f2578063671d36ed14610618578063744bfe611461062b57600080fd5b80635165f2f5146105725780635425d8ac146105855780635b6aa71c146105cc57600080fd5b80634b4fd03b1161025b5780634b4fd03b146105235780634ca16c52146105495780635147cd591461055257600080fd5b8063421d183b1461047a57806344cb70b8146104e057806348013d7b1461051357600080fd5b80631a2af011116102e3578063232c1cc5116102bd578063232c1cc5146104585780633b9cce591461045f5780633f4ba83a1461047257600080fd5b80631a2af011146103d45780631e010439146103e7578063207b65161461044557600080fd5b80631865c57d116103145780631865c57d14610388578063187256e8146103a157806319d97a94146103b457600080fd5b8063050ee65d1461033b57806306e3b632146103535780630b7d33e614610373575b600080fd5b6201adb05b6040519081526020015b60405180910390f35b610366610361366004613eaa565b6108d3565b60405161034a9190613ecc565b610386610381366004613f59565b6109f0565b005b610390610aaa565b60405161034a95949392919061416a565b6103866103af3660046142a1565b610f0e565b6103c76103c23660046142de565b610f7f565b60405161034a9190614365565b6103866103e2366004614378565b611021565b6104286103f53660046142de565b6000908152600460205260409020600101546c0100000000000000000000000090046bffffffffffffffffffffffff1690565b6040516bffffffffffffffffffffffff909116815260200161034a565b6103c76104533660046142de565b611127565b6014610340565b61038661046d36600461439d565b611144565b61038661139a565b61048d610488366004614412565b611400565b60408051951515865260ff90941660208601526bffffffffffffffffffffffff9283169385019390935216606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00161034a565b6105036104ee3660046142de565b60009081526008602052604090205460ff1690565b604051901515815260200161034a565b60005b60405161034a919061446e565b7f0000000000000000000000000000000000000000000000000000000000000000610516565b62015f90610340565b6105656105603660046142de565b61151f565b60405161034a9190614481565b6103866105803660046142de565b61152a565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161034a565b6104286105da3660046144ae565b6116a1565b6103c76105ed366004614412565b611839565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b6103866106263660046144e7565b61186d565b610386610639366004614378565b611947565b610386611d62565b6105a76106543660046142de565b6000908152600460205260409020546901000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b610386611e64565b610386611fbf565b6103866106a73660046142de565b612040565b60005473ffffffffffffffffffffffffffffffffffffffff166105a7565b6103866106d8366004613f59565b6121ba565b6103866106eb366004614523565b61220f565b6103866106fe366004614551565b612477565b6040516003815260200161034a565b611d4c610340565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b61038661074e366004614412565b61256c565b6103866107613660046142de565b612664565b6032610340565b61042861077b3660046142de565b612852565b61038661287f565b61079b6107963660046142de565b6129db565b60405161034a9190614574565b7f00000000000000000000000000000000000000000000000000000000000000006105a7565b6103866107dc366004613f59565b612dae565b6104286107ef3660046142de565b612e45565b610386610802366004614523565b612e50565b610861610815366004614412565b73ffffffffffffffffffffffffffffffffffffffff166000908152600c602090815260409182902082518084019093525460ff8082161515808552610100909204169290910182905291565b60408051921515835260ff90911660208301520161034a565b610386610888366004614412565b612fae565b6108c661089b366004614412565b73ffffffffffffffffffffffffffffffffffffffff166000908152601a602052604090205460ff1690565b60405161034a91906146ab565b606060006108e16002612fc2565b905080841061091c576040517f1390f2a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061092884866146ee565b905081811180610936575083155b6109405780610942565b815b905060006109508683614701565b67ffffffffffffffff81111561096857610968614714565b604051908082528060200260200182016040528015610991578160200160208202803683370190505b50905060005b81518110156109e4576109b56109ad88836146ee565b600290612fcc565b8282815181106109c7576109c7614743565b6020908102919091010152806109dc81614772565b915050610997565b50925050505b92915050565b6016546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314610a51576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152601d60205260409020610a6a82848361484c565b50827f2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae77698383604051610a9d929190614967565b60405180910390a2505050565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152604080516102008101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a08201526101c081018290526101e0810191909152604080516101408101825260155463ffffffff7401000000000000000000000000000000000000000082041682526bffffffffffffffffffffffff90811660208301526019549282019290925260125490911660608281019190915290819060009060808101610beb6002612fc2565b8152601554780100000000000000000000000000000000000000000000000080820463ffffffff9081166020808601919091527c01000000000000000000000000000000000000000000000000000000008404821660408087019190915260115460608088019190915260125474010000000000000000000000000000000000000000810485166080808a01919091527e01000000000000000000000000000000000000000000000000000000000000820460ff16151560a0998a01528351610200810185526c0100000000000000000000000080840488168252700100000000000000000000000000000000808504891697830197909752808a0488169582019590955296820462ffffff16928701929092527b01000000000000000000000000000000000000000000000000000000900461ffff16908501526014546bffffffffffffffffffffffff8116968501969096529304811660c083015260165480821660e08401526401000000008104821661010084015268010000000000000000900416610120820152601754610140820152601854610160820152910473ffffffffffffffffffffffffffffffffffffffff166101808201529095506101a08101610db86009612fdf565b81526016546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1660208083019190915260135460ff9081161515604093840152601254600d80548551818602810186019096528086529599508a958a959194600e947d01000000000000000000000000000000000000000000000000000000000090940490931692859190830182828015610e8d57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e62575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015610ef657602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610ecb575b50505050509150945094509450945094509091929394565b610f16612fec565b73ffffffffffffffffffffffffffffffffffffffff82166000908152601a6020526040902080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115610f7657610f7661442f565b02179055505050565b6000818152601d60205260409020805460609190610f9c906147aa565b80601f0160208091040260200160405190810160405280929190818152602001828054610fc8906147aa565b80156110155780601f10610fea57610100808354040283529160200191611015565b820191906000526020600020905b815481529060010190602001808311610ff857829003601f168201915b50505050509050919050565b61102a8261306f565b3373ffffffffffffffffffffffffffffffffffffffff821603611079576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116146111235760008281526006602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915590519091339185917fb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b3591a45b5050565b6000818152601b60205260409020805460609190610f9c906147aa565b61114c612fec565b600e548114611187576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b600e54811015611359576000600e82815481106111a9576111a9614743565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff908116808452600f909252604083205491935016908585858181106111f3576111f3614743565b90506020020160208101906112089190614412565b905073ffffffffffffffffffffffffffffffffffffffff8116158061129b575073ffffffffffffffffffffffffffffffffffffffff82161580159061127957508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561129b575073ffffffffffffffffffffffffffffffffffffffff81811614155b156112d2576040517fb387a23800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff818116146113435773ffffffffffffffffffffffffffffffffffffffff8381166000908152600f6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169183169190911790555b505050808061135190614772565b91505061118a565b507fa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725600e838360405161138e939291906149b4565b60405180910390a15050565b6113a2612fec565b601280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015282918291829182919082906114c65760608201516012546000916114b2916bffffffffffffffffffffffff16614a66565b600e549091506114c29082614aba565b9150505b8151602083015160408401516114dd908490614ae5565b6060949094015173ffffffffffffffffffffffffffffffffffffffff9a8b166000908152600f6020526040902054929b919a9499509750921694509092505050565b60006109ea82613123565b6115338161306f565b600081815260046020908152604091829020825160e081018452815460ff8116151580835263ffffffff610100830481169584019590955265010000000000820485169583019590955273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c082015290611632576040517f1b88a78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556116716002836131ce565b5060405182907f7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a4745690600090a25050565b60408051610140810182526012546bffffffffffffffffffffffff8116825263ffffffff6c010000000000000000000000008204811660208401527001000000000000000000000000000000008204811693830193909352740100000000000000000000000000000000000000008104909216606082015262ffffff7801000000000000000000000000000000000000000000000000830416608082015261ffff7b0100000000000000000000000000000000000000000000000000000083041660a082015260ff7d0100000000000000000000000000000000000000000000000000000000008304811660c08301527e0100000000000000000000000000000000000000000000000000000000000083048116151560e08301527f01000000000000000000000000000000000000000000000000000000000000009092048216151561010082015260135490911615156101208201526000908180611806836131da565b9150915061182f838787601460020160049054906101000a900463ffffffff16868660006133b8565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152601e60205260409020805460609190610f9c906147aa565b6016546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1633146118ce576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152601e602052604090206118fe82848361484c565b508273ffffffffffffffffffffffffffffffffffffffff167f7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d28383604051610a9d929190614967565b6012547f0100000000000000000000000000000000000000000000000000000000000000900460ff16156119a7576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff8116611a3d576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600460209081526040808320815160e081018352815460ff81161515825263ffffffff610100820481168387015265010000000000820481168386015273ffffffffffffffffffffffffffffffffffffffff6901000000000000000000909204821660608401526001909301546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a08401527801000000000000000000000000000000000000000000000000900490921660c082015286855260059093529220549091163314611b44576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b4c613403565b816040015163ffffffff161115611b8f576040517fff84e5dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600460205260409020600101546019546c010000000000000000000000009091046bffffffffffffffffffffffff1690611bcf908290614701565b60195560008481526004602081905260409182902060010180547fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff16905590517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116928201929092526bffffffffffffffffffffffff831660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015611cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd69190614b0a565b50604080516bffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff8516602082015285917ff3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318910160405180910390a25050601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611de8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611e6c612fec565b6015546019546bffffffffffffffffffffffff90911690611e8e908290614701565b601955601580547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690556040516bffffffffffffffffffffffff821681527f1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f19060200160405180910390a16040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526bffffffffffffffffffffffff821660248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044015b6020604051808303816000875af1158015611f9b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111239190614b0a565b611fc7612fec565b601280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016113f6565b6120498161306f565b600081815260046020908152604091829020825160e081018452815460ff8116158015835263ffffffff610100830481169584019590955265010000000000820485169583019590955273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c082015290612148576040517f514b6c2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561218a6002836134b8565b5060405182907f8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f90600090a25050565b6121c38361306f565b6000838152601c602052604090206121dc82848361484c565b50827f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf48508383604051610a9d929190614967565b73ffffffffffffffffffffffffffffffffffffffff811661225c576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600f60205260409020541633146122bc576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e546000916122df9185916bffffffffffffffffffffffff16906134c4565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600b6020526040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601954909150612349906bffffffffffffffffffffffff831690614701565b6019556040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526bffffffffffffffffffffffff831660248301527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156123ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124129190614b0a565b5060405133815273ffffffffffffffffffffffffffffffffffffffff808416916bffffffffffffffffffffffff8416918616907f9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f406989060200160405180910390a4505050565b6108fc8163ffffffff1610806124ac575060155463ffffffff7001000000000000000000000000000000009091048116908216115b156124e3576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6124ec8261306f565b60008281526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff861690810291909117909155915191825283917fc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c910160405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152601060205260409020541633146125cc576040517f6752e7aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600f602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556010909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b600081815260046020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452650100000000008104841694820185905273ffffffffffffffffffffffffffffffffffffffff69010000000000000000009091041660608201526001909101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a083015278010000000000000000000000000000000000000000000000009004821660c08201529114612761576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff1633146127be576040517f6352a85300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602090815260408083208054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821790935560069094528285208054909216909155905173ffffffffffffffffffffffffffffffffffffffff90911692839186917f5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c91a4505050565b60006109ea61286083613123565b600084815260046020526040902054610100900463ffffffff166116a1565b612887612fec565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612914573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129389190614b2c565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33601954846129859190614701565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401611f7c565b604080516101408101825260008082526020820181905260609282018390528282018190526080820181905260a0820181905260c0820181905260e082018190526101008201526101208101919091526000828152600460209081526040808320815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606083018190526001909101546bffffffffffffffffffffffff80821660808501526c0100000000000000000000000082041660a08401527801000000000000000000000000000000000000000000000000900490921660c0820152919015612b7357816060015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6e9190614b45565b612b76565b60005b90506040518061014001604052808273ffffffffffffffffffffffffffffffffffffffff168152602001836020015163ffffffff168152602001600760008781526020019081526020016000208054612bce906147aa565b80601f0160208091040260200160405190810160405280929190818152602001828054612bfa906147aa565b8015612c475780601f10612c1c57610100808354040283529160200191612c47565b820191906000526020600020905b815481529060010190602001808311612c2a57829003601f168201915b505050505081526020018360a001516bffffffffffffffffffffffff1681526020016005600087815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001836040015163ffffffff1667ffffffffffffffff1681526020018360c0015163ffffffff16815260200183608001516bffffffffffffffffffffffff168152602001836000015115158152602001601c60008781526020019081526020016000208054612d24906147aa565b80601f0160208091040260200160405190810160405280929190818152602001828054612d50906147aa565b8015612d9d5780601f10612d7257610100808354040283529160200191612d9d565b820191906000526020600020905b815481529060010190602001808311612d8057829003601f168201915b505050505081525092505050919050565b612db78361306f565b60165463ffffffff16811115612df9576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600760205260409020612e1282848361484c565b50827fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d8383604051610a9d929190614967565b60006109ea82612852565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600f6020526040902054163314612eb0576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821603612eff576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601060205260409020548116908216146111235773ffffffffffffffffffffffffffffffffffffffff82811660008181526010602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055513392917f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836791a45050565b612fb6612fec565b612fbf816136cc565b50565b60006109ea825490565b6000612fd883836137c1565b9392505050565b60606000612fd8836137eb565b60005473ffffffffffffffffffffffffffffffffffffffff16331461306d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401611ddf565b565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff1633146130cc576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090205465010000000000900463ffffffff90811614612fbf576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818160045b600f8110156131b0577fff00000000000000000000000000000000000000000000000000000000000000821683826020811061316857613168614743565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461319e57506000949350505050565b806131a881614772565b91505061312a565b5081600f1a60018111156131c6576131c661442f565b949350505050565b6000612fd88383613846565b6000806000836080015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613266573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061328a9190614b7c565b50945090925050506000811315806132a157508142105b806132c257508280156132c257506132b98242614701565b8463ffffffff16105b156132d15760175495506132d5565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613340573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133649190614b7c565b509450909250505060008113158061337b57508142105b8061339c575082801561339c57506133938242614701565b8463ffffffff16105b156133ab5760185494506133af565b8094505b50505050915091565b6000806133ca88878b60c00151613895565b90506000806133e58b8a63ffffffff16858a8a60018b613957565b90925090506133f48183614ae5565b9b9a5050505050505050505050565b600060017f000000000000000000000000000000000000000000000000000000000000000060028111156134395761343961442f565b036134b357606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561348a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134ae9190614b2c565b905090565b504390565b6000612fd88383613db0565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152906136c057600081606001518561355c9190614a66565b9050600061356a8583614aba565b9050808360400181815161357e9190614ae5565b6bffffffffffffffffffffffff169052506135998582614bcc565b836060018181516135aa9190614ae5565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b3373ffffffffffffffffffffffffffffffffffffffff82160361374b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401611ddf565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008260000182815481106137d8576137d8614743565b9060005260206000200154905092915050565b60608160000180548060200260200160405190810160405280929190818152602001828054801561101557602002820191906000526020600020905b8154815260200190600101908083116138275750505050509050919050565b600081815260018301602052604081205461388d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109ea565b5060006109ea565b600080808560018111156138ab576138ab61442f565b036138ba575062015f9061390f565b60018560018111156138ce576138ce61442f565b036138dd57506201adb061390f565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61392063ffffffff85166014614c00565b61392b846001614c3d565b61393a9060ff16611d4c614c00565b61394490836146ee565b61394e91906146ee565b95945050505050565b60008060008960a0015161ffff16876139709190614c00565b905083801561397e5750803a105b1561398657503a5b600060027f000000000000000000000000000000000000000000000000000000000000000060028111156139bc576139bc61442f565b03613b1b576040805160008152602081019091528515613a1a57600036604051806080016040528060488152602001614d2e60489139604051602001613a0493929190614c56565b6040516020818303038152906040529050613a82565b601654613a3690640100000000900463ffffffff166004614c7d565b63ffffffff1667ffffffffffffffff811115613a5457613a54614714565b6040519080825280601f01601f191660200182016040528015613a7e576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e90613ad2908490600401614365565b602060405180830381865afa158015613aef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b139190614b2c565b915050613c75565b60017f00000000000000000000000000000000000000000000000000000000000000006002811115613b4f57613b4f61442f565b03613c75578415613bd157606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bca9190614b2c565b9050613c75565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa158015613c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c439190614ca0565b5050601654929450613c6693505050640100000000900463ffffffff1682614c00565b613c71906010614c00565b9150505b84613c9157808b60a0015161ffff16613c8e9190614c00565b90505b613c9f61ffff871682614cea565b905060008782613caf8c8e6146ee565b613cb99086614c00565b613cc391906146ee565b613cd590670de0b6b3a7640000614c00565b613cdf9190614cea565b905060008c6040015163ffffffff1664e8d4a51000613cfe9190614c00565b898e6020015163ffffffff16858f88613d179190614c00565b613d2191906146ee565b613d2f90633b9aca00614c00565b613d399190614c00565b613d439190614cea565b613d4d91906146ee565b90506b033b2e3c9fd0803ce8000000613d6682846146ee565b1115613d9e576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b60008181526001830160205260408120548015613e99576000613dd4600183614701565b8554909150600090613de890600190614701565b9050818114613e4d576000866000018281548110613e0857613e08614743565b9060005260206000200154905080876000018481548110613e2b57613e2b614743565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613e5e57613e5e614cfe565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109ea565b60009150506109ea565b5092915050565b60008060408385031215613ebd57600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015613f0457835183529284019291840191600101613ee8565b50909695505050505050565b60008083601f840112613f2257600080fd5b50813567ffffffffffffffff811115613f3a57600080fd5b602083019150836020828501011115613f5257600080fd5b9250929050565b600080600060408486031215613f6e57600080fd5b83359250602084013567ffffffffffffffff811115613f8c57600080fd5b613f9886828701613f10565b9497909650939450505050565b600081518084526020808501945080840160005b83811015613feb57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613fb9565b509495945050505050565b805163ffffffff1682526000610200602083015161401c602086018263ffffffff169052565b506040830151614034604086018263ffffffff169052565b50606083015161404b606086018262ffffff169052565b506080830151614061608086018261ffff169052565b5060a083015161408160a08601826bffffffffffffffffffffffff169052565b5060c083015161409960c086018263ffffffff169052565b5060e08301516140b160e086018263ffffffff169052565b506101008381015163ffffffff908116918601919091526101208085015190911690850152610140808401519085015261016080840151908501526101808084015173ffffffffffffffffffffffffffffffffffffffff16908501526101a08084015181860183905261412683870182613fa5565b925050506101c0808401516141528287018273ffffffffffffffffffffffffffffffffffffffff169052565b50506101e09283015115159390920192909252919050565b855163ffffffff16815260006101c0602088015161419860208501826bffffffffffffffffffffffff169052565b506040880151604084015260608801516141c260608501826bffffffffffffffffffffffff169052565b506080880151608084015260a08801516141e460a085018263ffffffff169052565b5060c08801516141fc60c085018263ffffffff169052565b5060e088015160e08401526101008089015161421f8286018263ffffffff169052565b505061012088810151151590840152610140830181905261424281840188613ff6565b90508281036101608401526142578187613fa5565b905082810361018084015261426c8186613fa5565b91505061182f6101a083018460ff169052565b73ffffffffffffffffffffffffffffffffffffffff81168114612fbf57600080fd5b600080604083850312156142b457600080fd5b82356142bf8161427f565b91506020830135600481106142d357600080fd5b809150509250929050565b6000602082840312156142f057600080fd5b5035919050565b60005b838110156143125781810151838201526020016142fa565b50506000910152565b600081518084526143338160208601602086016142f7565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612fd8602083018461431b565b6000806040838503121561438b57600080fd5b8235915060208301356142d38161427f565b600080602083850312156143b057600080fd5b823567ffffffffffffffff808211156143c857600080fd5b818501915085601f8301126143dc57600080fd5b8135818111156143eb57600080fd5b8660208260051b850101111561440057600080fd5b60209290920196919550909350505050565b60006020828403121561442457600080fd5b8135612fd88161427f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110612fbf57612fbf61442f565b6020810161447b8361445e565b91905290565b602081016002831061447b5761447b61442f565b803563ffffffff811681146144a957600080fd5b919050565b600080604083850312156144c157600080fd5b8235600281106144d057600080fd5b91506144de60208401614495565b90509250929050565b6000806000604084860312156144fc57600080fd5b83356145078161427f565b9250602084013567ffffffffffffffff811115613f8c57600080fd5b6000806040838503121561453657600080fd5b82356145418161427f565b915060208301356142d38161427f565b6000806040838503121561456457600080fd5b823591506144de60208401614495565b6020815261459b60208201835173ffffffffffffffffffffffffffffffffffffffff169052565b600060208301516145b4604084018263ffffffff169052565b5060408301516101408060608501526145d161016085018361431b565b915060608501516145f260808601826bffffffffffffffffffffffff169052565b50608085015173ffffffffffffffffffffffffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015163ffffffff811660e08601525060e085015161010061465e818701836bffffffffffffffffffffffff169052565b86015190506101206146738682018315159052565b8601518584037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152905061182f838261431b565b602081016004831061447b5761447b61442f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156109ea576109ea6146bf565b818103818111156109ea576109ea6146bf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036147a3576147a36146bf565b5060010190565b600181811c908216806147be57607f821691505b6020821081036147f7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111561484757600081815260208120601f850160051c810160208610156148245750805b601f850160051c820191505b8181101561484357828155600101614830565b5050505b505050565b67ffffffffffffffff83111561486457614864614714565b6148788361487283546147aa565b836147fd565b6000601f8411600181146148ca57600085156148945750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355614960565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561491957868501358255602094850194600190920191016148f9565b5086821015614954577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b6000604082016040835280865480835260608501915087600052602092508260002060005b82811015614a0b57815473ffffffffffffffffffffffffffffffffffffffff16845292840192600191820191016149d9565b505050838103828501528481528590820160005b86811015614a5a578235614a328161427f565b73ffffffffffffffffffffffffffffffffffffffff1682529183019190830190600101614a1f565b50979650505050505050565b6bffffffffffffffffffffffff828116828216039080821115613ea357613ea36146bf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff80841680614ad957614ad9614a8b565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115613ea357613ea36146bf565b600060208284031215614b1c57600080fd5b81518015158114612fd857600080fd5b600060208284031215614b3e57600080fd5b5051919050565b600060208284031215614b5757600080fd5b8151612fd88161427f565b805169ffffffffffffffffffff811681146144a957600080fd5b600080600080600060a08688031215614b9457600080fd5b614b9d86614b62565b9450602086015193506040860151925060608601519150614bc060808701614b62565b90509295509295909350565b60006bffffffffffffffffffffffff80831681851681830481118215151615614bf757614bf76146bf565b02949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615614c3857614c386146bf565b500290565b60ff81811683821601908111156109ea576109ea6146bf565b828482376000838201600081528351614c738183602088016142f7565b0195945050505050565b600063ffffffff80831681851681830481118215151615614bf757614bf76146bf565b60008060008060008060c08789031215614cb957600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600082614cf957614cf9614a8b565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", } var AutomationRegistryLogicBABI = AutomationRegistryLogicBMetaData.ABI diff --git a/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go b/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go index 89da6bc9584..c85e5e06ae2 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper_2_2/keeper_registry_wrapper_2_2.go @@ -46,11 +46,12 @@ type AutomationRegistryBase22OnchainConfig struct { Transcoder common.Address Registrars []common.Address UpkeepPrivilegeManager common.Address + ReorgProtectionEnabled bool } var AutomationRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicB2_2\",\"name\":\"logicA\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"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\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x6101406040523480156200001257600080fd5b50604051620054c9380380620054c98339810160408190526200003591620003df565b80816001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b919062000406565b826001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003df565b836001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003df565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003df565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003df565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b9816200031b565b505050846002811115620002d157620002d162000429565b60e0816002811115620002e857620002e862000429565b9052506001600160a01b0393841660805291831660a052821660c0528116610100529190911661012052506200043f9050565b336001600160a01b03821603620003755760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003dc57600080fd5b50565b600060208284031215620003f257600080fd5b8151620003ff81620003c6565b9392505050565b6000602082840312156200041957600080fd5b815160038110620003ff57600080fd5b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e0516101005161012051615028620004a16000396000818160d6015261016f01526000505060008181612eb60152818161321f015281816133b20152613a4901526000505060005050600061043b01526150286000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063aed2e92911610081578063e29b753c1161005b578063e29b753c146102e8578063e3d0e712146102fb578063f2fde38b1461030e576100d4565b8063aed2e92914610262578063afcb95d71461028c578063b1dc65a4146102d5576100d4565b806381ff7048116100b257806381ff7048146101bc5780638da5cb5b14610231578063a4c0ed361461024f576100d4565b8063181f5a771461011b578063349e8cca1461016d57806379ba5097146101b4575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610114573d6000f35b3d6000fd5b005b6101576040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e322e30000000000000000081525081565b6040516101649190613cc8565b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610164565b610119610321565b61020e60145460115463ffffffff780100000000000000000000000000000000000000000000000083048116937c01000000000000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610164565b60005473ffffffffffffffffffffffffffffffffffffffff1661018f565b61011961025d366004613d4a565b610423565b610275610270366004613da6565b61063f565b604080519215158352602083019190915201610164565b601154601254604080516000815260208101939093527c010000000000000000000000000000000000000000000000000000000090910463ffffffff1690820152606001610164565b6101196102e3366004613e37565b6107a7565b6101196102f63660046142b2565b6112ea565b61011961030936600461437f565b6121e6565b61011961031c36600461440e565b61220f565b60015473ffffffffffffffffffffffffffffffffffffffff1633146103a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610492576040517fc8bad78d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081146104cc576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006104da8284018461442b565b60008181526004602052604090205490915065010000000000900463ffffffff90811614610534576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090206001015461056f9085906c0100000000000000000000000090046bffffffffffffffffffffffff16614473565b600082815260046020526040902060010180546bffffffffffffffffffffffff929092166c01000000000000000000000000027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff9092169190911790556018546105da908590614498565b6018556040516bffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff86169082907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a35050505050565b60008061064a612223565b6012546e010000000000000000000000000000900460ff1615610699576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff908116838601819052650100000000008304821684880152690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606084018190526001909401546bffffffffffffffffffffffff80821660808601526c0100000000000000000000000082041660a0850152780100000000000000000000000000000000000000000000000090041660c08301528451601f8901859004850281018501909552878552909361079893899089908190840183828082843760009201919091525061225d92505050565b9093509150505b935093915050565b60005a604080516101208101825260125460ff808216835261010080830463ffffffff90811660208601526501000000000084048116958501959095526901000000000000000000830462ffffff1660608501526c01000000000000000000000000830461ffff1660808501526e0100000000000000000000000000008304821615801560a08601526f010000000000000000000000000000008404909216151560c085015270010000000000000000000000000000000083046bffffffffffffffffffffffff1660e08501527c0100000000000000000000000000000000000000000000000000000000909204909316908201529192506108d5576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff1661091e576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011548a351461095a576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516109679060016144da565b60ff16861415806109785750858414155b156109af576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109bf8a8a8a8a8a8a8a8a612468565b60006109cb8a8a6126d1565b9050600081604001515167ffffffffffffffff8111156109ed576109ed613eee565b604051908082528060200260200182016040528015610ab157816020015b604080516101e0810182526000610100820181815261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610a0b5790505b5090506000805b836040015151811015610efa576004600085604001518381518110610adf57610adf6144ab565b6020908102919091018101518252818101929092526040908101600020815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c08201528351849083908110610bc457610bc46144ab565b602002602001015160000181905250610bf984604001518281518110610bec57610bec6144ab565b602002602001015161278c565b838281518110610c0b57610c0b6144ab565b6020026020010151608001906001811115610c2857610c286144f3565b90816001811115610c3b57610c3b6144f3565b81525050610caf85848381518110610c5557610c556144ab565b60200260200101516080015186606001518481518110610c7757610c776144ab565b60200260200101518760a001518581518110610c9557610c956144ab565b602002602001015151886000015189602001516001612837565b838281518110610cc157610cc16144ab565b6020026020010151604001906bffffffffffffffffffffffff1690816bffffffffffffffffffffffff1681525050610d4d84604001518281518110610d0857610d086144ab565b602002602001015185608001518381518110610d2657610d266144ab565b6020026020010151858481518110610d4057610d406144ab565b6020026020010151612882565b848381518110610d5f57610d5f6144ab565b6020026020010151602001858481518110610d7c57610d7c6144ab565b602002602001015160e0018281525082151515158152505050828181518110610da757610da76144ab565b60200260200101516020015115610dca57610dc3600183614522565b9150610dcf565b610ee8565b610e35838281518110610de457610de46144ab565b6020026020010151600001516060015185606001518381518110610e0a57610e0a6144ab565b60200260200101518660a001518481518110610e2857610e286144ab565b602002602001015161225d565b848381518110610e4757610e476144ab565b6020026020010151606001858481518110610e6457610e646144ab565b602002602001015160a0018281525082151515158152505050828181518110610e8f57610e8f6144ab565b602002602001015160a0015186610ea6919061453d565b9550610ee884604001518281518110610ec157610ec16144ab565b6020026020010151848381518110610edb57610edb6144ab565b6020026020010151612a00565b80610ef281614550565b915050610ab8565b508061ffff16600003610f115750505050506112e0565b8351610f1e9060016144da565b610f2d9060ff1661044c614588565b616b6c610f3b8d6010614588565b5a610f46908961453d565b610f509190614498565b610f5a9190614498565b610f649190614498565b9450611b58610f7761ffff8316876145f4565b610f819190614498565b945060008060008060005b87604001515181101561118257868181518110610fab57610fab6144ab565b60200260200101516020015115611170576110078a888381518110610fd257610fd26144ab565b6020026020010151608001518a60a001518481518110610ff457610ff46144ab565b6020026020010151518c60000151612b12565b878281518110611019576110196144ab565b602002602001015160c00181815250506110758989604001518381518110611043576110436144ab565b602002602001015189848151811061105d5761105d6144ab565b60200260200101518b600001518c602001518b612b32565b90935091506110848285614473565b93506110908386614473565b94508681815181106110a4576110a46144ab565b6020026020010151606001511515886040015182815181106110c8576110c86144ab565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b84866110fd9190614473565b8a858151811061110f5761110f6144ab565b602002602001015160a001518b868151811061112d5761112d6144ab565b602002602001015160c001518d60800151878151811061114f5761114f6144ab565b60200260200101516040516111679493929190614608565b60405180910390a35b8061117a81614550565b915050610f8c565b5050336000908152600b6020526040902080548492506002906111ba9084906201000090046bffffffffffffffffffffffff16614473565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080601260000160108282829054906101000a90046bffffffffffffffffffffffff166112149190614473565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060008f600160038110611257576112576144ab565b602002013560001c9050600060088264ffffffffff16901c905087610100015163ffffffff168163ffffffff1611156112d657601280547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff8416021790555b5050505050505050505b5050505050505050565b6112f2612c25565b601f8651111561132e576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff1660000361136b576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114158061138a5750611382846003614645565b60ff16865111155b156113c1576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e547001000000000000000000000000000000009091046bffffffffffffffffffffffff169060005b816bffffffffffffffffffffffff1681101561145657611443600e828154811061141a5761141a6144ab565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff168484612ca6565b508061144e81614550565b9150506113ee565b5060008060005b836bffffffffffffffffffffffff1681101561155f57600d8181548110611486576114866144ab565b600091825260209091200154600e805473ffffffffffffffffffffffffffffffffffffffff909216945090829081106114c1576114c16144ab565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff8681168452600c8352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559116808452600b90925290912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905591508061155781614550565b91505061145d565b5061156c600d6000613b9d565b611578600e6000613b9d565b604080516080810182526000808252602082018190529181018290526060810182905290805b8c518110156119e157600c60008e83815181106115bd576115bd6144ab565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615611628576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168d8281518110611652576116526144ab565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036116a7576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008f84815181106116d8576116d86144ab565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558b518c9082908110611780576117806144ab565b60200260200101519150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036117f0576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e010000000000000000000000000000900490921660608301529093506118ab576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001835260ff80821660208086019182526bffffffffffffffffffffffff808b166060880190815273ffffffffffffffffffffffffffffffffffffffff87166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090951694909417179190911692909217919091179055806119d981614550565b91505061159e565b50508a516119f79150600d9060208d0190613bbb565b508851611a0b90600e9060208c0190613bbb565b506040518061012001604052808960ff168152602001886000015163ffffffff168152602001886020015163ffffffff168152602001886060015162ffffff168152602001886080015161ffff1681526020016012600001600e9054906101000a900460ff16151581526020016012600001600f9054906101000a900460ff1615158152602001856bffffffffffffffffffffffff168152602001600063ffffffff16815250601260008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160056101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160096101000a81548162ffffff021916908362ffffff160217905550608082015181600001600c6101000a81548161ffff021916908361ffff16021790555060a082015181600001600e6101000a81548160ff02191690831515021790555060c082015181600001600f6101000a81548160ff02191690831515021790555060e08201518160000160106101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061010082015181600001601c6101000a81548163ffffffff021916908363ffffffff1602179055509050506040518061018001604052808860a001516bffffffffffffffffffffffff16815260200188610180015173ffffffffffffffffffffffffffffffffffffffff168152602001601360010160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001886040015163ffffffff1681526020018860c0015163ffffffff168152602001601360010160149054906101000a900463ffffffff1663ffffffff168152602001601360010160189054906101000a900463ffffffff1663ffffffff1681526020016013600101601c9054906101000a900463ffffffff1663ffffffff1681526020018860e0015163ffffffff16815260200188610100015163ffffffff16815260200188610120015163ffffffff168152602001886101c0015173ffffffffffffffffffffffffffffffffffffffff16815250601360008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550606082015181600101600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160106101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548163ffffffff021916908363ffffffff1602179055506101208201518160020160046101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160086101000a81548163ffffffff021916908363ffffffff16021790555061016082015181600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555090505086610140015160168190555086610160015160178190555060006013600101601c9054906101000a900463ffffffff169050611fcd612eb0565b601480547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff9384160217808255600192601891612048918591780100000000000000000000000000000000000000000000000090041661466e565b92506101000a81548163ffffffff021916908363ffffffff16021790555060008860405160200161207991906146dc565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181529190526014549091506120e290469030907801000000000000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f612f65565b60115560005b6120f2600961300f565b8110156121225761210f612107600983613019565b600990613025565b508061211a81614550565b9150506120e8565b5060005b896101a0015151811015612179576121668a6101a00151828151811061214e5761214e6144ab565b6020026020010151600961304790919063ffffffff16565b508061217181614550565b915050612126565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0582601154601360010160189054906101000a900463ffffffff168f8f8f878f8f6040516121d099989796959493929190614840565b60405180910390a1505050505050505050505050565b612207868686868060200190518101906122009190614971565b86866112ea565b505050505050565b612217612c25565b61222081613069565b50565b321561225b576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60125460009081906f01000000000000000000000000000000900460ff16156122b2576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff166f010000000000000000000000000000001790556040517f4585e33b000000000000000000000000000000000000000000000000000000009061231f908590602401613cc8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d16906123f29087908790600401614acb565b60408051808303816000875af1158015612410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124349190614ae4565b601280547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff16905590969095509350505050565b6000878760405161247a929190614b17565b604051908190038120612491918b90602001614b27565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b88811015612668576001858783602081106124fd576124fd6144ab565b61250a91901a601b6144da565b8c8c8581811061251c5761251c6144ab565b905060200201358b8b86818110612535576125356144ab565b9050602002013560405160008152602001604052604051612572949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612594573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612642576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b84019350808061266090614550565b9150506124e0565b50827e010101010101010101010101010101010101010101010101010101010101018416146126c3576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b61270a6040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b600061271883850185614c18565b604081015151606082015151919250908114158061273b57508082608001515114155b8061274b5750808260a001515114155b15612782576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090505b92915050565b6000818160045b600f811015612819577fff0000000000000000000000000000000000000000000000000000000000000082168382602081106127d1576127d16144ab565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461280757506000949350505050565b8061281181614550565b915050612793565b5081600f1a600181111561282f5761282f6144f3565b949350505050565b60008061284988878b6000015161315e565b90506000806128648b8a63ffffffff16858a8a60018b6131ea565b90925090506128738183614473565b9b9a5050505050505050505050565b60008080808460800151600181111561289d5761289d6144f3565b036128c1576128ad868686613643565b6128bc5760009250905061079f565b612937565b6001846080015160018111156128d9576128d96144f3565b036129055760006128ea8787613737565b92509050806128ff575060009250905061079f565b50612937565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61293f612eb0565b84516040015163ffffffff161161299357857fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636866040516129809190613cc8565b60405180910390a260009250905061079f565b83604001516bffffffffffffffffffffffff16846000015160a001516bffffffffffffffffffffffff1610156129f357857f377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e02866040516129809190613cc8565b6001969095509350505050565b600081608001516001811115612a1857612a186144f3565b03612a8a57612a25612eb0565b6000838152600460205260409020600101805463ffffffff929092167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9092169190911790555050565b600181608001516001811115612aa257612aa26144f3565b03612b0e5760e08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b5050565b6000612b1f84848461315e565b90508085101561282f5750929392505050565b600080612b4d888760a001518860c0015188888860016131ea565b90925090506000612b5e8284614473565b600089815260046020526040902060010180549192508291600c90612ba29084906c0100000000000000000000000090046bffffffffffffffffffffffff16614d05565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008a815260046020526040812060010180548594509092612beb91859116614473565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461225b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161039e565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015290612ea2576000816060015185612d3e9190614d05565b90506000612d4c8583614d2a565b90508083604001818151612d609190614473565b6bffffffffffffffffffffffff16905250612d7b8582614d55565b83606001818151612d8c9190614473565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b6040015190505b9392505050565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115612ee657612ee66144f3565b03612f6057606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5b9190614d89565b905090565b504390565b6000808a8a8a8a8a8a8a8a8a604051602001612f8999989796959493929190614da2565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b6000612786825490565b6000612ea983836138d0565b6000612ea98373ffffffffffffffffffffffffffffffffffffffff84166138fa565b6000612ea98373ffffffffffffffffffffffffffffffffffffffff84166139f4565b3373ffffffffffffffffffffffffffffffffffffffff8216036130e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161039e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008080856001811115613174576131746144f3565b03613183575062015f906131a2565b6001856001811115613197576131976144f3565b0361290557506201adb05b6131b363ffffffff85166014614588565b6131be8460016144da565b6131cd9060ff16611d4c614588565b6131d79083614498565b6131e19190614498565b95945050505050565b6000806000896080015161ffff16876132039190614588565b90508380156132115750803a105b1561321957503a5b600060027f0000000000000000000000000000000000000000000000000000000000000000600281111561324f5761324f6144f3565b036133ae5760408051600081526020810190915285156132ad57600036604051806080016040528060488152602001614fd46048913960405160200161329793929190614e37565b6040516020818303038152906040529050613315565b6015546132c990640100000000900463ffffffff166004614e5e565b63ffffffff1667ffffffffffffffff8111156132e7576132e7613eee565b6040519080825280601f01601f191660200182016040528015613311576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e90613365908490600401613cc8565b602060405180830381865afa158015613382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133a69190614d89565b915050613508565b60017f000000000000000000000000000000000000000000000000000000000000000060028111156133e2576133e26144f3565b0361350857841561346457606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613439573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061345d9190614d89565b9050613508565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa1580156134b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134d69190614e81565b50506015549294506134f993505050640100000000900463ffffffff1682614588565b613504906010614588565b9150505b8461352457808b6080015161ffff166135219190614588565b90505b61353261ffff8716826145f4565b9050600087826135428c8e614498565b61354c9086614588565b6135569190614498565b61356890670de0b6b3a7640000614588565b61357291906145f4565b905060008c6040015163ffffffff1664e8d4a510006135919190614588565b898e6020015163ffffffff16858f886135aa9190614588565b6135b49190614498565b6135c290633b9aca00614588565b6135cc9190614588565b6135d691906145f4565b6135e09190614498565b90506b033b2e3c9fd0803ce80000006135f98284614498565b1115613631576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b6000808380602001905181019061365a9190614ecb565b835160c00151815191925063ffffffff908116911610156136b757847f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8856040516136a59190613cc8565b60405180910390a26000915050612ea9565b6020810151158015906136de5750602081015181516136db9063ffffffff16613a43565b14155b806136f757506136ec612eb0565b815163ffffffff1610155b1561372c57847f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301856040516136a59190613cc8565b506001949350505050565b6000806000838060200190518101906137509190614f23565b90506000858260000151836020015184604001516040516020016137b294939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012060808301519091501580159061381457508160800151613811836060015163ffffffff16613a43565b14155b806138305750613822612eb0565b826060015163ffffffff1610155b1561387a57857f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301866040516138659190613cc8565b60405180910390a26000935091506138c99050565b60008181526008602052604090205460ff16156138c157857f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8866040516138659190613cc8565b600193509150505b9250929050565b60008260000182815481106138e7576138e76144ab565b9060005260206000200154905092915050565b600081815260018301602052604081205480156139e357600061391e60018361453d565b85549091506000906139329060019061453d565b9050818114613997576000866000018281548110613952576139526144ab565b9060005260206000200154905080876000018481548110613975576139756144ab565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806139a8576139a8614fa4565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612786565b6000915050612786565b5092915050565b6000818152600183016020526040812054613a3b57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612786565b506000612786565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115613a7957613a796144f3565b03613b93576000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613acc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af09190614d89565b90508083101580613b0b5750610100613b09848361453d565b115b15613b195750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa158015613b6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea99190614d89565b504090565b919050565b50805460008255906000526020600020908101906122209190613c45565b828054828255906000526020600020908101928215613c35579160200282015b82811115613c3557825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190613bdb565b50613c41929150613c45565b5090565b5b80821115613c415760008155600101613c46565b60005b83811015613c75578181015183820152602001613c5d565b50506000910152565b60008151808452613c96816020860160208601613c5a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612ea96020830184613c7e565b73ffffffffffffffffffffffffffffffffffffffff8116811461222057600080fd5b8035613b9881613cdb565b60008083601f840112613d1a57600080fd5b50813567ffffffffffffffff811115613d3257600080fd5b6020830191508360208285010111156138c957600080fd5b60008060008060608587031215613d6057600080fd5b8435613d6b81613cdb565b935060208501359250604085013567ffffffffffffffff811115613d8e57600080fd5b613d9a87828801613d08565b95989497509550505050565b600080600060408486031215613dbb57600080fd5b83359250602084013567ffffffffffffffff811115613dd957600080fd5b613de586828701613d08565b9497909650939450505050565b60008083601f840112613e0457600080fd5b50813567ffffffffffffffff811115613e1c57600080fd5b6020830191508360208260051b85010111156138c957600080fd5b60008060008060008060008060e0898b031215613e5357600080fd5b606089018a811115613e6457600080fd5b8998503567ffffffffffffffff80821115613e7e57600080fd5b613e8a8c838d01613d08565b909950975060808b0135915080821115613ea357600080fd5b613eaf8c838d01613df2565b909750955060a08b0135915080821115613ec857600080fd5b50613ed58b828c01613df2565b999c989b50969995989497949560c00135949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101e0810167ffffffffffffffff81118282101715613f4157613f41613eee565b60405290565b60405160c0810167ffffffffffffffff81118282101715613f4157613f41613eee565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613fb157613fb1613eee565b604052919050565b600067ffffffffffffffff821115613fd357613fd3613eee565b5060051b60200190565b600082601f830112613fee57600080fd5b81356020614003613ffe83613fb9565b613f6a565b82815260059290921b8401810191818101908684111561402257600080fd5b8286015b8481101561404657803561403981613cdb565b8352918301918301614026565b509695505050505050565b803560ff81168114613b9857600080fd5b63ffffffff8116811461222057600080fd5b8035613b9881614062565b62ffffff8116811461222057600080fd5b8035613b988161407f565b61ffff8116811461222057600080fd5b8035613b988161409b565b6bffffffffffffffffffffffff8116811461222057600080fd5b8035613b98816140b6565b60006101e082840312156140ee57600080fd5b6140f6613f1d565b905061410182614074565b815261410f60208301614074565b602082015261412060408301614074565b604082015261413160608301614090565b6060820152614142608083016140ab565b608082015261415360a083016140d0565b60a082015261416460c08301614074565b60c082015261417560e08301614074565b60e0820152610100614188818401614074565b9082015261012061419a838201614074565b90820152610140828101359082015261016080830135908201526101806141c2818401613cfd565b908201526101a08281013567ffffffffffffffff8111156141e257600080fd5b6141ee85828601613fdd565b8284015250506101c0614202818401613cfd565b9082015292915050565b803567ffffffffffffffff81168114613b9857600080fd5b600082601f83011261423557600080fd5b813567ffffffffffffffff81111561424f5761424f613eee565b61428060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613f6a565b81815284602083860101111561429557600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c087890312156142cb57600080fd5b863567ffffffffffffffff808211156142e357600080fd5b6142ef8a838b01613fdd565b9750602089013591508082111561430557600080fd5b6143118a838b01613fdd565b965061431f60408a01614051565b9550606089013591508082111561433557600080fd5b6143418a838b016140db565b945061434f60808a0161420c565b935060a089013591508082111561436557600080fd5b5061437289828a01614224565b9150509295509295509295565b60008060008060008060c0878903121561439857600080fd5b863567ffffffffffffffff808211156143b057600080fd5b6143bc8a838b01613fdd565b975060208901359150808211156143d257600080fd5b6143de8a838b01613fdd565b96506143ec60408a01614051565b9550606089013591508082111561440257600080fd5b6143418a838b01614224565b60006020828403121561442057600080fd5b8135612ea981613cdb565b60006020828403121561443d57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff8181168382160190808211156139ed576139ed614444565b8082018082111561278657612786614444565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60ff818116838216019081111561278657612786614444565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b61ffff8181168382160190808211156139ed576139ed614444565b8181038181111561278657612786614444565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361458157614581614444565b5060010190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156145c0576145c0614444565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614603576146036145c5565b500490565b6bffffffffffffffffffffffff8516815283602082015282604082015260806060820152600061463b6080830184613c7e565b9695505050505050565b600060ff821660ff84168160ff048111821515161561466657614666614444565b029392505050565b63ffffffff8181168382160190808211156139ed576139ed614444565b600081518084526020808501945080840160005b838110156146d157815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010161469f565b509495945050505050565b602081526146f360208201835163ffffffff169052565b6000602083015161470c604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015162ffffff8116608084015250608083015161ffff811660a08401525060a08301516bffffffffffffffffffffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006147858185018363ffffffff169052565b840151905061012061479e8482018363ffffffff169052565b84015190506101406147b78482018363ffffffff169052565b840151610160848101919091528401516101808085019190915284015190506101a06147fa8185018373ffffffffffffffffffffffffffffffffffffffff169052565b808501519150506101e06101c0818186015261481a61020086018461468b565b95015173ffffffffffffffffffffffffffffffffffffffff169301929092525090919050565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526148708184018a61468b565b90508281036080840152614884818961468b565b905060ff871660a084015282810360c08401526148a18187613c7e565b905067ffffffffffffffff851660e08401528281036101008401526148c68185613c7e565b9c9b505050505050505050505050565b8051613b9881614062565b8051613b988161407f565b8051613b988161409b565b8051613b98816140b6565b8051613b9881613cdb565b600082601f83011261491e57600080fd5b8151602061492e613ffe83613fb9565b82815260059290921b8401810191818101908684111561494d57600080fd5b8286015b8481101561404657805161496481613cdb565b8352918301918301614951565b60006020828403121561498357600080fd5b815167ffffffffffffffff8082111561499b57600080fd5b908301906101e082860312156149b057600080fd5b6149b8613f1d565b6149c1836148d6565b81526149cf602084016148d6565b60208201526149e0604084016148d6565b60408201526149f1606084016148e1565b6060820152614a02608084016148ec565b6080820152614a1360a084016148f7565b60a0820152614a2460c084016148d6565b60c0820152614a3560e084016148d6565b60e0820152610100614a488185016148d6565b90820152610120614a5a8482016148d6565b9082015261014083810151908201526101608084015190820152610180614a82818501614902565b908201526101a08381015183811115614a9a57600080fd5b614aa68882870161490d565b8284015250506101c09150614abc828401614902565b91810191909152949350505050565b82815260406020820152600061282f6040830184613c7e565b60008060408385031215614af757600080fd5b82518015158114614b0757600080fd5b6020939093015192949293505050565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f830112614b4e57600080fd5b81356020614b5e613ffe83613fb9565b82815260059290921b84018101918181019086841115614b7d57600080fd5b8286015b848110156140465780358352918301918301614b81565b600082601f830112614ba957600080fd5b81356020614bb9613ffe83613fb9565b82815260059290921b84018101918181019086841115614bd857600080fd5b8286015b8481101561404657803567ffffffffffffffff811115614bfc5760008081fd5b614c0a8986838b0101614224565b845250918301918301614bdc565b600060208284031215614c2a57600080fd5b813567ffffffffffffffff80821115614c4257600080fd5b9083019060c08286031215614c5657600080fd5b614c5e613f47565b8235815260208301356020820152604083013582811115614c7e57600080fd5b614c8a87828601614b3d565b604083015250606083013582811115614ca257600080fd5b614cae87828601614b3d565b606083015250608083013582811115614cc657600080fd5b614cd287828601614b98565b60808301525060a083013582811115614cea57600080fd5b614cf687828601614b98565b60a08301525095945050505050565b6bffffffffffffffffffffffff8281168282160390808211156139ed576139ed614444565b60006bffffffffffffffffffffffff80841680614d4957614d496145c5565b92169190910492915050565b60006bffffffffffffffffffffffff80831681851681830481118215151615614d8057614d80614444565b02949350505050565b600060208284031215614d9b57600080fd5b5051919050565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614de98285018b61468b565b91508382036080850152614dfd828a61468b565b915060ff881660a085015283820360c0850152614e1a8288613c7e565b90861660e085015283810361010085015290506148c68185613c7e565b828482376000838201600081528351614e54818360208801613c5a565b0195945050505050565b600063ffffffff80831681851681830481118215151615614d8057614d80614444565b60008060008060008060c08789031215614e9a57600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600060408284031215614edd57600080fd5b6040516040810181811067ffffffffffffffff82111715614f0057614f00613eee565b6040528251614f0e81614062565b81526020928301519281019290925250919050565b600060a08284031215614f3557600080fd5b60405160a0810181811067ffffffffffffffff82111715614f5857614f58613eee565b806040525082518152602083015160208201526040830151614f7981614062565b60408201526060830151614f8c81614062565b60608201526080928301519281019290925250919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicB2_2\",\"name\":\"logicA\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientFunds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"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\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"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\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"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\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"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\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"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\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"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\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6101406040523480156200001257600080fd5b50604051620055da380380620055da8339810160408190526200003591620003df565b80816001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b919062000406565b826001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003df565b836001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003df565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003df565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003df565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b9816200031b565b505050846002811115620002d157620002d162000429565b60e0816002811115620002e857620002e862000429565b9052506001600160a01b0393841660805291831660a052821660c0528116610100529190911661012052506200043f9050565b336001600160a01b03821603620003755760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003dc57600080fd5b50565b600060208284031215620003f257600080fd5b8151620003ff81620003c6565b9392505050565b6000602082840312156200041957600080fd5b815160038110620003ff57600080fd5b634e487b7160e01b600052602160045260246000fd5b60805160a05160c05160e0516101005161012051615139620004a16000396000818160d6015261016f0152600050506000818161253d01528181613447015281816135da0152613af701526000505060005050600061134a01526151396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063a4c0ed3611610081578063b1dc65a41161005b578063b1dc65a4146102e0578063e3d0e712146102f3578063f2fde38b14610306576100d4565b8063a4c0ed3614610262578063aed2e92914610275578063afcb95d71461029f576100d4565b806379ba5097116100b257806379ba5097146101c757806381ff7048146101cf5780638da5cb5b14610244576100d4565b8063181f5a771461011b578063349e8cca1461016d5780636cad5469146101b4575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610114573d6000f35b3d6000fd5b005b6101576040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e322e30000000000000000081525081565b6040516101649190613d76565b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610164565b6101196101c23660046141a5565b610319565b610119611230565b61022160155460115463ffffffff780100000000000000000000000000000000000000000000000083048116937c01000000000000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610164565b60005473ffffffffffffffffffffffffffffffffffffffff1661018f565b6101196102703660046142bb565b611332565b610288610283366004614317565b61154e565b604080519215158352602083019190915201610164565b601154601254604080516000815260208101939093527401000000000000000000000000000000000000000090910463ffffffff1690820152606001610164565b6101196102ee3660046143a8565b6116c6565b61011961030136600461445f565b61226f565b6101196103143660046144ee565b612298565b6103216122ac565b601f8651111561035d576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff1660000361039a576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845186511415806103b957506103b184600361453a565b60ff16865111155b156103f0576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e546bffffffffffffffffffffffff9091169060005b816bffffffffffffffffffffffff168110156104725761045f600e828154811061043657610436614563565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16848461232f565b508061046a81614592565b91505061040a565b5060008060005b836bffffffffffffffffffffffff1681101561057b57600d81815481106104a2576104a2614563565b600091825260209091200154600e805473ffffffffffffffffffffffffffffffffffffffff909216945090829081106104dd576104dd614563565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff8681168452600c8352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559116808452600b90925290912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905591508061057381614592565b915050610479565b50610588600d6000613c4b565b610594600e6000613c4b565b604080516080810182526000808252602082018190529181018290526060810182905290805b8c518110156109fd57600c60008e83815181106105d9576105d9614563565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615610644576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168d828151811061066e5761066e614563565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036106c3576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008f84815181106106f4576106f4614563565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558b518c908290811061079c5761079c614563565b60200260200101519150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361080c576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e010000000000000000000000000000900490921660608301529093506108c7576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001835260ff80821660208086019182526bffffffffffffffffffffffff808b166060880190815273ffffffffffffffffffffffffffffffffffffffff87166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090951694909417179190911692909217919091179055806109f581614592565b9150506105ba565b50508a51610a139150600d9060208d0190613c69565b508851610a2790600e9060208c0190613c69565b50604051806101400160405280856bffffffffffffffffffffffff168152602001886000015163ffffffff168152602001886020015163ffffffff168152602001600063ffffffff168152602001886060015162ffffff168152602001886080015161ffff1681526020018960ff1681526020016012600001601e9054906101000a900460ff16151581526020016012600001601f9054906101000a900460ff1615158152602001886101e001511515815250601260008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160106101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160186101000a81548162ffffff021916908362ffffff16021790555060a082015181600001601b6101000a81548161ffff021916908361ffff16021790555060c082015181600001601d6101000a81548160ff021916908360ff16021790555060e082015181600001601e6101000a81548160ff02191690831515021790555061010082015181600001601f6101000a81548160ff0219169083151502179055506101208201518160010160006101000a81548160ff0219169083151502179055509050506040518061018001604052808860a001516bffffffffffffffffffffffff16815260200188610180015173ffffffffffffffffffffffffffffffffffffffff168152602001601460010160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001886040015163ffffffff1681526020018860c0015163ffffffff168152602001601460010160149054906101000a900463ffffffff1663ffffffff168152602001601460010160189054906101000a900463ffffffff1663ffffffff1681526020016014600101601c9054906101000a900463ffffffff1663ffffffff1681526020018860e0015163ffffffff16815260200188610100015163ffffffff16815260200188610120015163ffffffff168152602001886101c0015173ffffffffffffffffffffffffffffffffffffffff16815250601460008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550606082015181600101600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160106101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548163ffffffff021916908363ffffffff1602179055506101208201518160020160046101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160086101000a81548163ffffffff021916908363ffffffff16021790555061016082015181600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555090505086610140015160178190555086610160015160188190555060006014600101601c9054906101000a900463ffffffff169050611017612537565b601580547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff938416021780825560019260189161109291859178010000000000000000000000000000000000000000000000009004166145ca565b92506101000a81548163ffffffff021916908363ffffffff1602179055506000886040516020016110c39190614638565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905260155490915061112c90469030907801000000000000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f6125ec565b60115560005b61113c6009612696565b81101561116c576111596111516009836126a6565b6009906126b9565b508061116481614592565b915050611132565b5060005b896101a00151518110156111c3576111b08a6101a00151828151811061119857611198614563565b602002602001015160096126db90919063ffffffff16565b50806111bb81614592565b915050611170565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0582601154601460010160189054906101000a900463ffffffff168f8f8f878f8f60405161121a999897969594939291906147b3565b60405180910390a1505050505050505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146112b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146113a1576040517fc8bad78d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081146113db576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006113e982840184614849565b60008181526004602052604090205490915065010000000000900463ffffffff90811614611443576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526004602052604090206001015461147e9085906c0100000000000000000000000090046bffffffffffffffffffffffff16614862565b600082815260046020526040902060010180546bffffffffffffffffffffffff929092166c01000000000000000000000000027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff9092169190911790556019546114e9908590614887565b6019556040516bffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff86169082907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a35050505050565b6000806115596126fd565b6012547e01000000000000000000000000000000000000000000000000000000000000900460ff16156115b8576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff908116838601819052650100000000008304821684880152690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606084018190526001909401546bffffffffffffffffffffffff80821660808601526c0100000000000000000000000082041660a0850152780100000000000000000000000000000000000000000000000090041660c08301528451601f890185900485028101850190955287855290936116b793899089908190840183828082843760009201919091525061273592505050565b9093509150505b935093915050565b60005a60408051610140810182526012546bffffffffffffffffffffffff8116825263ffffffff6c010000000000000000000000008204811660208401527001000000000000000000000000000000008204811693830193909352740100000000000000000000000000000000000000008104909216606082015262ffffff7801000000000000000000000000000000000000000000000000830416608082015261ffff7b0100000000000000000000000000000000000000000000000000000083041660a082015260ff7d0100000000000000000000000000000000000000000000000000000000008304811660c08301527e010000000000000000000000000000000000000000000000000000000000008304811615801560e08401527f010000000000000000000000000000000000000000000000000000000000000090930481161515610100830152601354161515610120820152919250611858576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff166118a1576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011548a35146118dd576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60c08101516118ed90600161489a565b60ff16861415806118fe5750858414155b15611935576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119458a8a8a8a8a8a8a8a61295e565b60006119518a8a612bc7565b9050600081604001515167ffffffffffffffff81111561197357611973613d89565b604051908082528060200260200182016040528015611a3757816020015b604080516101e0810182526000610100820181815261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816119915790505b5090506000805b836040015151811015611e81576004600085604001518381518110611a6557611a65614563565b6020908102919091018101518252818101929092526040908101600020815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c08201528351849083908110611b4a57611b4a614563565b602002602001015160000181905250611b7f84604001518281518110611b7257611b72614563565b6020026020010151612c80565b838281518110611b9157611b91614563565b6020026020010151608001906001811115611bae57611bae6148b3565b90816001811115611bc157611bc16148b3565b81525050611c3585848381518110611bdb57611bdb614563565b60200260200101516080015186606001518481518110611bfd57611bfd614563565b60200260200101518760a001518581518110611c1b57611c1b614563565b602002602001015151886000015189602001516001612d2b565b838281518110611c4757611c47614563565b6020026020010151604001906bffffffffffffffffffffffff1690816bffffffffffffffffffffffff1681525050611cd484604001518281518110611c8e57611c8e614563565b602002602001015185608001518381518110611cac57611cac614563565b6020026020010151858481518110611cc657611cc6614563565b602002602001015188612d76565b848381518110611ce657611ce6614563565b6020026020010151602001858481518110611d0357611d03614563565b602002602001015160e0018281525082151515158152505050828181518110611d2e57611d2e614563565b60200260200101516020015115611d5157611d4a6001836148e2565b9150611d56565b611e6f565b611dbc838281518110611d6b57611d6b614563565b6020026020010151600001516060015185606001518381518110611d9157611d91614563565b60200260200101518660a001518481518110611daf57611daf614563565b6020026020010151612735565b848381518110611dce57611dce614563565b6020026020010151606001858481518110611deb57611deb614563565b602002602001015160a0018281525082151515158152505050828181518110611e1657611e16614563565b602002602001015160a0015186611e2d91906148fd565b9550611e6f84604001518281518110611e4857611e48614563565b6020026020010151848381518110611e6257611e62614563565b6020026020010151612ef9565b80611e7981614592565b915050611a3e565b508061ffff16600003611e98575050505050612265565b60c0840151611ea890600161489a565b611eb79060ff1661044c614910565b616b6c611ec58d6010614910565b5a611ed090896148fd565b611eda9190614887565b611ee49190614887565b611eee9190614887565b9450611b58611f0161ffff83168761497c565b611f0b9190614887565b945060008060008060005b87604001515181101561210c57868181518110611f3557611f35614563565b602002602001015160200151156120fa57611f918a888381518110611f5c57611f5c614563565b6020026020010151608001518a60a001518481518110611f7e57611f7e614563565b6020026020010151518c60c0015161300b565b878281518110611fa357611fa3614563565b602002602001015160c0018181525050611fff8989604001518381518110611fcd57611fcd614563565b6020026020010151898481518110611fe757611fe7614563565b60200260200101518b600001518c602001518b61302b565b909350915061200e8285614862565b935061201a8386614862565b945086818151811061202e5761202e614563565b60200260200101516060015115158860400151828151811061205257612052614563565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b84866120879190614862565b8a858151811061209957612099614563565b602002602001015160a001518b86815181106120b7576120b7614563565b602002602001015160c001518d6080015187815181106120d9576120d9614563565b60200260200101516040516120f19493929190614990565b60405180910390a35b8061210481614592565b915050611f16565b5050336000908152600b6020526040902080548492506002906121449084906201000090046bffffffffffffffffffffffff16614862565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080601260000160008282829054906101000a90046bffffffffffffffffffffffff1661219e9190614862565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060008f6001600381106121e1576121e1614563565b602002013560001c9050600060088264ffffffffff16901c9050876060015163ffffffff168163ffffffff16111561225b57601280547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff8416021790555b5050505050505050505b5050505050505050565b612290868686868060200190518101906122899190614a73565b8686610319565b505050505050565b6122a06122ac565b6122a98161311e565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461232d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016112ad565b565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e010000000000000000000000000000900490911660608201529061252b5760008160600151856123c79190614be1565b905060006123d58583614c06565b905080836040018181516123e99190614862565b6bffffffffffffffffffffffff169052506124048582614c31565b836060018181516124159190614862565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b600060017f0000000000000000000000000000000000000000000000000000000000000000600281111561256d5761256d6148b3565b036125e757606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e29190614c65565b905090565b504390565b6000808a8a8a8a8a8a8a8a8a60405160200161261099989796959493929190614c7e565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b60006126a0825490565b92915050565b60006126b28383613213565b9392505050565b60006126b28373ffffffffffffffffffffffffffffffffffffffff841661323d565b60006126b28373ffffffffffffffffffffffffffffffffffffffff8416613337565b321561232d576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60125460009081907f0100000000000000000000000000000000000000000000000000000000000000900460ff161561279a576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790556040517f4585e33b0000000000000000000000000000000000000000000000000000000090612816908590602401613d76565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d16906128e99087908790600401614d13565b60408051808303816000875af1158015612907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292b9190614d2c565b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b60008787604051612970929190614d5a565b604051908190038120612987918b90602001614d6a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b88811015612b5e576001858783602081106129f3576129f3614563565b612a0091901a601b61489a565b8c8c85818110612a1257612a12614563565b905060200201358b8b86818110612a2b57612a2b614563565b9050602002013560405160008152602001604052604051612a68949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612a8a573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612b38576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193508080612b5690614592565b9150506129d6565b50827e01010101010101010101010101010101010101010101010101010101010101841614612bb9576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b612c006040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b6000612c0e83850185614e5b565b6040810151516060820151519192509081141580612c3157508082608001515114155b80612c415750808260a001515114155b15612c78576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509392505050565b6000818160045b600f811015612d0d577fff000000000000000000000000000000000000000000000000000000000000008216838260208110612cc557612cc5614563565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612cfb57506000949350505050565b80612d0581614592565b915050612c87565b5081600f1a6001811115612d2357612d236148b3565b949350505050565b600080612d3d88878b60c00151613386565b9050600080612d588b8a63ffffffff16858a8a60018b613412565b9092509050612d678183614862565b9b9a5050505050505050505050565b600080808085608001516001811115612d9157612d916148b3565b03612db657612da28787878761386b565b612db157600092509050612ef0565b612e2d565b600185608001516001811115612dce57612dce6148b3565b03612dfb576000612de088888761396d565b9250905080612df55750600092509050612ef0565b50612e2d565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e35612537565b85516040015163ffffffff1611612e8957867fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd563687604051612e769190613d76565b60405180910390a2600092509050612ef0565b84604001516bffffffffffffffffffffffff16856000015160a001516bffffffffffffffffffffffff161015612ee957867f377c8b0c126ae5248d27aca1c76fac4608aff85673ee3caf09747e1044549e0287604051612e769190613d76565b6001925090505b94509492505050565b600081608001516001811115612f1157612f116148b3565b03612f8357612f1e612537565b6000838152600460205260409020600101805463ffffffff929092167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9092169190911790555050565b600181608001516001811115612f9b57612f9b6148b3565b036130075760e08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b5050565b6000613018848484613386565b905080851015612d235750929392505050565b600080613046888760a001518860c001518888886001613412565b909250905060006130578284614862565b600089815260046020526040902060010180549192508291600c9061309b9084906c0100000000000000000000000090046bffffffffffffffffffffffff16614be1565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008a8152600460205260408120600101805485945090926130e491859116614862565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555050965096945050505050565b3373ffffffffffffffffffffffffffffffffffffffff82160361319d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016112ad565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600082600001828154811061322a5761322a614563565b9060005260206000200154905092915050565b600081815260018301602052604081205480156133265760006132616001836148fd565b8554909150600090613275906001906148fd565b90508181146132da57600086600001828154811061329557613295614563565b90600052602060002001549050808760000184815481106132b8576132b8614563565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806132eb576132eb614f48565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506126a0565b60009150506126a0565b5092915050565b600081815260018301602052604081205461337e575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556126a0565b5060006126a0565b6000808085600181111561339c5761339c6148b3565b036133ab575062015f906133ca565b60018560018111156133bf576133bf6148b3565b03612dfb57506201adb05b6133db63ffffffff85166014614910565b6133e684600161489a565b6133f59060ff16611d4c614910565b6133ff9083614887565b6134099190614887565b95945050505050565b60008060008960a0015161ffff168761342b9190614910565b90508380156134395750803a105b1561344157503a5b600060027f00000000000000000000000000000000000000000000000000000000000000006002811115613477576134776148b3565b036135d65760408051600081526020810190915285156134d5576000366040518060800160405280604881526020016150e5604891396040516020016134bf93929190614f77565b604051602081830303815290604052905061353d565b6016546134f190640100000000900463ffffffff166004614f9e565b63ffffffff1667ffffffffffffffff81111561350f5761350f613d89565b6040519080825280601f01601f191660200182016040528015613539576020820181803683370190505b5090505b6040517f49948e0e00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000000f906349948e0e9061358d908490600401613d76565b602060405180830381865afa1580156135aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ce9190614c65565b915050613730565b60017f0000000000000000000000000000000000000000000000000000000000000000600281111561360a5761360a6148b3565b0361373057841561368c57606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613661573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136859190614c65565b9050613730565b6000606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa1580156136da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136fe9190614fc1565b505060165492945061372193505050640100000000900463ffffffff1682614910565b61372c906010614910565b9150505b8461374c57808b60a0015161ffff166137499190614910565b90505b61375a61ffff87168261497c565b90506000878261376a8c8e614887565b6137749086614910565b61377e9190614887565b61379090670de0b6b3a7640000614910565b61379a919061497c565b905060008c6040015163ffffffff1664e8d4a510006137b99190614910565b898e6020015163ffffffff16858f886137d29190614910565b6137dc9190614887565b6137ea90633b9aca00614910565b6137f49190614910565b6137fe919061497c565b6138089190614887565b90506b033b2e3c9fd0803ce80000006138218284614887565b1115613859576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909c909b509950505050505050505050565b60008084806020019051810190613882919061500b565b845160c00151815191925063ffffffff908116911610156138df57857f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8866040516138cd9190613d76565b60405180910390a26000915050612d23565b826101200151801561391357506020810151158015906139135750602081015181516139109063ffffffff16613af1565b14155b8061392c5750613921612537565b815163ffffffff1610155b1561396157857f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301866040516138cd9190613d76565b50600195945050505050565b6000806000848060200190518101906139869190615063565b90506000868260000151836020015184604001516040516020016139e894939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b6040516020818303038152906040528051906020012090508461012001518015613a365750608082015115801590613a3657508160800151613a33836060015163ffffffff16613af1565b14155b80613a525750613a44612537565b826060015163ffffffff1610155b15613a9c57867f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc30187604051613a879190613d76565b60405180910390a26000935091506116be9050565b60008181526008602052604090205460ff1615613ae357867f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e887604051613a879190613d76565b600197909650945050505050565b600060017f00000000000000000000000000000000000000000000000000000000000000006002811115613b2757613b276148b3565b03613c41576000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9e9190614c65565b90508083101580613bb95750610100613bb784836148fd565b115b15613bc75750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa158015613c1d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b29190614c65565b504090565b919050565b50805460008255906000526020600020908101906122a99190613cf3565b828054828255906000526020600020908101928215613ce3579160200282015b82811115613ce357825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190613c89565b50613cef929150613cf3565b5090565b5b80821115613cef5760008155600101613cf4565b60005b83811015613d23578181015183820152602001613d0b565b50506000910152565b60008151808452613d44816020860160208601613d08565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006126b26020830184613d2c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610200810167ffffffffffffffff81118282101715613ddc57613ddc613d89565b60405290565b60405160c0810167ffffffffffffffff81118282101715613ddc57613ddc613d89565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613e4c57613e4c613d89565b604052919050565b600067ffffffffffffffff821115613e6e57613e6e613d89565b5060051b60200190565b73ffffffffffffffffffffffffffffffffffffffff811681146122a957600080fd5b8035613c4681613e78565b600082601f830112613eb657600080fd5b81356020613ecb613ec683613e54565b613e05565b82815260059290921b84018101918181019086841115613eea57600080fd5b8286015b84811015613f0e578035613f0181613e78565b8352918301918301613eee565b509695505050505050565b803560ff81168114613c4657600080fd5b63ffffffff811681146122a957600080fd5b8035613c4681613f2a565b62ffffff811681146122a957600080fd5b8035613c4681613f47565b61ffff811681146122a957600080fd5b8035613c4681613f63565b6bffffffffffffffffffffffff811681146122a957600080fd5b8035613c4681613f7e565b80151581146122a957600080fd5b8035613c4681613fa3565b60006102008284031215613fcf57600080fd5b613fd7613db8565b9050613fe282613f3c565b8152613ff060208301613f3c565b602082015261400160408301613f3c565b604082015261401260608301613f58565b606082015261402360808301613f73565b608082015261403460a08301613f98565b60a082015261404560c08301613f3c565b60c082015261405660e08301613f3c565b60e0820152610100614069818401613f3c565b9082015261012061407b838201613f3c565b90820152610140828101359082015261016080830135908201526101806140a3818401613e9a565b908201526101a08281013567ffffffffffffffff8111156140c357600080fd5b6140cf85828601613ea5565b8284015250506101c06140e3818401613e9a565b908201526101e06140f5838201613fb1565b9082015292915050565b803567ffffffffffffffff81168114613c4657600080fd5b600082601f83011261412857600080fd5b813567ffffffffffffffff81111561414257614142613d89565b61417360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613e05565b81815284602083860101111561418857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c087890312156141be57600080fd5b863567ffffffffffffffff808211156141d657600080fd5b6141e28a838b01613ea5565b975060208901359150808211156141f857600080fd5b6142048a838b01613ea5565b965061421260408a01613f19565b9550606089013591508082111561422857600080fd5b6142348a838b01613fbc565b945061424260808a016140ff565b935060a089013591508082111561425857600080fd5b5061426589828a01614117565b9150509295509295509295565b60008083601f84011261428457600080fd5b50813567ffffffffffffffff81111561429c57600080fd5b6020830191508360208285010111156142b457600080fd5b9250929050565b600080600080606085870312156142d157600080fd5b84356142dc81613e78565b935060208501359250604085013567ffffffffffffffff8111156142ff57600080fd5b61430b87828801614272565b95989497509550505050565b60008060006040848603121561432c57600080fd5b83359250602084013567ffffffffffffffff81111561434a57600080fd5b61435686828701614272565b9497909650939450505050565b60008083601f84011261437557600080fd5b50813567ffffffffffffffff81111561438d57600080fd5b6020830191508360208260051b85010111156142b457600080fd5b60008060008060008060008060e0898b0312156143c457600080fd5b606089018a8111156143d557600080fd5b8998503567ffffffffffffffff808211156143ef57600080fd5b6143fb8c838d01614272565b909950975060808b013591508082111561441457600080fd5b6144208c838d01614363565b909750955060a08b013591508082111561443957600080fd5b506144468b828c01614363565b999c989b50969995989497949560c00135949350505050565b60008060008060008060c0878903121561447857600080fd5b863567ffffffffffffffff8082111561449057600080fd5b61449c8a838b01613ea5565b975060208901359150808211156144b257600080fd5b6144be8a838b01613ea5565b96506144cc60408a01613f19565b955060608901359150808211156144e257600080fd5b6142348a838b01614117565b60006020828403121561450057600080fd5b81356126b281613e78565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600060ff821660ff84168160ff048111821515161561455b5761455b61450b565b029392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036145c3576145c361450b565b5060010190565b63ffffffff8181168382160190808211156133305761333061450b565b600081518084526020808501945080840160005b8381101561462d57815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016145fb565b509495945050505050565b6020815261464f60208201835163ffffffff169052565b60006020830151614668604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015162ffffff8116608084015250608083015161ffff811660a08401525060a08301516bffffffffffffffffffffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006146e18185018363ffffffff169052565b84015190506101206146fa8482018363ffffffff169052565b84015190506101406147138482018363ffffffff169052565b840151610160848101919091528401516101808085019190915284015190506101a06147568185018373ffffffffffffffffffffffffffffffffffffffff169052565b808501519150506102006101c081818601526147766102208601846145e7565b908601519092506101e06147a18682018373ffffffffffffffffffffffffffffffffffffffff169052565b90950151151593019290925250919050565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526147e38184018a6145e7565b905082810360808401526147f781896145e7565b905060ff871660a084015282810360c08401526148148187613d2c565b905067ffffffffffffffff851660e08401528281036101008401526148398185613d2c565b9c9b505050505050505050505050565b60006020828403121561485b57600080fd5b5035919050565b6bffffffffffffffffffffffff8181168382160190808211156133305761333061450b565b808201808211156126a0576126a061450b565b60ff81811683821601908111156126a0576126a061450b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b61ffff8181168382160190808211156133305761333061450b565b818103818111156126a0576126a061450b565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156149485761494861450b565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261498b5761498b61494d565b500490565b6bffffffffffffffffffffffff851681528360208201528260408201526080606082015260006149c36080830184613d2c565b9695505050505050565b8051613c4681613f2a565b8051613c4681613f47565b8051613c4681613f63565b8051613c4681613f7e565b8051613c4681613e78565b600082601f830112614a1557600080fd5b81516020614a25613ec683613e54565b82815260059290921b84018101918181019086841115614a4457600080fd5b8286015b84811015613f0e578051614a5b81613e78565b8352918301918301614a48565b8051613c4681613fa3565b600060208284031215614a8557600080fd5b815167ffffffffffffffff80821115614a9d57600080fd5b908301906102008286031215614ab257600080fd5b614aba613db8565b614ac3836149cd565b8152614ad1602084016149cd565b6020820152614ae2604084016149cd565b6040820152614af3606084016149d8565b6060820152614b04608084016149e3565b6080820152614b1560a084016149ee565b60a0820152614b2660c084016149cd565b60c0820152614b3760e084016149cd565b60e0820152610100614b4a8185016149cd565b90820152610120614b5c8482016149cd565b9082015261014083810151908201526101608084015190820152610180614b848185016149f9565b908201526101a08381015183811115614b9c57600080fd5b614ba888828701614a04565b8284015250506101c09150614bbe8284016149f9565b828201526101e09150614bd2828401614a68565b91810191909152949350505050565b6bffffffffffffffffffffffff8281168282160390808211156133305761333061450b565b60006bffffffffffffffffffffffff80841680614c2557614c2561494d565b92169190910492915050565b60006bffffffffffffffffffffffff80831681851681830481118215151615614c5c57614c5c61450b565b02949350505050565b600060208284031215614c7757600080fd5b5051919050565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614cc58285018b6145e7565b91508382036080850152614cd9828a6145e7565b915060ff881660a085015283820360c0850152614cf68288613d2c565b90861660e085015283810361010085015290506148398185613d2c565b828152604060208201526000612d236040830184613d2c565b60008060408385031215614d3f57600080fd5b8251614d4a81613fa3565b6020939093015192949293505050565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f830112614d9157600080fd5b81356020614da1613ec683613e54565b82815260059290921b84018101918181019086841115614dc057600080fd5b8286015b84811015613f0e5780358352918301918301614dc4565b600082601f830112614dec57600080fd5b81356020614dfc613ec683613e54565b82815260059290921b84018101918181019086841115614e1b57600080fd5b8286015b84811015613f0e57803567ffffffffffffffff811115614e3f5760008081fd5b614e4d8986838b0101614117565b845250918301918301614e1f565b600060208284031215614e6d57600080fd5b813567ffffffffffffffff80821115614e8557600080fd5b9083019060c08286031215614e9957600080fd5b614ea1613de2565b8235815260208301356020820152604083013582811115614ec157600080fd5b614ecd87828601614d80565b604083015250606083013582811115614ee557600080fd5b614ef187828601614d80565b606083015250608083013582811115614f0957600080fd5b614f1587828601614ddb565b60808301525060a083013582811115614f2d57600080fd5b614f3987828601614ddb565b60a08301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b828482376000838201600081528351614f94818360208801613d08565b0195945050505050565b600063ffffffff80831681851681830481118215151615614c5c57614c5c61450b565b60008060008060008060c08789031215614fda57600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b60006040828403121561501d57600080fd5b6040516040810181811067ffffffffffffffff8211171561504057615040613d89565b604052825161504e81613f2a565b81526020928301519281019290925250919050565b600060a0828403121561507557600080fd5b60405160a0810181811067ffffffffffffffff8211171561509857615098613d89565b8060405250825181526020830151602082015260408301516150b981613f2a565b604082015260608301516150cc81613f2a565b6060820152608092830151928101929092525091905056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000810000a", } var AutomationRegistryABI = AutomationRegistryMetaData.ABI diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index e967964daa1..46be4857d75 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -8,7 +8,7 @@ automation_forwarder_logic: ../../contracts/solc/v0.8.16/AutomationForwarderLogi automation_registrar_wrapper2_1: ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.bin eb06d853aab39d3196c593b03e555851cbe8386e0fe54a74c2479f62d14b3c42 automation_registrar_wrapper2_2: ../../contracts/solc/v0.8.16/AutomationRegistrar2_2/AutomationRegistrar2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_2/AutomationRegistrar2_2.bin 1da3ede9459c097482de224a47025ef7d578c7a73bafdbe4d471989ca4ecb608 automation_utils_2_1: ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin 331bfa79685aee6ddf63b64c0747abee556c454cae3fb8175edff425b615d8aa -automation_utils_2_2: ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.abi ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.bin 4f8055e0d47c0fa88f545d0160b04666a0d7720da8fd1206405da0bcf410b426 +automation_utils_2_2: ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.abi ../../contracts/solc/v0.8.16/AutomationUtils2_2/AutomationUtils2_2.bin 30aee16011a019108133e9ae153cc3feeece9c8bd957ee2789b3b32243a4c7a2 batch_blockhash_store: ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.abi ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.bin 14356c48ef70f66ef74f22f644450dbf3b2a147c1b68deaa7e7d1eb8ffab15db batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.bin d0a54963260d8c1f1bbd984b758285e6027cfb5a7e42701bcb562ab123219332 batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin 7bb76ae241cf1b37b41920830b836cb99f1ad33efd7435ca2398ff6cd2fe5d48 @@ -24,7 +24,7 @@ flux_aggregator_wrapper: ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator gas_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin 4a5dcdac486d18fcd58e3488c15c1710ae76b977556a3f3191bd269a4bc75723 gas_wrapper_mock: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin a9b08f18da59125c6fc305855710241f3d35161b8b9f3e3f635a7b1d5c6da9c8 i_keeper_registry_master_wrapper_2_1: ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin 6501bb9bcf5048bab2737b00685c6984a24867e234ddf5b60a65904eee9a4ebc -i_keeper_registry_master_wrapper_2_2: ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.bin 69fe8b127b71739606600760fc8fb01b3af1e8dba786ba0d047d766416acf83b +i_keeper_registry_master_wrapper_2_2: ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.16/IAutomationRegistryMaster/IAutomationRegistryMaster.bin 37aabed3df4c1ab6ac63def4aa8e251da975bbbb15a8bcf1c22ff7467d70f70f i_log_automation: ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin 296beccb6af655d6fc3a6e676b244831cce2da6688d3afc4f21f8738ae59e03e keeper_consumer_performance_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 keeper_consumer_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 2c6163b145082fbab74b7343577a9cec8fda8b0da9daccf2a82581b1f5a84b83 @@ -34,16 +34,16 @@ keeper_registrar_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/Keep keeper_registry_logic1_3: ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.bin 903f8b9c8e25425ca6d0b81b89e339d695a83630bfbfa24a6f3b38869676bc5a keeper_registry_logic2_0: ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.bin d69d2bc8e4844293dbc2d45abcddc50b84c88554ecccfa4fa77c0ca45ec80871 keeper_registry_logic_a_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.bin 77481ab75c9aa86a62a7b2a708599b5ea1a6346ed1c0def6d4826e7ae523f1ee -keeper_registry_logic_a_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.bin 202d0d7829daa0e6cb7b2cf55bacf5f0b0ea7d17ffe3d8241e5bf96a37f1b416 +keeper_registry_logic_a_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.bin 0d7847916491adac72568227177427daa9f47841ae6b4750c02d5e0ba14f8d4d keeper_registry_logic_b_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.bin 467d10741a04601b136553a2b1c6ab37f2a65d809366faf03180a22ff26be215 -keeper_registry_logic_b_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin b1cca74805530042f1255b4cf50115c47e4186603d3f9d276bbe3773ad1d8285 +keeper_registry_logic_b_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin 73d0822f2ecef6388f2b46f587795cdaa23316b96704e93467e947e1e3c5fe5f keeper_registry_wrapper1_1: ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.abi ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.bin 6ce079f2738f015f7374673a2816e8e9787143d00b780ea7652c8aa9ad9e1e20 keeper_registry_wrapper1_1_mock: ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.abi ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.bin 98ddb3680e86359de3b5d17e648253ba29a84703f087a1b52237824003a8c6df keeper_registry_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.bin a40ff877dd7c280f984cbbb2b428e160662b0c295e881d5f778f941c0088ca22 keeper_registry_wrapper1_3: ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.bin d4dc760b767ae274ee25c4a604ea371e1fa603a7b6421b69efb2088ad9e8abb3 keeper_registry_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.bin c32dea7d5ef66b7c58ddc84ddf69aa44df1b3ae8601fbc271c95be4ff5853056 keeper_registry_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.bin 604e4a0cd980c713929b523b999462a3aa0ed06f96ff563a4c8566cf59c8445b -keeper_registry_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.bin 9242df418f129e08d4e2eb0ee02a67781d3261f9c303035d87864765eed35b9b +keeper_registry_wrapper_2_2: ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.16/AutomationRegistry2_2/AutomationRegistry2_2.bin 8f1c6d3eddf36201a556a101350abd6eb733d4c3177da2ddb5eca0d24d9a88ef keepers_vrf_consumer: ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin fa75572e689c9e84705c63e8dbe1b7b8aa1a8fe82d66356c4873d024bb9166e8 log_emitter: ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin 4b129ab93432c95ff9143f0631323e189887668889e0b36ccccf18a571e41ccf log_triggered_streams_lookup_wrapper: ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin f8da43a927c1a66238a9f4fd5d5dd7e280e361daa0444da1f7f79498ace901e1 From 23b612bef9029e44d9bbe59d6b4586b29b1ae7f9 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Thu, 25 Jan 2024 03:23:33 +0200 Subject: [PATCH 212/234] VRF-858: small refactoring - rename CTF VRF V2 and V2.5 packages (#11877) * VRF-858: small refactoring - rename CTF VRF V2 and V2.5 packages * VRF-858: small refactoring - rename VRF v3 packages * VRF-858: increasing parallelization for vrf v2 runner in GHA integration-tests.yml * Revert "VRF-858: small refactoring - rename VRF v3 packages" This reverts commit 55f47455b63e050d90a2c0f374a4cc067f4cb46a. * fixing deadlock when waiting for force fulfilment --- .github/workflows/integration-tests.yml | 2 +- .../actions/{ => vrf}/vrfv1/actions.go | 0 .../vrfv2}/vrfv2_models.go | 2 +- .../vrfv2}/vrfv2_steps.go | 4 +- .../{ => vrf}/vrfv2plus/vrfv2plus_models.go | 0 .../{ => vrf}/vrfv2plus/vrfv2plus_steps.go | 0 integration-tests/load/vrfv2/gun.go | 10 ++--- integration-tests/load/vrfv2/vrfv2_test.go | 24 +++++------ integration-tests/load/vrfv2plus/gun.go | 2 +- .../load/vrfv2plus/vrfv2plus_test.go | 2 +- integration-tests/smoke/vrf_test.go | 2 +- integration-tests/smoke/vrfv2_test.go | 43 +++++++++---------- integration-tests/smoke/vrfv2plus_test.go | 3 +- 13 files changed, 46 insertions(+), 48 deletions(-) rename integration-tests/actions/{ => vrf}/vrfv1/actions.go (100%) rename integration-tests/actions/{vrfv2_actions => vrf/vrfv2}/vrfv2_models.go (98%) rename integration-tests/actions/{vrfv2_actions => vrf/vrfv2}/vrfv2_steps.go (99%) rename integration-tests/actions/{ => vrf}/vrfv2plus/vrfv2plus_models.go (100%) rename integration-tests/actions/{ => vrf}/vrfv2plus/vrfv2plus_steps.go (100%) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 376d49a8e18..d224b810874 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -450,7 +450,7 @@ jobs: os: ubuntu-latest pyroscope_env: ci-smoke-vrf-evm-simulated - name: vrfv2 - nodes: 2 + nodes: 3 os: ubuntu-latest pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus diff --git a/integration-tests/actions/vrfv1/actions.go b/integration-tests/actions/vrf/vrfv1/actions.go similarity index 100% rename from integration-tests/actions/vrfv1/actions.go rename to integration-tests/actions/vrf/vrfv1/actions.go diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_models.go b/integration-tests/actions/vrf/vrfv2/vrfv2_models.go similarity index 98% rename from integration-tests/actions/vrfv2_actions/vrfv2_models.go rename to integration-tests/actions/vrf/vrfv2/vrfv2_models.go index 54449eef372..be627b43e4f 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_models.go +++ b/integration-tests/actions/vrf/vrfv2/vrfv2_models.go @@ -1,4 +1,4 @@ -package vrfv2_actions +package vrfv2 import ( "math/big" diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go similarity index 99% rename from integration-tests/actions/vrfv2_actions/vrfv2_steps.go rename to integration-tests/actions/vrf/vrfv2/vrfv2_steps.go index 1a958548965..276105d20ef 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go @@ -1,4 +1,4 @@ -package vrfv2_actions +package vrfv2 import ( "context" @@ -888,7 +888,7 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( case randomWordsForcedEvent = <-randWordsForcedEventChannel: LogRandomWordsForcedEvent(l, vrfOwner, randomWordsForcedEvent) case <-time.After(randomWordsFulfilledEventTimeout): - err = fmt.Errorf("timeout waiting for ConfigSet, RandomWordsFulfilled and RandomWordsForced events") + return nil, nil, nil, fmt.Errorf("timeout waiting for ConfigSet, RandomWordsFulfilled and RandomWordsForced events") } } return configSetEvent, randomWordsFulfilledEvent, randomWordsForcedEvent, err diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_models.go b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go similarity index 100% rename from integration-tests/actions/vrfv2plus/vrfv2plus_models.go rename to integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go similarity index 100% rename from integration-tests/actions/vrfv2plus/vrfv2plus_steps.go rename to integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index bd1dc96fadf..4746c73081a 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -6,14 +6,14 @@ import ( "github.com/rs/zerolog" "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" "github.com/smartcontractkit/chainlink/integration-tests/types" ) /* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { - contracts *vrfv2_actions.VRFV2Contracts + contracts *vrfv2.VRFV2Contracts keyHash [32]byte subIDs []uint64 testConfig types.VRFv2TestConfig @@ -21,7 +21,7 @@ type SingleHashGun struct { } func NewSingleHashGun( - contracts *vrfv2_actions.VRFV2Contracts, + contracts *vrfv2.VRFV2Contracts, keyHash [32]byte, subIDs []uint64, testConfig types.VRFv2TestConfig, @@ -43,14 +43,14 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { vrfv2Config := m.testConfig.GetVRFv2Config().General //randomly increase/decrease randomness request count per TX randomnessRequestCountPerRequest := deviateValue(*vrfv2Config.RandomnessRequestCountPerRequest, *vrfv2Config.RandomnessRequestCountPerRequestDeviation) - _, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + _, err := vrfv2.RequestRandomnessAndWaitForFulfillment( m.logger, //the same consumer is used for all requests and in all subs m.contracts.LoadTestConsumers[0], m.contracts.Coordinator, //randomly pick a subID from pool of subIDs m.subIDs[randInRange(0, len(m.subIDs)-1)], - &vrfv2_actions.VRFV2Data{VRFV2KeyData: vrfv2_actions.VRFV2KeyData{KeyHash: m.keyHash}}, + &vrfv2.VRFV2Data{VRFV2KeyData: vrfv2.VRFV2KeyData{KeyHash: m.keyHash}}, *vrfv2Config.MinimumConfirmations, *vrfv2Config.CallbackGasLimit, *vrfv2Config.NumberOfWords, diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 142764b1719..3a29e729dea 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" @@ -23,14 +24,13 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) var ( env *test_env.CLClusterTestEnv - vrfv2Contracts *vrfv2_actions.VRFV2Contracts - vrfv2Data *vrfv2_actions.VRFV2Data + vrfv2Contracts *vrfv2.VRFV2Contracts + vrfv2Data *vrfv2.VRFV2Data subIDs []uint64 eoaWalletAddress string @@ -110,15 +110,15 @@ func TestVRFV2Performance(t *testing.T) { if *cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2Config.Performance.LinkAddress) require.NoError(t, err) - consumers, err = vrfv2_actions.DeployVRFV2Consumers(env.ContractDeployer, coordinator.Address(), 1) + consumers, err = vrfv2.DeployVRFV2Consumers(env.ContractDeployer, coordinator.Address(), 1) require.NoError(t, err) err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + require.NoError(t, err, vrfv2.ErrWaitTXsComplete) l.Info(). Str("Coordinator", *cfg.ExistingEnvConfig.CoordinatorAddress). Int("Number of Subs to create", *vrfv2Config.General.NumberOfSubToCreate). Msg("Creating and funding subscriptions, deploying and adding consumers to subs") - subIDs, err = vrfv2_actions.CreateFundSubsAndAddConsumers( + subIDs, err = vrfv2.CreateFundSubsAndAddConsumers( env, big.NewFloat(*cfg.General.SubscriptionFundingAmountLink), linkToken, @@ -137,14 +137,14 @@ func TestVRFV2Performance(t *testing.T) { err = FundNodesIfNeeded(&testConfig, env.EVMClient, l) require.NoError(t, err) - vrfv2Contracts = &vrfv2_actions.VRFV2Contracts{ + vrfv2Contracts = &vrfv2.VRFV2Contracts{ Coordinator: coordinator, LoadTestConsumers: consumers, BHS: nil, } - vrfv2Data = &vrfv2_actions.VRFV2Data{ - VRFV2KeyData: vrfv2_actions.VRFV2KeyData{ + vrfv2Data = &vrfv2.VRFV2Data{ + VRFV2KeyData: vrfv2.VRFV2KeyData{ VRFKey: nil, EncodedProvingKey: [2]*big.Int{}, KeyHash: common.HexToHash(*vrfv2Config.Performance.KeyHash), @@ -200,7 +200,7 @@ func TestVRFV2Performance(t *testing.T) { useVRFOwner := true useTestCoordinator := true - vrfv2Contracts, subIDs, vrfv2Data, err = vrfv2_actions.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2Data, err = vrfv2.SetupVRFV2Environment( env, &testConfig, useVRFOwner, @@ -222,7 +222,7 @@ func TestVRFV2Performance(t *testing.T) { for _, subID := range subIDs { subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) require.NoError(t, err, "error getting subscription information for subscription %d", subID) - vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) } singleFeedConfig := &wasp.Config{ @@ -262,7 +262,7 @@ func TestVRFV2Performance(t *testing.T) { var wg sync.WaitGroup wg.Add(1) //todo - timeout should be configurable depending on the perf test type - requestCount, fulfilmentCount, err := vrfv2_actions.WaitForRequestCountEqualToFulfilmentCount(consumer, 2*time.Minute, &wg) + requestCount, fulfilmentCount, err := vrfv2.WaitForRequestCountEqualToFulfilmentCount(consumer, 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index bfa56d5f3dc..faf5e6ef211 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog" "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/types" ) diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index b32d40e983e..51a3116dca2 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -17,10 +17,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index c296fcd0f34..f5aeb86173b 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -12,9 +12,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv1" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv1" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 0d136b62a98..e4ff1600649 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" @@ -20,8 +21,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -59,7 +58,7 @@ func TestVRFv2Basic(t *testing.T) { defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 1 - vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2.SetupVRFV2Environment( env, &config, useVRFOwner, @@ -79,7 +78,7 @@ func TestVRFv2Basic(t *testing.T) { subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) require.NoError(t, err, "error getting subscription information") - vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) t.Run("Request Randomness", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) @@ -89,7 +88,7 @@ func TestVRFv2Basic(t *testing.T) { require.NoError(t, err, "error reading job runs") // test and assert - randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + randomWordsFulfilledEvent, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, @@ -128,7 +127,7 @@ func TestVRFv2Basic(t *testing.T) { t.Run("Direct Funding (VRFV2Wrapper)", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - wrapperContracts, wrapperSubID, err := vrfv2_actions.SetupVRFV2WrapperEnvironment( + wrapperContracts, wrapperSubID, err := vrfv2.SetupVRFV2WrapperEnvironment( env, &configCopy, linkToken, @@ -148,7 +147,7 @@ func TestVRFv2Basic(t *testing.T) { subBalanceBeforeRequest := wrapperSubscription.Balance // Request Randomness and wait for fulfillment event - randomWordsFulfilledEvent, err := vrfv2_actions.DirectFundingRequestRandomnessAndWaitForFulfillment( + randomWordsFulfilledEvent, err := vrfv2.DirectFundingRequestRandomnessAndWaitForFulfillment( l, wrapperConsumer, vrfv2Contracts.Coordinator, @@ -204,7 +203,7 @@ func TestVRFv2Basic(t *testing.T) { t.Run("Oracle Withdraw", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - subIDsForOracleWithDraw, err := vrfv2_actions.CreateFundSubsAndAddConsumers( + subIDsForOracleWithDraw, err := vrfv2.CreateFundSubsAndAddConsumers( env, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, @@ -216,7 +215,7 @@ func TestVRFv2Basic(t *testing.T) { subIDForOracleWithdraw := subIDsForOracleWithDraw[0] - fulfilledEventLink, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + fulfilledEventLink, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, @@ -245,7 +244,7 @@ func TestVRFv2Basic(t *testing.T) { require.NoError(t, err, "Error withdrawing LINK from coordinator to default wallet") err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + require.NoError(t, err, vrfv2.ErrWaitTXsComplete) defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) @@ -260,7 +259,7 @@ func TestVRFv2Basic(t *testing.T) { t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( + subIDsForCancelling, err := vrfv2.CreateFundSubsAndAddConsumers( env, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, @@ -337,7 +336,7 @@ func TestVRFv2Basic(t *testing.T) { // Underfund subscription to force fulfillments to fail configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) // 1 Juel - subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers( + subIDsForCancelling, err := vrfv2.CreateFundSubsAndAddConsumers( env, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, @@ -352,7 +351,7 @@ func TestVRFv2Basic(t *testing.T) { subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "Error getting subscription information") - vrfv2_actions.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.Coordinator) // No GetActiveSubscriptionIds function available - skipping check @@ -362,7 +361,7 @@ func TestVRFv2Basic(t *testing.T) { // Request randomness - should fail due to underfunded subscription randomWordsFulfilledEventTimeout := 5 * time.Second - _, err = vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + _, err = vrfv2.RequestRandomnessAndWaitForFulfillment( l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, @@ -482,7 +481,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 2 - vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2.SetupVRFV2Environment( env, &config, useVRFOwner, @@ -502,7 +501,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) require.NoError(t, err, "error getting subscription information") - vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") @@ -512,7 +511,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { var fulfillmentTxFromAddresses []string for i := 0; i < numberOfTxKeysToCreate+1; i++ { - randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + randomWordsFulfilledEvent, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, @@ -580,7 +579,7 @@ func TestVRFOwner(t *testing.T) { defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 1 - vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2.SetupVRFV2Environment( env, &config, useVRFOwner, @@ -600,7 +599,7 @@ func TestVRFOwner(t *testing.T) { subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) require.NoError(t, err, "error getting subscription information") - vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) t.Run("Request Randomness With Force-Fulfill", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) @@ -616,7 +615,7 @@ func TestVRFOwner(t *testing.T) { require.NoError(t, err, "error transferring link to consumer contract") err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + require.NoError(t, err, vrfv2.ErrWaitTXsComplete) consumerLinkBalance, err := linkToken.BalanceOf(testcontext.Get(t), vrfv2Contracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting consumer link balance") @@ -628,10 +627,10 @@ func TestVRFOwner(t *testing.T) { err = mockETHLinkFeed.SetBlockTimestampDeduction(big.NewInt(3)) require.NoError(t, err) err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2_actions.ErrWaitTXsComplete) + require.NoError(t, err, vrfv2.ErrWaitTXsComplete) // test and assert - _, randFulfilledEvent, _, err := vrfv2_actions.RequestRandomnessWithForceFulfillAndWaitForFulfillment( + _, randFulfilledEvent, _, err := vrfv2.RequestRandomnessWithForceFulfillAndWaitForFulfillment( l, vrfv2Contracts.LoadTestConsumers[0], vrfv2Contracts.Coordinator, diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index f1f11d3e1f0..9309070f647 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -16,10 +16,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" @@ -42,7 +42,6 @@ func TestVRFv2Plus(t *testing.T) { WithTestInstance(t). WithTestConfig(&config). WithPrivateEthereumNetwork(network). - WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). WithStandardCleanup(). From e9f138c2490bbf712b4720a897ff1039928bc079 Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Wed, 24 Jan 2024 20:24:34 -0800 Subject: [PATCH 213/234] This should restore "make chainlink" to previous behavior (#11886) Previously, running "make chainlink" and similar commands used to be able to rebuild the chainlink executable. That stopped working recently, after some dependency changes. This will allow it to rebuild without needing to explicitly run "rm chainlink" each time you modify the source code or change branches. --- GNUmakefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GNUmakefile b/GNUmakefile index a6e886b7f6c..549a7222a82 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -42,12 +42,15 @@ godoc: ## Install and run godoc install-chainlink: operator-ui ## Install the chainlink binary. go install $(GOFLAGS) . +.PHONY: chainlink chainlink: ## Build the chainlink binary. go build $(GOFLAGS) . +.PHONY: chainlink-dev chainlink-dev: ## Build a dev build of chainlink binary. go build -tags dev $(GOFLAGS) . +.PHONY: chainlink-test chainlink-test: ## Build a test build of chainlink binary. go build $(GOFLAGS) . From 8014d40e3030e17859ea632f6e18f19a5931d719 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:40:26 +0400 Subject: [PATCH 214/234] Fix Automation load, nightly tests (#11891) * fix automation load test - pyroscope config * TTL of test based on load duration * fix automation load test - pyroscope config * remove duplicate config * remove whitespace * fix nightly test action * fix nightly test action --- .../action.yml | 0 .github/workflows/automation-load-tests.yml | 20 +++++++++++++ .../workflows/automation-nightly-tests.yml | 5 ++-- .../automationv2_1/automationv2_1_test.go | 30 +++++++++---------- 4 files changed, 38 insertions(+), 17 deletions(-) rename .github/actions/{setup-create-base64-upgrade-config => setup-create-base64-upgrade-config}/action.yml (100%) diff --git a/.github/actions/setup-create-base64-upgrade-config /action.yml b/.github/actions/setup-create-base64-upgrade-config/action.yml similarity index 100% rename from .github/actions/setup-create-base64-upgrade-config /action.yml rename to .github/actions/setup-create-base64-upgrade-config/action.yml diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index 583314f438e..8045b589edc 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -41,6 +41,26 @@ jobs: BASE64_CONFIG_OVERRIDE=$(jq -r '.inputs.base64Config' $GITHUB_EVENT_PATH) echo ::add-mask::$BASE64_CONFIG_OVERRIDE echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + - name: Merge Pyrsoscope config + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: "automation-load-test" + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + run: | + decoded_toml=$(echo $BASE64_CONFIG_OVERRIDE | base64 -d) + + # use Pyroscope config from GH secrets and merge it with base64 input + cat << EOF > config.toml + server_url="$PYROSCOPE_SERVER" + environment="$PYROSCOPE_ENVIRONMENT" + key="$PYROSCOPE_KEY" + EOF + + echo "$decoded_toml" >> final_config.toml + cat config.toml >> final_config.toml + BASE64_CONFIG_OVERRIDE=$(cat final_config.toml | base64 -w 0) + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Parse base64 config uses: ./.github/actions/setup-parse-base64-config with: diff --git a/.github/workflows/automation-nightly-tests.yml b/.github/workflows/automation-nightly-tests.yml index 9d45ec99e74..9f4e4353e2d 100644 --- a/.github/workflows/automation-nightly-tests.yml +++ b/.github/workflows/automation-nightly-tests.yml @@ -54,6 +54,7 @@ jobs: CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} TEST_LOG_LEVEL: debug + SELECTED_NETWORKS: "SIMULATED" strategy: fail-fast: false matrix: @@ -77,8 +78,8 @@ jobs: selectedNetworks: ${{ env.SELECTED_NETWORKS }} chainlinkImage: "public.ecr.aws/chainlink/chainlink" chainlinkVersion: "latest" - upgradeImage: ${{ env.UPGRADE_IMAGE }} - upgradeVersion: ${{ env.UPGRADE_VERSION }} + upgradeImage: ${{ env.CHAINLINK_IMAGE }} + upgradeVersion: ${{ github.sha }} - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index 49758477767..ffce754c9de 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -30,7 +30,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" @@ -38,7 +38,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - a_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" + aconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" @@ -142,15 +142,15 @@ func TestLogTrigger(t *testing.T) { Msg("Test Config") testConfigFormat := `Number of Nodes: %d - Duration: %d - Block Time: %d - Spec Type: %s - Log Level: %s - Image: %s - Tag: %s +Duration: %d +Block Time: %d +Spec Type: %s +Log Level: %s +Image: %s +Tag: %s - Load Config: - %s` +Load Config: +%s` prettyLoadConfig, err := toml.Marshal(loadedTestConfig.Automation.Load) require.NoError(t, err, "Error marshalling load config") @@ -181,7 +181,7 @@ func TestLogTrigger(t *testing.T) { } testEnvironment := environment.New(&environment.Config{ - TTL: time.Hour * 24, // 1 day, + TTL: loadDuration + time.Hour*6, NamespacePrefix: fmt.Sprintf( "automation-%s-%s", testType, @@ -245,8 +245,8 @@ func TestLogTrigger(t *testing.T) { nodeTOML = networks.AddNetworksConfig(nodeTOML, loadedTestConfig.Pyroscope, testNetwork) var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(loadedTestConfig.ChainlinkImage, target) - ctf_config.MightConfigOverridePyroscopeKey(loadedTestConfig.Pyroscope, target) + ctfconfig.MustConfigOverrideChainlinkVersion(loadedTestConfig.ChainlinkImage, target) + ctfconfig.MightConfigOverridePyroscopeKey(loadedTestConfig.Pyroscope, target) } cd := chainlink.NewWithOverride(i, map[string]any{ @@ -336,7 +336,7 @@ func TestLogTrigger(t *testing.T) { } upkeepConfigs := make([]automationv2.UpkeepConfig, 0) - loadConfigs := make([]a_config.Load, 0) + loadConfigs := make([]aconfig.Load, 0) cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) require.NoError(t, err, "Error building concurrent chain client") @@ -351,7 +351,7 @@ func TestLogTrigger(t *testing.T) { Int("Out Of", *u.NumberOfUpkeeps). Msg("Deployed Automation Log Trigger Consumer Contract") - loadCfg := a_config.Load{ + loadCfg := aconfig.Load{ NumberOfEvents: u.NumberOfEvents, NumberOfSpamMatchingEvents: u.NumberOfSpamMatchingEvents, NumberOfSpamNonMatchingEvents: u.NumberOfSpamNonMatchingEvents, From 645fbd6b42a1b3f8b9e26ecda89d61d8b14c66bd Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Thu, 25 Jan 2024 17:12:27 +0100 Subject: [PATCH 215/234] Fix live test reporting (#11879) * Updates GitHub Action to Fixed Version * look for test summary in current directory * list directories before running test summary * remove whitespaces from actions, update ctf to 1.23.2, fix a typo in TOML config --------- Co-authored-by: Adam Hamrick --- .../action.yml | 20 ++++++------- .../setup-create-base64-config/action.yml | 30 +++++++++---------- .../action.yml | 10 +++---- .../setup-merge-base64-config/action.yml | 12 ++++---- .../setup-parse-base64-config/action.yml | 16 +++++----- .../workflows/integration-staging-tests.yml | 16 +++++----- .github/workflows/live-testnet-tests.yml | 24 ++++++++++++++- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +-- 9 files changed, 77 insertions(+), 57 deletions(-) diff --git a/.github/actions/setup-create-base64-config-live-testnets/action.yml b/.github/actions/setup-create-base64-config-live-testnets/action.yml index 5ba8150989a..5bf83191b59 100644 --- a/.github/actions/setup-create-base64-config-live-testnets/action.yml +++ b/.github/actions/setup-create-base64-config-live-testnets/action.yml @@ -14,7 +14,7 @@ inputs: description: The git commit sha to use for the image tag pyroscopeServer: description: URL of Pyroscope server - pyroscopeEnvironment: + pyroscopeEnvironment: description: Name of Pyroscope environment pyroscopeKey: description: Pyroscope server key @@ -47,9 +47,9 @@ runs: id: base64-config-override env: RUN_ID: ${{ inputs.runId }} - PYROSCOPE_SERVER: ${{ inputs.pyroscopeServer }} - PYROSCOPE_ENVIRONMENT: ${{ inputs.pyroscopeEnvironment }} - PYROSCOPE_KEY: ${{ inputs.pyroscopeKey }} + PYROSCOPE_SERVER: ${{ inputs.pyroscopeServer }} + PYROSCOPE_ENVIRONMENT: ${{ inputs.pyroscopeEnvironment }} + PYROSCOPE_KEY: ${{ inputs.pyroscopeKey }} CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} LOKI_ENDPOINT: ${{ inputs.lokiEndpoint }} @@ -81,15 +81,15 @@ runs: else pyroscope_enabled=false fi - + cat << EOF > config.toml [Common] - chainlink_node_funding=0.5 - + chainlink_node_funding=0.5 + [ChainlinkImage] image="$CHAINLINK_IMAGE" version="$CHAINLINK_VERSION" - + [Pyroscope] enabled=$pyroscope_enabled server_url="$PYROSCOPE_SERVER" @@ -109,7 +109,7 @@ runs: [Logging.Grafana] base_url="$GRAFANA_URL" - dasboard_url="$GRAFANA_DASHBOARD_URL" + dashboard_url="$GRAFANA_DASHBOARD_URL" [Network] selected_networks=["$NETWORK"] @@ -123,7 +123,7 @@ runs: [Network.WalletKeys] "$NETWORK" = $(convert_to_toml_array "$FUNDING_KEYS") EOF - + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) echo ::add-mask::$BASE64_CONFIG_OVERRIDE echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV diff --git a/.github/actions/setup-create-base64-config/action.yml b/.github/actions/setup-create-base64-config/action.yml index eafd670226f..55e5924bd76 100644 --- a/.github/actions/setup-create-base64-config/action.yml +++ b/.github/actions/setup-create-base64-config/action.yml @@ -8,7 +8,7 @@ inputs: description: Whether to always collect logs, even for passing tests default: "false" selectedNetworks: - description: The networks to run tests against + description: The networks to run tests against chainlinkImage: description: The chainlink image to use default: "public.ecr.aws/chainlink/chainlink" @@ -16,7 +16,7 @@ inputs: description: The git commit sha to use for the image tag pyroscopeServer: description: URL of Pyroscope server - pyroscopeEnvironment: + pyroscopeEnvironment: description: Name of Pyroscope environment pyroscopeKey: description: Pyroscope server key @@ -43,9 +43,9 @@ runs: RUN_ID: ${{ inputs.runId }} TEST_LOG_COLLECT: ${{ inputs.testLogCollect }} SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} - PYROSCOPE_SERVER: ${{ inputs.pyroscopeServer }} - PYROSCOPE_ENVIRONMENT: ${{ inputs.pyroscopeEnvironment }} - PYROSCOPE_KEY: ${{ inputs.pyroscopeKey }} + PYROSCOPE_SERVER: ${{ inputs.pyroscopeServer }} + PYROSCOPE_ENVIRONMENT: ${{ inputs.pyroscopeEnvironment }} + PYROSCOPE_KEY: ${{ inputs.pyroscopeKey }} CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} LOKI_ENDPOINT: ${{ inputs.lokiEndpoint }} @@ -55,7 +55,7 @@ runs: GRAFANA_URL: ${{ inputs.grafanaUrl }} GRAFANA_DASHBOARD_URL: ${{ inputs.grafanaDashboardUrl }} run: | - echo ::add-mask::$CHAINLINK_IMAGE + echo ::add-mask::$CHAINLINK_IMAGE function convert_to_toml_array() { local IFS=',' local input_array=($1) @@ -68,10 +68,10 @@ runs: toml_array_format="${toml_array_format%,}]" echo "$toml_array_format" } - - selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") log_targets=$(convert_to_toml_array "$LOGSTREAM_LOG_TARGETS") - + if [ -n "$PYROSCOPE_SERVER" ]; then pyroscope_enabled=true else @@ -82,16 +82,16 @@ runs: test_log_collect=true else test_log_collect=false - fi + fi cat << EOF > config.toml [Network] selected_networks=$selected_networks - + [ChainlinkImage] image="$CHAINLINK_IMAGE" version="$CHAINLINK_VERSION" - + [Pyroscope] enabled=$pyroscope_enabled server_url="$PYROSCOPE_SERVER" @@ -109,14 +109,12 @@ runs: tenant_id="$LOKI_TENANT_ID" endpoint="$LOKI_ENDPOINT" basic_auth="$LOKI_BASIC_AUTH" - # legacy, you only need this to access the cloud version - # bearer_token="bearer_token" [Logging.Grafana] base_url="$GRAFANA_URL" - dasboard_url="$GRAFANA_DASHBOARD_URL" + dashboard_url="$GRAFANA_DASHBOARD_URL" EOF - + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) echo ::add-mask::$BASE64_CONFIG_OVERRIDE echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV diff --git a/.github/actions/setup-create-base64-upgrade-config/action.yml b/.github/actions/setup-create-base64-upgrade-config/action.yml index f3acde2ea97..900b15e515a 100644 --- a/.github/actions/setup-create-base64-upgrade-config/action.yml +++ b/.github/actions/setup-create-base64-upgrade-config/action.yml @@ -3,7 +3,7 @@ description: A composite action that creates a base64-encoded config to be used inputs: selectedNetworks: - description: The networks to run tests against + description: The networks to run tests against chainlinkImage: description: The chainlink image to upgrade from default: "public.ecr.aws/chainlink/chainlink" @@ -40,13 +40,13 @@ runs: toml_array_format="${toml_array_format%,}]" echo "$toml_array_format" } - - selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") + + selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") cat << EOF > config.toml [Network] selected_networks=$selected_networks - + [ChainlinkImage] image="$CHAINLINK_IMAGE" version="$CHAINLINK_VERSION" @@ -55,7 +55,7 @@ runs: image="$UPGRADE_IMAGE" version="$UPGRADE_VERSION" EOF - + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) echo ::add-mask::$BASE64_CONFIG_OVERRIDE echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV diff --git a/.github/actions/setup-merge-base64-config/action.yml b/.github/actions/setup-merge-base64-config/action.yml index e5bf2a7d276..9651242f8f1 100644 --- a/.github/actions/setup-merge-base64-config/action.yml +++ b/.github/actions/setup-merge-base64-config/action.yml @@ -6,7 +6,7 @@ inputs: description: Base64-encoded config to decode runs: - using: composite + using: composite steps: - name: Add masks and export base64 config shell: bash @@ -18,7 +18,7 @@ runs: decoded_toml=$(echo $BASE64_CONFIG_OVERRIDE | base64 -d) CHAINLINK_IMAGE=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*image[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) echo ::add-mask::$CHAINLINK_IMAGE - CHAINLINK_VERSION=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*version[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + CHAINLINK_VERSION=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*version[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) NETWORKS=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*selected_networks[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) if [ -n "$CHAINLINK_IMAGE" ]; then @@ -31,11 +31,11 @@ runs: echo "CHAINLINK_VERSION=$CHAINLINK_VERSION" >> $GITHUB_ENV else echo "No Chainlink Version found in base64-ed config. Exiting" - exit 1 - fi + exit 1 + fi if [ -n "$NETWORKS" ]; then - echo "NETWORKS=$NETWORKS" >> $GITHUB_ENV - fi + echo "NETWORKS=$NETWORKS" >> $GITHUB_ENV + fi # use Loki config from GH secrets and merge it with base64 input cat << EOF > config.toml diff --git a/.github/actions/setup-parse-base64-config/action.yml b/.github/actions/setup-parse-base64-config/action.yml index a744abae9e0..3acd2ab8940 100644 --- a/.github/actions/setup-parse-base64-config/action.yml +++ b/.github/actions/setup-parse-base64-config/action.yml @@ -6,7 +6,7 @@ inputs: description: Base64-encoded config to decode runs: - using: composite + using: composite steps: - name: Add masks and export base64 config shell: bash @@ -14,9 +14,9 @@ runs: decoded_toml=$(echo $BASE64_CONFIG_OVERRIDE | base64 -d) CHAINLINK_IMAGE=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*image[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) echo ::add-mask::$CHAINLINK_IMAGE - CHAINLINK_VERSION=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*version[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + CHAINLINK_VERSION=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*version[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) NETWORKS=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*selected_networks[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) - ETH2_EL_CLIENT=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*execution_layer[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) + ETH2_EL_CLIENT=$(echo "$decoded_toml" | awk -F'=' '/^[[:space:]]*execution_layer[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2}' 2>/dev/null) if [ -n "$CHAINLINK_IMAGE" ]; then echo "CHAINLINK_IMAGE=$CHAINLINK_IMAGE" >> $GITHUB_ENV @@ -28,11 +28,11 @@ runs: echo "CHAINLINK_VERSION=$CHAINLINK_VERSION" >> $GITHUB_ENV else echo "No Chainlink Version found in base64-ed config. Exiting" - exit 1 - fi + exit 1 + fi if [ -n "$NETWORKS" ]; then - echo "NETWORKS=$NETWORKS" >> $GITHUB_ENV - fi + echo "NETWORKS=$NETWORKS" >> $GITHUB_ENV + fi if [ -n "$ETH2_EL_CLIENT" ]; then - echo "ETH2_EL_CLIENT=$ETH2_EL_CLIENT" >> $GITHUB_ENV + echo "ETH2_EL_CLIENT=$ETH2_EL_CLIENT" >> $GITHUB_ENV fi \ No newline at end of file diff --git a/.github/workflows/integration-staging-tests.yml b/.github/workflows/integration-staging-tests.yml index 24e4acfe0fb..ea0691b7ca5 100644 --- a/.github/workflows/integration-staging-tests.yml +++ b/.github/workflows/integration-staging-tests.yml @@ -56,7 +56,7 @@ jobs: env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} run: | convert_to_toml_array() { local IFS=',' @@ -76,15 +76,15 @@ jobs: else pyroscope_enabled=false fi - + cat << EOF > config.toml [Common] - chainlink_node_funding=0.5 - + chainlink_node_funding=0.5 + [ChainlinkImage] image="$CHAINLINK_IMAGE" version="${{ github.sha }}" - + [Pyroscope] enabled=$pyroscope_enabled server_url="$PYROSCOPE_SERVER" @@ -104,7 +104,7 @@ jobs: [Logging.Grafana] base_url="$GRAFANA_URL" - dasboard_url="/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" + dashboard_url="/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" [Network] selected_networks=["sepolia"] @@ -118,10 +118,10 @@ jobs: [Network.WalletKeys] sepolia = $(convert_to_toml_array "$EVM_KEYS") EOF - + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) echo ::add-mask::$BASE64_CONFIG_OVERRIDE - echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Run E2E soak tests run: | cd integration-tests/load/functions diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 611fd3815bd..45207fad3ce 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -265,6 +265,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" bsc-testnet-tests: # TODO: BSC RPCs are all in a bad state right now, so we're skipping these tests until they're fixed @@ -337,6 +339,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" optimism-sepolia-smoke-tests: environment: integration @@ -407,6 +411,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" arbitrum-sepolia-smoke-tests: environment: integration @@ -477,6 +483,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" base-sepolia-smoke-tests: environment: integration @@ -543,6 +551,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" polygon-mumbai-smoke-tests: environment: integration @@ -613,6 +623,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" avalanche-fuji-smoke-tests: environment: integration @@ -683,6 +695,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" fantom-testnet-smoke-tests: environment: integration @@ -753,6 +767,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" celo-alfajores-smoke-tests: environment: integration @@ -819,6 +835,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" scroll-sepolia-smoke-tests: # TODO: Disabled until bug TT-767 is fixed @@ -887,6 +905,8 @@ jobs: - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" linea-goerli-smoke-tests: environment: integration @@ -952,4 +972,6 @@ jobs: QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 \ No newline at end of file + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 + with: + test_directory: "./" diff --git a/integration-tests/go.mod b/integration-tests/go.mod index cbcb47e38ec..36ce802621d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa - github.com/smartcontractkit/chainlink-testing-framework v1.23.1 + github.com/smartcontractkit/chainlink-testing-framework v1.23.2 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 8fddde84880..3a7652dc967 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1514,8 +1514,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8b github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= -github.com/smartcontractkit/chainlink-testing-framework v1.23.1 h1:3Ev/twUcnhoe/l3ZreSoR6fx/GKKlIHxdCT3449U2hI= -github.com/smartcontractkit/chainlink-testing-framework v1.23.1/go.mod h1:StIOdxvwd8AMO6xuBtmD6FQfJXktEn/mJJEr7728BTc= +github.com/smartcontractkit/chainlink-testing-framework v1.23.2 h1:haXPd9Pg++Zs5/QIZnhFd9RElmz/d0+4nNeletUg9ZM= +github.com/smartcontractkit/chainlink-testing-framework v1.23.2/go.mod h1:StIOdxvwd8AMO6xuBtmD6FQfJXktEn/mJJEr7728BTc= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= From 1e791acd414ab4c8ea2f36f8ad8cff3ac8605524 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Thu, 25 Jan 2024 08:20:49 -0800 Subject: [PATCH 216/234] Bump version and update CHANGELOG for core v2.9.0 (#11893) --- VERSION | 2 +- docs/CHANGELOG.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 834f2629538..c8e38b61405 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.8.0 +2.9.0 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5941ae8a747..0f3d9a2f053 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] +... + +## 2.9.0 - UNRELEASED + ### Added - `chainlink health` CLI command and HTML `/health` endpoint, to provide human-readable views of the underlying JSON health data. From 18b185f3380d1759ce2dc42efc64d3e28189c342 Mon Sep 17 00:00:00 2001 From: Lei Date: Fri, 26 Jan 2024 08:26:59 -0800 Subject: [PATCH 217/234] move the error to after confirming it is intended to use mercury (#11897) (#11904) (cherry picked from commit 56e24d629d0c6382c2cb5476cfe3a9c1b8d0d182) --- .../evmregistry/v21/mercury/streams/streams.go | 9 ++++----- sonar-project.properties | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index a0dba9c8ac3..48ee0492f9e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -123,11 +123,6 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper block := big.NewInt(int64(checkResult.Trigger.BlockNumber)) upkeepId := checkResult.UpkeepID - if s.mercuryConfig.Credentials() == nil { - lookupLggr.Errorf("at block %d upkeep %s tries to access mercury server but mercury credential is not configured", block, upkeepId) - return - } - // Try to decode the revert error into streams lookup format. User upkeeps can revert with any reason, see if they // tried to call mercury lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) @@ -138,6 +133,10 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper return } streamsLookupResponse := &mercury.StreamsLookup{StreamsLookupError: streamsLookupErr} + if s.mercuryConfig.Credentials() == nil { + lookupLggr.Errorf("at block %d upkeep %s tries to access mercury server but mercury credential is not configured", block, upkeepId) + return + } if len(streamsLookupResponse.Feeds) == 0 { checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput) diff --git a/sonar-project.properties b/sonar-project.properties index 97ed0a4c7ff..6ffaba8b19b 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,7 +5,7 @@ sonar.python.version=3.8 # Full exclusions from the static analysis sonar.exclusions=**/node_modules/**/*,**/mocks/**/*, **/testdata/**/*, **/contracts/typechain/**/*, **/contracts/artifacts/**/*, **/contracts/cache/**/*, **/contracts/scripts/**/*, **/generated/**/*, **/fixtures/**/*, **/docs/**/*, **/tools/**/*, **/*.pb.go, **/*report.xml, **/*.config.ts, **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go, core/services/relay/evm/types/*_gen.go, core/services/relay/evm/types/gen/main.go, core/services/relay/evm/testfiles/*, **/core/web/assets**, core/scripts/chaincli/handler/debug.go # Coverage exclusions -sonar.coverage.exclusions=**/*.test.ts, **/*_test.go, **/contracts/test/**/*, **/contracts/**/tests/**/*, **/core/**/testutils/**/*, **/core/**/mocks/**/*, **/core/**/cltest/**/*, **/integration-tests/**/*, **/generated/**/*, **/core/scripts**/* , **/*.pb.go, ./plugins/**/*, **/main.go, **/0195_add_not_null_to_evm_chain_id_in_job_specs.go +sonar.coverage.exclusions=**/*.test.ts, **/*_test.go, **/contracts/test/**/*, **/contracts/**/tests/**/*, **/core/**/testutils/**/*, **/core/**/mocks/**/*, **/core/**/cltest/**/*, **/integration-tests/**/*, **/generated/**/*, **/core/scripts**/* , **/*.pb.go, ./plugins/**/*, **/main.go, **/0195_add_not_null_to_evm_chain_id_in_job_specs.go, **/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go # Duplication exclusions sonar.cpd.exclusions=**/contracts/**/*.sol, **/config.go, /core/services/ocr2/plugins/ocr2keeper/evm*/* From 76c6bfce118ea932c17a3fc22af43dbc2bfee605 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:57:19 +0100 Subject: [PATCH 218/234] do not call an RPC if it's not Alive (#11999) (cherry picked from commit e78d3b81fa50dc05d3f7b6c2777a9dbc8a00f1ad) --- common/client/multi_node.go | 8 ++++ common/client/multi_node_test.go | 68 +++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 7d55784e68f..30d21ba48a1 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -394,6 +394,10 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // 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]) { @@ -557,6 +561,10 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // main node is used at the end for the return value continue } + + if n.State() != nodeStateAlive { + continue + } // Parallel send to all other nodes with ignored return value // Async - we do not want to block the main thread with secondary nodes // in case they are unreliable/slow. diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 82af7411080..397150890d5 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -3,7 +3,7 @@ package client import ( "errors" "fmt" - big "math/big" + "math/big" "math/rand" "testing" "time" @@ -533,8 +533,10 @@ func TestMultiNode_BatchCallContextAll(t *testing.T) { // setup ok and failed auxiliary nodes okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) okNode.On("RPC").Return(okRPC).Once() + okNode.On("State").Return(nodeStateAlive).Once() failedNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) failedNode.On("RPC").Return(failedRPC).Once() + failedNode.On("State").Return(nodeStateAlive).Once() // setup main node mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) @@ -555,6 +557,34 @@ func TestMultiNode_BatchCallContextAll(t *testing.T) { require.NoError(t, err) tests.RequireLogMessage(t, observedLogs, "Secondary node BatchCallContext failed") }) + t.Run("Skips nodes that are not alive", 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 + okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) + okNode.On("RPC").Return(okRPC).Once() + okNode.On("State").Return(nodeStateAlive).Once() + unhealthyNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + unhealthyNode.On("State").Return(nodeStateUnreachable).Once() + + // 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]{unhealthyNode, mainNode}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{okNode}, + }) + mn.nodeSelector = nodeSelector + + err := mn.BatchCallContextAll(tests.Context(t), nil) + require.NoError(t, err) + }) } func TestMultiNode_SendTransaction(t *testing.T) { @@ -601,9 +631,11 @@ func TestMultiNode_SendTransaction(t *testing.T) { okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) okNode.On("RPC").Return(okRPC).Once() okNode.On("String").Return("okNode") + okNode.On("State").Return(nodeStateAlive).Once() failedNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) failedNode.On("RPC").Return(failedRPC).Once() failedNode.On("String").Return("failedNode") + failedNode.On("State").Return(nodeStateAlive).Once() // setup main node mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) @@ -632,4 +664,38 @@ func TestMultiNode_SendTransaction(t *testing.T) { tests.AssertLogEventually(t, observedLogs, "Sendonly node sent transaction") tests.AssertLogEventually(t, observedLogs, "RPC returned error") }) + t.Run("Skips RPCs that are unhealthy", func(t *testing.T) { + // setup RPCs + okRPC := newMultiNodeRPCClient(t) + okRPC.On("SendTransaction", mock.Anything, mock.Anything).Return(nil).Once() + + // setup ok and failed auxiliary nodes + unhealthySendOnlyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) + unhealthySendOnlyNode.On("State").Return(nodeStateUnreachable).Once() + unhealthyNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + unhealthyNode.On("State").Return(nodeStateUnreachable).Once() + + // 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]{unhealthyNode, mainNode}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{unhealthySendOnlyNode}, + sendOnlyErrorParser: func(err error) SendTxReturnCode { + if err != nil { + return Fatal + } + + return Successful + }, + }) + mn.nodeSelector = nodeSelector + + err := mn.SendTransaction(tests.Context(t), nil) + require.NoError(t, err) + }) } From 1e83bd5224bee29a16cc4c650282cb40e7a0bd8a Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri Date: Thu, 22 Feb 2024 08:53:18 -0800 Subject: [PATCH 219/234] Finalize date on changelog for $VERSION Signed-off-by: Sneha Agnihotri --- docs/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0f3d9a2f053..8b96baa0e9d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,7 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ... -## 2.9.0 - UNRELEASED + + +## 2.9.0 - 2024-02-22 ### Added @@ -40,8 +42,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ServerPubKey = '...' ``` - - ## 2.8.0 - 2024-01-24 ### Added From 9b423aaaf947fc64fa9d859f1efdb3731a655b47 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri Date: Thu, 22 Feb 2024 18:11:15 -0800 Subject: [PATCH 220/234] Bump version for release 2.9.1 Signed-off-by: Sneha Agnihotri --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c8e38b61405..dedcc7d4335 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.9.0 +2.9.1 From c523f9f8cd83d8ec25311baee5dfea13caa2db7c Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 22 Feb 2024 08:24:39 -0600 Subject: [PATCH 221/234] core/chains/evm/client: eth_call: include duplicate legacy field for compatability --- core/chains/evm/client/rpc_client.go | 84 +++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index ce3a67162ed..ac8c44ef901 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" @@ -775,13 +776,17 @@ func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumb lggr.Debug("RPC call: evmclient.Client#CallContract") start := time.Now() + var hex hexutil.Bytes if http != nil { - val, err = http.geth.CallContract(ctx, message, blockNumber) + err = http.rpc.CallContext(ctx, &hex, "eth_call", toCallArg(message), toBlockNumArg(blockNumber)) err = r.wrapHTTP(err) } else { - val, err = ws.geth.CallContract(ctx, message, blockNumber) + err = ws.rpc.CallContext(ctx, &hex, "eth_call", toCallArg(message), toBlockNumArg(blockNumber)) err = r.wrapWS(err) } + if err == nil { + val = hex + } duration := time.Since(start) r.logResult(lggr, err, duration, r.getRPCDomain(), "CallContract", @@ -789,7 +794,82 @@ 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, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("callMsg", msg) + message := msg.(ethereum.CallMsg) + + lggr.Debug("RPC call: evmclient.Client#PendingCallContract") + start := time.Now() + var hex hexutil.Bytes + if http != nil { + err = http.rpc.CallContext(ctx, &hex, "eth_call", toCallArg(message), "pending") + err = r.wrapHTTP(err) + } else { + err = ws.rpc.CallContext(ctx, &hex, "eth_call", toCallArg(message), "pending") + err = r.wrapWS(err) + } + if err == nil { + val = hex + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "PendingCallContract", + "val", val, + ) + + return +} +// COPIED FROM go-ethereum/ethclient/gethclient - must be kept up to date! +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + if number.Sign() >= 0 { + return hexutil.EncodeBig(number) + } + // It's negative. + if number.IsInt64() { + return rpc.BlockNumber(number.Int64()).String() + } + // It's negative and large, which is invalid. + return fmt.Sprintf("", number) +} + +// COPIED FROM go-ethereum/ethclient/gethclient - must be kept up to date! +// Modified to include legacy 'data' as well as 'input' in order to support non-compliant servers. +func toCallArg(msg ethereum.CallMsg) interface{} { + arg := map[string]interface{}{ + "from": msg.From, + "to": msg.To, + } + if len(msg.Data) > 0 { + arg["input"] = hexutil.Bytes(msg.Data) + arg["data"] = hexutil.Bytes(msg.Data) // duplicate legacy field for compatibility + } + if msg.Value != nil { + arg["value"] = (*hexutil.Big)(msg.Value) + } + if msg.Gas != 0 { + arg["gas"] = hexutil.Uint64(msg.Gas) + } + if msg.GasPrice != nil { + arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) + } + if msg.GasFeeCap != nil { + arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) + } + if msg.GasTipCap != nil { + arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) + } + return arg } func (r *rpcClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { From 23daeff9e83d47d987304379591a214974d3eb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedemann=20F=C3=BCrst?= Date: Mon, 26 Feb 2024 13:43:32 +0100 Subject: [PATCH 222/234] core/chains/evm/client/errors.go: wrap log msgs with Sprintf (#12168) --- core/chains/evm/client/errors.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index bb748cb52fb..6488ba495c9 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -424,7 +424,7 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti return commonclient.Fatal } if sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() { - lggr.Debugw("Transaction already confirmed for this nonce: %d", tx.Nonce(), "err", sendError, "etx", tx) + lggr.Debugw(fmt.Sprintf("Transaction already confirmed for this nonce: %d", tx.Nonce()), "err", sendError, "etx", tx) // Nonce too low indicated that a transaction at this nonce was confirmed already. // Mark it as TransactionAlreadyKnown. return commonclient.TransactionAlreadyKnown @@ -472,7 +472,7 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti return commonclient.InsufficientFunds } if sendError.IsTimeout() { - lggr.Errorw("timeout while sending transaction %x", tx.Hash(), "err", sendError, "etx", tx) + lggr.Errorw(fmt.Sprintf("timeout while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) return commonclient.Retryable } if sendError.IsTxFeeExceedsCap() { From 04c5ab83ccecd765ddb6daa9e570bf24294ea95f Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Tue, 27 Feb 2024 05:13:56 -0800 Subject: [PATCH 223/234] Revert "core/chains/evm/client/errors.go: wrap log msgs with Sprintf (#12168)" --- core/chains/evm/client/errors.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 6488ba495c9..bb748cb52fb 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -424,7 +424,7 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti return commonclient.Fatal } if sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() { - lggr.Debugw(fmt.Sprintf("Transaction already confirmed for this nonce: %d", tx.Nonce()), "err", sendError, "etx", tx) + lggr.Debugw("Transaction already confirmed for this nonce: %d", tx.Nonce(), "err", sendError, "etx", tx) // Nonce too low indicated that a transaction at this nonce was confirmed already. // Mark it as TransactionAlreadyKnown. return commonclient.TransactionAlreadyKnown @@ -472,7 +472,7 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti return commonclient.InsufficientFunds } if sendError.IsTimeout() { - lggr.Errorw(fmt.Sprintf("timeout while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) + lggr.Errorw("timeout while sending transaction %x", tx.Hash(), "err", sendError, "etx", tx) return commonclient.Retryable } if sendError.IsTxFeeExceedsCap() { From b9de3beb60fc3e8cec9980d5204d48ce1c4394b9 Mon Sep 17 00:00:00 2001 From: Jim W Date: Wed, 28 Feb 2024 14:30:16 -0500 Subject: [PATCH 224/234] change error log to warn log (#12204) Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- core/services/pg/transaction.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/pg/transaction.go b/core/services/pg/transaction.go index fd7e74baca3..d60270b4fe8 100644 --- a/core/services/pg/transaction.go +++ b/core/services/pg/transaction.go @@ -78,7 +78,7 @@ func sqlxTransactionQ(ctx context.Context, db txBeginner, lggr logger.Logger, fn panic(fmt.Sprintf("panic in transaction; aborting rollback that took longer than 10s: %s", p)) } } else if err != nil { - lggr.Errorf("Error in transaction, rolling back: %s", err) + lggr.Warnf("Error in transaction, rolling back: %s", err) // An error occurred, rollback and return error if rerr := tx.Rollback(); rerr != nil { err = multierr.Combine(err, errors.WithStack(rerr)) From 5ba08b1be1143f7dc184e811fe03437dff742fb7 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 29 Feb 2024 13:23:52 -0700 Subject: [PATCH 225/234] update changelog for eth_call input/data fields --- docs/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8b96baa0e9d..c7dae48060b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## 2.9.1 + +### Changed + +- `eth_call` RPC requests are now sent with both `input` and `data` fields to increase compability with servers that recognize only one. + ## 2.9.0 - 2024-02-22 ### Added From ed480f4165725e29b09ce97c871459065f8e5b27 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Mon, 4 Mar 2024 07:52:44 -0800 Subject: [PATCH 226/234] Revert "Revert "core/chains/evm/client/errors.go: wrap log msgs with Sprintf (#12168)"" --- core/chains/evm/client/errors.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index bb748cb52fb..6488ba495c9 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -424,7 +424,7 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti return commonclient.Fatal } if sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() { - lggr.Debugw("Transaction already confirmed for this nonce: %d", tx.Nonce(), "err", sendError, "etx", tx) + lggr.Debugw(fmt.Sprintf("Transaction already confirmed for this nonce: %d", tx.Nonce()), "err", sendError, "etx", tx) // Nonce too low indicated that a transaction at this nonce was confirmed already. // Mark it as TransactionAlreadyKnown. return commonclient.TransactionAlreadyKnown @@ -472,7 +472,7 @@ func ClassifySendError(err error, lggr logger.SugaredLogger, tx *types.Transacti return commonclient.InsufficientFunds } if sendError.IsTimeout() { - lggr.Errorw("timeout while sending transaction %x", tx.Hash(), "err", sendError, "etx", tx) + lggr.Errorw(fmt.Sprintf("timeout while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) return commonclient.Retryable } if sendError.IsTxFeeExceedsCap() { From 3dbca37f5135088138094e29fdd63cce0e5efd0b Mon Sep 17 00:00:00 2001 From: Simson Date: Tue, 5 Mar 2024 18:33:10 +0530 Subject: [PATCH 227/234] Include 0x3 type transactions in gas estimatrion (#12286) * Cancun Hardfork: support for new Transaction type (#12063) * testing block unparsing * Pdded arsing tests for 0x3 * Adding type 0x3 to gas estimator * Sonar cube code quality fix --------- Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> * Updated changelog for Blob transaction type inclusion --------- Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- .../chains/evm/gas/block_history_estimator.go | 60 ++-- .../evm/gas/block_history_estimator_test.go | 35 +- core/chains/evm/types/models_test.go | 314 ++++++++++++++++++ docs/CHANGELOG.md | 1 + 4 files changed, 379 insertions(+), 31 deletions(-) diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index dc95240fd42..b94083a1ae7 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -858,42 +858,46 @@ func (b *BlockHistoryEstimator) EffectiveGasPrice(block evmtypes.Block, tx evmty switch tx.Type { case 0x0, 0x1: return tx.GasPrice - case 0x2: - if block.BaseFeePerGas == nil || tx.MaxPriorityFeePerGas == nil || tx.MaxFeePerGas == nil { - b.logger.Warnw("Got transaction type 0x2 but one of the required EIP1559 fields was missing, falling back to gasPrice", "block", block, "tx", tx) - return tx.GasPrice - } - if tx.GasPrice != nil { - // Always use the gas price if provided - return tx.GasPrice - } - if tx.MaxFeePerGas.Cmp(block.BaseFeePerGas) < 0 { - b.logger.AssumptionViolationw("MaxFeePerGas >= BaseFeePerGas", "block", block, "tx", tx) - return nil - } - if tx.MaxFeePerGas.Cmp(tx.MaxPriorityFeePerGas) < 0 { - b.logger.AssumptionViolationw("MaxFeePerGas >= MaxPriorityFeePerGas", "block", block, "tx", tx) - return nil - } - - // From: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md - priorityFeePerGas := tx.MaxPriorityFeePerGas - maxFeeMinusBaseFee := tx.MaxFeePerGas.Sub(block.BaseFeePerGas) - if maxFeeMinusBaseFee.Cmp(priorityFeePerGas) < 0 { - priorityFeePerGas = maxFeeMinusBaseFee - } - - effectiveGasPrice := priorityFeePerGas.Add(block.BaseFeePerGas) - return effectiveGasPrice + case 0x2, 0x3: + return b.getEffectiveGasPrice(block, tx) default: b.logger.Warnw(fmt.Sprintf("Ignoring unknown transaction type %v", tx.Type), "block", block, "tx", tx) return nil } } +func (b *BlockHistoryEstimator) getEffectiveGasPrice(block evmtypes.Block, tx evmtypes.Transaction) *assets.Wei { + if block.BaseFeePerGas == nil || tx.MaxPriorityFeePerGas == nil || tx.MaxFeePerGas == nil { + b.logger.Warnw("Got transaction type 0x2 but one of the required EIP1559 fields was missing, falling back to gasPrice", "block", block, "tx", tx) + return tx.GasPrice + } + if tx.GasPrice != nil { + // Always use the gas price if provided + return tx.GasPrice + } + if tx.MaxFeePerGas.Cmp(block.BaseFeePerGas) < 0 { + b.logger.AssumptionViolationw("MaxFeePerGas >= BaseFeePerGas", "block", block, "tx", tx) + return nil + } + if tx.MaxFeePerGas.Cmp(tx.MaxPriorityFeePerGas) < 0 { + b.logger.AssumptionViolationw("MaxFeePerGas >= MaxPriorityFeePerGas", "block", block, "tx", tx) + return nil + } + + // From: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md + priorityFeePerGas := tx.MaxPriorityFeePerGas + maxFeeMinusBaseFee := tx.MaxFeePerGas.Sub(block.BaseFeePerGas) + if maxFeeMinusBaseFee.Cmp(priorityFeePerGas) < 0 { + priorityFeePerGas = maxFeeMinusBaseFee + } + + effectiveGasPrice := priorityFeePerGas.Add(block.BaseFeePerGas) + return effectiveGasPrice +} + func (b *BlockHistoryEstimator) EffectiveTipCap(block evmtypes.Block, tx evmtypes.Transaction) *assets.Wei { switch tx.Type { - case 0x2: + case 0x2, 0x3: return tx.MaxPriorityFeePerGas case 0x0, 0x1: if tx.GasPrice == nil { diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index eae6fbc2ad3..dfb4adf11ef 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -1396,7 +1396,7 @@ func TestBlockHistoryEstimator_EffectiveTipCap(t *testing.T) { res := bhe.EffectiveTipCap(eipblock, tx) assert.Equal(t, "42 wei", res.String()) }) - t.Run("tx type 2 should calculate gas price", func(t *testing.T) { + t.Run("tx type 2 & type 3 should calculate gas price", func(t *testing.T) { // 0x2 transaction (should use MaxPriorityFeePerGas) tx := evmtypes.Transaction{Type: 0x2, MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} res := bhe.EffectiveTipCap(eipblock, tx) @@ -1405,6 +1405,15 @@ func TestBlockHistoryEstimator_EffectiveTipCap(t *testing.T) { tx = evmtypes.Transaction{Type: 0x2, GasPrice: assets.NewWeiI(400), MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(350), GasLimit: 42, Hash: utils.NewHash()} res = bhe.EffectiveTipCap(eipblock, tx) assert.Equal(t, "200 wei", res.String()) + + // 0x3 transaction (should use MaxPriorityFeePerGas) + tx = evmtypes.Transaction{Type: 0x3, MaxPriorityFeePerGas: assets.NewWeiI(100), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} + res = bhe.EffectiveTipCap(eipblock, tx) + assert.Equal(t, "100 wei", res.String()) + // 0x3 transaction (should use MaxPriorityFeePerGas, ignoring gas price) + tx = evmtypes.Transaction{Type: 0x3, GasPrice: assets.NewWeiI(400), MaxPriorityFeePerGas: assets.NewWeiI(100), MaxFeePerGas: assets.NewWeiI(350), GasLimit: 42, Hash: utils.NewHash()} + res = bhe.EffectiveTipCap(eipblock, tx) + assert.Equal(t, "100 wei", res.String()) }) t.Run("missing field returns nil", func(t *testing.T) { tx := evmtypes.Transaction{Type: 0x2, GasPrice: assets.NewWeiI(132), MaxFeePerGas: assets.NewWeiI(200), GasLimit: 42, Hash: utils.NewHash()} @@ -1412,7 +1421,7 @@ func TestBlockHistoryEstimator_EffectiveTipCap(t *testing.T) { assert.Nil(t, res) }) t.Run("unknown type returns nil", func(t *testing.T) { - tx := evmtypes.Transaction{Type: 0x3, GasPrice: assets.NewWeiI(55555), MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} + tx := evmtypes.Transaction{Type: 0x4, GasPrice: assets.NewWeiI(55555), MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} res := bhe.EffectiveTipCap(eipblock, tx) assert.Nil(t, res) }) @@ -1466,6 +1475,26 @@ func TestBlockHistoryEstimator_EffectiveGasPrice(t *testing.T) { res = bhe.EffectiveGasPrice(eipblock, tx) assert.Equal(t, "32 wei", res.String()) }) + + t.Run("tx type 3 should calculate gas price", func(t *testing.T) { + // 0x3 transaction (should calculate to 250) + tx := evmtypes.Transaction{Type: 0x3, MaxPriorityFeePerGas: assets.NewWeiI(100), MaxFeePerGas: assets.NewWeiI(110), GasLimit: 42, Hash: utils.NewHash()} + res := bhe.EffectiveGasPrice(eipblock, tx) + assert.Equal(t, "110 wei", res.String()) + // 0x3 transaction (should calculate to 300) + tx = evmtypes.Transaction{Type: 0x3, MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(350), GasLimit: 42, Hash: utils.NewHash()} + res = bhe.EffectiveGasPrice(eipblock, tx) + assert.Equal(t, "300 wei", res.String()) + // 0x3 transaction (should calculate to 300, ignoring gas price) + tx = evmtypes.Transaction{Type: 0x3, MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(350), GasLimit: 42, Hash: utils.NewHash()} + res = bhe.EffectiveGasPrice(eipblock, tx) + assert.Equal(t, "300 wei", res.String()) + // 0x3 transaction (should fall back to gas price since MaxFeePerGas is missing) + tx = evmtypes.Transaction{Type: 0x3, GasPrice: assets.NewWeiI(5), MaxPriorityFeePerGas: assets.NewWeiI(200), GasLimit: 42, Hash: utils.NewHash()} + res = bhe.EffectiveGasPrice(eipblock, tx) + assert.Equal(t, "5 wei", res.String()) + }) + t.Run("tx type 2 has block missing base fee (should never happen but must handle gracefully)", func(t *testing.T) { // 0x2 transaction (should calculate to 250) tx := evmtypes.Transaction{Type: 0x2, GasPrice: assets.NewWeiI(55555), MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} @@ -1473,7 +1502,7 @@ func TestBlockHistoryEstimator_EffectiveGasPrice(t *testing.T) { assert.Equal(t, "55.555 kwei", res.String()) }) t.Run("unknown type returns nil", func(t *testing.T) { - tx := evmtypes.Transaction{Type: 0x3, GasPrice: assets.NewWeiI(55555), MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} + tx := evmtypes.Transaction{Type: 0x4, GasPrice: assets.NewWeiI(55555), MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasLimit: 42, Hash: utils.NewHash()} res := bhe.EffectiveGasPrice(block, tx) assert.Nil(t, res) }) diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index a32deba697d..7617b45f9a0 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -647,6 +647,265 @@ const paritySampleBlock = ` ` +const eip4844Block = ` { + "baseFeePerGas": "0x16740b3cb5", + "blobGasUsed": "0xc0000", + "difficulty": "0x0", + "excessBlobGas": "0x4b80000", + "extraData": "0xd883010d0c846765746888676f312e32302e31856c696e7578", + "gasLimit": "0x1c9c380", + "gasUsed": "0x18db0e0", + "hash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "logsBloom": "0xc820da8f9c4ac1db241964223d4015549765668065d0c0418691262e44eda11970c271ead2b03ecffbb2ad52348c00cb0e11c083c3e26323855296236025e1d4198c892bb5cf0135cacc4f1bc138c88d8d44790497c6c201901a894b48966b2068f144bb16614271e6c05a854421290500081cea818eaae7950a8217a0d8d43a8b80018d800a43bd542d77230e5030861051152088a0d514d65177c2fa4198642eefd29e4dae806bfd4250ec63147fee38e368d00c01da1d67c1a5a24ed87331289bba074e1c2434147502a90df300a311b67056524e78a5b792930272b4607fb676060ea623d64ac1174a22b382a0648f5a35994b669131e3ea074ea128de45", + "miner": "0x6a7aa9b882d50bb7bc5da1a244719c99f12f06a3", + "mixHash": "0x27337177ae9744acb30c9d13e187c582367d75f702cc3ef2e77f9c6b10394ebf", + "nonce": "0x0000000000000000", + "number": "0x50e1d6", + "parentBeaconBlockRoot": "0xcdfe5d7bfd13221a86881bee926ec7e7e6c1eed2e460bd7772caa64bed1582ec", + "parentHash": "0x077c1d68b52f8203cb90a71759a09b11c2a6577f97ea1fd4a8686a387fbedac8", + "receiptsRoot": "0xff81f4fddbcfcc550c4358136953b56a66a95c24089501e2085d8c4588ffdb1f", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x2b41f", + "stateRoot": "0xc9541af90707127dde506b208de22cfda50ad2ef6e8b4f67212e6402d7099708", + "timestamp": "0x65cf57dc", + "totalDifficulty": "0x3c656d23029ab0", + "transactions": [ + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x3c352ea32dfbb757ccdf4b457e52daf6ecc21917", + "gas": "0x14820", + "gasPrice": "0x5d426cae68", + "hash": "0x8857a9064210e138a996c82a9d58b001d198b2362e8ad2ede7056a9c942011aa", + "input": "0x", + "nonce": "0xa07aa", + "to": "0xd3234d7985fdaefc68cbfa04e91ecca334b6545c", + "transactionIndex": "0x0", + "value": "0x6f05b59d3b20000", + "type": "0x0", + "chainId": "0xaa36a7", + "v": "0x1546d72", + "r": "0x2acc4f73342578b09d5152f60305f7cfe4f5d377da504e99ed3795b65b9bd5c", + "s": "0x33470e89e7508816c8c71d2135fc16a49b34ffbd86d8f6506658819afb578944" + }, + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x87c9b02a10ec2cb4dcb3b2e573e26169cf3cd9bf", + "gas": "0x14820", + "gasPrice": "0x5d426cae68", + "hash": "0x83170edd8c637791fc8bdf07ad687ab11456409546fea23d8db90933d18eb566", + "input": "0x", + "nonce": "0xa2e22", + "to": "0x2853502c56fb2160fcdc044d4477408b9670058e", + "transactionIndex": "0x1", + "value": "0x6f05b59d3b20000", + "type": "0x0", + "chainId": "0xaa36a7", + "v": "0x1546d72", + "r": "0xf5f12880149214ef1109c83bd9e80a6851a581aba2cf10c1c3d0e8ad473db9f5", + "s": "0x150c309b585f0b76fdb6ee4e26048fae88464e6ef2ff41a9341e84ddbbf4f7a7" + }, + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x0bf8d3a5bc2f3ce4ce93d0ef13c2d519d2efe7ab", + "gas": "0x32aa5", + "gasPrice": "0x17e3776320", + "hash": "0xe9a8dc19ca3ace8b06fdb120fbb1333f75a14e06229536adbdae1ca018f39d2f", + "input": "0x79df76820000000000000000000000000bf8d3a5bc2f3ce4ce93d0ef13c2d519d2efe7ab000000000000000000000000000000000000000000000000000000000002c3380000000000000000000000000000000000000000000000000000018db1eefe820000000000000000000000000000000000000000000000000000000191ea03c00000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001cbeaa1a18797feaaae677a2c47f551623362b25a4cbed88a14cf06318974302be1e3a13405cc803063bb4549a79efa547f2024d470b53d21631f42e3ce993bdd7", + "nonce": "0x3", + "to": "0x321ce961084fcf3a56de4be2f2006707a0421aa4", + "transactionIndex": "0x4a", + "value": "0x0", + "type": "0x0", + "chainId": "0xaa36a7", + "v": "0x1546d72", + "r": "0x53fa23875340ac5bd7af3e5b4791981cad94f9a86da431bdafdaf0d4fccbfab9", + "s": "0x1f3826a1d0358425f2bddacd9d87efeee07065492b7ccc499357d69437856859" + }, + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x1803c760451dc8da8935c7b7e47e1c60910e6986", + "gas": "0x5208", + "gasPrice": "0x17d9abf8b5", + "maxFeePerGas": "0x8bb2c97000", + "maxPriorityFeePerGas": "0x165a0bc00", + "maxFeePerBlobGas": "0xdf8475800", + "hash": "0x48a6a2294bd57cd0defc54968bf00179ef3c2d0c647d386e1840ee347d2dd0b8", + "input": "0x", + "nonce": "0x2861", + "to": "0x4f56ffc63c28b72f79b02e91f11a4707bac4043c", + "transactionIndex": "0x4b", + "value": "0x0", + "type": "0x3", + "accessList": [], + "chainId": "0xaa36a7", + "blobVersionedHashes": [ + "0x01b3722197a3bb82f25a26ab702b63e7c5ce7296ec18f7a53d5e2c28081084a3", + "0x0125c472df6afa04cbab756630f5bcf560fc929ee12c7dd4738cfd67fa7926d8", + "0x016316f61a259aa607096440fc3eeb90356e079be01975d2fb18347bd50df33c" + ], + "v": "0x0", + "r": "0xf9cfd7e18a156807a42fc05d2255a9741168363a7184b4fa40a9dc9f83dcc3d4", + "s": "0x9b12570519ef421bd31461bed09b3599db43354d3c88ff4bed17ddd964fadf6", + "yParity": "0x0" + }, + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x56272f6932e6ae0116d270a2e919d20d3052f206", + "gas": "0x5208", + "gasPrice": "0x17d9abf8b5", + "maxFeePerGas": "0x8bb2c97000", + "maxPriorityFeePerGas": "0x165a0bc00", + "maxFeePerBlobGas": "0xdf8475800", + "hash": "0xa97e14a2e87d322fcb97edc4b25cd976d18963cfad19bfd4b9c8066a6a2d97cf", + "input": "0x", + "nonce": "0x2836", + "to": "0x1803c760451dc8da8935c7b7e47e1c60910e6986", + "transactionIndex": "0x4c", + "value": "0x0", + "type": "0x3", + "accessList": [], + "chainId": "0xaa36a7", + "blobVersionedHashes": [ + "0x01adde6e37a3ba9e2a40052556b51960c2a4cc69c257992ae954c6d4f8d479c2", + "0x0142116f5d1b4b60672ac5b69edb7431ffddb700227d9b4dc9adafe599b38943", + "0x010ba493f0c9891f7109bd29e4914c4e79c7a35530981e746d02df66e5d57056" + ], + "v": "0x1", + "r": "0x82045878948921a0330ea7eb23b74f103f9f353704eaeab358e777bea238bfd", + "s": "0x378128f0fa8059df16cdc95f215f1e669dc81cee7c39b804e6371e486aa1e753", + "yParity": "0x1" + }, + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x779fa3507ad952f447c32f7d7e1f86ceddf2cc9d", + "gas": "0x1118b", + "gasPrice": "0x2e90edd000", + "maxFeePerGas": "0x2e90edd000", + "maxPriorityFeePerGas": "0x2e90edd000", + "hash": "0x6e94385ca341d5763ebb21601a86cddbc77acb879735ce1d013b22dc03fb07a2", + "input": "0x095ea7b3000000000000000000000000d3ec28ad6d777f5aa92377294b9b6522c719307900000000000000000000000000000000000000000000003635c9adc5dea00000", + "nonce": "0x57", + "to": "0xf7f928b2bf5cc7e0bd0f258165249fa3c2ef85ac", + "transactionIndex": "0x10", + "value": "0x0", + "type": "0x2", + "accessList": [], + "chainId": "0xaa36a7", + "v": "0x0", + "r": "0x4b4f0ccda6366f4897cb8eb3dee4a72bba76758159ff7030a134022c0a11def2", + "s": "0x27621a604938c68b98c52b57e2b27cb104e3838fd2864d18b6429d42ac2b2f0a", + "yParity": "0x0" + } + ], + "transactionsRoot": "0xb9773c32f06f914e3e6da66a114e3ddf84e38dff62e26621b0ffecee38d5fe6e", + "uncles": [], + "withdrawals": [ + { + "index": "0x233d1c2", + "validatorIndex": "0x384", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c3", + "validatorIndex": "0x392", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c4", + "validatorIndex": "0x393", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c5", + "validatorIndex": "0x395", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c6", + "validatorIndex": "0x396", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c7", + "validatorIndex": "0x398", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c8", + "validatorIndex": "0x39a", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1c9", + "validatorIndex": "0x39c", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1ca", + "validatorIndex": "0x39d", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xec0" + }, + { + "index": "0x233d1cb", + "validatorIndex": "0x39e", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + }, + { + "index": "0x233d1cc", + "validatorIndex": "0x39f", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + }, + { + "index": "0x233d1cd", + "validatorIndex": "0x3a3", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + }, + { + "index": "0x233d1ce", + "validatorIndex": "0x3a4", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + }, + { + "index": "0x233d1cf", + "validatorIndex": "0x3a5", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + }, + { + "index": "0x233d1d0", + "validatorIndex": "0x3ad", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + }, + { + "index": "0x233d1d1", + "validatorIndex": "0x3ae", + "address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e", + "amount": "0xb10" + } + ], + "withdrawalsRoot": "0x26a577aa6ab5e73684b15665f8510c7da8f29a66ed17bf73c1c11f4c3af9c955" +}` + func TestBlock_UnmarshalJSON(t *testing.T) { t.Run("unmarshals parity block", func(t *testing.T) { b := new(evmtypes.Block) @@ -679,6 +938,17 @@ func TestBlock_UnmarshalJSON(t *testing.T) { assert.Equal(t, errors.Cause(err), evmtypes.ErrMissingBlock) assert.True(t, errors.Is(err, evmtypes.ErrMissingBlock)) }) + t.Run("unmarshals EIP-4844 block", func(t *testing.T) { + b := new(evmtypes.Block) + err := b.UnmarshalJSON([]byte(eip4844Block)) + assert.NoError(t, err) + assert.Equal(t, int64(5300694), b.Number) + assert.Equal(t, "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", b.Hash.Hex()) + assert.Equal(t, "0x077c1d68b52f8203cb90a71759a09b11c2a6577f97ea1fd4a8686a387fbedac8", b.ParentHash.Hex()) + assert.Equal(t, assets.NewWeiI(96436174005), b.BaseFeePerGas) + assert.Equal(t, int64(1708087260), b.Timestamp.Unix()) + assert.Len(t, b.Transactions, 6) + }) } func TestTransaction_UnmarshalJSON(t *testing.T) { @@ -728,6 +998,50 @@ func TestTransaction_UnmarshalJSON(t *testing.T) { Hash: common.HexToHash("0x754f49f0a2ca7680806d261dd36ee95ac88a81da59fef0b5d8d691478f075d46"), }, }, + { + name: "sample EIP4844 txn", + args: args{ + []byte( + ` + { + "blockHash": "0x3edd900025edab70dde26a52377c3d0a9474c3f540bd0131d58f508711272590", + "blockNumber": "0x50e1d6", + "from": "0x56272f6932e6ae0116d270a2e919d20d3052f206", + "gas": "0x5208", + "gasPrice": "0x17d9abf8b5", + "maxFeePerGas": "0x8bb2c97000", + "maxPriorityFeePerGas": "0x165a0bc00", + "maxFeePerBlobGas": "0xdf8475800", + "hash": "0xa97e14a2e87d322fcb97edc4b25cd976d18963cfad19bfd4b9c8066a6a2d97cf", + "input": "0x", + "nonce": "0x2836", + "to": "0x1803c760451dc8da8935c7b7e47e1c60910e6986", + "transactionIndex": "0x4c", + "value": "0x0", + "type": "0x3", + "accessList": [], + "chainId": "0xaa36a7", + "blobVersionedHashes": [ + "0x01adde6e37a3ba9e2a40052556b51960c2a4cc69c257992ae954c6d4f8d479c2", + "0x0142116f5d1b4b60672ac5b69edb7431ffddb700227d9b4dc9adafe599b38943", + "0x010ba493f0c9891f7109bd29e4914c4e79c7a35530981e746d02df66e5d57056" + ], + "v": "0x1", + "r": "0x82045878948921a0330ea7eb23b74f103f9f353704eaeab358e777bea238bfd", + "s": "0x378128f0fa8059df16cdc95f215f1e669dc81cee7c39b804e6371e486aa1e753", + "yParity": "0x1" + }`, + ), + }, + want: &evmtypes.Transaction{ + GasPrice: assets.NewWei(mustHexToBig(t, "17d9abf8b5")), + GasLimit: mustHextoUint32(t, "0x5208"), + MaxFeePerGas: assets.NewWei(mustHexToBig(t, "8bb2c97000")), + MaxPriorityFeePerGas: assets.NewWei(mustHexToBig(t, "165a0bc00")), + Type: 0x3, + Hash: common.HexToHash("0xa97e14a2e87d322fcb97edc4b25cd976d18963cfad19bfd4b9c8066a6a2d97cf"), + }, + }, { name: "sample parity txn", args: args{[]byte( diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c7dae48060b..7650a4ec4e9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `eth_call` RPC requests are now sent with both `input` and `data` fields to increase compability with servers that recognize only one. +- GasEstimator will now include Type `0x3` (Blob) transactions in the gas calculations to estimate it more accurately. ## 2.9.0 - 2024-02-22 From 7c7d049c6922d5c848d932972d0342cd2f419e10 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri Date: Thu, 7 Mar 2024 09:15:47 -0800 Subject: [PATCH 228/234] Finalize date on changelog for 2.9.1 --- docs/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7650a4ec4e9..e71aac01f45 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,11 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 -## 2.9.1 +## 2.9.1 - 2024-03-07 ### Changed -- `eth_call` RPC requests are now sent with both `input` and `data` fields to increase compability with servers that recognize only one. +- `eth_call` RPC requests are now sent with both `input` and `data` fields to increase compatibility with servers that recognize only one. - GasEstimator will now include Type `0x3` (Blob) transactions in the gas calculations to estimate it more accurately. ## 2.9.0 - 2024-02-22 From 41aa34b51e380ca266909a9690f2c6169cd8fbca Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 22 Mar 2024 15:27:42 +0000 Subject: [PATCH 229/234] Automation fixes. Override core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams to release/2.9.1 branch --- .../v21/mercury/streams/streams.go | 59 +------------------ .../v21/mercury/streams/streams_test.go | 12 +++- 2 files changed, 14 insertions(+), 57 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index db04a066433..48ee0492f9e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -148,7 +148,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper if streamsLookupResponse.IsMercuryV02() { // check permission on the registry for mercury v0.2 opts := s.buildCallOpts(ctx, block) - if state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId.BigInt()); err != nil { + if state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId.BigInt()); err != nil { lookupLggr.Warnf("at block %s upkeep %s failed to query mercury allow list: %s", block, upkeepId, err) checkResults[i].PipelineExecutionState = uint8(state) checkResults[i].IneligibilityReason = uint8(reason) @@ -257,42 +257,10 @@ func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsL for j, v := range values { s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) } - - state, retryable, mercuryBytes, err := s.checkCallback(ctx, values, lookup) - if err != nil { - s.lggr.Errorf("at block %d upkeep %s checkCallback err: %s", lookup.Block, lookup.UpkeepId, err.Error()) - checkResults[i].Retryable = retryable - checkResults[i].PipelineExecutionState = uint8(state) - return - } - s.lggr.Infof("at block %d upkeep %s requested time %s checkCallback mercuryBytes: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(mercuryBytes)) - - unpackCallBackState, needed, performData, failureReason, _, err := s.packer.UnpackCheckCallbackResult(mercuryBytes) - if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s UnpackCheckCallbackResult err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) - checkResults[i].PipelineExecutionState = unpackCallBackState - return - } - - if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) - s.lggr.Debugf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) - return - } - - if !needed { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) - s.lggr.Debugf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) - return - } - - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) - checkResults[i].Eligible = true - checkResults[i].PerformData = performData - s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) + return values, nil } -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if +// AllowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if // this upkeep is allowed to use Mercury service. func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state encoding.PipelineExecutionState, reason encoding.UpkeepFailureReason, retryable bool, allow bool, err error) { allowed, ok := s.mercuryConfig.IsUpkeepAllowed(upkeepId.String()) @@ -314,7 +282,6 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s "data": hexutil.Bytes(payload), } - // call checkCallback function at the block which OCR3 has agreed upon if err = s.client.CallContext(opts.Context, &resultBytes, "eth_call", args, hexutil.EncodeBig(opts.BlockNumber)); err != nil { return encoding.RpcFlakyFailure, encoding.UpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } @@ -341,26 +308,6 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s return encoding.NoPipelineError, encoding.UpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil } -func (s *streams) checkCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup) (mercury.MercuryUpkeepState, bool, hexutil.Bytes, error) { - payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) - if err != nil { - return mercury.PackUnpackDecodeFailed, false, nil, err - } - - var b hexutil.Bytes - args := map[string]interface{}{ - "to": s.registry.Address().Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - if err := s.client.CallContext(ctx, &b, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { - return mercury.RpcFlakyFailure, true, nil, err - } - - return mercury.NoPipelineError, false, b, nil -} - func (s *streams) buildCallOpts(ctx context.Context, block *big.Int) *bind.CallOpts { opts := bind.CallOpts{ Context: ctx, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 51dd70bbe54..c7bff2eac7a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -126,6 +126,7 @@ func TestStreams_CheckCallback(t *testing.T) { tests := []struct { name string lookup *mercury.StreamsLookup + input []ocr2keepers.CheckResult values [][]byte statusCode int @@ -153,6 +154,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -185,6 +189,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -216,6 +223,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{}, @@ -434,7 +444,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { BlockNumber: big.NewInt(10), } - state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId) + state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId) assert.Equal(t, tt.err, err) assert.Equal(t, tt.allowed, allowed) assert.Equal(t, tt.state, state) From 2c3ae641ec35ed6b8edb95ff19fe7ef1b6514d61 Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 22 Mar 2024 15:42:25 +0000 Subject: [PATCH 230/234] Fix core/scripts/chaincli to 2.9.1 version --- core/scripts/chaincli/handler/debug.go | 53 +- .../handler/mercury_lookup_handler.go | 534 ------------------ core/scripts/go.mod | 1 + 3 files changed, 29 insertions(+), 559 deletions(-) delete mode 100644 core/scripts/chaincli/handler/mercury_lookup_handler.go diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index cfd260e16a9..8b06937fc2c 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -35,6 +35,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) @@ -42,12 +43,7 @@ import ( const ( ConditionTrigger uint8 = iota LogTrigger - - blockNumber = "blockNumber" expectedTypeAndVersion = "KeeperRegistry 2.1.0" - feedIdHex = "feedIdHex" - feedIDs = "feedIDs" - timestamp = "timestamp" ) var mercuryPacker = mercury.NewAbiPacker() @@ -139,6 +135,8 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var checkResult iregistry21.CheckUpkeep var blockNum uint64 var performData []byte + var workID [32]byte + var trigger ocr2keepers.Trigger upkeepNeeded := false // check upkeep if triggerType == ConditionTrigger { @@ -213,7 +211,8 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } // check that tx for this upkeep / tx was not already performed message(fmt.Sprintf("LogTrigger{blockNum: %d, blockHash: %s, txHash: %s, logIndex: %d}", blockNum, receipt.BlockHash.Hex(), txHash, logIndex)) - workID := mustUpkeepWorkID(upkeepID, blockNum, receipt.BlockHash, txHash, logIndex) + trigger = mustAutomationTrigger(txHash, logIndex, blockNum, receipt.BlockHash) + workID = mustUpkeepWorkID(upkeepID, trigger) message(fmt.Sprintf("workID computed: %s", hex.EncodeToString(workID[:]))) var hasKey bool hasKey, err = keeperRegistry21.HasDedupKey(latestCallOpts, workID) @@ -309,15 +308,12 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if !allowed { resolveIneligible("upkeep reverted with StreamsLookup but is not allowed to access streams") } - } else if streamsLookupErr.FeedParamKey != feedIDs || streamsLookupErr.TimeParamKey != timestamp { + } else if streamsLookup.IsMercuryV03() { // handle v0.3 message("using data streams lookup v0.3") } else { resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") - } else { - message("using mercury lookup v0.3") } - streamsLookup := &StreamsLookup{streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time, streamsLookupErr.ExtraData, upkeepID, blockNum} if k.cfg.DataStreamsLegacyURL == "" || k.cfg.DataStreamsURL == "" || k.cfg.DataStreamsID == "" || k.cfg.DataStreamsKey == "" { failCheckConfig("Data streams configs not set properly, check your DATA_STREAMS_LEGACY_URL, DATA_STREAMS_URL, DATA_STREAMS_ID and DATA_STREAMS_KEY", nil) @@ -490,9 +486,27 @@ func packTriggerData(log *types.Log, blockTime uint64) ([]byte, error) { return b, nil } -func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, txHash [32]byte, logIndex int64) [32]byte { - // TODO - this is a copy of the code in core.UpkeepWorkID - // We should refactor that code to be more easily exported ex not rely on Trigger structs +func mustUpkeepWorkID(upkeepID *big.Int, trigger ocr2keepers.Trigger) [32]byte { + upkeepIdentifier := mustUpkeepIdentifier(upkeepID) + + workID := core.UpkeepWorkID(upkeepIdentifier, trigger) + workIDBytes, err := hex.DecodeString(workID) + if err != nil { + failUnknown("failed to decode workID", err) + } + + var result [32]byte + copy(result[:], workIDBytes[:]) + return result +} + +func mustUpkeepIdentifier(upkeepID *big.Int) ocr2keepers.UpkeepIdentifier { + upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} + upkeepIdentifier.FromBigInt(upkeepID) + return *upkeepIdentifier +} + +func mustAutomationTrigger(txHash [32]byte, logIndex int64, blockNum uint64, blockHash [32]byte) ocr2keepers.Trigger { trigger := ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ TxHash: txHash, @@ -501,16 +515,7 @@ func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, tx BlockHash: blockHash, }, } - upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} - upkeepIdentifier.FromBigInt(upkeepID) - workID := core.UpkeepWorkID(*upkeepIdentifier, trigger) - workIDBytes, err := hex.DecodeString(workID) - if err != nil { - failUnknown("failed to decode workID", err) - } - var result [32]byte - copy(result[:], workIDBytes[:]) - return result + return trigger } func message(msg string) { @@ -628,5 +633,3 @@ func tenderlySimLink(ctx context.Context, cfg *config.Config, chainID int64, blo } return common.TenderlySimLink(responseJSON.Simulation.Id) } - -// TODO - link to performUpkeep tx if exists diff --git a/core/scripts/chaincli/handler/mercury_lookup_handler.go b/core/scripts/chaincli/handler/mercury_lookup_handler.go deleted file mode 100644 index 1bd4b2e183c..00000000000 --- a/core/scripts/chaincli/handler/mercury_lookup_handler.go +++ /dev/null @@ -1,534 +0,0 @@ -package handler - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/avast/retry-go" - ethabi "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" -) - -// MercuryLookupHandler is responsible for initiating the calls to the Mercury server -// to determine whether the upkeeps are eligible -type MercuryLookupHandler struct { - credentials *MercuryCredentials - httpClient HttpClient - rpcClient *rpc.Client -} - -func NewMercuryLookupHandler( - credentials *MercuryCredentials, - rpcClient *rpc.Client, -) *MercuryLookupHandler { - return &MercuryLookupHandler{ - credentials: credentials, - httpClient: http.DefaultClient, - rpcClient: rpcClient, - } -} - -type MercuryVersion string - -type StreamsLookup struct { - feedParamKey string - feeds []string - timeParamKey string - time *big.Int - extraData []byte - upkeepId *big.Int - block uint64 -} - -//go:generate mockery --quiet --name HttpClient --output ./mocks/ --case=underscore -type HttpClient interface { - Do(req *http.Request) (*http.Response, error) -} - -type MercuryCredentials struct { - LegacyURL string - URL string - ClientID string - ClientKey string -} - -func (mc *MercuryCredentials) Validate() bool { - return mc.URL != "" && mc.ClientID != "" && mc.ClientKey != "" -} - -type MercuryData struct { - Index int - Error error - Retryable bool - Bytes [][]byte - State PipelineExecutionState -} - -// MercuryV02Response represents a JSON structure used by Mercury v0.2 -type MercuryV02Response struct { - ChainlinkBlob string `json:"chainlinkBlob"` -} - -// MercuryV03Response represents a JSON structure used by Mercury v0.3 -type MercuryV03Response struct { - Reports []MercuryV03Report `json:"reports"` -} - -type MercuryV03Report struct { - FeedID string `json:"feedID"` // feed id in hex encoded - ValidFromTimestamp uint32 `json:"validFromTimestamp"` - ObservationsTimestamp uint32 `json:"observationsTimestamp"` - FullReport string `json:"fullReport"` // the actual hex encoded mercury report of this feed, can be sent to verifier -} - -const ( - // DefaultAllowListExpiration decides how long an upkeep's allow list info will be valid for. - DefaultAllowListExpiration = 20 * time.Minute - // CleanupInterval decides when the expired items in cache will be deleted. - CleanupInterval = 25 * time.Minute -) - -const ( - ApplicationJson = "application/json" - BlockNumber = "blockNumber" // valid for v0.2 - FeedIDs = "feedIDs" // valid for v0.3 - FeedIdHex = "feedIdHex" // valid for v0.2 - HeaderAuthorization = "Authorization" - HeaderContentType = "Content-Type" - HeaderTimestamp = "X-Authorization-Timestamp" - HeaderSignature = "X-Authorization-Signature-SHA256" - HeaderUpkeepId = "X-Authorization-Upkeep-Id" - MercuryPathV2 = "/client?" // only used to access mercury v0.2 server - MercuryBatchPathV3 = "/api/v1/reports/bulk?" // only used to access mercury v0.3 server - RetryDelay = 500 * time.Millisecond - Timestamp = "timestamp" // valid for v0.3 - TotalAttempt = 3 - UserId = "userId" -) - -type UpkeepFailureReason uint8 -type PipelineExecutionState uint8 - -const ( - // upkeep failure onchain reasons - UpkeepFailureReasonNone UpkeepFailureReason = 0 - UpkeepFailureReasonUpkeepCancelled UpkeepFailureReason = 1 - UpkeepFailureReasonUpkeepPaused UpkeepFailureReason = 2 - UpkeepFailureReasonTargetCheckReverted UpkeepFailureReason = 3 - UpkeepFailureReasonUpkeepNotNeeded UpkeepFailureReason = 4 - UpkeepFailureReasonPerformDataExceedsLimit UpkeepFailureReason = 5 - UpkeepFailureReasonInsufficientBalance UpkeepFailureReason = 6 - UpkeepFailureReasonMercuryCallbackReverted UpkeepFailureReason = 7 - UpkeepFailureReasonRevertDataExceedsLimit UpkeepFailureReason = 8 - UpkeepFailureReasonRegistryPaused UpkeepFailureReason = 9 - // leaving a gap here for more onchain failure reasons in the future - // upkeep failure offchain reasons - UpkeepFailureReasonMercuryAccessNotAllowed UpkeepFailureReason = 32 - UpkeepFailureReasonTxHashNoLongerExists UpkeepFailureReason = 33 - UpkeepFailureReasonInvalidRevertDataInput UpkeepFailureReason = 34 - UpkeepFailureReasonSimulationFailed UpkeepFailureReason = 35 - UpkeepFailureReasonTxHashReorged UpkeepFailureReason = 36 - - // pipeline execution error - NoPipelineError PipelineExecutionState = 0 - CheckBlockTooOld PipelineExecutionState = 1 - CheckBlockInvalid PipelineExecutionState = 2 - RpcFlakyFailure PipelineExecutionState = 3 - MercuryFlakyFailure PipelineExecutionState = 4 - PackUnpackDecodeFailed PipelineExecutionState = 5 - MercuryUnmarshalError PipelineExecutionState = 6 - InvalidMercuryRequest PipelineExecutionState = 7 - InvalidMercuryResponse PipelineExecutionState = 8 // this will only happen if Mercury server sends bad responses - UpkeepNotAuthorized PipelineExecutionState = 9 -) - -// UpkeepPrivilegeConfig represents the administrative offchain config for each upkeep. It can be set by s_upkeepPrivilegeManager -// role on the registry. Upkeeps allowed to use Mercury server will have this set to true. -type UpkeepPrivilegeConfig struct { - MercuryEnabled bool `json:"mercuryEnabled"` -} - -// generateHMAC calculates a user HMAC for Mercury server authentication. -func (mlh *MercuryLookupHandler) generateHMAC(method string, path string, body []byte, clientId string, secret string, ts int64) string { - bodyHash := sha256.New() - bodyHash.Write(body) - hashString := fmt.Sprintf("%s %s %s %s %d", - method, - path, - hex.EncodeToString(bodyHash.Sum(nil)), - clientId, - ts) - signedMessage := hmac.New(sha256.New, []byte(secret)) - signedMessage.Write([]byte(hashString)) - userHmac := hex.EncodeToString(signedMessage.Sum(nil)) - return userHmac -} - -// singleFeedRequest sends a v0.2 Mercury request for a single feed report. -func (mlh *MercuryLookupHandler) singleFeedRequest(ctx context.Context, ch chan<- MercuryData, index int, ml *StreamsLookup) { - q := url.Values{ - ml.feedParamKey: {ml.feeds[index]}, - ml.timeParamKey: {ml.time.String()}, - } - mercuryURL := mlh.credentials.LegacyURL - reqUrl := fmt.Sprintf("%s%s%s", mercuryURL, MercuryPathV2, q.Encode()) - // mlh.logger.Debugf("request URL for upkeep %s feed %s: %s", ml.upkeepId.String(), ml.feeds[index], reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: index, Error: err, Retryable: false, State: InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := mlh.generateHMAC(http.MethodGet, MercuryPathV2+q.Encode(), []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) - req.Header.Set(HeaderContentType, ApplicationJson) - req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) - req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) - req.Header.Set(HeaderSignature, signature) - - // in the case of multiple retries here, use the last attempt's data - state := NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := mlh.httpClient.Do(req) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup GET request failed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) - retryable = true - state = MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - // mlh.logger.Errorf("Encountered error when closing the body of the response in single feed: %s", err) - } - }(resp.Body) - - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = InvalidMercuryResponse - return err1 - } - - if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError { - // mlh.logger.Errorw("StreamsLookup received retryable status code", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "statusCode", resp.StatusCode, "feed", ml.feeds[index]) - retryable = true - state = MercuryFlakyFailure - return errors.New(strconv.FormatInt(int64(resp.StatusCode), 10)) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("StreamsLookup upkeep %s block %s received status code %d for feed %s", ml.upkeepId.String(), ml.time.String(), resp.StatusCode, ml.feeds[index]) - } - - // mlh.logger.Debugf("at block %s upkeep %s received status code %d from mercury v0.2 with BODY=%s", ml.time.String(), ml.upkeepId.String(), resp.StatusCode, hexutil.Encode(body)) - - var m MercuryV02Response - err1 = json.Unmarshal(body, &m) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) - retryable = false - state = MercuryUnmarshalError - return err1 - } - blobBytes, err1 := hexutil.Decode(m.ChainlinkBlob) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to decode chainlinkBlob for feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "blob", m.ChainlinkBlob, "feed", ml.feeds[index], "error", err1) - retryable = false - state = InvalidMercuryResponse - return err1 - } - ch <- MercuryData{ - Index: index, - Bytes: [][]byte{blobBytes}, - Retryable: false, - State: NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found or 500 Internal Server Error - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) - }), - retry.Context(ctx), - retry.Delay(RetryDelay), - retry.Attempts(TotalAttempt)) - - if !sent { - md := MercuryData{ - Index: index, - Bytes: [][]byte{}, - Retryable: retryable, - Error: fmt.Errorf("failed to request feed for %s: %w", ml.feeds[index], retryErr), - State: state, - } - ch <- md - } -} - -// multiFeedsRequest sends a Mercury v0.3 request for a multi-feed report -func (mlh *MercuryLookupHandler) multiFeedsRequest(ctx context.Context, ch chan<- MercuryData, ml *StreamsLookup) { - params := fmt.Sprintf("%s=%s&%s=%s", FeedIDs, strings.Join(ml.feeds, ","), Timestamp, ml.time.String()) - reqUrl := fmt.Sprintf("%s%s%s", mlh.credentials.URL, MercuryBatchPathV3, params) - // mlh.logger.Debugf("request URL for upkeep %s userId %s: %s", ml.upkeepId.String(), mlh.credentials.ClientID, reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: 0, Error: err, Retryable: false, State: InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := mlh.generateHMAC(http.MethodGet, MercuryBatchPathV3+params, []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) - req.Header.Set(HeaderContentType, ApplicationJson) - // username here is often referred to as user id - req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) - req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) - req.Header.Set(HeaderSignature, signature) - // mercury will inspect authorization headers above to make sure this user (in automation's context, this node) is eligible to access mercury - // and if it has an automation role. it will then look at this upkeep id to check if it has access to all the requested feeds. - req.Header.Set(HeaderUpkeepId, ml.upkeepId.String()) - - // in the case of multiple retries here, use the last attempt's data - state := NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := mlh.httpClient.Do(req) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup GET request fails for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) - retryable = true - state = MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - // mlh.logger.Errorf("Encountered error when closing the body of the response in the multi feed: %s", err) - } - }(resp.Body) - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = InvalidMercuryResponse - return err1 - } - - // mlh.logger.Infof("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - if resp.StatusCode == http.StatusUnauthorized { - retryable = false - state = UpkeepNotAuthorized - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by unauthorized upkeep", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusBadRequest { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by invalid format of timestamp", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusInternalServerError { - retryable = true - state = MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusInternalServerError) - } else if resp.StatusCode == 420 { - // in 0.3, this will happen when missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } - - var response MercuryV03Response - err1 = json.Unmarshal(body, &response) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) - retryable = false - state = MercuryUnmarshalError - return err1 - } - // in v0.3, if some feeds are not available, the server will only return available feeds, but we need to make sure ALL feeds are retrieved before calling user contract - // hence, retry in this case. retry will help when we send a very new timestamp and reports are not yet generated - if len(response.Reports) != len(ml.feeds) { - // TODO: AUTO-5044: calculate what reports are missing and log a warning - retryable = true - state = MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusNotFound) - } - var reportBytes [][]byte - for _, rsp := range response.Reports { - b, err := hexutil.Decode(rsp.FullReport) - if err != nil { - retryable = false - state = InvalidMercuryResponse - return err - } - reportBytes = append(reportBytes, b) - } - ch <- MercuryData{ - Index: 0, - Bytes: reportBytes, - Retryable: false, - State: NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found or 500 Internal Server Error - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) - }), - retry.Context(ctx), - retry.Delay(RetryDelay), - retry.Attempts(TotalAttempt)) - - if !sent { - md := MercuryData{ - Index: 0, - Bytes: [][]byte{}, - Retryable: retryable, - Error: retryErr, - State: state, - } - ch <- md - } -} - -// doMercuryRequest sends requests to Mercury API to retrieve ChainlinkBlob. -func (mlh *MercuryLookupHandler) doMercuryRequest(ctx context.Context, ml *StreamsLookup) (PipelineExecutionState, UpkeepFailureReason, [][]byte, bool, error) { - var isMercuryV03 bool - resultLen := len(ml.feeds) - ch := make(chan MercuryData, resultLen) - if len(ml.feeds) == 0 { - return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) - } - if ml.feedParamKey == FeedIdHex && ml.timeParamKey == BlockNumber { - // only v0.2 - for i := range ml.feeds { - go mlh.singleFeedRequest(ctx, ch, i, ml) - } - } else if ml.feedParamKey == FeedIDs && ml.timeParamKey == Timestamp { - // only v0.3 - resultLen = 1 - isMercuryV03 = true - ch = make(chan MercuryData, resultLen) - go mlh.multiFeedsRequest(ctx, ch, ml) - } else { - return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) - } - - var reqErr error - results := make([][]byte, len(ml.feeds)) - retryable := true - allSuccess := true - // in v0.2, use the last execution error as the state, if no execution errors, state will be no error - state := NoPipelineError - for i := 0; i < resultLen; i++ { - m := <-ch - if m.Error != nil { - if reqErr == nil { - reqErr = errors.New(m.Error.Error()) - } else { - reqErr = errors.New(reqErr.Error() + m.Error.Error()) - } - retryable = retryable && m.Retryable - allSuccess = false - if m.State != NoPipelineError { - state = m.State - } - continue - } - if isMercuryV03 { - results = m.Bytes - } else { - results[m.Index] = m.Bytes[0] - } - } - // only retry when not all successful AND none are not retryable - return state, UpkeepFailureReasonNone, results, retryable && !allSuccess, reqErr -} - -// decodeStreamsLookup decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string timeParamKey, uint256 time, byte[] extraData) -// func (mlh *MercuryLookupHandler) decodeStreamsLookup(data []byte) (*StreamsLookup, error) { -// e := mlh.mercuryConfig.Abi.Errors["StreamsLookup"] -// unpack, err := e.Unpack(data) -// if err != nil { -// return nil, fmt.Errorf("unpack error: %w", err) -// } -// errorParameters := unpack.([]interface{}) - -// return &StreamsLookup{ -// feedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), -// feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), -// timeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), -// time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), -// extraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), -// }, nil -// } - -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if -// this upkeep is allowed to use Mercury service. -// func (mlh *MercuryLookupHandler) allowedToUseMercury(upkeep models.Upkeep) (bool, error) { -// allowed, ok := mlh.mercuryConfig.AllowListCache.Get(upkeep.Admin.Hex()) -// if ok { -// return allowed.(bool), nil -// } - -// if upkeep.UpkeepPrivilegeConfig == nil { -// return false, fmt.Errorf("the upkeep privilege config was not retrieved for upkeep with ID %s", upkeep.UpkeepID) -// } - -// if len(upkeep.UpkeepPrivilegeConfig) == 0 { -// return false, fmt.Errorf("the upkeep privilege config is empty") -// } - -// var a UpkeepPrivilegeConfig -// err := json.Unmarshal(upkeep.UpkeepPrivilegeConfig, &a) -// if err != nil { -// return false, fmt.Errorf("failed to unmarshal privilege config for upkeep ID %s: %v", upkeep.UpkeepID, err) -// } - -// mlh.mercuryConfig.AllowListCache.Set(upkeep.Admin.Hex(), a.MercuryEnabled, cache.DefaultExpiration) -// return a.MercuryEnabled, nil -// } - -func (mlh *MercuryLookupHandler) CheckCallback(ctx context.Context, values [][]byte, lookup *StreamsLookup, registryABI ethabi.ABI, registryAddress common.Address) (hexutil.Bytes, error) { - payload, err := registryABI.Pack("checkCallback", lookup.upkeepId, values, lookup.extraData) - if err != nil { - return nil, err - } - - var theBytes hexutil.Bytes - args := map[string]interface{}{ - "to": registryAddress.Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - err = mlh.rpcClient.CallContext(ctx, &theBytes, "eth_call", args, hexutil.EncodeUint64(lookup.block)) - if err != nil { - return nil, err - } - return theBytes, nil -} diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 6a126d81aeb..32dfd6a5818 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -225,6 +225,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pressly/goose/v3 v3.16.0 // indirect From b3765034e25b8984e844db54dbc9c0bbf01243b5 Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 22 Mar 2024 15:45:44 +0000 Subject: [PATCH 231/234] fix integration-tests package --- integration-tests/actions/ocr2_helpers.go | 4 -- integration-tests/benchmark/keeper_test.go | 1 + integration-tests/config/config.go | 4 -- integration-tests/smoke/ocr2_test.go | 77 ---------------------- 4 files changed, 1 insertion(+), 85 deletions(-) diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index 8c7af76866a..829d85a8498 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -30,10 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) // DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 8997289a7eb..12a7e4c75aa 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -38,6 +38,7 @@ Enabled = true [P2P] [P2P.V2] +Enabled = true AnnounceAddresses = ["0.0.0.0:6690"] ListenAddresses = ["0.0.0.0:6690"] [Keeper] diff --git a/integration-tests/config/config.go b/integration-tests/config/config.go index 5065198b5a6..6d00ceb32bc 100644 --- a/integration-tests/config/config.go +++ b/integration-tests/config/config.go @@ -4,10 +4,6 @@ var ( BaseOCR1Config = `[OCR] Enabled = true -[P2P] -[P2P.V2] -Enabled = false - [P2P] [P2P.V2] AnnounceAddresses = ["0.0.0.0:6690"] diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 959ea412e11..6d3cf796cea 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -211,83 +211,6 @@ func TestOCRv2Request(t *testing.T) { } } -// Tests that just calling requestNewRound() will properly induce more rounds -func TestOCRv2Request(t *testing.T) { - t.Parallel() - l := logging.GetTestLogger(t) - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestLogger(t). - WithGeth(). - WithMockAdapter(). - WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), - node.WithOCR2(), - node.WithP2Pv2(), - node.WithTracing(), - )). - WithCLNodes(6). - WithFunding(big.NewFloat(.1)). - WithStandardCleanup(). - Build() - require.NoError(t, err) - - env.ParallelTransactions(true) - - nodeClients := env.ClCluster.NodeAPIs() - bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - // Gather transmitters - var transmitters []string - for _, node := range workerNodes { - addr, err := node.PrimaryEthAddress() - if err != nil { - require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) - } - transmitters = append(transmitters, addr) - } - - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) - require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) - require.NoError(t, err, "Error creating OCRv2 jobs") - - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") - - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error starting new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 5 but got %d", - roundData.Answer.Int64(), - ) - - // Keep the mockserver value the same and continually request new rounds - for round := 2; round <= 4; round++ { - err = actions.StartNewOCR2Round(int64(round), aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error starting new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(int64(round))) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected round %d answer from OCR contract to be 5 but got %d", - round, - roundData.Answer.Int64(), - ) - } -} - func TestOCRv2JobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) From 87352fb0a67859d18323ece97b59a6a88736fd0d Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 22 Mar 2024 15:50:20 +0000 Subject: [PATCH 232/234] Delete core/chains/evm/config/v2/ to be consistent with 2.9.1 --- .../evm/config/v2/defaults/Base_Goerli.toml | 29 ------------------- .../evm/config/v2/defaults/Base_Mainnet.toml | 29 ------------------- 2 files changed, 58 deletions(-) delete mode 100644 core/chains/evm/config/v2/defaults/Base_Goerli.toml delete mode 100644 core/chains/evm/config/v2/defaults/Base_Mainnet.toml diff --git a/core/chains/evm/config/v2/defaults/Base_Goerli.toml b/core/chains/evm/config/v2/defaults/Base_Goerli.toml deleted file mode 100644 index 5ecfd036f46..00000000000 --- a/core/chains/evm/config/v2/defaults/Base_Goerli.toml +++ /dev/null @@ -1,29 +0,0 @@ -ChainID = '84531' -ChainType = 'optimismBedrock' -FinalityDepth = 200 -LogPollInterval = '2s' -NoNewHeadsThreshold = '40s' -MinIncomingConfirmations = 1 - -[GasEstimator] -EIP1559DynamicFees = true -PriceMin = '1 wei' -BumpMin = '100 wei' - -[GasEstimator.BlockHistory] -BlockHistorySize = 60 - -[Transactions] -ResendAfterThreshold = '30s' - -[HeadTracker] -HistoryDepth = 300 - -[NodePool] -SyncThreshold = 10 - -[OCR] -ContractConfirmations = 1 - -[OCR2.Automation] -GasLimit = 6500000 diff --git a/core/chains/evm/config/v2/defaults/Base_Mainnet.toml b/core/chains/evm/config/v2/defaults/Base_Mainnet.toml deleted file mode 100644 index 314c12f8c54..00000000000 --- a/core/chains/evm/config/v2/defaults/Base_Mainnet.toml +++ /dev/null @@ -1,29 +0,0 @@ -ChainID = '8453' -ChainType = 'optimismBedrock' -FinalityDepth = 200 -LogPollInterval = '2s' -NoNewHeadsThreshold = '40s' -MinIncomingConfirmations = 1 - -[GasEstimator] -EIP1559DynamicFees = true -PriceMin = '1 wei' -BumpMin = '100 wei' - -[GasEstimator.BlockHistory] -BlockHistorySize = 24 - -[Transactions] -ResendAfterThreshold = '30s' - -[HeadTracker] -HistoryDepth = 300 - -[NodePool] -SyncThreshold = 10 - -[OCR] -ContractConfirmations = 1 - -[OCR2.Automation] -GasLimit = 6500000 From 7baea10a9af8107dcaf188035cfd4df0d4f3bcbb Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 22 Mar 2024 15:52:04 +0000 Subject: [PATCH 233/234] fix .github/workflows/integration-tests.yml to 2.9.1 version --- .github/workflows/integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 08ecd670a4d..d224b810874 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -980,7 +980,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana - ref: a28100b7f2954604a8ca2ff9ec7bccc6ec952953 # temporarily using specific commit for release branch ${{ needs.get_solana_sha.outputs.sha }} + ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Run Setup if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 From 1919c6054ea5965e2d70dd1bf069cf5355128c03 Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 22 Mar 2024 15:54:15 +0000 Subject: [PATCH 234/234] fix changelog to 2.9.1 --- docs/CHANGELOG.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 19e50b87a23..e71aac01f45 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -148,7 +148,6 @@ Starting in `v2.9.0`: - Added new configuration field named `LeaseDuration` for `EVM.NodePool` that will periodically check if internal subscriptions are connected to the "best" (as defined by the `SelectionMode`) node and switch to it if necessary. Setting this value to `0s` will disable this feature. - Added multichain telemetry support. Each network/chainID pair must be configured using the new fields: - ```toml [[TelemetryIngress.Endpoints]] Network = '...' # e.g. EVM. Solana, Starknet, Cosmos @@ -156,9 +155,7 @@ ChainID = '...' # e.g. 1, 5, devnet, mainnet-beta URL = '...' ServerPubKey = '...' ``` - These will eventually replace `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey`. Setting `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` alongside `[[TelemetryIngress.Endpoints]]` will prevent the node from booting. Only one way of configuring telemetry endpoints is supported. - - Added bridge_name label to `pipeline_tasks_total_finished` prometheus metric. This should make it easier to see directly what bridge was failing out from the CL NODE perspective. - LogPoller will now use finality tags to dynamically determine finality on evm chains if `EVM.FinalityTagEnabled=true`, rather than the fixed `EVM.FinalityDepth` specified in toml config @@ -207,9 +204,7 @@ All nodes will have to remove the following configuration field: `ExplorerURL` - Fixed a bug where `evmChainId` is requested instead of `id` or `evm-chain-id` in CLI error verbatim - Fixed a bug that would cause the node to shut down while performing backup - Fixed health checker to include more services in the prometheus `health` metric and HTTP `/health` endpoint - <<<<<<< HEAD -- # Fixed a bug where prices would not be parsed correctly in telemetry data - > > > > > > > master +- Fixed a bug where prices would not be parsed correctly in telemetry data ## 2.5.0 - 2023-09-13

    4J0+-})x>8(0F4nWpm;kMao0?N&tHjv*gn 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